Skip to content

Commit

Permalink
Merge pull request bnb-chain#206 from andyzhang2023/fix/pevm-tstore-s…
Browse files Browse the repository at this point in the history
…elfdestruct6780-revert

bugfix: tstorage revert and selfdestruct6780 revert
  • Loading branch information
andyzhang2023 authored Oct 25, 2024
2 parents 3973f55 + 5ae6ffd commit d58b8e0
Show file tree
Hide file tree
Showing 5 changed files with 246 additions and 37 deletions.
25 changes: 14 additions & 11 deletions core/parallel_state_scheduler.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,22 @@ import (
)

var runner chan func()
var runnerOnce sync.Once

func initParallelRunner(targetNum int) {
if targetNum == 0 {
targetNum = runtime.GOMAXPROCS(0)
}
runner = make(chan func(), targetNum)
for i := 0; i < targetNum; i++ {
go func() {
for f := range runner {
f()
}
}()
}
runnerOnce.Do(func() {
if targetNum == 0 {
targetNum = runtime.GOMAXPROCS(0)
}
runner = make(chan func(), targetNum)
for i := 0; i < targetNum; i++ {
go func() {
for f := range runner {
f()
}
}()
}
})
}

func ParallelNum() int {
Expand Down
5 changes: 5 additions & 0 deletions core/parallel_state_scheduler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"io/ioutil"
"math/big"
"os"
"runtime"
"sync"
"sync/atomic"
"testing"
Expand Down Expand Up @@ -81,6 +82,10 @@ func (mt *mockTx) To() int {
return int(mt.req.value)
}

func init() {
initParallelRunner(runtime.GOMAXPROCS(0))
}

func (mt *mockTx) execute(req *PEVMTxRequest) *PEVMTxResult {
result := &PEVMTxResult{
txReq: req,
Expand Down
24 changes: 2 additions & 22 deletions core/state/pevm_journal.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,17 +188,7 @@ func newJSelfDestruct(obj *state) *jSelfDestruct {
}
return &jSelfDestruct{
addr: obj.addr,
obj: &state{
modified: obj.modified,
addr: obj.addr,
balance: new(uint256.Int).Set(obj.balance),
nonce: obj.nonce,
code: append([]byte(nil), obj.code...),
codeHash: append([]byte(nil), obj.codeHash...),
codeSize: obj.codeSize,
created: obj.created,
deleted: obj.deleted,
},
obj: obj.clone(),
}
}

Expand Down Expand Up @@ -292,14 +282,7 @@ func newJTransientStorage(addr common.Address, key, val common.Hash) *jTransient
}

func (j *jTransientStorage) revert(db *UncommittedDB) {
storage, ok := db.transientStorage[j.addr]
if !ok {
return
}
delete(storage, j.key)
if len(storage) == 0 {
delete(db.transientStorage, j.addr)
}
db.transientStorage.Set(j.addr, j.key, j.val)
}

type ujournal []ustate
Expand All @@ -309,9 +292,6 @@ func (j *ujournal) append(st ustate) {
}

func (j *ujournal) revertTo(db *UncommittedDB, snapshot int) {
if snapshot < 0 || snapshot >= len(*j) {
return // invalid snapshot index
}
for i := len(*j) - 1; i >= snapshot; i-- {
(*j)[i].revert(db)
}
Expand Down
12 changes: 9 additions & 3 deletions core/state/pevm_statedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ func (pst *UncommittedDB) Selfdestruct6780(addr common.Address) {
return
}
if obj.created {
pst.cache.selfDestruct(addr)
pst.SelfDestruct(addr)
}
}

Expand Down Expand Up @@ -316,7 +316,11 @@ func (pst *UncommittedDB) GetTransientState(addr common.Address, key common.Hash
return pst.transientStorage.Get(addr, key)
}
func (pst *UncommittedDB) SetTransientState(addr common.Address, key, value common.Hash) {
pst.journal.append(newJTransientStorage(addr, key, value))
prev := pst.transientStorage.Get(addr, key)
if prev == value {
return // no need to record the same value
}
pst.journal.append(newJTransientStorage(addr, key, prev))
pst.transientStorage.Set(addr, key, value)
}

Expand Down Expand Up @@ -345,8 +349,10 @@ func (pst *UncommittedDB) AddSlotToAccessList(addr common.Address, slot common.H

// ===============================================
// Snapshot Methods
// (is it necessary to do snapshot and revert ?)
func (pst *UncommittedDB) RevertToSnapshot(id int) {
if id < 0 || id > len(pst.journal) {
panic(fmt.Sprintf("invalid snapshot index, out of range, snapshot:%d, len:%d", id, len(pst.journal)))
}
pst.journal.revertTo(pst, id)
}
func (pst *UncommittedDB) Snapshot() int {
Expand Down
217 changes: 216 additions & 1 deletion core/state/pevm_statedb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -799,6 +799,218 @@ func TestPevmSelfDestruct(t *testing.T) {
}
}

func TestPevmSelfDestruct6780AndRevert(t *testing.T) {
shadow := newStateDB()
uncommitted := newUncommittedDB(newStateDB())
//prepare: create an account, set state, set balance
// A1{key1: val1, balance: 100, accesslist: 0x33}
prepare := Tx{
{"Create", Address1},
{"SetState", Address1, "key1", "val1"},
{"AddBalance", Address1, big.NewInt(100)},
{"AddSlots", Address1, common.Hash{0x33}},
}
prepareCheck := Checks{
{"balance", Address1, big.NewInt(100)},
{"state", Address1, "key1", "val1"},
{"slot", Address1, common.Hash{0x33}, true},
}
check := func(verify Checks, shadow, uncommitted vm.StateDB) {
if err := verify.Verify(shadow); err != nil {
t.Fatalf("maindb verify failed, err=%s", err.Error())
}
if err := verify.Verify(uncommitted); err != nil {
t.Fatalf("uncommitted verify failed, err=%s", err.Error())
}
}
prepare.Call(shadow)
prepare.Call(uncommitted)
check(prepareCheck, shadow, uncommitted)

// do the following operations, and then selfdestruct, and then revert
// 1. add balance 200,
// 2. set state key1: val2
// 3. add slot 0x34
// 4. selfdestruct
// 5. revert
case1 := Tx{
{"AddBalance", Address1, big.NewInt(200)},
{"SetState", Address1, "key1", "val2"},
{"SetState", Address1, "key2", "val0"},
{"AddSlots", Address1, common.Hash{0x34}},
}
case1SelfDestruct := Tx{
{"SelfDestruct6780", Address1},
}
beforeSelfDestruct := Checks{
{"balance", Address1, big.NewInt(300)},
{"state", Address1, "key1", "val2"},
{"state", Address1, "key2", "val0"},
{"slot", Address1, common.Hash{0x34}, true},
{"slot", Address1, common.Hash{0x33}, true},
}
beforeRevert := Checks{
{"balance", Address1, big.NewInt(0)},
{"state", Address1, "key1", "val2"},
{"state", Address1, "key2", "val0"},
{"slot", Address1, common.Hash{0x34}, true},
{"slot", Address1, common.Hash{0x33}, true},
}
afterRevert := Checks{
{"balance", Address1, big.NewInt(100)},
{"state", Address1, "key1", "val1"},
{"state", Address1, "key2", ""},
{"slot", Address1, common.Hash{0x34}, false},
{"slot", Address1, common.Hash{0x33}, true},
}
// run the case1 on shadow
snapshot := shadow.Snapshot()
case1.Call(shadow)
if err := beforeSelfDestruct.Verify(shadow); err != nil {
t.Fatalf("maindb ut failed, err:%s", err.Error())
}
case1SelfDestruct.Call(shadow)
if err := beforeRevert.Verify(shadow); err != nil {
t.Fatalf("maindb ut failed, err:%s", err.Error())
}
shadow.RevertToSnapshot(snapshot)
if err := afterRevert.Verify(shadow); err != nil {
t.Fatalf("maindb ut failed, err:%s", err.Error())
}

// now on uncommitted
snapshot = uncommitted.Snapshot()
case1.Call(uncommitted)
if err := beforeSelfDestruct.Verify(uncommitted); err != nil {
t.Fatalf("uncommitted ut failed, err:%s", err.Error())
}
case1SelfDestruct.Call(uncommitted)
if err := beforeRevert.Verify(uncommitted); err != nil {
t.Fatalf("uncommitted ut failed, err:%s", err.Error())
}
uncommitted.RevertToSnapshot(snapshot)
if err := afterRevert.Verify(uncommitted); err != nil {
t.Fatalf("uncommitted ut failed, err:%s", err.Error())
}

// now compare the mercle root of shadow and uncommitted
shadow.Finalise(true)
if err := uncommitted.Merge(true); err != nil {
t.Fatalf("ut failed, err:%s", err.Error())
}
uncommitted.Finalise(true)
if shadow.IntermediateRoot(true) != uncommitted.maindb.IntermediateRoot(true) {
t.Fatalf("ut failed, err: the mercle root of shadow and uncommitted are different")
}
}

func TestPevmSelfDestructAndRevert(t *testing.T) {
shadow := newStateDB()
uncommitted := newUncommittedDB(newStateDB())
//prepare: create an account, set state, set balance
// A1{key1: val1, balance: 100, accesslist: 0x33}
prepare := Tx{
{"Create", Address1},
{"SetState", Address1, "key1", "val1"},
{"AddBalance", Address1, big.NewInt(100)},
{"AddSlots", Address1, common.Hash{0x33}},
}
prepareCheck := Checks{
{"balance", Address1, big.NewInt(100)},
{"state", Address1, "key1", "val1"},
{"slot", Address1, common.Hash{0x33}, true},
}
check := func(verify Checks, shadow, uncommitted vm.StateDB) {
if err := verify.Verify(shadow); err != nil {
t.Fatalf("maindb verify failed, err=%s", err.Error())
}
if err := verify.Verify(uncommitted); err != nil {
t.Fatalf("uncommitted verify failed, err=%s", err.Error())
}
}
prepare.Call(shadow)
prepare.Call(uncommitted)
shadow.Finalise(true)
uncommitted.Finalise(true)
check(prepareCheck, shadow, uncommitted)

// do the following operations, and then selfdestruct, and then revert
// 1. add balance 200,
// 2. set state key1: val2
// 3. add slot 0x34
// 4. selfdestruct
// 5. revert
case1 := Tx{
{"AddBalance", Address1, big.NewInt(200)},
{"SetState", Address1, "key1", "val2"},
{"SetState", Address1, "key2", "val0"},
{"AddSlots", Address1, common.Hash{0x34}},
}
case1SelfDestruct := Tx{
{"SelfDestruct", Address1},
}
beforeSelfDestruct := Checks{
{"balance", Address1, big.NewInt(300)},
{"state", Address1, "key1", "val2"},
{"state", Address1, "key2", "val0"},
{"slot", Address1, common.Hash{0x34}, true},
{"slot", Address1, common.Hash{0x33}, true},
}
beforeRevert := Checks{
{"balance", Address1, big.NewInt(0)},
{"state", Address1, "key1", "val2"},
{"state", Address1, "key2", "val0"},
{"slot", Address1, common.Hash{0x34}, true},
{"slot", Address1, common.Hash{0x33}, true},
}
afterRevert := Checks{
{"balance", Address1, big.NewInt(100)},
{"state", Address1, "key1", "val1"},
{"state", Address1, "key2", ""},
{"slot", Address1, common.Hash{0x34}, false},
{"slot", Address1, common.Hash{0x33}, true},
}
// run the case1 on shadow
snapshot := shadow.Snapshot()
case1.Call(shadow)
if err := beforeSelfDestruct.Verify(shadow); err != nil {
t.Fatalf("maindb ut failed, err:%s", err.Error())
}
case1SelfDestruct.Call(shadow)
if err := beforeRevert.Verify(shadow); err != nil {
t.Fatalf("maindb ut failed, err:%s", err.Error())
}
shadow.RevertToSnapshot(snapshot)
if err := afterRevert.Verify(shadow); err != nil {
t.Fatalf("maindb ut failed, err:%s", err.Error())
}

// now on uncommitted
snapshot = uncommitted.Snapshot()
case1.Call(uncommitted)
if err := beforeSelfDestruct.Verify(uncommitted); err != nil {
t.Fatalf("uncommitted ut failed, err:%s", err.Error())
}
case1SelfDestruct.Call(uncommitted)
if err := beforeRevert.Verify(uncommitted); err != nil {
t.Fatalf("uncommitted ut failed, err:%s", err.Error())
}
uncommitted.RevertToSnapshot(snapshot)
if err := afterRevert.Verify(uncommitted); err != nil {
t.Fatalf("uncommitted ut failed, err:%s", err.Error())
}

// now compare the mercle root of shadow and uncommitted
shadow.Finalise(true)
if err := uncommitted.Merge(true); err != nil {
t.Fatalf("ut failed, err:%s", err.Error())
}
uncommitted.Finalise(true)
if shadow.IntermediateRoot(true) != uncommitted.maindb.IntermediateRoot(true) {
t.Fatalf("ut failed, err: the mercle root of shadow and uncommitted are different")
}
}

func TestPevmSelfDestruct6780(t *testing.T) {
// case 1. no previous state
txs := Txs{
Expand Down Expand Up @@ -2003,10 +2215,13 @@ func runConflictCase(prepare, txs1, txs2 Txs, checks []Check) error {
if err := txs2.Call(un2); err != nil {
return fmt.Errorf("failed to call txs2, err:%s", err.Error())
}
if err := un1.ConflictsToMaindb(); err != nil {
return fmt.Errorf("failed to check conflicts of un1, err:%s", err.Error())
}
if err := un1.Merge(true); err != nil {
return fmt.Errorf("failed to merge un1, err:%s", err.Error())
}
if err := un2.Merge(true); err == nil {
if err := un2.ConflictsToMaindb(); err == nil {
return fmt.Errorf("un2 merge is expected to be failed")
}
for _, c := range checks {
Expand Down

0 comments on commit d58b8e0

Please sign in to comment.