diff --git a/blockchain.go b/blockchain.go index c1a8293..b94b678 100644 --- a/blockchain.go +++ b/blockchain.go @@ -21,7 +21,6 @@ import ( ) type BlockChain interface { - // SubscribeChainEvent(ch chan<- plugeth.ChainEvent) plugeth.Subscription SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription CurrentBlock() *types.Header GetBlockByHash(hash common.Hash) *types.Block @@ -33,26 +32,30 @@ type BlockChain interface { StateCache() adapt.StateView } -// Adapts the plugeth Backend to the blockChain interface -type backendBlockChain struct { - restricted.Backend - ctx context.Context - - // code mappings exposed in StateCache, safe for concurrent access - code map[common.Hash][]byte +// CodeStore stores contract code hashes to data in a synchronized map +type CodeStore struct { + code map[plugeth.Hash][]byte codeMtx sync.RWMutex } -var _ BlockChain = (*backendBlockChain)(nil) +// pluginBlockChain adapts the plugeth Backend to the blockChain interface +type pluginBlockChain struct { + restricted.Backend + ctx context.Context + code *CodeStore +} -func NewPluginBlockChain(backend restricted.Backend) BlockChain { - return &backendBlockChain{ +var _ BlockChain = (*pluginBlockChain)(nil) + +func NewPluginBlockChain(backend restricted.Backend, code *CodeStore) BlockChain { + return &pluginBlockChain{ Backend: backend, ctx: context.Background(), + code: code, } } -func (b backendBlockChain) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription { +func (b *pluginBlockChain) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription { bufferChan := make(chan plugeth.ChainEvent, chainEventChanSize) sub := b.Backend.SubscribeChainEvent(bufferChan) go func() { @@ -69,12 +72,12 @@ func (b backendBlockChain) SubscribeChainEvent(ch chan<- core.ChainEvent) event. return sub } -func (b *backendBlockChain) CurrentBlock() *types.Header { +func (b *pluginBlockChain) CurrentBlock() *types.Header { buf := b.Backend.CurrentBlock() return utils.MustDecode[types.Header](buf) } -func (b *backendBlockChain) GetBlockByHash(hash common.Hash) *types.Block { +func (b *pluginBlockChain) GetBlockByHash(hash common.Hash) *types.Block { buf, err := b.Backend.BlockByHash(b.ctx, plugeth.Hash(hash)) if err != nil { panic(err) @@ -82,7 +85,7 @@ func (b *backendBlockChain) GetBlockByHash(hash common.Hash) *types.Block { return utils.MustDecode[types.Block](buf) } -func (b *backendBlockChain) GetBlockByNumber(number uint64) *types.Block { +func (b *pluginBlockChain) GetBlockByNumber(number uint64) *types.Block { buf, err := b.Backend.BlockByNumber(b.ctx, int64(number)) if err != nil { panic(err) @@ -90,7 +93,7 @@ func (b *backendBlockChain) GetBlockByNumber(number uint64) *types.Block { return utils.MustDecode[types.Block](buf) } -func (b *backendBlockChain) GetReceiptsByHash(hash common.Hash) types.Receipts { +func (b *pluginBlockChain) GetReceiptsByHash(hash common.Hash) types.Receipts { buf, err := b.Backend.GetReceipts(b.ctx, plugeth.Hash(hash)) if err != nil { panic(err) @@ -103,37 +106,21 @@ func (b *backendBlockChain) GetReceiptsByHash(hash common.Hash) types.Receipts { return receipts } -func (b *backendBlockChain) GetTd(hash common.Hash, number uint64) *big.Int { +func (b *pluginBlockChain) GetTd(hash common.Hash, number uint64) *big.Int { return b.Backend.GetTd(b.ctx, plugeth.Hash(hash)) } -func (b *backendBlockChain) StateCache() adapt.StateView { +func (b *pluginBlockChain) StateCache() adapt.StateView { return &pluginStateView{backend: b} } -func (b *backendBlockChain) ChainConfig() *params.ChainConfig { +func (b *pluginBlockChain) ChainConfig() *params.ChainConfig { return adapt.ChainConfig(b.Backend.ChainConfig()) } -func (b *backendBlockChain) getCode(hash common.Hash) ([]byte, error) { - b.codeMtx.RLock() - defer b.codeMtx.RUnlock() - code, has := b.code[hash] - if !has { - return nil, errors.New("code not found") - } - return copybytes(code), nil -} - -func (b *backendBlockChain) setCode(hash common.Hash, code []byte) { - b.codeMtx.Lock() - defer b.codeMtx.Unlock() - b.code[hash] = code -} - // exposes a StateView from a combination of plugeth's core Backend and cached contract code type pluginStateView struct { - backend *backendBlockChain + backend *pluginBlockChain } func (p *pluginStateView) OpenTrie(root common.Hash) (adapt.StateTrie, error) { @@ -145,7 +132,21 @@ func (p *pluginStateView) OpenTrie(root common.Hash) (adapt.StateTrie, error) { } func (p *pluginStateView) ContractCode(hash common.Hash) ([]byte, error) { - return p.backend.getCode(hash) + return p.backend.code.Get(plugeth.Hash(hash)) } -func copybytes(b []byte) []byte { return []byte(string(b)) } +func (b *CodeStore) Get(hash plugeth.Hash) ([]byte, error) { + b.codeMtx.RLock() + defer b.codeMtx.RUnlock() + code, has := b.code[hash] + if !has { + return nil, errors.New("code not found") + } + return []byte(string(code)), nil +} + +func (b *CodeStore) Set(hash plugeth.Hash, code []byte) { + b.codeMtx.Lock() + defer b.codeMtx.Unlock() + b.code[hash] = code +} diff --git a/main/main.go b/main/main.go index 6941d78..9c51d16 100644 --- a/main/main.go +++ b/main/main.go @@ -20,6 +20,7 @@ var ( gethContext core.Context service *statediff.Service blockchain statediff.BlockChain + codeStore statediff.CodeStore ) func Initialize(ctx core.Context, pl core.PluginLoader, logger core.Logger) { @@ -47,12 +48,12 @@ func InitializeNode(stack core.Node, b core.Backend) { return } serviceConfig := GetConfig() - chain := statediff.NewPluginBlockChain(backend) + blockchain = statediff.NewPluginBlockChain(backend, &codeStore) var indexer interfaces.StateDiffIndexer if serviceConfig.IndexerConfig != nil { info := node.Info{ - GenesisBlock: chain.GetBlockByNumber(0).Hash().String(), + GenesisBlock: blockchain.GetBlockByNumber(0).Hash().String(), NetworkID: strconv.FormatUint(networkid, 10), ChainID: backend.ChainConfig().ChainID.Uint64(), ID: serviceConfig.ID, @@ -65,7 +66,7 @@ func InitializeNode(stack core.Node, b core.Backend) { log.Error("failed to construct indexer", "error", err) } } - service, err := statediff.NewService(serviceConfig, chain, backend, indexer) + service, err := statediff.NewService(serviceConfig, blockchain, backend, indexer) if err != nil { log.Error("failed to construct service", "error", err) } @@ -88,16 +89,19 @@ func GetAPIs(stack core.Node, backend core.Backend) []core.API { // StateUpdate gives us updates about state changes made in each block. // We extract contract code here, since it's not exposed by plugeth's state interfaces. -func StateUpdate(blockRoot core.Hash, +func StateUpdate( + blockRoot core.Hash, parentRoot core.Hash, destructs map[core.Hash]struct{}, accounts map[core.Hash][]byte, storage map[core.Hash]map[core.Hash][]byte, codeUpdates map[core.Hash][]byte) { - if service == nil { - log.Error("StateUpdate called before InitializeNode", "root", blockRoot) + if blockchain == nil { + log.Warn("StateUpdate called before InitializeNode", "root", blockRoot) return } - // blockchain. + for hash, code := range codeUpdates { + codeStore.Set(hash, code) + } }