Skip to content

Commit 9b1e316

Browse files
committed
collect and emit codehash=>code mappings for state objects
1 parent 7c0492a commit 9b1e316

File tree

4 files changed

+147
-53
lines changed

4 files changed

+147
-53
lines changed

statediff/builder.go

Lines changed: 77 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ var (
3636
nullHashBytes = common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000")
3737
emptyNode, _ = rlp.EncodeToBytes([]byte{})
3838
emptyContractRoot = crypto.Keccak256Hash(emptyNode)
39+
nullCodeHash = crypto.Keccak256Hash([]byte{}).Bytes()
3940
)
4041

4142
// Builder interface exposes the method for building a state diff between two blocks
@@ -62,19 +63,21 @@ func (sdb *builder) BuildStateTrieObject(current *types.Block) (StateObject, err
6263
return StateObject{}, fmt.Errorf("error creating trie for block %d: %v", current.Number(), err)
6364
}
6465
it := currentTrie.NodeIterator([]byte{})
65-
stateNodes, err := sdb.buildStateTrie(it)
66+
stateNodes, codeAndCodeHashes, err := sdb.buildStateTrie(it)
6667
if err != nil {
6768
return StateObject{}, fmt.Errorf("error collecting state nodes for block %d: %v", current.Number(), err)
6869
}
6970
return StateObject{
70-
BlockNumber: current.Number(),
71-
BlockHash: current.Hash(),
72-
Nodes: stateNodes,
71+
BlockNumber: current.Number(),
72+
BlockHash: current.Hash(),
73+
Nodes: stateNodes,
74+
CodeAndCodeHashes: codeAndCodeHashes,
7375
}, nil
7476
}
7577

76-
func (sdb *builder) buildStateTrie(it trie.NodeIterator) ([]StateNode, error) {
78+
func (sdb *builder) buildStateTrie(it trie.NodeIterator) ([]StateNode, []CodeAndCodeHash, error) {
7779
stateNodes := make([]StateNode, 0)
80+
codeAndCodeHashes := make([]CodeAndCodeHash, 0)
7881
for it.Next(true) {
7982
// skip value nodes
8083
if it.Leaf() {
@@ -87,48 +90,62 @@ func (sdb *builder) buildStateTrie(it trie.NodeIterator) ([]StateNode, error) {
8790
copy(nodePath, it.Path())
8891
node, err := sdb.stateCache.TrieDB().Node(it.Hash())
8992
if err != nil {
90-
return nil, err
93+
return nil, nil, err
9194
}
9295
var nodeElements []interface{}
9396
if err := rlp.DecodeBytes(node, &nodeElements); err != nil {
94-
return nil, err
97+
return nil, nil, err
9598
}
9699
ty, err := CheckKeyType(nodeElements)
97100
if err != nil {
98-
return nil, err
101+
return nil, nil, err
99102
}
100103
switch ty {
101104
case Leaf:
102105
var account state.Account
103106
if err := rlp.DecodeBytes(nodeElements[1].([]byte), &account); err != nil {
104-
return nil, fmt.Errorf("error decoding account for leaf node at path %x nerror: %v", nodePath, err)
107+
return nil, nil, fmt.Errorf("error decoding account for leaf node at path %x nerror: %v", nodePath, err)
105108
}
106109
partialPath := trie.CompactToHex(nodeElements[0].([]byte))
107110
valueNodePath := append(nodePath, partialPath...)
108111
encodedPath := trie.HexToCompact(valueNodePath)
109112
leafKey := encodedPath[1:]
110-
storageNodes, err := sdb.buildStorageNodesEventual(account.Root, nil, true)
111-
if err != nil {
112-
return nil, fmt.Errorf("failed building eventual storage diffs for account %+v\r\nerror: %v", account, err)
113+
node := StateNode{
114+
NodeType: ty,
115+
Path: nodePath,
116+
LeafKey: leafKey,
117+
NodeValue: node,
113118
}
114-
stateNodes = append(stateNodes, StateNode{
115-
NodeType: ty,
116-
Path: nodePath,
117-
LeafKey: leafKey,
118-
NodeValue: node,
119-
StorageNodes: storageNodes,
120-
})
119+
if !bytes.Equal(account.CodeHash, nullCodeHash) {
120+
storageNodes, err := sdb.buildStorageNodesEventual(account.Root, nil, true)
121+
if err != nil {
122+
return nil, nil, fmt.Errorf("failed building eventual storage diffs for account %+v\r\nerror: %v", account, err)
123+
}
124+
node.StorageNodes = storageNodes
125+
// emit codehash => code mappings for cod
126+
codeHash := common.BytesToHash(account.CodeHash)
127+
addrHash := common.BytesToHash(leafKey)
128+
code, err := sdb.stateCache.ContractCode(addrHash, codeHash)
129+
if err != nil {
130+
return nil, nil, fmt.Errorf("failed to retrieve code for codehash %s for account with leafkey %s\r\n error: %v", codeHash.String(), addrHash.String(), err)
131+
}
132+
codeAndCodeHashes = append(codeAndCodeHashes, CodeAndCodeHash{
133+
Hash: codeHash,
134+
Code: code,
135+
})
136+
}
137+
stateNodes = append(stateNodes, node)
121138
case Extension, Branch:
122139
stateNodes = append(stateNodes, StateNode{
123140
NodeType: ty,
124141
Path: nodePath,
125142
NodeValue: node,
126143
})
127144
default:
128-
return nil, fmt.Errorf("unexpected node type %s", ty)
145+
return nil, nil, fmt.Errorf("unexpected node type %s", ty)
129146
}
130147
}
131-
return stateNodes, it.Error()
148+
return stateNodes, codeAndCodeHashes, it.Error()
132149
}
133150

134151
// BuildStateDiffObject builds a statediff object from two blocks and the provided parameters
@@ -181,16 +198,17 @@ func (sdb *builder) buildStateDiffWithIntermediateStateNodes(args Args, params P
181198
return StateObject{}, fmt.Errorf("error building diff for updated accounts: %v", err)
182199
}
183200
// build the diff nodes for created accounts
184-
createdAccounts, err := sdb.buildAccountCreations(diffAccountsAtB, params.WatchedStorageSlots, params.IntermediateStorageNodes)
201+
createdAccounts, codeAndCodeHashes, err := sdb.buildAccountCreations(diffAccountsAtB, params.WatchedStorageSlots, params.IntermediateStorageNodes)
185202
if err != nil {
186203
return StateObject{}, fmt.Errorf("error building diff for created accounts: %v", err)
187204
}
188205

189206
// assemble all of the nodes into the statediff object, including the intermediate nodes
190207
return StateObject{
191-
BlockNumber: args.BlockNumber,
192-
BlockHash: args.BlockHash,
193-
Nodes: append(append(append(updatedAccounts, createdAccounts...), createdOrUpdatedIntermediateNodes...), emptiedPaths...),
208+
BlockNumber: args.BlockNumber,
209+
BlockHash: args.BlockHash,
210+
Nodes: append(append(append(updatedAccounts, createdAccounts...), createdOrUpdatedIntermediateNodes...), emptiedPaths...),
211+
CodeAndCodeHashes: codeAndCodeHashes,
194212
}, nil
195213
}
196214

@@ -235,16 +253,17 @@ func (sdb *builder) buildStateDiffWithoutIntermediateStateNodes(args Args, param
235253
return StateObject{}, fmt.Errorf("error building diff for updated accounts: %v", err)
236254
}
237255
// build the diff nodes for created accounts
238-
createdAccounts, err := sdb.buildAccountCreations(diffAccountsAtB, params.WatchedStorageSlots, params.IntermediateStorageNodes)
256+
createdAccounts, codeAndCodeHashes, err := sdb.buildAccountCreations(diffAccountsAtB, params.WatchedStorageSlots, params.IntermediateStorageNodes)
239257
if err != nil {
240258
return StateObject{}, fmt.Errorf("error building diff for created accounts: %v", err)
241259
}
242260

243261
// assemble all of the nodes into the statediff object
244262
return StateObject{
245-
BlockNumber: args.BlockNumber,
246-
BlockHash: args.BlockHash,
247-
Nodes: append(append(updatedAccounts, createdAccounts...), emptiedPaths...),
263+
BlockNumber: args.BlockNumber,
264+
BlockHash: args.BlockHash,
265+
Nodes: append(append(updatedAccounts, createdAccounts...), emptiedPaths...),
266+
CodeAndCodeHashes: codeAndCodeHashes,
248267
}, nil
249268
}
250269

@@ -470,24 +489,40 @@ func (sdb *builder) buildAccountUpdates(creations, deletions AccountMap, updated
470489
}
471490

472491
// buildAccountCreations returns the statediff node objects for all the accounts that exist at B but not at A
473-
func (sdb *builder) buildAccountCreations(accounts AccountMap, watchedStorageKeys []common.Hash, intermediateStorageNodes bool) ([]StateNode, error) {
492+
// it also returns the code and codehash for created contract accounts
493+
func (sdb *builder) buildAccountCreations(accounts AccountMap, watchedStorageKeys []common.Hash, intermediateStorageNodes bool) ([]StateNode, []CodeAndCodeHash, error) {
474494
accountDiffs := make([]StateNode, 0, len(accounts))
495+
codeAndCodeHashes := make([]CodeAndCodeHash, 0)
475496
for _, val := range accounts {
476-
// For account creations, any storage node contained is a diff
477-
storageDiffs, err := sdb.buildStorageNodesEventual(val.Account.Root, watchedStorageKeys, intermediateStorageNodes)
478-
if err != nil {
479-
return nil, fmt.Errorf("failed building eventual storage diffs for node %x\r\nerror: %v", val.Path, err)
497+
diff := StateNode{
498+
NodeType: val.NodeType,
499+
Path: val.Path,
500+
LeafKey: val.LeafKey,
501+
NodeValue: val.NodeValue,
502+
}
503+
if !bytes.Equal(val.Account.CodeHash, nullCodeHash) {
504+
// For contract creations, any storage node contained is a diff
505+
storageDiffs, err := sdb.buildStorageNodesEventual(val.Account.Root, watchedStorageKeys, intermediateStorageNodes)
506+
if err != nil {
507+
return nil, nil, fmt.Errorf("failed building eventual storage diffs for node %x\r\nerror: %v", val.Path, err)
508+
}
509+
diff.StorageNodes = storageDiffs
510+
// emit codehash => code mappings for cod
511+
codeHash := common.BytesToHash(val.Account.CodeHash)
512+
addrHash := common.BytesToHash(val.LeafKey)
513+
code, err := sdb.stateCache.ContractCode(addrHash, codeHash)
514+
if err != nil {
515+
return nil, nil, fmt.Errorf("failed to retrieve code for codehash %s for account with leafkey %s\r\n error: %v", codeHash.String(), addrHash.String(), err)
516+
}
517+
codeAndCodeHashes = append(codeAndCodeHashes, CodeAndCodeHash{
518+
Hash: codeHash,
519+
Code: code,
520+
})
480521
}
481-
accountDiffs = append(accountDiffs, StateNode{
482-
NodeType: val.NodeType,
483-
Path: val.Path,
484-
LeafKey: val.LeafKey,
485-
NodeValue: val.NodeValue,
486-
StorageNodes: storageDiffs,
487-
})
522+
accountDiffs = append(accountDiffs, diff)
488523
}
489524

490-
return accountDiffs, nil
525+
return accountDiffs, codeAndCodeHashes, nil
491526
}
492527

493528
// buildStorageNodesEventual builds the storage diff node objects for a created account

statediff/builder_test.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,12 @@ func TestBuilder(t *testing.T) {
617617
StorageNodes: emptyStorage,
618618
},
619619
},
620+
CodeAndCodeHashes: []statediff.CodeAndCodeHash{
621+
{
622+
Hash: testhelpers.CodeHash,
623+
Code: testhelpers.ByteCodeAfterDeployment,
624+
},
625+
},
620626
},
621627
},
622628
{
@@ -863,6 +869,12 @@ func TestBuilderWithIntermediateNodes(t *testing.T) {
863869
StorageNodes: emptyStorage,
864870
},
865871
},
872+
CodeAndCodeHashes: []statediff.CodeAndCodeHash{
873+
{
874+
Hash: testhelpers.CodeHash,
875+
Code: testhelpers.ByteCodeAfterDeployment,
876+
},
877+
},
866878
},
867879
},
868880
{
@@ -1071,6 +1083,12 @@ func TestBuilderWithWatchedAddressList(t *testing.T) {
10711083
StorageNodes: emptyStorage,
10721084
},
10731085
},
1086+
CodeAndCodeHashes: []statediff.CodeAndCodeHash{
1087+
{
1088+
Hash: testhelpers.CodeHash,
1089+
Code: testhelpers.ByteCodeAfterDeployment,
1090+
},
1091+
},
10741092
},
10751093
},
10761094
{
@@ -1235,6 +1253,12 @@ func TestBuilderWithWatchedAddressAndStorageKeyList(t *testing.T) {
12351253
StorageNodes: emptyStorage,
12361254
},
12371255
},
1256+
CodeAndCodeHashes: []statediff.CodeAndCodeHash{
1257+
{
1258+
Hash: testhelpers.CodeHash,
1259+
Code: testhelpers.ByteCodeAfterDeployment,
1260+
},
1261+
},
12381262
},
12391263
},
12401264
{
@@ -1826,6 +1850,12 @@ func TestBuilderWithMovedAccount(t *testing.T) {
18261850
},
18271851
},
18281852
},
1853+
CodeAndCodeHashes: []statediff.CodeAndCodeHash{
1854+
{
1855+
Hash: testhelpers.CodeHash,
1856+
Code: testhelpers.ByteCodeAfterDeployment,
1857+
},
1858+
},
18291859
},
18301860
},
18311861
{
@@ -1942,6 +1972,12 @@ func TestBuilderWithMovedAccountOnlyLeafs(t *testing.T) {
19421972
},
19431973
},
19441974
},
1975+
CodeAndCodeHashes: []statediff.CodeAndCodeHash{
1976+
{
1977+
Hash: testhelpers.CodeHash,
1978+
Code: testhelpers.ByteCodeAfterDeployment,
1979+
},
1980+
},
19451981
},
19461982
},
19471983
{
@@ -2118,6 +2154,12 @@ func TestBuildStateTrie(t *testing.T) {
21182154
StorageNodes: emptyStorage,
21192155
},
21202156
},
2157+
CodeAndCodeHashes: []statediff.CodeAndCodeHash{
2158+
{
2159+
Hash: testhelpers.CodeHash,
2160+
Code: testhelpers.ByteCodeAfterDeployment,
2161+
},
2162+
},
21212163
},
21222164
},
21232165
{
@@ -2193,6 +2235,12 @@ func TestBuildStateTrie(t *testing.T) {
21932235
StorageNodes: emptyStorage,
21942236
},
21952237
},
2238+
CodeAndCodeHashes: []statediff.CodeAndCodeHash{
2239+
{
2240+
Hash: testhelpers.CodeHash,
2241+
Code: testhelpers.ByteCodeAfterDeployment,
2242+
},
2243+
},
21962244
},
21972245
},
21982246
}

statediff/testhelpers/test_data.go

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -57,14 +57,16 @@ var (
5757
TestBankFunds = big.NewInt(100000000)
5858
Genesis = core.GenesisBlockForTesting(Testdb, TestBankAddress, TestBankFunds)
5959

60-
Account1Key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
61-
Account2Key, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee")
62-
Account1Addr = crypto.PubkeyToAddress(Account1Key.PublicKey) //0x703c4b2bD70c169f5717101CaeE543299Fc946C7
63-
Account2Addr = crypto.PubkeyToAddress(Account2Key.PublicKey) //0x0D3ab14BBaD3D99F4203bd7a11aCB94882050E7e
64-
Account1LeafKey = AddressToLeafKey(Account1Addr)
65-
Account2LeafKey = AddressToLeafKey(Account2Addr)
66-
ContractCode = common.Hex2Bytes("608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506040518060200160405280600160ff16815250600190600161007492919061007a565b506100e4565b82606481019282156100ae579160200282015b828111156100ad578251829060ff1690559160200191906001019061008d565b5b5090506100bb91906100bf565b5090565b6100e191905b808211156100dd5760008160009055506001016100c5565b5090565b90565b6101ca806100f36000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806343d726d61461003b578063c16431b914610045575b600080fd5b61004361007d565b005b61007b6004803603604081101561005b57600080fd5b81019080803590602001909291908035906020019092919050505061015c565b005b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610122576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806101746022913960400191505060405180910390fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16ff5b806001836064811061016a57fe5b0181905550505056fe4f6e6c79206f776e65722063616e2063616c6c20746869732066756e6374696f6e2ea265627a7a72305820e3747183708fb6bff3f6f7a80fb57dcc1c19f83f9cb25457a3ed5c0424bde66864736f6c634300050a0032")
67-
ContractAddr common.Address
60+
Account1Key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
61+
Account2Key, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee")
62+
Account1Addr = crypto.PubkeyToAddress(Account1Key.PublicKey) //0x703c4b2bD70c169f5717101CaeE543299Fc946C7
63+
Account2Addr = crypto.PubkeyToAddress(Account2Key.PublicKey) //0x0D3ab14BBaD3D99F4203bd7a11aCB94882050E7e
64+
Account1LeafKey = AddressToLeafKey(Account1Addr)
65+
Account2LeafKey = AddressToLeafKey(Account2Addr)
66+
ContractCode = common.Hex2Bytes("608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506040518060200160405280600160ff16815250600190600161007492919061007a565b506100e4565b82606481019282156100ae579160200282015b828111156100ad578251829060ff1690559160200191906001019061008d565b5b5090506100bb91906100bf565b5090565b6100e191905b808211156100dd5760008160009055506001016100c5565b5090565b90565b6101ca806100f36000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806343d726d61461003b578063c16431b914610045575b600080fd5b61004361007d565b005b61007b6004803603604081101561005b57600080fd5b81019080803590602001909291908035906020019092919050505061015c565b005b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610122576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806101746022913960400191505060405180910390fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16ff5b806001836064811061016a57fe5b0181905550505056fe4f6e6c79206f776e65722063616e2063616c6c20746869732066756e6374696f6e2ea265627a7a72305820e3747183708fb6bff3f6f7a80fb57dcc1c19f83f9cb25457a3ed5c0424bde66864736f6c634300050a0032")
67+
ByteCodeAfterDeployment = common.Hex2Bytes("608060405234801561001057600080fd5b50600436106100365760003560e01c806343d726d61461003b578063c16431b914610045575b600080fd5b61004361007d565b005b61007b6004803603604081101561005b57600080fd5b81019080803590602001909291908035906020019092919050505061015c565b005b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610122576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806101746022913960400191505060405180910390fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16ff5b806001836064811061016a57fe5b0181905550505056fe4f6e6c79206f776e65722063616e2063616c6c20746869732066756e6374696f6e2ea265627a7a72305820e3747183708fb6bff3f6f7a80fb57dcc1c19f83f9cb25457a3ed5c0424bde66864736f6c634300050a0032")
68+
CodeHash = common.HexToHash("0xaaea5efba4fd7b45d7ec03918ac5d8b31aa93b48986af0e6b591f0f087c80127")
69+
ContractAddr common.Address
6870

6971
EmptyRootNode, _ = rlp.EncodeToBytes([]byte{})
7072
EmptyContractRoot = crypto.Keccak256Hash(EmptyRootNode)

statediff/types.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ type Params struct {
4141
IncludeBlock bool
4242
IncludeReceipts bool
4343
IncludeTD bool
44+
IncludeCode bool
4445
WatchedAddresses []common.Address
4546
WatchedStorageSlots []common.Hash
4647
}
@@ -82,9 +83,17 @@ func (sd *Payload) Encode() ([]byte, error) {
8283

8384
// StateObject is the final output structure from the builder
8485
type StateObject struct {
85-
BlockNumber *big.Int `json:"blockNumber" gencodec:"required"`
86-
BlockHash common.Hash `json:"blockHash" gencodec:"required"`
87-
Nodes []StateNode `json:"nodes" gencodec:"required"`
86+
BlockNumber *big.Int `json:"blockNumber" gencodec:"required"`
87+
BlockHash common.Hash `json:"blockHash" gencodec:"required"`
88+
Nodes []StateNode `json:"nodes" gencodec:"required"`
89+
CodeAndCodeHashes []CodeAndCodeHash `json:"codeMapping"`
90+
}
91+
92+
// CodeAndCodeHash struct for holding codehash => code mappings
93+
// we can't use an actual map because they are not rlp serializable
94+
type CodeAndCodeHash struct {
95+
Hash common.Hash
96+
Code []byte
8897
}
8998

9099
// StateNode holds the data for a single state diff node

0 commit comments

Comments
 (0)