Skip to content

Commit 1cb2384

Browse files
authored
Merge pull request #28 from vulcanize/codeAndCodeHash
subscription endpoint for retrieving all the codehash=>code mappings …
2 parents ca9f5a8 + dc399a8 commit 1cb2384

File tree

6 files changed

+97
-13
lines changed

6 files changed

+97
-13
lines changed

statediff/api.go

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,10 @@ func (api *PublicStateDiffAPI) Stream(ctx context.Context, params Params) (*rpc.
6363
for {
6464
select {
6565
case payload := <-payloadChannel:
66-
if notifyErr := notifier.Notify(rpcSub.ID, payload); notifyErr != nil {
67-
log.Error("Failed to send state diff packet; error: " + notifyErr.Error())
68-
unSubErr := api.sds.Unsubscribe(rpcSub.ID)
69-
if unSubErr != nil {
70-
log.Error("Failed to unsubscribe from the state diff service; error: " + unSubErr.Error())
66+
if err := notifier.Notify(rpcSub.ID, payload); err != nil {
67+
log.Error("Failed to send state diff packet; error: " + err.Error())
68+
if err := api.sds.Unsubscribe(rpcSub.ID); err != nil {
69+
log.Error("Failed to unsubscribe from the state diff service; error: " + err.Error())
7170
}
7271
return
7372
}
@@ -99,3 +98,36 @@ func (api *PublicStateDiffAPI) StateDiffAt(ctx context.Context, blockNumber uint
9998
func (api *PublicStateDiffAPI) StateTrieAt(ctx context.Context, blockNumber uint64, params Params) (*Payload, error) {
10099
return api.sds.StateTrieAt(blockNumber, params)
101100
}
101+
102+
// StreamCodeAndCodeHash writes all of the codehash=>code pairs out to a websocket channel
103+
func (api *PublicStateDiffAPI) StreamCodeAndCodeHash(ctx context.Context, blockNumber uint64) (*rpc.Subscription, error) {
104+
// ensure that the RPC connection supports subscriptions
105+
notifier, supported := rpc.NotifierFromContext(ctx)
106+
if !supported {
107+
return nil, rpc.ErrNotificationsUnsupported
108+
}
109+
110+
// create subscription and start waiting for events
111+
rpcSub := notifier.CreateSubscription()
112+
payloadChan := make(chan CodeAndCodeHash, chainEventChanSize)
113+
quitChan := make(chan bool)
114+
api.sds.StreamCodeAndCodeHash(blockNumber, payloadChan, quitChan)
115+
go func() {
116+
for {
117+
select {
118+
case payload := <-payloadChan:
119+
if err := notifier.Notify(rpcSub.ID, payload); err != nil {
120+
log.Error("Failed to send code and codehash packet", "err", err)
121+
return
122+
}
123+
case err := <-rpcSub.Err():
124+
log.Error("State diff service rpcSub error", "err", err)
125+
return
126+
case <-quitChan:
127+
return
128+
}
129+
}
130+
}()
131+
132+
return rpcSub, nil
133+
}

statediff/builder.go

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -124,10 +124,9 @@ func (sdb *builder) buildStateTrie(it trie.NodeIterator) ([]StateNode, []CodeAnd
124124
node.StorageNodes = storageNodes
125125
// emit codehash => code mappings for cod
126126
codeHash := common.BytesToHash(account.CodeHash)
127-
addrHash := common.BytesToHash(leafKey)
128-
code, err := sdb.stateCache.ContractCode(addrHash, codeHash)
127+
code, err := sdb.stateCache.ContractCode(common.Hash{}, codeHash)
129128
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)
129+
return nil, nil, fmt.Errorf("failed to retrieve code for codehash %s\r\n error: %v", codeHash.String(), err)
131130
}
132131
codeAndCodeHashes = append(codeAndCodeHashes, CodeAndCodeHash{
133132
Hash: codeHash,
@@ -509,10 +508,9 @@ func (sdb *builder) buildAccountCreations(accounts AccountMap, watchedStorageKey
509508
diff.StorageNodes = storageDiffs
510509
// emit codehash => code mappings for cod
511510
codeHash := common.BytesToHash(val.Account.CodeHash)
512-
addrHash := common.BytesToHash(val.LeafKey)
513-
code, err := sdb.stateCache.ContractCode(addrHash, codeHash)
511+
code, err := sdb.stateCache.ContractCode(common.Hash{}, codeHash)
514512
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)
513+
return nil, nil, fmt.Errorf("failed to retrieve code for codehash %s\r\n error: %v", codeHash.String(), err)
516514
}
517515
codeAndCodeHashes = append(codeAndCodeHashes, CodeAndCodeHash{
518516
Hash: codeHash,

statediff/service.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525

2626
"github.com/ethereum/go-ethereum/common"
2727
"github.com/ethereum/go-ethereum/core"
28+
"github.com/ethereum/go-ethereum/core/state"
2829
"github.com/ethereum/go-ethereum/core/types"
2930
"github.com/ethereum/go-ethereum/crypto"
3031
"github.com/ethereum/go-ethereum/event"
@@ -33,6 +34,7 @@ import (
3334
"github.com/ethereum/go-ethereum/p2p"
3435
"github.com/ethereum/go-ethereum/rlp"
3536
"github.com/ethereum/go-ethereum/rpc"
37+
"github.com/ethereum/go-ethereum/trie"
3638
)
3739

3840
const chainEventChanSize = 20000
@@ -44,6 +46,7 @@ type blockChain interface {
4446
GetReceiptsByHash(hash common.Hash) types.Receipts
4547
GetTdByHash(hash common.Hash) *big.Int
4648
UnlockTrie(root common.Hash)
49+
StateCache() state.Database
4750
}
4851

4952
// IService is the state-diffing service interface
@@ -60,6 +63,8 @@ type IService interface {
6063
StateDiffAt(blockNumber uint64, params Params) (*Payload, error)
6164
// Method to get state trie object at specific block
6265
StateTrieAt(blockNumber uint64, params Params) (*Payload, error)
66+
// Method to stream out all code and codehash pairs
67+
StreamCodeAndCodeHash(blockNumber uint64, outChan chan<- CodeAndCodeHash, quitChan chan<- bool)
6368
}
6469

6570
// Service is the underlying struct for the state diffing service
@@ -361,3 +366,42 @@ func sendNonBlockingQuit(id rpc.ID, sub Subscription) {
361366
log.Info("unable to close subscription %s; channel has no receiver", id)
362367
}
363368
}
369+
370+
// StreamCodeAndCodeHash subscription method for extracting all the codehash=>code mappings that exist in the trie at the provided height
371+
func (sds *Service) StreamCodeAndCodeHash(blockNumber uint64, outChan chan<- CodeAndCodeHash, quitChan chan<- bool) {
372+
current := sds.BlockChain.GetBlockByNumber(blockNumber)
373+
log.Info(fmt.Sprintf("sending code and codehash at block %d", blockNumber))
374+
currentTrie, err := sds.BlockChain.StateCache().OpenTrie(current.Root())
375+
if err != nil {
376+
log.Error("error creating trie for block", "number", current.Number(), "err", err)
377+
close(quitChan)
378+
return
379+
}
380+
it := currentTrie.NodeIterator([]byte{})
381+
leafIt := trie.NewIterator(it)
382+
go func() {
383+
defer close(quitChan)
384+
for leafIt.Next() {
385+
select {
386+
case <-sds.QuitChan:
387+
return
388+
default:
389+
}
390+
account := new(state.Account)
391+
if err := rlp.DecodeBytes(leafIt.Value, account); err != nil {
392+
log.Error("error decoding state account", "err", err)
393+
return
394+
}
395+
codeHash := common.BytesToHash(account.CodeHash)
396+
code, err := sds.BlockChain.StateCache().ContractCode(common.Hash{}, codeHash)
397+
if err != nil {
398+
log.Error("error collecting contract code", "err", err)
399+
return
400+
}
401+
outChan <- CodeAndCodeHash{
402+
Hash: codeHash,
403+
Code: code,
404+
}
405+
}
406+
}()
407+
}

statediff/testhelpers/mocks/blockchain.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import (
2121
"math/big"
2222
"time"
2323

24+
"github.com/ethereum/go-ethereum/core/state"
25+
2426
"github.com/ethereum/go-ethereum/common"
2527
"github.com/ethereum/go-ethereum/core"
2628
"github.com/ethereum/go-ethereum/core/types"
@@ -126,3 +128,7 @@ func (blockChain *BlockChain) SetTdByHash(hash common.Hash, td *big.Int) {
126128
}
127129

128130
func (blockChain *BlockChain) UnlockTrie(root common.Hash) {}
131+
132+
func (BlockChain *BlockChain) StateCache() state.Database {
133+
return nil
134+
}

statediff/testhelpers/mocks/service.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,10 @@ func (sds *MockStateDiffService) closeType(subType common.Hash) {
276276
delete(sds.SubscriptionTypes, subType)
277277
}
278278

279+
func (sds *MockStateDiffService) StreamCodeAndCodeHash(blockNumber uint64, outChan chan<- statediff.CodeAndCodeHash, quitChan chan<- bool) {
280+
panic("implement me")
281+
}
282+
279283
func sendNonBlockingQuit(id rpc.ID, sub statediff.Subscription) {
280284
select {
281285
case sub.QuitChan <- true:

statediff/types.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,8 @@ type StateObject struct {
9292
// CodeAndCodeHash struct for holding codehash => code mappings
9393
// we can't use an actual map because they are not rlp serializable
9494
type CodeAndCodeHash struct {
95-
Hash common.Hash
96-
Code []byte
95+
Hash common.Hash `json:"codeHash"`
96+
Code []byte `json:"code"`
9797
}
9898

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

0 commit comments

Comments
 (0)