package wrappers import ( "context" "encoding/json" "fmt" "math/big" "reflect" "sync" "time" "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" gcore "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/node" // "github.com/ethereum/go-ethereum/plugins/interfaces" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" "github.com/openrelayxyz/plugeth-utils/core" "github.com/openrelayxyz/plugeth-utils/restricted" "github.com/openrelayxyz/plugeth-utils/restricted/params" ) type WrappedScopeContext struct { s *vm.ScopeContext } func NewWrappedScopeContext(s *vm.ScopeContext) *WrappedScopeContext { return &WrappedScopeContext{s} } func (w *WrappedScopeContext) Memory() core.Memory { return w.s.Memory } func (w *WrappedScopeContext) Stack() core.Stack { return w.s.Stack } func (w *WrappedScopeContext) Contract() core.Contract { return &WrappedContract{w.s.Contract} } type WrappedContract struct { c *vm.Contract } func (w *WrappedContract) AsDelegate() core.Contract { return &WrappedContract{w.c.AsDelegate()} } func (w *WrappedContract) GetOp(n uint64) core.OpCode { return core.OpCode(w.c.GetOp(n)) } func (w *WrappedContract) GetByte(n uint64) byte { return w.c.GetByte(n) } func (w *WrappedContract) Caller() core.Address { return core.Address(w.c.Caller()) } func (w *WrappedContract) Address() core.Address { return core.Address(w.c.Address()) } func (w *WrappedContract) Value() *big.Int { return w.c.Value() } // added UseGas bc compiler compained without it. Should investigate if the false return with effect performance. // take this out of core.interface func (w *WrappedContract) UseGas(gas uint64) (ok bool) { return false } type WrappedTracer struct { r core.TracerResult } func NewWrappedTracer(r core.TracerResult) *WrappedTracer { return &WrappedTracer{r} } func (w WrappedTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { w.r.CaptureStart(core.Address(from), core.Address(to), create, input, gas, value) } func (w WrappedTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { w.r.CaptureState(pc, core.OpCode(op), gas, cost, &WrappedScopeContext{scope}, rData, depth, err) } func (w WrappedTracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) { w.r.CaptureFault(pc, core.OpCode(op), gas, cost, &WrappedScopeContext{scope}, depth, err) } func (w WrappedTracer) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) { w.r.CaptureEnd(output, gasUsed, t, err) } // TODO: Align these with PluGeth-utils func (w WrappedTracer) CaptureEnter(vm.OpCode, common.Address, common.Address, []byte, uint64, *big.Int) {} func (w WrappedTracer) CaptureExit([]byte, uint64, error) {} func (w WrappedTracer) GetResult() (interface{}, error) { return w.r.Result() } type WrappedStateDB struct { s *state.StateDB } func NewWrappedStateDB(d *state.StateDB) *WrappedStateDB { return &WrappedStateDB{d} } // GetBalance(Address) *big.Int func (w *WrappedStateDB) GetBalance(addr core.Address) *big.Int { return w.s.GetBalance(common.Address(addr)) } // GetNonce(Address) uint64 func (w *WrappedStateDB) GetNonce(addr core.Address) uint64 { return w.s.GetNonce(common.Address(addr)) } // GetCodeHash(Address) Hash func (w *WrappedStateDB) GetCodeHash(addr core.Address) core.Hash { return core.Hash(w.s.GetCodeHash(common.Address(addr))) } // sort this out // GetCode(Address) []byte func (w *WrappedStateDB) GetCode(addr core.Address) []byte { return w.s.GetCode(common.Address(addr)) } // GetCodeSize(Address) int func (w *WrappedStateDB) GetCodeSize(addr core.Address) int { return w.s.GetCodeSize(common.Address(addr)) } //GetRefund() uint64 func (w *WrappedStateDB) GetRefund() uint64 { //are we sure we want to include this? getting a refund seems like changing state return w.s.GetRefund() } // GetCommittedState(Address, Hash) Hash func (w *WrappedStateDB) GetCommittedState(addr core.Address, hsh core.Hash) core.Hash { return core.Hash(w.s.GetCommittedState(common.Address(addr), common.Hash(hsh))) } // GetState(Address, Hash) Hash func (w *WrappedStateDB) GetState(addr core.Address, hsh core.Hash) core.Hash { return core.Hash(w.s.GetState(common.Address(addr), common.Hash(hsh))) } // HasSuicided(Address) bool func (w *WrappedStateDB) HasSuicided(addr core.Address) bool { // I figured we'd skip some of the future labor and update the name now return w.s.HasSuicided(common.Address(addr)) } // // Exist reports whether the given account exists in state. // // Notably this should also return true for suicided accounts. // Exist(Address) bool func (w *WrappedStateDB) Exist(addr core.Address) bool { return w.s.Exist(common.Address(addr)) } // // Empty returns whether the given account is empty. Empty // // is defined according to EIP161 (balance = nonce = code = 0). // Empty(Address) bool func (w *WrappedStateDB) Empty(addr core.Address) bool { return w.s.Empty(common.Address(addr)) } // AddressInAccessList(addr Address) bool func (w *WrappedStateDB) AddressInAccessList(addr core.Address) bool { return w.s.AddressInAccessList(common.Address(addr)) } // SlotInAccessList(addr Address, slot Hash) (addressOk bool, slotOk bool) func (w *WrappedStateDB) SlotInAccessList(addr core.Address, slot core.Hash) (addressOK, slotOk bool) { return w.s.SlotInAccessList(common.Address(addr), common.Hash(slot)) } type Node struct { n *node.Node } func NewNode(n *node.Node) *Node { return &Node{n} } func (n *Node) Server() core.Server { return n.n.Server() } func (n *Node) DataDir() string { return n.n.DataDir() } func (n *Node) InstanceDir() string { return n.n.InstanceDir() } func (n *Node) IPCEndpoint() string { return n.n.IPCEndpoint() } func (n *Node) HTTPEndpoint() string { return n.n.HTTPEndpoint() } func (n *Node) WSEndpoint() string { return n.n.WSEndpoint() } func (n *Node) ResolvePath(x string) string { return n.n.ResolvePath(x) } func (n *Node) Attach() (core.Client, error) { return n.n.Attach() } type Backend struct { b ethapi.Backend newTxsFeed event.Feed newTxsOnce sync.Once chainFeed event.Feed chainOnce sync.Once chainHeadFeed event.Feed chainHeadOnce sync.Once chainSideFeed event.Feed chainSideOnce sync.Once logsFeed event.Feed logsOnce sync.Once pendingLogsFeed event.Feed pendingLogsOnce sync.Once removedLogsFeed event.Feed removedLogsOnce sync.Once chainConfig *params.ChainConfig } func NewBackend(b ethapi.Backend) *Backend { return &Backend{b: b} } func (b *Backend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) { return b.b.SuggestGasTipCap(ctx) } func (b *Backend) ChainDb() restricted.Database { return &dbWrapper{b.b.ChainDb()} } func (b *Backend) ExtRPCEnabled() bool { return b.b.ExtRPCEnabled() } func (b *Backend) RPCGasCap() uint64 { return b.b.RPCGasCap() } func (b *Backend) RPCTxFeeCap() float64 { return b.b.RPCTxFeeCap() } func (b *Backend) UnprotectedAllowed() bool { return b.b.UnprotectedAllowed() } func (b *Backend) SetHead(number uint64) { b.b.SetHead(number) } func (b *Backend) HeaderByNumber(ctx context.Context, number int64) ([]byte, error) { header, err := b.b.HeaderByNumber(ctx, rpc.BlockNumber(number)) if err != nil { return nil, err } return rlp.EncodeToBytes(header) } func (b *Backend) HeaderByHash(ctx context.Context, hash core.Hash) ([]byte, error) { header, err := b.b.HeaderByHash(ctx, common.Hash(hash)) if err != nil { return nil, err } return rlp.EncodeToBytes(header) } func (b *Backend) CurrentHeader() []byte { ret, _ := rlp.EncodeToBytes(b.b.CurrentHeader()) return ret } func (b *Backend) CurrentBlock() []byte { ret, _ := rlp.EncodeToBytes(b.b.CurrentBlock()) return ret } func (b *Backend) BlockByNumber(ctx context.Context, number int64) ([]byte, error) { block, err := b.b.BlockByNumber(ctx, rpc.BlockNumber(number)) if err != nil { return nil, err } return rlp.EncodeToBytes(block) } func (b *Backend) BlockByHash(ctx context.Context, hash core.Hash) ([]byte, error) { block, err := b.b.BlockByHash(ctx, common.Hash(hash)) if err != nil { return nil, err } return rlp.EncodeToBytes(block) } func (b *Backend) GetReceipts(ctx context.Context, hash core.Hash) ([]byte, error) { receipts, err := b.b.GetReceipts(ctx, common.Hash(hash)) if err != nil { return nil, err } return json.Marshal(receipts) } func (b *Backend) GetTd(ctx context.Context, hash core.Hash) *big.Int { return b.b.GetTd(ctx, common.Hash(hash)) } func (b *Backend) SendTx(ctx context.Context, signedTx []byte) error { tx := new(types.Transaction) if err := tx.UnmarshalBinary(signedTx); err != nil { return err } return b.b.SendTx(ctx, tx) } func (b *Backend) GetTransaction(ctx context.Context, txHash core.Hash) ([]byte, core.Hash, uint64, uint64, error) { // RLP Encoded transaction { tx, blockHash, blockNumber, index, err := b.b.GetTransaction(ctx, common.Hash(txHash)) if err != nil { return nil, core.Hash(blockHash), blockNumber, index, err } enc, err := tx.MarshalBinary() return enc, core.Hash(blockHash), blockNumber, index, err } func (b *Backend) GetPoolTransactions() ([][]byte, error) { txs, err := b.b.GetPoolTransactions() if err != nil { return nil, err } results := make([][]byte, len(txs)) for i, tx := range txs { results[i], _ = rlp.EncodeToBytes(tx) } return results, nil } func (b *Backend) GetPoolTransaction(txHash core.Hash) []byte { tx := b.b.GetPoolTransaction(common.Hash(txHash)) if tx == nil { return []byte{} } enc, _ := rlp.EncodeToBytes(tx) return enc } func (b *Backend) GetPoolNonce(ctx context.Context, addr core.Address) (uint64, error) { return b.b.GetPoolNonce(ctx, common.Address(addr)) } func (b *Backend) Stats() (pending int, queued int) { return b.b.Stats() } func (b *Backend) TxPoolContent() (map[core.Address][][]byte, map[core.Address][][]byte) { pending, queued := b.b.TxPoolContent() trpending, trqueued := make(map[core.Address][][]byte), make(map[core.Address][][]byte) for k, v := range pending { trpending[core.Address(k)] = make([][]byte, len(v)) for i, tx := range v { trpending[core.Address(k)][i], _ = tx.MarshalBinary() } } for k, v := range queued { trqueued[core.Address(k)] = make([][]byte, len(v)) for i, tx := range v { trpending[core.Address(k)][i], _ = tx.MarshalBinary() } } return trpending, trqueued } // RLP encoded transactions func (b *Backend) BloomStatus() (uint64, uint64) { return b.b.BloomStatus() } func (b *Backend) GetLogs(ctx context.Context, blockHash core.Hash) ([][]byte, error) { logs, err := b.b.GetLogs(ctx, common.Hash(blockHash)) if err != nil { return nil, err } encLogs := make([][]byte, len(logs)) for i, log := range logs { encLogs[i], _ = rlp.EncodeToBytes(log) } return encLogs, nil } // []RLP encoded logs type dli interface { SyncProgress() ethereum.SyncProgress } type dl struct { dl dli } type progress struct { p ethereum.SyncProgress } func (p *progress) StartingBlock() uint64 { return p.p.StartingBlock } func (p *progress) CurrentBlock() uint64 { return p.p.CurrentBlock } func (p *progress) HighestBlock() uint64 { return p.p.HighestBlock } func (p *progress) PulledStates() uint64 { return p.p.PulledStates } func (p *progress) KnownStates() uint64 { return p.p.KnownStates } func (d *dl) Progress() core.Progress { return &progress{d.dl.SyncProgress()} } func (b *Backend) Downloader() core.Downloader { return &dl{b.b} } func (b *Backend) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) core.Subscription { var sub event.Subscription b.newTxsOnce.Do(func() { bch := make(chan gcore.NewTxsEvent, 100) sub = b.b.SubscribeNewTxsEvent(bch) go func() { for { select { case item := <-bch: txe := core.NewTxsEvent{ Txs: make([][]byte, len(item.Txs)), } for i, tx := range item.Txs { txe.Txs[i], _ = tx.MarshalBinary() } b.newTxsFeed.Send(txe) case err := <-sub.Err(): log.Warn("Subscription error for NewTxs", "err", err) return } } }() }) return b.newTxsFeed.Subscribe(ch) } func (b *Backend) SubscribeChainEvent(ch chan<- core.ChainEvent) core.Subscription { var sub event.Subscription b.chainOnce.Do(func() { bch := make(chan gcore.ChainEvent, 100) sub = b.b.SubscribeChainEvent(bch) go func() { for { select { case item := <-bch: ce := core.ChainEvent{ Hash: core.Hash(item.Hash), } ce.Block, _ = rlp.EncodeToBytes(item.Block) ce.Logs, _ = rlp.EncodeToBytes(item.Logs) b.chainFeed.Send(ce) case err := <-sub.Err(): log.Warn("Subscription error for Chain", "err", err) return } } }() }) return b.chainFeed.Subscribe(ch) } func (b *Backend) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) core.Subscription { var sub event.Subscription b.chainHeadOnce.Do(func() { bch := make(chan gcore.ChainHeadEvent, 100) sub = b.b.SubscribeChainHeadEvent(bch) go func() { for { select { case item := <-bch: che := core.ChainHeadEvent{} che.Block, _ = rlp.EncodeToBytes(item.Block) b.chainHeadFeed.Send(che) case err := <-sub.Err(): log.Warn("Subscription error for ChainHead", "err", err) return } } }() }) return b.chainHeadFeed.Subscribe(ch) } func (b *Backend) SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) core.Subscription { var sub event.Subscription b.chainSideOnce.Do(func() { bch := make(chan gcore.ChainSideEvent, 100) sub = b.b.SubscribeChainSideEvent(bch) go func() { for { select { case item := <-bch: cse := core.ChainSideEvent{} cse.Block, _ = rlp.EncodeToBytes(item.Block) b.chainSideFeed.Send(cse) case err := <-sub.Err(): log.Warn("Subscription error for ChainSide", "err", err) return } } }() }) return b.chainSideFeed.Subscribe(ch) } func (b *Backend) SubscribeLogsEvent(ch chan<- [][]byte) core.Subscription { var sub event.Subscription b.logsOnce.Do(func() { bch := make(chan []*types.Log, 100) sub = b.b.SubscribeLogsEvent(bch) go func() { for { select { case item := <-bch: logs := make([][]byte, len(item)) for i, log := range item { logs[i], _ = rlp.EncodeToBytes(log) } b.logsFeed.Send(logs) case err := <-sub.Err(): log.Warn("Subscription error for Logs", "err", err) return } } }() }) return b.logsFeed.Subscribe(ch) } // []RLP encoded logs func (b *Backend) SubscribePendingLogsEvent(ch chan<- [][]byte) core.Subscription { var sub event.Subscription b.pendingLogsOnce.Do(func() { bch := make(chan []*types.Log, 100) sub = b.b.SubscribePendingLogsEvent(bch) go func() { for { select { case item := <-bch: logs := make([][]byte, len(item)) for i, log := range item { logs[i], _ = rlp.EncodeToBytes(log) } b.pendingLogsFeed.Send(logs) case err := <-sub.Err(): log.Warn("Subscription error for PendingLogs", "err", err) return } } }() }) return b.pendingLogsFeed.Subscribe(ch) } // RLP Encoded logs func (b *Backend) SubscribeRemovedLogsEvent(ch chan<- []byte) core.Subscription { var sub event.Subscription b.removedLogsOnce.Do(func() { bch := make(chan gcore.RemovedLogsEvent, 100) sub = b.b.SubscribeRemovedLogsEvent(bch) go func() { for { select { case item := <-bch: logs := make([][]byte, len(item.Logs)) for i, log := range item.Logs { logs[i], _ = rlp.EncodeToBytes(log) } b.removedLogsFeed.Send(item) case err := <-sub.Err(): log.Warn("Subscription error for RemovedLogs", "err", err) return } } }() }) return b.removedLogsFeed.Subscribe(ch) } // RLP encoded logs 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 (b *Backend) ChainConfig() *params.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 b.chainConfig != nil { return b.chainConfig } b.chainConfig = ¶ms.ChainConfig{} nval := reflect.ValueOf(b.b.ChainConfig()) ntype := nval.Elem().Type() lval := reflect.ValueOf(b.chainConfig) 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 b.chainConfig }