erigon-pulse/cmd/restapi/apis/remote_reader.go
2020-10-24 09:09:20 +01:00

203 lines
5.9 KiB
Go

package apis
import (
"bytes"
"math/big"
"github.com/ledgerwatch/turbo-geth/common"
"github.com/ledgerwatch/turbo-geth/common/dbutils"
"github.com/ledgerwatch/turbo-geth/consensus"
"github.com/ledgerwatch/turbo-geth/core/rawdb"
"github.com/ledgerwatch/turbo-geth/core/state"
"github.com/ledgerwatch/turbo-geth/core/types"
"github.com/ledgerwatch/turbo-geth/core/types/accounts"
"github.com/ledgerwatch/turbo-geth/crypto"
"github.com/ledgerwatch/turbo-geth/ethdb"
"github.com/ledgerwatch/turbo-geth/params"
"github.com/ledgerwatch/turbo-geth/rpc"
"golang.org/x/net/context"
)
// ChangeSetReader is a mock StateWriter that accumulates changes in-memory into ChangeSets.
type RemoteReader struct {
accountReads map[common.Address]struct{}
storageReads map[common.Address]map[common.Hash]struct{}
codeReads map[common.Address]struct{}
blockNr uint64
db ethdb.KV
}
type RemoteContext struct {
kv ethdb.KV
db ethdb.Getter
}
type powEngine struct {
}
func (c *powEngine) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header, seal bool) error {
panic("must not be called")
}
func (c *powEngine) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header, seals []bool) (func(), <-chan error) {
panic("must not be called")
}
func (c *powEngine) VerifyUncles(chain consensus.ChainReader, block *types.Block) error {
panic("must not be called")
}
func (c *powEngine) VerifySeal(chain consensus.ChainHeaderReader, header *types.Header) error {
panic("must not be called")
}
func (c *powEngine) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error {
panic("must not be called")
}
func (c *powEngine) Finalize(chainConfig *params.ChainConfig, header *types.Header, state *state.IntraBlockState, txs []*types.Transaction, uncles []*types.Header) {
panic("must not be called")
}
func (c *powEngine) FinalizeAndAssemble(chainConfig *params.ChainConfig, header *types.Header, state *state.IntraBlockState, txs []*types.Transaction,
uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
panic("must not be called")
}
func (c *powEngine) Seal(_ consensus.Cancel, chain consensus.ChainHeaderReader, block *types.Block, results chan<- consensus.ResultWithContext, stop <-chan struct{}) error {
panic("must not be called")
}
func (c *powEngine) SealHash(header *types.Header) common.Hash {
panic("must not be called")
}
func (c *powEngine) CalcDifficulty(_ consensus.ChainHeaderReader, _, _ uint64, _, _ *big.Int, _, _ common.Hash) *big.Int {
panic("must not be called")
}
func (c *powEngine) APIs(chain consensus.ChainHeaderReader) []rpc.API {
panic("must not be called")
}
func (c *powEngine) Close() error {
panic("must not be called")
}
func (c *powEngine) Author(header *types.Header) (common.Address, error) {
return header.Coinbase, nil
}
func NewRemoteReader(db ethdb.KV, blockNr uint64) *RemoteReader {
return &RemoteReader{
accountReads: make(map[common.Address]struct{}),
storageReads: make(map[common.Address]map[common.Hash]struct{}),
codeReads: make(map[common.Address]struct{}),
db: db,
blockNr: blockNr,
}
}
func (r *RemoteReader) GetAccountReads() [][]byte {
output := make([][]byte, 0)
for address := range r.accountReads {
output = append(output, address.Bytes())
}
return output
}
func (r *RemoteReader) GetStorageReads() [][]byte {
output := make([][]byte, 0)
for address, m := range r.storageReads {
for key := range m {
output = append(output, append(address.Bytes(), key.Bytes()...))
}
}
return output
}
func (r *RemoteReader) GetCodeReads() [][]byte {
output := make([][]byte, 0)
for key := range r.codeReads {
output = append(output, key.Bytes())
}
return output
}
func (r *RemoteReader) ReadAccountData(address common.Address) (*accounts.Account, error) {
r.accountReads[address] = struct{}{}
addrHash, err := common.HashData(address[:])
if err != nil {
return nil, err
}
enc, err := state.GetAsOf(r.db.(ethdb.HasTx).Tx(), false /* storage */, addrHash[:], r.blockNr+1)
if err != nil || enc == nil || len(enc) == 0 {
return nil, nil
}
var acc accounts.Account
if err := acc.DecodeForStorage(enc); err != nil {
return nil, err
}
return &acc, nil
}
func (r *RemoteReader) ReadAccountStorage(address common.Address, incarnation uint64, key *common.Hash) ([]byte, error) {
m, ok := r.storageReads[address]
if !ok {
m = make(map[common.Hash]struct{})
r.storageReads[address] = m
}
m[*key] = struct{}{}
keyHash, err := common.HashData(key[:])
if err != nil {
return nil, err
}
addrHash, err := common.HashData(address[:])
if err != nil {
return nil, err
}
compositeKey := dbutils.GenerateCompositeStorageKey(addrHash, incarnation, keyHash)
enc, err := state.GetAsOf(r.db.(ethdb.HasTx).Tx(), true /* storage */, compositeKey, r.blockNr+1)
if err != nil || enc == nil {
return nil, nil
}
return enc, nil
}
func (r *RemoteReader) ReadAccountCode(address common.Address, codeHash common.Hash) ([]byte, error) {
r.codeReads[address] = struct{}{}
if bytes.Equal(codeHash[:], crypto.Keccak256(nil)) {
return nil, nil
}
var val []byte
err := r.db.View(context.Background(), func(tx ethdb.Tx) error {
v, err := tx.GetOne(dbutils.CodeBucket, codeHash[:])
val = v
return err
})
if err != nil {
return nil, err
}
return val, nil
}
func (r *RemoteReader) ReadAccountCodeSize(address common.Address, codeHash common.Hash) (int, error) {
code, err := r.ReadAccountCode(address, codeHash)
if err != nil {
return 0, err
}
return len(code), nil
}
func (r *RemoteReader) ReadAccountIncarnation(address common.Address) (uint64, error) {
return 0, nil
}
func NewRemoteContext(kv ethdb.KV, db ethdb.Getter) *RemoteContext {
return &RemoteContext{
kv: kv,
db: db,
}
}
func (e *RemoteContext) Engine() consensus.Engine {
return &powEngine{}
}
func (e *RemoteContext) GetHeader(hash common.Hash, number uint64) *types.Header {
return rawdb.ReadHeader(e.db, hash, number)
}