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
16 changes: 12 additions & 4 deletions core/tracing/journal.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,10 +155,18 @@ func (j *journal) OnBalanceChange(addr common.Address, prev, new *big.Int, reaso
}

func (j *journal) OnNonceChangeV2(addr common.Address, prev, new uint64, reason NonceChangeReason) {
// When a contract is created, the nonce of the creator is incremented.
// This change is not reverted when the creation fails.
if reason != NonceChangeContractCreator {
j.entries = append(j.entries, nonceChange{addr: addr, prev: prev, new: new})
j.entries = append(j.entries, nonceChange{addr: addr, prev: prev, new: new})
if reason == NonceChangeContractCreator {
// When a contract is created via CREATE/CREATE2, the creator's nonce is
// incremented. The EVM does not revert this when the CREATE frame itself
// fails (the nonce change happens before the EVM snapshot). However, if
// a parent frame reverts, the nonce must be reverted along with everything
// else.
//
// To achieve this, advance the current frame's revision point past this
// entry. The CREATE frame's revert won't touch it (it's below the revision),
// but a parent frame's revert will (it's above the parent's revision).
j.revisions[len(j.revisions)-1] = len(j.entries)
}
if j.hooks.OnNonceChangeV2 != nil {
j.hooks.OnNonceChangeV2(addr, prev, new, reason)
Expand Down
36 changes: 36 additions & 0 deletions core/tracing/journal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,42 @@ func TestNonceIncOnCreate(t *testing.T) {
}
}

// TestNonceIncOnCreateParentReverts checks that the creator's nonce increment
// from CREATE survives the CREATE frame's own revert but is properly reverted
// when the parent call frame reverts.
func TestNonceIncOnCreateParentReverts(t *testing.T) {
const opCREATE = 0xf0

tr := &testTracer{t: t}
wr, err := WrapWithJournal(&Hooks{OnNonceChange: tr.OnNonceChange})
if err != nil {
t.Fatalf("failed to wrap test tracer: %v", err)
}

addr := common.HexToAddress("0x1234")
{
// Parent call frame
wr.OnEnter(0, 0, addr, addr, nil, 1000, big.NewInt(0))
{
// CREATE frame — creator nonce incremented, then CREATE reverts
wr.OnEnter(1, opCREATE, addr, addr, nil, 1000, big.NewInt(0))
wr.OnNonceChangeV2(addr, 0, 1, NonceChangeContractCreator)
wr.OnExit(1, nil, 100, errors.New("revert"), true)
}
// After CREATE reverts, nonce should still be 1
if tr.nonce != 1 {
t.Fatalf("nonce after CREATE revert: got %v, want 1", tr.nonce)
}
// Parent frame also reverts
wr.OnExit(0, nil, 150, errors.New("revert"), true)
}

// After parent reverts, nonce should be back to 0
if tr.nonce != 0 {
t.Fatalf("nonce after parent revert: got %v, want 0", tr.nonce)
}
}

func TestOnNonceChangeV2(t *testing.T) {
tr := &testTracer{t: t}
wr, err := WrapWithJournal(&Hooks{OnNonceChangeV2: tr.OnNonceChangeV2})
Expand Down
Loading