Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions chain33.para.toml
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,18 @@ genesisBlockTime=1514533390
genesis="14KEKbYtKKQm4wMthSK9J4La4nAiidGozt"
minerExecs=["paracross"] #配置挖矿合约

# 账户黑名单:命中地址禁止收发任何交易,支持base58与0x两种地址格式
# 名单按高度分版本演进,每个 [mver.blacklist.<分叉名>] 段是一份全量名单而非增量,
# 高度 h 生效的是「高度不大于 h 的最大分叉」那一份;下面的基线段在任何分叉生效前使用
# 启用时须在 [fork.system] 把 ForkAccountBlacklist 配成真实高度,并取消下面 V1 段注释
[mver.blacklist]
accountBlacklist=[]

#[mver.blacklist.ForkAccountBlacklist]
#accountBlacklist=["1xxx", "0xyyy"]
#[mver.blacklist.ForkAccountBlacklistV2]
#accountBlacklist=["1xxx", "0xyyy", "0xzzz"]

[mver.consensus]
fundKeyAddr = "1BQXS6TxaYYG5mADaWij4AxhZZUTpw95a5"
powLimitBits = "0x1f00ffff"
Expand Down
11 changes: 11 additions & 0 deletions chain33.toml
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,17 @@ genesis="14KEKbYtKKQm4wMthSK9J4La4nAiidGozt"
minerExecs=["ticket", "autonomy"]
enableBestBlockCmp=true

# 账户黑名单:命中地址禁止收发任何交易,支持base58与0x两种地址格式
# 名单按高度分版本演进,每个 [mver.blacklist.<分叉名>] 段是一份全量名单而非增量,
# 高度 h 生效的是「高度不大于 h 的最大分叉」那一份;下面的基线段在任何分叉生效前使用
# 启用时须在 [fork.system] 把 ForkAccountBlacklist 配成真实高度,并取消下面 V1 段注释
[mver.blacklist]
accountBlacklist=[]

#[mver.blacklist.ForkAccountBlacklist]
#accountBlacklist=["1xxx", "0xyyy"]
#[mver.blacklist.ForkAccountBlacklistV2]
#accountBlacklist=["1xxx", "0xyyy", "0xzzz"]

[mver.consensus]
fundKeyAddr = "1BQXS6TxaYYG5mADaWij4AxhZZUTpw95a5"
Expand Down
29 changes: 19 additions & 10 deletions docs/security/evm-account-blacklist.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,16 @@ chain33 在框架层引入 `ForkAccountBlacklist` 与 `types.CheckTxBlockedAccou

## 1. chain33 框架层覆盖了什么

三处入口最终都走 `types.checkTxBlockedAccountCore`(chain33 `types/account_blacklist.go`):
三处入口最终都走 `types.checkTxBlockedAccount`(chain33 `types/account_blacklist.go`)。
启动时按 `[mver.blacklist.*]` 与分叉高度打成只读快照,运行期用 `blacklistAt(height)` 取
「高度不大于 h 的最大版本」,**调用处不再写 `IsFork(height, ForkAccountBlacklist)`**。
分叉高度之前取到的是空名单,效果等同未启用。

| 层 | 位置 | fork 门控 |
| 层 | 位置 | 选版高度 |
|---|---|---|
| mempool 入口 | `system/mempool/check.go:76`、`eventprocess.go:216/339` | 无,随升级立即生效 |
| 出块 / 验块 | `system/consensus/base.go:606` `AddTxsToBlock` | |
| executor.checkTx | `executor/execenv.go:173` | 有 |
| mempool 入口 | `CheckTxBlockedAccountImmediate`(`mempool/check.go`、延时交易入口) | 即将打包的下一高度 `last+1` |
| 出块 / 验块 | `BaseClient.AddTxsToBlock` | 当前块高度 |
| executor.checkTx | `executor/execenv.go` `checkTx` / `checkTxGroup` | `e.height`(出块/回放=块高度;进池 EventCheckTx 目前仍是 last) |

判定四个维度:`tx.From()`、`tx.GetTo()`、`tx.GetRealToAddr()`、
EVM payload 中的 `ContractAddr` 与**恰好 20 字节**的 `Para`。
Expand All @@ -48,7 +51,7 @@ EVM payload 中的 `ContractAddr` 与**恰好 20 字节**的 `Para`。

框架只解析交易**信封**。合约跑起来之后内部 CALL 了谁、以谁的名义付款,
只存在于运行时栈和 calldata 里。以下每条路径在 `blacklist_gap_test.go` 都有对应用例,
用真实 coins 账户断言余额,并以 fork 关闭为对照证明路径确实是活的
用真实 coins 账户断言余额,并以名单尚未生效的高度为对照证明路径确实是活的

### B1. 黑名单合约被内部 CALL 唤醒后转出自身余额

Expand Down Expand Up @@ -151,8 +154,14 @@ chain33 的 `Para` 维度看不见,而 `BytesToAddress` 取后 20 字节仍能

在此之前,"纵深防御"只作为评审结论记录在源码注释与本文中,不在本 PR 内删除。

## 6. fork 门控
## 6. 按高度选版(不再单独 IsFork)

statedb / runtime 两层的检查都以 `cfg.IsFork(height, ForkAccountBlacklist)` 门控。
未到分叉高度时不得改变执行结果,否则与未升级节点分链(用例 `TestGap_ForkGateBlocksNothingBeforeHeight`)。
mempool 入口无门控是故意的:它是节点本地行为、不进状态计算,随二进制升级立即止血。
statedb / runtime / `checkEvmBlockedAccount` 都走 `cfg.IsBlockedAccount(addr, height)`,
与框架共用同一份快照。未到对应分叉高度时该版本名单为空,不得改变执行结果,
否则与未升级节点分链(用例 `TestGap_ForkGateBlocksNothingBeforeHeight` 用
`SetBlockedAccountsForTest(fromHeight, addrs)` 把名单推到未来高度来验证)。

不要在 EVM 侧再加一层 `IsFork(height, ForkAccountBlacklist)`:门控已经固化在快照的版本高度里,
重复门控会在名单演进(V2 空名单解除、V3 再拦)时和框架选版不一致。

mempool 入口按 `last+1` 选版是为了和下一块打包对齐;真正的网络级拦截点仍是共识在已到达高度上的判定。
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ toolchain go1.22.12
replace github.com/ava-labs/avalanchego => github.com/33cn/avalanchego v1.10.10-0.20240529041529-ada691598153

require (
github.com/33cn/chain33 v1.69.1-0.20260806044231-1edb5ac8d4fd
github.com/33cn/chain33 v1.71.0
github.com/BurntSushi/toml v1.3.2
github.com/NebulousLabs/Sia v1.3.7
github.com/bitly/go-simplejson v0.5.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBr
dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
github.com/33cn/chain33 v1.69.1-0.20260806044231-1edb5ac8d4fd h1:1W3IAqtTf9ILrH326JWYcdBFsLxNhsvWSGD6lOyVbZw=
github.com/33cn/chain33 v1.69.1-0.20260806044231-1edb5ac8d4fd/go.mod h1:QCdEczXYJI43WTeFuzEpEhNZ/SfEnLOdFaw6r6LvPz8=
github.com/33cn/chain33 v1.71.0 h1:n3XbgHU0vcCL/wBG3+KlnKX3bzXiZAvn5IC6c4lHQTo=
github.com/33cn/chain33 v1.71.0/go.mod h1:QCdEczXYJI43WTeFuzEpEhNZ/SfEnLOdFaw6r6LvPz8=
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M=
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
github.com/Azure/azure-sdk-for-go v41.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
Expand Down
13 changes: 13 additions & 0 deletions plugin/dapp/evm/cmd/ci/chain33.evm.test.toml
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,19 @@ genesisBlockTime=1514533394
genesis="0xd83b69C56834E85e023B1738E69BFA2F0dd52905"
hotkeyAddr="0xd83b69C56834E85e023B1738E69BFA2F0dd52905"
waitTxMs=10

# 账户黑名单:命中地址禁止收发任何交易,支持base58与0x两种地址格式
# 名单按高度分版本演进,每个 [mver.blacklist.<分叉名>] 段是一份全量名单而非增量,
# 高度 h 生效的是「高度不大于 h 的最大分叉」那一份;下面的基线段在任何分叉生效前使用
# 启用时须在 [fork.system] 把 ForkAccountBlacklist 配成真实高度,并取消下面 V1 段注释
[mver.blacklist]
accountBlacklist=[]

#[mver.blacklist.ForkAccountBlacklist]
#accountBlacklist=["1xxx", "0xyyy"]
#[mver.blacklist.ForkAccountBlacklistV2]
#accountBlacklist=["1xxx", "0xyyy", "0xzzz"]

[mver.consensus]
fundKeyAddr = "1BQXS6TxaYYG5mADaWij4AxhZZUTpw95a5"
powLimitBits="0x1f00ffff"
Expand Down
11 changes: 11 additions & 0 deletions plugin/dapp/evm/cmd/ci2/chain33.proxyminer.toml
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,17 @@ genesis="0xDe79A84DD3A16BB91044167075dE17a1CA4b1d6b"
minerExecs=["ticket", "autonomy"]
enableBestBlockCmp=true

# 账户黑名单:命中地址禁止收发任何交易,支持base58与0x两种地址格式
# 名单按高度分版本演进,每个 [mver.blacklist.<分叉名>] 段是一份全量名单而非增量,
# 高度 h 生效的是「高度不大于 h 的最大分叉」那一份;下面的基线段在任何分叉生效前使用
# 启用时须在 [fork.system] 把 ForkAccountBlacklist 配成真实高度,并取消下面 V1 段注释
[mver.blacklist]
accountBlacklist=[]

#[mver.blacklist.ForkAccountBlacklist]
#accountBlacklist=["1xxx", "0xyyy"]
#[mver.blacklist.ForkAccountBlacklistV2]
#accountBlacklist=["1xxx", "0xyyy", "0xzzz"]

[mver.consensus]
fundKeyAddr = "1BQXS6TxaYYG5mADaWij4AxhZZUTpw95a5"
Expand Down
2 changes: 1 addition & 1 deletion plugin/dapp/evm/executor/attack_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ func TestWBTYOverflowAttackIntegration(t *testing.T) {
// 使用 IsBlockedAccount 能识别的 ETH 地址形态(addrFromRole 返回 tx.From())。
attackerAddr := addrFromRole(cfg, roleAttacker)
accompliceAddr := addrFromRole(cfg, roleAccomplice)
restore := ctypes.SetBlockedAccountsForTest([]string{attackerAddr, accompliceAddr})
restore := cfg.SetBlockedAccountsForTest(0, []string{attackerAddr, accompliceAddr})
t.Cleanup(restore)

// 真实节点上的闸门在 chain33 框架层:executor.checkTx 在调用驱动 Exec 之前
Expand Down
9 changes: 5 additions & 4 deletions plugin/dapp/evm/executor/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -441,8 +441,8 @@ func getCaller(tx *types.Transaction) common.Address {
}

// checkEvmBlockedAccount 在 EVM 执行内部拦截命中黑名单的地址(发送方/接收方/合约地址)。
// 与 types.CheckTxBlockedAccount 共用同一黑名单,但按地址维度检查(此时地址已解析为字符串)。
// 这里走 fork 门控:仅在 ForkAccountBlacklist 高度后生效
// 与 types.CheckTxBlockedAccount 共用同一份按高度分版本的名单,但按地址维度检查(此时地址已解析为字符串)。
// 不需要额外的 fork 门控:名单版本已按高度选定,分叉高度之前取到的是空名单
// 返回 error 后由调用方按 ExecPack 语义处理(保持 revert + 扣费),不升级为 ExecErr。
//
// 分层说明(详见 docs/security/evm-account-blacklist.md):
Expand All @@ -453,14 +453,15 @@ func getCaller(tx *types.Transaction) common.Address {
// chain33 的 Para 维度看不见,而 BytesToAddress 取后 20 字节仍能解析出黑名单地址。
// 这些分支已随本分支在主网执行过,撤除需新开 fork 或先审计历史,不可直接删除。
func checkEvmBlockedAccount(cfg *types.Chain33Config, height int64, addrs ...string) error {
if cfg == nil || !cfg.IsFork(height, types.ForkAccountBlacklist) {
if cfg == nil {
return nil
}
for _, addr := range addrs {
if addr == "" {
continue
}
if types.IsBlockedAccount(addr) {
if cfg.IsBlockedAccount(addr, height) {
log.Error("checkEvmBlockedAccount hit", "height", height, "addr", addr)
return fmt.Errorf("%w: %s", types.ErrBlockedAccount, addr)
}
}
Expand Down
18 changes: 14 additions & 4 deletions plugin/dapp/evm/executor/vm/runtime/account_blacklist_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ const blockedRuntimeAddr = "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0"
func newBlockedEVM(t *testing.T, blockedAddrs []string) *EVM {
t.Helper()
cfg := types.NewChain33Config(types.GetDefaultCfgstring())
// local 标题下 SetAllFork(0),ForkAccountBlacklist 从高度 0 启用
restore := types.SetBlockedAccountsForTest(blockedAddrs)
// 名单自高度 0 起生效,覆盖下面 Context 的区块高度 1
restore := cfg.SetBlockedAccountsForTest(0, blockedAddrs)
t.Cleanup(restore)
ctx := Context{BlockNumber: big.NewInt(1)}
return NewEVM(ctx, &state.MemoryStateDB{}, Config{}, cfg)
Expand Down Expand Up @@ -53,11 +53,21 @@ func TestCheckBlockedAccount(t *testing.T) {

t.Run("empty blocklist pass", func(t *testing.T) {
cfg := types.NewChain33Config(types.GetDefaultCfgstring())
restore := types.SetBlockedAccountsForTest([]string{})
defer restore()
defer cfg.SetBlockedAccountsForTest(0, []string{})()
evm := NewEVM(Context{BlockNumber: big.NewInt(1)}, &state.MemoryStateDB{}, Config{}, cfg)
assert.NoError(t, checkBlockedAccount(evm, blocked, blocked))
})

// 名单自高度 H 起生效时,H 之前的区块必须放行,保证历史回放结果不变
t.Run("before fork height pass", func(t *testing.T) {
cfg := types.NewChain33Config(types.GetDefaultCfgstring())
defer cfg.SetBlockedAccountsForTest(100, []string{blockedRuntimeAddr})()
evm := NewEVM(Context{BlockNumber: big.NewInt(99)}, &state.MemoryStateDB{}, Config{}, cfg)
assert.NoError(t, checkBlockedAccount(evm, blocked, blocked))

evm = NewEVM(Context{BlockNumber: big.NewInt(100)}, &state.MemoryStateDB{}, Config{}, cfg)
assert.Error(t, checkBlockedAccount(evm, blocked, blocked))
})
}

// TestCallBlockedAccount 验证 EVM.Call 在黑名单地址下返回 error(触发上层 revert)
Expand Down
29 changes: 15 additions & 14 deletions plugin/dapp/evm/executor/vm/runtime/blacklist_gap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ package runtime
// 只能由 EVM 内部检查拦截的资产打出路径。每个用例都:
// 1. 用真实 MemoryStateDB + coins 账户,断言余额而不是只断言 error;
// 2. 先证明交易信封上的地址全部干净(chain33 放行),再证明 EVM 层拦下;
// 3. 以 fork 关闭作为对照,证明放行时资产确实会被打出,测试不是空转。
// 3. 以清空名单作为对照,证明放行时资产确实会被打出,测试不是空转。
//
// 场景编号对应 docs/security/evm-account-blacklist.md 的 B1-B4。

Expand Down Expand Up @@ -60,7 +60,7 @@ func newGapEnv(t *testing.T, blocked []string) *gapEnv {
mdb := state.NewMemoryStateDB(stateDB, localDB, coins, gapTestHeight, api)
mdb.Prepare(vmcommon.BytesToHash([]byte("blacklist-gap")), 0)

restore := ctypes.SetBlockedAccountsForTest(blocked)
restore := cfg.SetBlockedAccountsForTest(0, blocked)
t.Cleanup(restore)

env := &gapEnv{t: t, cfg: cfg, mdb: mdb, coins: coins}
Expand Down Expand Up @@ -95,7 +95,7 @@ func (e *gapEnv) deploy(addr, creator vmcommon.Address, code []byte) {
func (e *gapEnv) assertEnvelopeClean(addrs ...vmcommon.Address) {
e.t.Helper()
for _, a := range addrs {
require.False(e.t, ctypes.IsBlockedAccount(a.String()), "envelope address %s must be clean", a)
require.False(e.t, e.cfg.IsBlockedAccount(a.String(), gapTestHeight), "envelope address %s must be clean", a)
}
}

Expand Down Expand Up @@ -174,8 +174,8 @@ func TestGapB1_BlockedContractWokenByInnerCall(t *testing.T) {
require.Equal(t, stash, env.balance(blockedC), "blocked contract must keep its balance")
require.Zero(t, env.balance(user), "user must not receive blocked funds")

// 对照:fork 关闭时同一条链路会把钱打出去,证明上面的断言不是空转。
env.cfg.SetFork(ctypes.ForkAccountBlacklist, ctypes.MaxHeight)
// 对照:名单清空后同一条链路会把钱打出去,证明上面的断言不是空转。
env.cfg.SetBlockedAccountsForTest(0, []string{})
_, _, _, err = env.evm.Call(AccountRef(user), relay, nil, 5_000_000, 0)
require.NoError(t, err)
require.Zero(t, env.balance(blockedC), "with fork off the funds must leave (proves the path is live)")
Expand Down Expand Up @@ -211,14 +211,14 @@ func TestGapB2_TokenPrecompileThirdPartyFrom(t *testing.T) {
copy(calldata[4+12:], victim.Bytes())
copy(calldata[36+12:], receiver.Bytes())
calldata[len(calldata)-1] = 100
require.False(t, ctypes.IsBlockedAccountRaw(calldata), "chain33 Para check must be blind to 100-byte calldata")
require.False(t, env.cfg.IsBlockedAccountRaw(calldata, gapTestHeight), "chain33 Para check must be blind to 100-byte calldata")

ret, _, err := RunStateFulPrecompiledContract(env.evm, AccountRef(caller), CustomizePrecompiledContracts[precompile.ToHash160()], calldata, 100000)
require.ErrorIs(t, err, ctypes.ErrBlockedAccount, "ret=%s", ret)

// 检查一旦被移除,同一调用会落到 tokenStatus 的 ErrNotFound(本测试不注册 token),
// 不再是 ErrBlockedAccount —— 用它区分"被黑名单拦下"和"因别的原因失败"。
env.cfg.SetFork(ctypes.ForkAccountBlacklist, ctypes.MaxHeight)
env.cfg.SetBlockedAccountsForTest(0, []string{})
_, _, err = RunStateFulPrecompiledContract(env.evm, AccountRef(caller), CustomizePrecompiledContracts[precompile.ToHash160()], calldata, 100000)
require.Error(t, err)
require.NotErrorIs(t, err, ctypes.ErrBlockedAccount)
Expand Down Expand Up @@ -247,7 +247,7 @@ func TestGapB3_SelfdestructBeneficiary(t *testing.T) {
require.Equal(t, stash, env.balance(blockedC))
require.Zero(t, env.balance(user))

env.cfg.SetFork(ctypes.ForkAccountBlacklist, ctypes.MaxHeight)
env.cfg.SetBlockedAccountsForTest(0, []string{})
_, _, _, err = env.evm.Call(AccountRef(user), relay2, nil, 5_000_000, 0)
require.NoError(t, err)
require.Zero(t, env.balance(blockedC), "with fork off SELFDESTRUCT pays the beneficiary")
Expand Down Expand Up @@ -281,8 +281,8 @@ func TestGapB3b_DelegatecallSelfdestructDrainsCodeAddr(t *testing.T) {
require.Equal(t, stash, env.balance(blockedC), "delegatecall+selfdestruct must not drain the blocked code address")
require.Zero(t, env.balance(user))

// 对照:fork 关闭时 A 的余额确实经由 CodeAddr 被打出(proxy 已自毁,换 proxy2 触发)。
env.cfg.SetFork(ctypes.ForkAccountBlacklist, ctypes.MaxHeight)
// 对照:名单清空后 A 的余额确实经由 CodeAddr 被打出(proxy 已自毁,换 proxy2 触发)。
env.cfg.SetBlockedAccountsForTest(0, []string{})
_, _, _, err = env.evm.Call(AccountRef(user), proxy2, nil, 5_000_000, 0)
require.NoError(t, err)
require.Zero(t, env.balance(blockedC), "with fork off, opSuicide pays from CodeAddr (=A)")
Expand Down Expand Up @@ -317,7 +317,7 @@ func TestGapB4_BlockedContractInnerValueCall(t *testing.T) {
func TestGap_ForkGateBlocksNothingBeforeHeight(t *testing.T) {
user, blockedC := addr(0x16), addr(0xa6)
env := newGapEnv(t, []string{blockedC.String()})
env.cfg.SetFork(ctypes.ForkAccountBlacklist, gapTestHeight+1)
env.cfg.SetBlockedAccountsForTest(gapTestHeight+1, []string{blockedC.String()})

const stash = int64(100_000)
env.fund(blockedC, stash)
Expand All @@ -334,14 +334,15 @@ func TestGap_ForkGateBlocksNothingBeforeHeight(t *testing.T) {
// 由 innerExec 的 receiver 检查兜底。此处固定该行为,防止两侧被无意改成不一致。
func TestGap_ParaLengthAsymmetry(t *testing.T) {
blockedC := addr(0xa7)
restore := ctypes.SetBlockedAccountsForTest([]string{blockedC.String()})
cfg := ctypes.NewChain33Config(ctypes.GetDefaultCfgstring())
restore := cfg.SetBlockedAccountsForTest(0, []string{blockedC.String()})
t.Cleanup(restore)

padded := make([]byte, 32)
copy(padded[12:], blockedC.Bytes())

require.False(t, ctypes.IsBlockedAccountRaw(padded), "chain33 is blind to 32-byte padded Para")
require.False(t, cfg.IsBlockedAccountRaw(padded, 0), "chain33 is blind to 32-byte padded Para")
require.Equal(t, blockedC, vmcommon.BytesToAddress(padded), "EVM resolves the same bytes to the blocked address")
require.True(t, ctypes.IsBlockedAccount(vmcommon.BytesToAddress(padded).String()),
require.True(t, cfg.IsBlockedAccount(vmcommon.BytesToAddress(padded).String(), 0),
"so the EVM-side receiver check catches what chain33 misses")
}
10 changes: 6 additions & 4 deletions plugin/dapp/evm/executor/vm/runtime/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ import (
evmtypes "github.com/33cn/plugin/plugin/dapp/evm/types"
)

// checkBlockedAccount 合约内部调用/创建的黑名单拦截(fork 门控)
// checkBlockedAccount 合约内部调用/创建的黑名单拦截,按当前区块高度选取名单版本
// 命中返回包装后的 types.ErrBlockedAccount,由调用方返回 error 触发 RevertToSnapshot。
// 与 executor 层共用 types 黑名单,按 EVM 地址(0x 字符串)检查。
// 与 executor 层共用同一份按高度分版本的名单,按 EVM 地址(0x 字符串)检查。
//
// 这是 chain33 框架层看不见的一层:框架只解析交易信封(from/to/ContractAddr/Para),
// 合约运行后内部 CALL 了谁只存在于运行时栈上。Call 的 target 检查是阻止
Expand All @@ -35,11 +35,13 @@ import (
// 唯一例外(DELEGATECALL 到黑名单代码执行 SELFDESTRUCT)由 statedb.Transfer 兜底。
func checkBlockedAccount(evm *EVM, addrs ...common.Address) error {
cfg := evm.cfg
if cfg == nil || evm.BlockNumber == nil || !cfg.IsFork(evm.BlockNumber.Int64(), types.ForkAccountBlacklist) {
if cfg == nil || evm.BlockNumber == nil {
return nil
}
height := evm.BlockNumber.Int64()
for _, addr := range addrs {
if types.IsBlockedAccount(addr.String()) {
if cfg.IsBlockedAccount(addr.String(), height) {
log.Error("checkBlockedAccount hit", "height", height, "addr", addr.String())
return fmt.Errorf("%w: %s", types.ErrBlockedAccount, addr.String())
}
}
Expand Down
Loading
Loading