Skip to content

Commit

Permalink
Merge pull request ethereum#5 from OffchainLabs/backend
Browse files Browse the repository at this point in the history
arbitrum: initial create backend and API
  • Loading branch information
PlasmaPower authored Oct 22, 2021
2 parents 694bf69 + b70e499 commit f54ee9b
Show file tree
Hide file tree
Showing 3 changed files with 372 additions and 0 deletions.
256 changes: 256 additions & 0 deletions arbitrum/apibackend.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
package arbitrum

import (
"context"
"errors"
"math/big"
"time"

"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/bloombits"
"github.com/ethereum/go-ethereum/core/rawdb"
"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/eth/filters"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
)

type APIBackend struct {
b *Backend
}

func createRegisterAPIBackend(backend *Backend) {
backend.apiBackend = &APIBackend{
b: backend,
}
backend.stack.RegisterAPIs(backend.apiBackend.GetAPIs())
}

func (a *APIBackend) GetAPIs() []rpc.API {
apis := ethapi.GetAPIs(a)

apis = append(apis, rpc.API{
Namespace: "eth",
Version: "1.0",
Service: filters.NewPublicFilterAPI(a, false, 5*time.Minute),
Public: true,
})
return apis
}

// General Ethereum API
func (a *APIBackend) SyncProgress() ethereum.SyncProgress {
panic("not implemented") // TODO: Implement
}

func (a *APIBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
panic("not implemented") // TODO: Implement
}

func (a *APIBackend) FeeHistory(ctx context.Context, blockCount int, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, error) {
panic("not implemented") // TODO: Implement
}

func (a *APIBackend) ChainDb() ethdb.Database {
return a.b.ethDatabase
}

func (a *APIBackend) AccountManager() *accounts.Manager {
return a.b.stack.AccountManager()
}

func (a *APIBackend) ExtRPCEnabled() bool {
panic("not implemented") // TODO: Implement
}

func (a *APIBackend) RPCGasCap() uint64 {
panic("not implemented") // TODO: Implement
}

func (a *APIBackend) RPCTxFeeCap() float64 {
return a.b.ethConfig.RPCTxFeeCap
}

func (a *APIBackend) UnprotectedAllowed() bool {
return true // TODO: is that true?
}

// Blockchain API
func (a *APIBackend) SetHead(number uint64) {
panic("not implemented") // TODO: Implement
}

func (a *APIBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) {
if number == rpc.LatestBlockNumber {
return a.b.blockChain.CurrentBlock().Header(), nil
}
return a.b.blockChain.GetHeaderByNumber(uint64(number.Int64())), nil
}

func (a *APIBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) {
return a.b.blockChain.GetHeaderByHash(hash), nil
}

func (a *APIBackend) HeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Header, error) {
number, isnum := blockNrOrHash.Number()
if isnum {
return a.HeaderByNumber(ctx, number)
}
hash, ishash := blockNrOrHash.Hash()
if ishash {
return a.HeaderByHash(ctx, hash)
}
return nil, errors.New("invalid arguments; neither block nor hash specified")
}

func (a *APIBackend) CurrentHeader() *types.Header {
return a.b.blockChain.CurrentHeader()
}

func (a *APIBackend) CurrentBlock() *types.Block {
return a.b.blockChain.CurrentBlock()
}

func (a *APIBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) {
return a.b.blockChain.GetBlockByNumber(uint64(number.Int64())), nil
}

func (a *APIBackend) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) {
return a.b.blockChain.GetBlockByHash(hash), nil
}

func (a *APIBackend) BlockByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Block, error) {
number, isnum := blockNrOrHash.Number()
if isnum {
return a.BlockByNumber(ctx, number)
}
hash, ishash := blockNrOrHash.Hash()
if ishash {
return a.BlockByHash(ctx, hash)
}
return nil, errors.New("invalid arguments; neither block nor hash specified")
}

func (a *APIBackend) stateAndHeaderFromHeader(header *types.Header, err error) (*state.StateDB, *types.Header, error) {
if err != nil {
return nil, header, err
}
if header == nil {
return nil, nil, errors.New("header not found")
}
state, err := a.b.blockChain.StateAt(header.Root)
return state, header, err
}

func (a *APIBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*state.StateDB, *types.Header, error) {
return a.stateAndHeaderFromHeader(a.HeaderByNumber(ctx, number))
}

func (a *APIBackend) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *types.Header, error) {
return a.stateAndHeaderFromHeader(a.HeaderByNumberOrHash(ctx, blockNrOrHash))
}

func (a *APIBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
return a.b.blockChain.GetReceiptsByHash(hash), nil
}

func (a *APIBackend) GetTd(ctx context.Context, hash common.Hash) *big.Int {
panic("not implemented") // TODO: Implement
}

func (a *APIBackend) GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header, vmConfig *vm.Config) (*vm.EVM, func() error, error) {
panic("not implemented") // TODO: Implement
}

func (a *APIBackend) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription {
return a.b.blockChain.SubscribeChainEvent(ch)
}

func (a *APIBackend) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription {
return a.b.blockChain.SubscribeChainHeadEvent(ch)
}

func (a *APIBackend) SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription {
return a.b.blockChain.SubscribeChainSideEvent(ch)
}

// Transaction pool API
func (a *APIBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
return a.b.EnqueueL2Message(signedTx)
}

func (a *APIBackend) GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) {
tx, blockHash, blockNumber, index := rawdb.ReadTransaction(a.b.ethDatabase, txHash)
return tx, blockHash, blockNumber, index, nil
}

func (a *APIBackend) GetPoolTransactions() (types.Transactions, error) {
panic("not implemented") // TODO: Implement
}

func (a *APIBackend) GetPoolTransaction(txHash common.Hash) *types.Transaction {
panic("not implemented") // TODO: Implement
}

func (a *APIBackend) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) {
panic("not implemented") // TODO: Implement
}

func (a *APIBackend) Stats() (pending int, queued int) {
panic("not implemented") // TODO: Implement
}

func (a *APIBackend) TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) {
panic("not implemented") // TODO: Implement
}

func (a *APIBackend) TxPoolContentFrom(addr common.Address) (types.Transactions, types.Transactions) {
panic("not implemented") // TODO: Implement
}

func (a *APIBackend) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription {
return a.b.SubscribeNewTxsEvent(ch)
}

// Filter API
func (a *APIBackend) BloomStatus() (uint64, uint64) {
panic("not implemented") // TODO: Implement
}

func (a *APIBackend) GetLogs(ctx context.Context, blockHash common.Hash) ([][]*types.Log, error) {
panic("not implemented") // TODO: Implement
}

func (a *APIBackend) ServiceFilter(ctx context.Context, session *bloombits.MatcherSession) {
panic("not implemented") // TODO: Implement
}

func (a *APIBackend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription {
return a.b.blockChain.SubscribeLogsEvent(ch)
}

func (a *APIBackend) SubscribePendingLogsEvent(ch chan<- []*types.Log) event.Subscription {
//Arbitrum doesn't really need pending logs. Logs are published as soon as we know them..
return a.SubscribeLogsEvent(ch)
}

func (a *APIBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription {
return a.b.blockChain.SubscribeRemovedLogsEvent(ch)
}

func (a *APIBackend) ChainConfig() *params.ChainConfig {
return a.b.blockChain.Config()
}

func (a *APIBackend) Engine() consensus.Engine {
return a.b.blockChain.Engine()
}
12 changes: 12 additions & 0 deletions arbitrum/arbos_interface.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package arbitrum

import (
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
)

type ArbosWrapper interface {
BuildBlock(force bool) (*types.Block, types.Receipts, *state.StateDB)

EnqueueSequencerTx(tx *types.Transaction) error
}
104 changes: 104 additions & 0 deletions arbitrum/backend.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package arbitrum

import (
"math/big"

"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/eth/ethconfig"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/node"
)

type Backend struct {
arbos ArbosWrapper
blockChain *core.BlockChain
stack *node.Node
chainId *big.Int
apiBackend *APIBackend
ethConfig *ethconfig.Config
ethDatabase ethdb.Database

txFeed event.Feed
scope event.SubscriptionScope

chanTxs chan *types.Transaction
chanClose chan struct{} //close coroutine
chanNewBlock chan struct{} //create new L2 block unless empty
}

func NewBackend(stack *node.Node, config *ethconfig.Config, ethDatabase ethdb.Database, blockChain *core.BlockChain, chainId *big.Int, arbos ArbosWrapper) (*Backend, error) {
backend := &Backend{
arbos: arbos,
blockChain: blockChain,
stack: stack,
chainId: chainId,
ethConfig: config,
ethDatabase: ethDatabase,
chanTxs: make(chan *types.Transaction, 100),
chanClose: make(chan struct{}, 1),
chanNewBlock: make(chan struct{}, 1),
}
stack.RegisterLifecycle(backend)
go backend.segmentQueueRoutine()

createRegisterAPIBackend(backend)
return backend, nil
}

func (b *Backend) APIBackend() *APIBackend {
return b.apiBackend
}

func (b *Backend) EnqueueL2Message(tx *types.Transaction) error {
b.chanTxs <- tx
return nil
}

func (b *Backend) CloseBlock() {
b.chanNewBlock <- struct{}{}
}

func (b *Backend) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription {
return b.scope.Track(b.txFeed.Subscribe(ch))
}

func (b *Backend) enqueueBlock(block *types.Block, reciepts types.Receipts, state *state.StateDB) {
if block == nil {
return
}
logs := make([]*types.Log, 0)
for _, receipt := range reciepts {
logs = append(logs, receipt.Logs...)
}
b.blockChain.WriteBlockWithState(block, reciepts, logs, state, true)
}

func (b *Backend) segmentQueueRoutine() {
for {
select {
case tx := <-b.chanTxs:
b.txFeed.Send(core.NewTxsEvent{Txs: []*types.Transaction{tx}})
b.arbos.EnqueueSequencerTx(tx)
case <-b.chanNewBlock:
b.enqueueBlock(b.arbos.BuildBlock(true))
case <-b.chanClose:
return
}
}
}

//TODO: this is used when registering backend as lifecycle in stack
func (b *Backend) Start() error {
return nil
}

func (b *Backend) Stop() error {

b.scope.Close()
b.blockChain.Stop()

return nil
}

0 comments on commit f54ee9b

Please sign in to comment.