forked from ethereum/go-ethereum
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request ethereum#5 from OffchainLabs/backend
arbitrum: initial create backend and API
- Loading branch information
Showing
3 changed files
with
372 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |