plugeth/plugins/wrappers/engine/enginewrapper.go

428 lines
14 KiB
Go

package engine
import (
"fmt"
"math/big"
"reflect"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/plugins/wrappers"
"github.com/openrelayxyz/plugeth-utils/core"
ptypes "github.com/openrelayxyz/plugeth-utils/restricted/types"
pconsensus "github.com/openrelayxyz/plugeth-utils/restricted/consensus"
pparams "github.com/openrelayxyz/plugeth-utils/restricted/params"
)
func gethToUtilsHeader(header *types.Header) *ptypes.Header {
if header == nil { return nil }
return &ptypes.Header{
ParentHash: core.Hash(header.ParentHash),
UncleHash: core.Hash(header.UncleHash),
Coinbase: core.Address(header.Coinbase),
Root: core.Hash(header.Root),
TxHash: core.Hash(header.TxHash),
ReceiptHash: core.Hash(header.ReceiptHash),
Bloom: ptypes.Bloom(header.Bloom),
Difficulty: header.Difficulty,
Number: header.Number,
GasLimit: header.GasLimit,
GasUsed: header.GasUsed,
Time: header.Time,
Extra: header.Extra,
MixDigest: core.Hash(header.MixDigest),
Nonce: ptypes.BlockNonce(header.Nonce),
BaseFee: header.BaseFee,
WithdrawalsHash: (*core.Hash)(header.WithdrawalsHash),
}
}
func utilsToGethHeader(header *ptypes.Header) *types.Header {
if header == nil { return nil }
return &types.Header{
ParentHash: common.Hash(header.ParentHash),
UncleHash: common.Hash(header.UncleHash),
Coinbase: common.Address(header.Coinbase),
Root: common.Hash(header.Root),
TxHash: common.Hash(header.TxHash),
ReceiptHash: common.Hash(header.ReceiptHash),
Bloom: types.Bloom(header.Bloom),
Difficulty: header.Difficulty,
Number: header.Number,
GasLimit: header.GasLimit,
GasUsed: header.GasUsed,
Time: header.Time,
Extra: header.Extra,
MixDigest: common.Hash(header.MixDigest),
Nonce: types.BlockNonce(header.Nonce),
BaseFee: header.BaseFee,
WithdrawalsHash: (*common.Hash)(header.WithdrawalsHash),
}
}
func gethToUtilsTransactions(transactions []*types.Transaction) []*ptypes.Transaction {
if transactions == nil { return nil }
txs := make([]*ptypes.Transaction, len(transactions))
for i, tx := range transactions {
bin, err := tx.MarshalBinary()
if err != nil { panic (err) }
txs[i] = &ptypes.Transaction{}
txs[i].UnmarshalBinary(bin)
}
return txs
}
func gethToUtilsHeaders(headers []*types.Header) []*ptypes.Header {
if headers == nil { return nil }
pheaders := make([]*ptypes.Header, len(headers))
for i, header := range headers {
pheaders[i] = gethToUtilsHeader(header)
}
return pheaders
}
func gethToUtilsReceipts(receipts []*types.Receipt) []*ptypes.Receipt {
if receipts == nil { return nil }
preceipts := make([]*ptypes.Receipt, len(receipts))
for i, receipt := range receipts {
preceipts[i] = &ptypes.Receipt{
Type: receipt.Type,
PostState: receipt.PostState,
Status: receipt.Status,
CumulativeGasUsed: receipt.CumulativeGasUsed,
Bloom: ptypes.Bloom(receipt.Bloom),
Logs: gethToUtilsLogs(receipt.Logs),
TxHash: core.Hash(receipt.TxHash),
ContractAddress: core.Address(receipt.ContractAddress),
GasUsed: receipt.GasUsed,
BlockHash: core.Hash(receipt.BlockHash),
BlockNumber: receipt.BlockNumber,
TransactionIndex: receipt.TransactionIndex,
}
}
return preceipts
}
func gethToUtilsWithdrawals(withdrawals []*types.Withdrawal) []*ptypes.Withdrawal {
if withdrawals == nil { return nil }
pwithdrawals := make([]*ptypes.Withdrawal, len(withdrawals))
for i, withdrawal := range withdrawals {
pwithdrawals[i] = &ptypes.Withdrawal{
Index: withdrawal.Index,
Validator: withdrawal.Validator,
Address: core.Address(withdrawal.Address),
Amount: withdrawal.Amount,
}
}
return pwithdrawals
}
func gethToUtilsBlock(block *types.Block) *ptypes.Block {
if block == nil { return nil }
return ptypes.NewBlockWithHeader(gethToUtilsHeader(block.Header())).WithBody(gethToUtilsTransactions(block.Transactions()), gethToUtilsHeaders(block.Uncles())).WithWithdrawals(gethToUtilsWithdrawals(block.Withdrawals()))
}
func utilsToGethBlock(block *ptypes.Block) *types.Block {
if block == nil { return nil }
return types.NewBlockWithHeader(utilsToGethHeader(block.Header())).WithBody(utilsToGethTransactions(block.Transactions()), utilsToGethHeaders(block.Uncles())).WithWithdrawals(utilsToGethWithdrawals(block.Withdrawals()))
}
func utilsToGethHeaders(headers []*ptypes.Header) []*types.Header {
if headers == nil { return nil }
pheaders := make([]*types.Header, len(headers))
for i, header := range headers {
pheaders[i] = utilsToGethHeader(header)
}
return pheaders
}
func utilsToGethTransactions(transactions []*ptypes.Transaction) []*types.Transaction {
if transactions == nil { return nil }
txs := make([]*types.Transaction, len(transactions))
for i, tx := range transactions {
bin, err := tx.MarshalBinary()
if err != nil { panic (err) }
txs[i] = &types.Transaction{}
txs[i].UnmarshalBinary(bin)
}
return txs
}
func utilsToGethWithdrawals(withdrawals []*ptypes.Withdrawal) []*types.Withdrawal {
if withdrawals == nil { return nil }
pwithdrawals := make([]*types.Withdrawal, len(withdrawals))
for i, withdrawal := range withdrawals {
pwithdrawals[i] = &types.Withdrawal{
Index: withdrawal.Index,
Validator: withdrawal.Validator,
Address: common.Address(withdrawal.Address),
Amount: withdrawal.Amount,
}
}
return pwithdrawals
}
func gethToUtilsBlockChan(ch chan<- *types.Block) chan<- *ptypes.Block {
pchan := make(chan *ptypes.Block)
go func() {
for block := range pchan {
ch <- utilsToGethBlock(block)
}
}()
return pchan
}
func gethToUtilsLog(logRecord *types.Log) *ptypes.Log {
if logRecord == nil { return nil }
topics := make([]core.Hash, len(logRecord.Topics))
for i, t := range logRecord.Topics {
topics[i] = core.Hash(t)
}
return &ptypes.Log{
Address: core.Address(logRecord.Address),
Topics: topics,
Data: logRecord.Data,
BlockNumber: logRecord.BlockNumber,
TxHash: core.Hash(logRecord.TxHash),
TxIndex: logRecord.TxIndex,
BlockHash: core.Hash(logRecord.BlockHash),
Index: logRecord.Index,
Removed: logRecord.Removed,
}
}
func utilsToGethLog(logRecord *ptypes.Log) *types.Log {
if logRecord == nil { return nil }
topics := make([]common.Hash, len(logRecord.Topics))
for i, t := range logRecord.Topics {
topics[i] = common.Hash(t)
}
return &types.Log{
Address: common.Address(logRecord.Address),
Topics: topics,
Data: logRecord.Data,
BlockNumber: logRecord.BlockNumber,
TxHash: common.Hash(logRecord.TxHash),
TxIndex: logRecord.TxIndex,
BlockHash: common.Hash(logRecord.BlockHash),
Index: logRecord.Index,
Removed: logRecord.Removed,
}
}
func gethToUtilsLogs(logs []*types.Log) []*ptypes.Log {
result := make([]*ptypes.Log, len(logs))
for i, logRecord := range logs {
result[i] = gethToUtilsLog(logRecord)
}
return result
}
func utilsToGethLogs(logs []*ptypes.Log) []*types.Log {
result := make([]*types.Log, len(logs))
for i, logRecord := range logs {
result[i] = utilsToGethLog(logRecord)
}
return result
}
func convertAndSet(a, b reflect.Value) (err error) {
defer func() {
if recover() != nil {
fmt.Errorf("error converting: %v", err.Error())
}
}()
a.Set(b.Convert(a.Type()))
return nil
}
func gethToUtilsConfig(gcfg *params.ChainConfig) *pparams.ChainConfig {
cfg := &pparams.ChainConfig{}
nval := reflect.ValueOf(gcfg)
ntype := nval.Elem().Type()
lval := reflect.ValueOf(cfg)
for i := 0; i < nval.Elem().NumField(); i++ {
field := ntype.Field(i)
v := nval.Elem().FieldByName(field.Name)
lv := lval.Elem().FieldByName(field.Name)
log.Info("Checking value for", "field", field.Name)
if lv.Kind() != reflect.Invalid {
// If core.ChainConfig doesn't have this field, skip it.
if v.Type() == lv.Type() && lv.CanSet() {
lv.Set(v)
} else {
convertAndSet(lv, v)
}
}
}
return cfg
}
type WrappedHeaderReader struct {
chr consensus.ChainHeaderReader
cfg *pparams.ChainConfig
}
func (whr *WrappedHeaderReader) Config() *pparams.ChainConfig {
if whr.cfg == nil {
whr.cfg = gethToUtilsConfig(whr.chr.Config())
}
return whr.cfg
}
// CurrentHeader retrieves the current header from the local chain.
func (whr *WrappedHeaderReader) CurrentHeader() *ptypes.Header {
return gethToUtilsHeader(whr.chr.CurrentHeader())
}
// GetHeader retrieves a block header from the database by hash and number.
func (whr *WrappedHeaderReader) GetHeader(hash core.Hash, number uint64) *ptypes.Header {
return gethToUtilsHeader(whr.chr.GetHeader(common.Hash(hash), number))
}
// GetHeaderByNumber retrieves a block header from the database by number.
func (whr *WrappedHeaderReader) GetHeaderByNumber(number uint64) *ptypes.Header {
return gethToUtilsHeader(whr.chr.GetHeaderByNumber(number))
}
// GetHeaderByHash retrieves a block header from the database by its hash.
func (whr *WrappedHeaderReader) GetHeaderByHash(hash core.Hash) *ptypes.Header {
return gethToUtilsHeader(whr.chr.GetHeaderByHash(common.Hash(hash)))
}
// GetTd retrieves the total difficulty from the database by hash and number.
func (whr *WrappedHeaderReader) GetTd(hash core.Hash, number uint64) *big.Int {
return whr.chr.GetTd(common.Hash(hash), number)
}
type WrappedChainReader struct {
chr consensus.ChainReader
cfg *pparams.ChainConfig
}
func (whr *WrappedChainReader) Config() *pparams.ChainConfig {
// We're using the reflect library to copy data from params.ChainConfig to
// pparams.ChainConfig, so this function shouldn't need to be touched for
// simple changes to ChainConfig (though pparams.ChainConfig may need to be
// updated). Note that this probably won't carry over consensus engine data.
if whr.cfg == nil {
whr.cfg = gethToUtilsConfig(whr.chr.Config())
}
return whr.cfg
}
// CurrentHeader retrieves the current header from the local chain.
func (whr *WrappedChainReader) CurrentHeader() *ptypes.Header {
return gethToUtilsHeader(whr.chr.CurrentHeader())
}
// GetHeader retrieves a block header from the database by hash and number.
func (whr *WrappedChainReader) GetHeader(hash core.Hash, number uint64) *ptypes.Header {
return gethToUtilsHeader(whr.chr.GetHeader(common.Hash(hash), number))
}
// GetHeaderByNumber retrieves a block header from the database by number.
func (whr *WrappedChainReader) GetHeaderByNumber(number uint64) *ptypes.Header {
return gethToUtilsHeader(whr.chr.GetHeaderByNumber(number))
}
// GetHeaderByHash retrieves a block header from the database by its hash.
func (whr *WrappedChainReader) GetHeaderByHash(hash core.Hash) *ptypes.Header {
return gethToUtilsHeader(whr.chr.GetHeaderByHash(common.Hash(hash)))
}
// GetTd retrieves the total difficulty from the database by hash and number.
func (whr *WrappedChainReader) GetTd(hash core.Hash, number uint64) *big.Int {
return whr.chr.GetTd(common.Hash(hash), number)
}
func (whr *WrappedChainReader) GetBlock(hash core.Hash, number uint64) *ptypes.Block {
return gethToUtilsBlock(whr.chr.GetBlock(common.Hash(hash), number))
}
type hasherWrapper struct {
th types.TrieHasher
}
func (hw *hasherWrapper) Reset() {
hw.th.Reset()
}
func (hw *hasherWrapper) Update(a, b []byte) {
hw.th.Update(a, b)
}
func (hw *hasherWrapper) Hash() core.Hash {
return core.Hash(hw.th.Hash())
}
type engineWrapper struct {
engine pconsensus.Engine
}
func NewWrappedEngine(e pconsensus.Engine) consensus.Engine {
return &engineWrapper {
engine: e,
}
}
func (ew *engineWrapper) Author(header *types.Header) (common.Address, error) {
addr, err := ew.engine.Author(gethToUtilsHeader(header))
return common.Address(addr), err
}
func (ew *engineWrapper) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header, seal bool) error {
return ew.engine.VerifyHeader(&WrappedHeaderReader{chain, nil}, gethToUtilsHeader(header), seal)
}
func (ew *engineWrapper) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) {
pheaders := make([]*ptypes.Header, len(headers))
for i, header := range headers {
pheaders[i] = gethToUtilsHeader(header)
}
return ew.engine.VerifyHeaders(&WrappedHeaderReader{chain, nil}, pheaders, seals)
}
func (ew *engineWrapper) VerifyUncles(chain consensus.ChainReader, block *types.Block) error {
return ew.engine.VerifyUncles(&WrappedChainReader{chain, nil}, gethToUtilsBlock(block))
}
func (ew *engineWrapper) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error {
uHeader := gethToUtilsHeader(header)
if err := ew.engine.Prepare(&WrappedHeaderReader{chain, nil}, uHeader); err != nil {
return err
}
*header = *utilsToGethHeader(uHeader)
return nil
}
func (ew *engineWrapper) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, withdrawals []*types.Withdrawal) {
ew.engine.Finalize(&WrappedHeaderReader{chain, nil}, gethToUtilsHeader(header), wrappers.NewWrappedStateDB(state), gethToUtilsTransactions(txs), gethToUtilsHeaders(uncles), gethToUtilsWithdrawals(withdrawals))
}
func (ew *engineWrapper) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt, withdrawals []*types.Withdrawal) (*types.Block, error) {
block, err := ew.engine.FinalizeAndAssemble(&WrappedHeaderReader{chain, nil}, gethToUtilsHeader(header), wrappers.NewWrappedStateDB(state), gethToUtilsTransactions(txs), gethToUtilsHeaders(uncles), gethToUtilsReceipts(receipts), gethToUtilsWithdrawals(withdrawals))
return utilsToGethBlock(block), err
}
func (ew *engineWrapper) Seal(chain consensus.ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error {
return ew.engine.Seal(&WrappedHeaderReader{chain, nil}, gethToUtilsBlock(block), gethToUtilsBlockChan(results), stop)
}
func (ew *engineWrapper) SealHash(header *types.Header) common.Hash {
return common.Hash(ew.engine.SealHash(gethToUtilsHeader(header)))
}
func (ew *engineWrapper) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int {
return ew.engine.CalcDifficulty(&WrappedHeaderReader{chain, nil}, time, gethToUtilsHeader(parent))
}
func (ew *engineWrapper) APIs(chain consensus.ChainHeaderReader) []rpc.API {
papis := ew.engine.APIs(&WrappedHeaderReader{chain, nil})
apis := make([]rpc.API, len(papis))
for i, api := range papis {
apis[i] = rpc.API{
Namespace: api.Namespace,
Version: api.Version,
Service: api.Service,
Public: api.Public,
}
}
return apis
}
func (ew *engineWrapper) Close() error {
return ew.engine.Close()
}