Skip to content

Commit 0405b68

Browse files
committed
generate diff layer by replaying block
1 parent 4905aab commit 0405b68

File tree

3 files changed

+252
-0
lines changed

3 files changed

+252
-0
lines changed

core/blockchain.go

+60
Original file line numberDiff line numberDiff line change
@@ -3190,6 +3190,66 @@ func (bc *BlockChain) GetTrustedDiffLayer(blockHash common.Hash) *types.DiffLaye
31903190
return diff
31913191
}
31923192

3193+
// GenerateDiffLayer generates DiffLayer of a specified block by replaying the block's transactions.
3194+
// If the block is an empty block, no DiffLayer will be generated.
3195+
// The generated DiffLayer whose Receipts are empty, whose DiffAccounts' storage root is empty.
3196+
func (bc *BlockChain) GenerateDiffLayer(blockHash common.Hash) (*types.DiffLayer, error) {
3197+
if bc.snaps == nil {
3198+
return nil, fmt.Errorf("snapshot disabled, can't generate difflayer")
3199+
}
3200+
3201+
block := bc.GetBlockByHash(blockHash)
3202+
if block == nil {
3203+
return nil, fmt.Errorf("block not found, block number: %d, blockhash: %v", block.NumberU64(), blockHash)
3204+
}
3205+
3206+
parent := bc.GetBlockByHash(block.ParentHash())
3207+
if parent == nil {
3208+
return nil, fmt.Errorf("block not found, block number: %d, blockhash: %v", block.NumberU64()-1, block.ParentHash())
3209+
}
3210+
statedb, err := bc.StateAt(parent.Root())
3211+
if err != nil {
3212+
return nil, fmt.Errorf("state not found for block number (%d): %v", parent.NumberU64(), err)
3213+
}
3214+
3215+
// Empty block, no DiffLayer would be generated.
3216+
if block.Header().TxHash == types.EmptyRootHash {
3217+
return nil, nil
3218+
}
3219+
3220+
// Replay transactions.
3221+
signer := types.MakeSigner(bc.Config(), block.Number())
3222+
for _, tx := range block.Transactions() {
3223+
msg, _ := tx.AsMessage(signer)
3224+
txContext := NewEVMTxContext(msg)
3225+
context := NewEVMBlockContext(block.Header(), bc, nil)
3226+
vmenv := vm.NewEVM(context, txContext, statedb, bc.Config(), vm.Config{})
3227+
3228+
if posa, ok := bc.Engine().(consensus.PoSA); ok {
3229+
if isSystem, _ := posa.IsSystemTransaction(tx, block.Header()); isSystem {
3230+
balance := statedb.GetBalance(consensus.SystemAddress)
3231+
if balance.Cmp(common.Big0) > 0 {
3232+
statedb.SetBalance(consensus.SystemAddress, big.NewInt(0))
3233+
statedb.AddBalance(block.Header().Coinbase, balance)
3234+
}
3235+
}
3236+
}
3237+
3238+
if _, err := ApplyMessage(vmenv, msg, new(GasPool).AddGas(tx.Gas())); err != nil {
3239+
return nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
3240+
}
3241+
statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number()))
3242+
}
3243+
3244+
diffLayer := statedb.GenerateDiffLayer()
3245+
if diffLayer != nil {
3246+
diffLayer.BlockHash = blockHash
3247+
diffLayer.Number = block.NumberU64()
3248+
}
3249+
3250+
return diffLayer, nil
3251+
}
3252+
31933253
func GetTrustedDiffHash(d *types.DiffLayer) (common.Hash, error) {
31943254
diff := &types.ExtDiffLayer{
31953255
BlockHash: d.BlockHash,

core/blockchain_diff_test.go

+117
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ import (
2727
"testing"
2828
"time"
2929

30+
"github.com/ethereum/go-ethereum/consensus/clique"
31+
3032
"golang.org/x/crypto/sha3"
3133

3234
"github.com/ethereum/go-ethereum/common"
@@ -644,3 +646,118 @@ func TestGetRootByDiffHash(t *testing.T) {
644646
testGetRootByDiffHash(t, chain1, chain2, 24, types.StatusBlockNewer)
645647
testGetRootByDiffHash(t, chain1, chain2, 35, types.StatusBlockTooNew)
646648
}
649+
650+
func newBlockChainWithCliqueEngine(blocks int) *BlockChain {
651+
signer := types.HomesteadSigner{}
652+
db := rawdb.NewMemoryDatabase()
653+
engine := clique.New(params.AllCliqueProtocolChanges.Clique, db)
654+
genspec := &Genesis{
655+
//Config: params.TestChainConfig,
656+
ExtraData: make([]byte, 32+common.AddressLength+65),
657+
Alloc: GenesisAlloc{testAddr: {Balance: big.NewInt(100000000000000000)}},
658+
}
659+
copy(genspec.ExtraData[32:], testAddr[:])
660+
genesis := genspec.MustCommit(db)
661+
662+
chain, _ := NewBlockChain(db, nil, params.AllCliqueProtocolChanges, engine, vm.Config{}, nil, nil)
663+
generator := func(i int, block *BlockGen) {
664+
// The chain maker doesn't have access to a chain, so the difficulty will be
665+
// lets unset (nil). Set it here to the correct value.
666+
// block.SetCoinbase(testAddr)
667+
block.SetDifficulty(big.NewInt(2))
668+
669+
for idx, testBlock := range testBlocks {
670+
// Specific block setting, the index in this generator has 1 diff from specified blockNr.
671+
if i+1 == testBlock.blockNr {
672+
for _, testTransaction := range testBlock.txs {
673+
var transaction *types.Transaction
674+
if testTransaction.to == nil {
675+
transaction = types.NewContractCreation(block.TxNonce(testAddr),
676+
testTransaction.value, uint64(commonGas), nil, testTransaction.data)
677+
} else {
678+
transaction = types.NewTransaction(block.TxNonce(testAddr), *testTransaction.to,
679+
testTransaction.value, uint64(commonGas), nil, testTransaction.data)
680+
}
681+
tx, err := types.SignTx(transaction, signer, testKey)
682+
if err != nil {
683+
panic(err)
684+
}
685+
block.AddTxWithChain(chain, tx)
686+
}
687+
break
688+
}
689+
690+
// Default block setting.
691+
if idx == len(testBlocks)-1 {
692+
// We want to simulate an empty middle block, having the same state as the
693+
// first one. The last is needs a state change again to force a reorg.
694+
for _, testTransaction := range testBlocks[0].txs {
695+
tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddr), *testTransaction.to,
696+
testTransaction.value, uint64(commonGas), nil, testTransaction.data), signer, testKey)
697+
if err != nil {
698+
panic(err)
699+
}
700+
block.AddTxWithChain(chain, tx)
701+
}
702+
}
703+
}
704+
705+
}
706+
bs, _ := GenerateChain(params.AllCliqueProtocolChanges, genesis, engine, db, blocks, generator)
707+
for i, block := range bs {
708+
header := block.Header()
709+
if i > 0 {
710+
header.ParentHash = bs[i-1].Hash()
711+
}
712+
header.Extra = make([]byte, 32+65)
713+
header.Difficulty = big.NewInt(2)
714+
715+
sig, _ := crypto.Sign(clique.SealHash(header).Bytes(), testKey)
716+
copy(header.Extra[len(header.Extra)-65:], sig)
717+
bs[i] = block.WithSeal(header)
718+
}
719+
720+
if _, err := chain.InsertChain(bs); err != nil {
721+
panic(err)
722+
}
723+
724+
return chain
725+
}
726+
727+
func TestGenerateDiffLayer(t *testing.T) {
728+
blockNum := 32
729+
chain := newBlockChainWithCliqueEngine(blockNum)
730+
defer chain.Stop()
731+
732+
for blockNr := 1; blockNr <= blockNum; blockNr++ {
733+
block := chain.GetBlockByNumber(uint64(blockNr))
734+
if block == nil {
735+
t.Fatal("block should not be nil")
736+
}
737+
738+
expDiffLayer := chain.GetTrustedDiffLayer(block.Hash())
739+
if expDiffLayer == nil {
740+
// Skip empty block.
741+
if blockNr == 15 {
742+
continue
743+
}
744+
t.Fatalf("unexpected nil diff layer, block number: %v, block hash: %v", blockNr, block.Hash())
745+
}
746+
expDiffHash, err := GetTrustedDiffHash(expDiffLayer)
747+
if err != nil {
748+
t.Fatalf("compute diff hash failed: %v", err)
749+
}
750+
751+
diffLayer, err := chain.GenerateDiffLayer(block.Hash())
752+
if err != nil || diffLayer == nil {
753+
t.Fatalf("generate diff layer failed: %v", err)
754+
}
755+
diffHash, err := GetTrustedDiffHash(diffLayer)
756+
if err != nil {
757+
t.Fatalf("compute diff hash failed: %v", err)
758+
}
759+
if expDiffHash != diffHash {
760+
t.Fatalf("generated wrong diff layer for block: %d, expected hash: %v, real hash: %v", blockNr, expDiffHash, diffHash)
761+
}
762+
}
763+
}

core/state/statedb.go

+75
Original file line numberDiff line numberDiff line change
@@ -1534,6 +1534,81 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er
15341534
return root, diffLayer, nil
15351535
}
15361536

1537+
// GenerateDiffLayer generates block's DiffLayer after executing the block's txs.
1538+
// Attention, the DiffLayer returned include no Receipts, whose accounts' storage root
1539+
// is empty, whose BlockHash and Number field is empty, should further process by caller.
1540+
func (s *StateDB) GenerateDiffLayer() *types.DiffLayer {
1541+
if s.snap == nil {
1542+
return nil
1543+
}
1544+
1545+
for addr := range s.stateObjectsPending {
1546+
if obj := s.stateObjects[addr]; !obj.deleted {
1547+
// The snapshot storage map for the object
1548+
var storage map[string][]byte
1549+
obj.finalise(false)
1550+
for key, value := range obj.pendingStorage {
1551+
// Skip noop changes, persist actual changes
1552+
if value == obj.originStorage[key] {
1553+
continue
1554+
}
1555+
obj.originStorage[key] = value
1556+
1557+
var v []byte
1558+
if (value != common.Hash{}) {
1559+
// Encoding []byte cannot fail, ok to ignore the error.
1560+
v, _ = rlp.EncodeToBytes(common.TrimLeftZeroes(value[:]))
1561+
}
1562+
1563+
obj.db.snapMux.Lock()
1564+
if storage == nil {
1565+
// Retrieve the old storage map, if available, create a new one otherwise
1566+
if storage = obj.db.snapStorage[obj.address]; storage == nil {
1567+
storage = make(map[string][]byte)
1568+
obj.db.snapStorage[obj.address] = storage
1569+
}
1570+
}
1571+
storage[string(key[:])] = v // v will be nil if value is 0x00
1572+
obj.db.snapMux.Unlock()
1573+
}
1574+
1575+
if !obj.deleted {
1576+
s.snapMux.Lock()
1577+
// The storage root hasn't been intermediate, pass empty storage root here.
1578+
s.snapAccounts[obj.address] = snapshot.SlimAccountRLP(obj.data.Nonce, obj.data.Balance, common.Hash{}, obj.data.CodeHash)
1579+
s.snapMux.Unlock()
1580+
}
1581+
}
1582+
}
1583+
1584+
var diffLayer = &types.DiffLayer{}
1585+
for addr := range s.stateObjectsDirty {
1586+
if obj := s.stateObjects[addr]; !obj.deleted {
1587+
if obj.code != nil && obj.dirtyCode {
1588+
diffLayer.Codes = append(diffLayer.Codes, types.DiffCode{
1589+
Hash: common.BytesToHash(obj.CodeHash()),
1590+
Code: obj.code,
1591+
})
1592+
}
1593+
}
1594+
}
1595+
1596+
diffLayer.Destructs, diffLayer.Accounts, diffLayer.Storages = s.SnapToDiffLayer()
1597+
sort.SliceStable(diffLayer.Codes, func(i, j int) bool {
1598+
return diffLayer.Codes[i].Hash.Hex() < diffLayer.Codes[j].Hash.Hex()
1599+
})
1600+
sort.SliceStable(diffLayer.Destructs, func(i, j int) bool {
1601+
return diffLayer.Destructs[i].Hex() < (diffLayer.Destructs[j].Hex())
1602+
})
1603+
sort.SliceStable(diffLayer.Accounts, func(i, j int) bool {
1604+
return diffLayer.Accounts[i].Account.Hex() < diffLayer.Accounts[j].Account.Hex()
1605+
})
1606+
sort.SliceStable(diffLayer.Storages, func(i, j int) bool {
1607+
return diffLayer.Storages[i].Account.Hex() < diffLayer.Storages[j].Account.Hex()
1608+
})
1609+
return diffLayer
1610+
}
1611+
15371612
func (s *StateDB) DiffLayerToSnap(diffLayer *types.DiffLayer) (map[common.Address]struct{}, map[common.Address][]byte, map[common.Address]map[string][]byte, error) {
15381613
snapDestructs := make(map[common.Address]struct{})
15391614
snapAccounts := make(map[common.Address][]byte)

0 commit comments

Comments
 (0)