Commit 6b703c0d authored by vicotor's avatar vicotor

add chaindb and genesis

parent 411de197
...@@ -5,6 +5,7 @@ import ( ...@@ -5,6 +5,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/exchain/go-exchain/exchain" "github.com/exchain/go-exchain/exchain"
"github.com/exchain/go-exchain/exchain/chaindb"
nebulav1 "github.com/exchain/go-exchain/exchain/protocol/gen/go/nebula/v1" nebulav1 "github.com/exchain/go-exchain/exchain/protocol/gen/go/nebula/v1"
"github.com/exchain/go-exchain/op-node/p2p" "github.com/exchain/go-exchain/op-node/p2p"
"github.com/exchain/go-exchain/op-node/rollup/driver" "github.com/exchain/go-exchain/op-node/rollup/driver"
...@@ -13,79 +14,76 @@ import ( ...@@ -13,79 +14,76 @@ import (
"math/big" "math/big"
) )
type EngineAPI struct { type ExChainAPI struct {
chain chaindb.ChainDB
} }
func (e *EngineAPI) BlockRefByNumber(ctx context.Context, num uint64) (eth.BlockRef, error) { func (e *ExChainAPI) BlockRefByNumber(ctx context.Context, num uint64) (eth.BlockRef, error) {
//TODO implement me //TODO implement me
panic("implement me") panic("implement me")
} }
func (e *EngineAPI) FetchReceipts(ctx context.Context, blockHash common.Hash) (eth.BlockInfo, types.Receipts, error) { func (e *ExChainAPI) FetchReceipts(ctx context.Context, blockHash common.Hash) (eth.BlockInfo, types.Receipts, error) {
//TODO implement me //TODO implement me
panic("implement me") panic("implement me")
} }
func (e *EngineAPI) OutputV0AtBlock(ctx context.Context, blockHash common.Hash) (*eth.OutputV0, error) { func (e *ExChainAPI) OutputV0AtBlock(ctx context.Context, blockHash common.Hash) (*eth.OutputV0, error) {
//TODO implement me //TODO implement me
panic("implement me") panic("implement me")
} }
func (e *EngineAPI) ChainID(ctx context.Context) (*big.Int, error) { func (e *ExChainAPI) ChainID(ctx context.Context) (*big.Int, error) {
//TODO implement me //TODO implement me
panic("implement me") panic("implement me")
} }
func (e *EngineAPI) NewPayload(params exchain.PayloadParams) (exchain.ExecutionResult, error) { func (e *ExChainAPI) NewPayload(params exchain.PayloadParams) (exchain.ExecutionResult, error) {
//TODO implement me //TODO implement me
panic("implement me") panic("implement me")
} }
func (e *EngineAPI) ProcessPayload(block *nebulav1.Block) error { func (e *ExChainAPI) ProcessPayload(block *nebulav1.Block) error {
//TODO implement me //TODO implement me
panic("implement me") panic("implement me")
} }
//func (e *EngineAPI) PayloadByHash(ctx context.Context, hash common.Hash) (*eth.ExecutionPayloadEnvelope, error) { func (e *ExChainAPI) PayloadByNumber(ctx context.Context, u uint64) (*eth.ExecutionPayloadEnvelope, error) {
// //TODO implement me
// panic("implement me")
//}
func (e *EngineAPI) PayloadByNumber(ctx context.Context, u uint64) (*eth.ExecutionPayloadEnvelope, error) {
//TODO implement me //TODO implement me
panic("implement me") panic("implement me")
} }
func (e *EngineAPI) L2BlockRefByLabel(ctx context.Context, label eth.BlockLabel) (eth.L2BlockRef, error) { func (e *ExChainAPI) L2BlockRefByLabel(ctx context.Context, label eth.BlockLabel) (eth.L2BlockRef, error) {
//TODO implement me //TODO implement me
panic("implement me") panic("implement me")
} }
func (e *EngineAPI) L2BlockRefByHash(ctx context.Context, l2Hash common.Hash) (eth.L2BlockRef, error) { func (e *ExChainAPI) L2BlockRefByHash(ctx context.Context, l2Hash common.Hash) (eth.L2BlockRef, error) {
//TODO implement me //TODO implement me
panic("implement me") panic("implement me")
} }
func (e *EngineAPI) L2BlockRefByNumber(ctx context.Context, num uint64) (eth.L2BlockRef, error) { func (e *ExChainAPI) L2BlockRefByNumber(ctx context.Context, num uint64) (eth.L2BlockRef, error) {
//TODO implement me //TODO implement me
panic("implement me") panic("implement me")
} }
func (e *EngineAPI) SystemConfigByL2Hash(ctx context.Context, hash common.Hash) (eth.SystemConfig, error) { func (e *ExChainAPI) SystemConfigByL2Hash(ctx context.Context, hash common.Hash) (eth.SystemConfig, error) {
//TODO implement me //TODO implement me
panic("implement me") panic("implement me")
} }
func (e *EngineAPI) Close() { func (e *ExChainAPI) Close() {
} }
var ( var (
_ p2p.L2Chain = (*EngineAPI)(nil) _ p2p.L2Chain = (*ExChainAPI)(nil)
_ sync.L2Chain = (*EngineAPI)(nil) _ sync.L2Chain = (*ExChainAPI)(nil)
_ driver.L2Chain = (*EngineAPI)(nil) _ driver.L2Chain = (*ExChainAPI)(nil)
) )
func NewEngineAPI(database exchain.Database) *EngineAPI { func NewEngineAPI(database chaindb.ChainDB) *ExChainAPI {
return &EngineAPI{} return &ExChainAPI{
chain: database,
}
} }
package chaindb
import (
"errors"
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/event"
"github.com/exchain/go-exchain/exchain"
nebulav1 "github.com/exchain/go-exchain/exchain/protocol/gen/go/nebula/v1"
"github.com/exchain/go-exchain/metadb"
"github.com/exchain/go-exchain/metadb/memdb"
"github.com/exchain/go-exchain/op-service/grouptask"
"github.com/golang/protobuf/proto"
"github.com/holiman/uint256"
log "github.com/sirupsen/logrus"
"sync"
"sync/atomic"
"time"
lru "github.com/hashicorp/golang-lru"
)
type ChainDB interface {
Database() metadb.Database
ChainId() (*uint256.Int, error)
CurrentHeight() uint256.Int
GetOriginBlockData(*uint256.Int) ([]byte, error)
GetBlock(*uint256.Int) *nebulav1.Block
GetBlockHeader(*uint256.Int) *nebulav1.BlockHeader
BlockHeaderByHash(hash common.Hash) *nebulav1.BlockHeader
BlockByHash(hash common.Hash) *nebulav1.Block
GetTransaction(hash common.Hash) (*nebulav1.Transaction, error)
GetReceipt(hash common.Hash) *nebulav1.TransactionReceipt
SaveBlockHeader(header *nebulav1.BlockHeader) error
SaveBlockData(*nebulav1.Block, *nebulav1.TransactionReceiptList) error
SubscribeChainEvent(ch chan<- exchain.ChainEvent) event.Subscription
EmitChain(block *nebulav1.Block, hash common.Hash)
ResetHeight(*uint256.Int, bool) error
}
var (
big10 = uint256.NewInt(10)
big1 = uint256.NewInt(1)
)
func NewChainDB(database metadb.Database) ChainDB {
chain := &chaindb{
database: database,
cache: memdb.NewMemDB(),
toSaveData: make(chan chainData, 1000000),
}
var err error
if chain.txCache, err = lru.New(1000000); err != nil {
panic(err)
}
if chain.receiptCache, err = lru.New(1000000); err != nil {
panic(err)
}
chain.storeTask()
return chain
}
type chainData struct {
block *nebulav1.Block
receipts *nebulav1.TransactionReceiptList
}
type chaindb struct {
cache metadb.CacheKV
txCache *lru.Cache
receiptCache *lru.Cache
database metadb.Database
height atomic.Value
toSaveData chan chainData
startHeight *uint256.Int
blockConfirmFeed event.Feed
chainFeed event.Feed
chainHeadFeed event.Feed
logsFeed event.Feed
rmlogsFeed event.Feed
scope event.SubscriptionScope
}
func (m *chaindb) ChainId() (*uint256.Int, error) {
k := chainIdKey()
if v, err := m.database.Get([]byte(k)); err != nil {
return nil, err
} else {
return new(uint256.Int).SetBytes(v), nil
}
}
func (m *chaindb) SaveChainId(chainid *uint256.Int) error {
k := chainIdKey()
return m.database.Put([]byte(k), chainid.Bytes())
}
func (m *chaindb) SubscribeChainEvent(ch chan<- exchain.ChainEvent) event.Subscription {
return m.scope.Track(m.chainFeed.Subscribe(ch))
}
func (m *chaindb) GetTransaction(hash common.Hash) (*nebulav1.Transaction, error) {
if tx, exist := m.txCache.Get(hash); exist {
ptx := tx.(*nebulav1.Transaction)
return ptx, nil
} else {
entry, err := m.getTxEntry(hash)
if err != nil {
return nil, err
}
return m.getTransaction(uint256.NewInt(entry.BlockNumber), int(entry.Index))
}
}
func (m *chaindb) GetReceipt(txhash common.Hash) *nebulav1.TransactionReceipt {
if r, exist := m.receiptCache.Get(txhash); exist {
return r.(*nebulav1.TransactionReceipt)
} else {
entry, err := m.getTxEntry(txhash)
if err != nil {
return nil
}
return m.getReceipt(uint256.NewInt(entry.BlockNumber), int(entry.Index))
}
}
func (m *chaindb) CurrentHeight() uint256.Int {
var height = uint256.NewInt(0)
if v := m.height.Load(); v != nil {
return *v.(*uint256.Int)
} else {
// load height string
h, err := m.database.Get([]byte(chainHeightKey()))
if err == nil {
height, _ = uint256.FromDecimal(string(h))
}
m.height.Store(height)
}
return *height
}
func (m *chaindb) GetBlock(num *uint256.Int) *nebulav1.Block {
return m.GetBlockBody(num)
}
func (m *chaindb) GetBlockBody(num *uint256.Int) *nebulav1.Block {
k := blockBodyKey(num)
if b, exist := m.cache.Get(k); exist {
return b.(*nebulav1.Block)
} else {
d, err := m.database.Get([]byte(k))
if err != nil {
return nil
}
block := new(nebulav1.Block)
if err = proto.Unmarshal(d, block); err != nil {
return nil
}
return block
}
}
func (m *chaindb) GetBlockHeader(num *uint256.Int) *nebulav1.BlockHeader {
k := blockHeaderKey(num)
if h, exist := m.cache.Get(k); exist {
return h.(*nebulav1.BlockHeader)
} else {
d, err := m.database.Get([]byte(k))
if err != nil {
return nil
}
header := new(nebulav1.BlockHeader)
if err = proto.Unmarshal(d, header); err != nil {
return nil
}
return header
}
}
func (m *chaindb) GetOriginBlockData(num *uint256.Int) (block []byte, err error) {
k := blockBodyKey(num)
if b, exist := m.cache.Get(k); exist {
blk := b.(*nebulav1.Block)
return proto.Marshal(blk)
}
return m.database.Get([]byte(k))
}
func (m *chaindb) BlockHeaderByHash(hash common.Hash) *nebulav1.BlockHeader {
number := m.blockNumberByHash(hash)
if number == nil {
return nil
}
return m.GetBlockHeader(number)
}
func (m *chaindb) BlockByHash(hash common.Hash) *nebulav1.Block {
number := m.blockNumberByHash(hash)
if number == nil {
return nil
}
return m.GetBlock(number)
}
func (m *chaindb) GetBlockTransactions(num *uint256.Int) *nebulav1.TransactionList {
txs := m.getBlockTxs(num)
return txs
}
func (m *chaindb) GetBlockReceipts(num *uint256.Int) *nebulav1.TransactionReceiptList {
return m.getBlockReceipts(num)
}
func (m *chaindb) Database() metadb.Database {
return m.database
}
func (m *chaindb) chainDataSaveTask() {
var duration = time.Second * 10
tm := time.NewTicker(duration)
defer tm.Stop()
for {
select {
case data := <-m.toSaveData:
// 1. save txhash -> entry
// 2. save blockHash -> number
// 3. save block header
// 4. save block body
// 5. save block receipts
// 6. save block accounts
// 7. save chain height
block := data.block
log.Infof("save block with block number %d", block.Header.Height)
blockHash := common.BytesToHash(block.Header.Hash)
blockHeight := uint256.NewInt(block.Header.Height)
header := block.Header
{
t1 := time.Now()
// save blockHash -> number.Bytes()
bnk := blockNumKey(blockHash)
m.database.Put([]byte(bnk), blockHeight.Bytes())
t2 := time.Now()
log.Debugf("save block number key cost %d ms", t2.Sub(t1).Milliseconds())
}
{
t1 := time.Now()
// save block header data with number
hk := blockHeaderKey(blockHeight)
if dh, err := proto.Marshal(header); err != nil {
log.Error("save block header", "err", err)
panic(fmt.Sprintf("save block header with err %v", err))
} else {
if err := m.database.Put([]byte(hk), dh); err != nil {
log.Error("save block header", "err", err)
panic(fmt.Sprintf("save block header with err %v", err))
}
}
t2 := time.Now()
log.Debugf("save block header cost %d ms", t2.Sub(t1).Milliseconds())
// remove from cache.
m.cache.Delete(hk)
}
{
t1 := time.Now()
// save block body
bodyk := blockBodyKey(blockHeight)
dbody, err := proto.Marshal(block)
if err != nil {
log.Error("save block body", "err", err)
panic(fmt.Sprintf("save block body with err %v", err))
}
if err := m.database.Put([]byte(bodyk), dbody); err != nil {
log.Error("save block body", "err", err)
panic(fmt.Sprintf("save block body with err %v", err))
}
// remove from cache.
m.cache.Delete(bodyk)
t2 := time.Now()
log.Debugf("save block body with number %d, cost %d ms, size = %d", header.Height, t2.Sub(t1).Milliseconds(), len(dbody))
}
if data.receipts != nil && len(data.receipts.Receipts) > 0 {
t1 := time.Now()
// save block receipts
t2 := time.Now()
log.Debugf("save block receipts convert cost %d ms", t2.Sub(t1).Milliseconds())
t1 = time.Now()
receiptsk := blockReceiptsKey(blockHeight)
dreceipts, err := proto.Marshal(data.receipts)
if err != nil {
log.Error("save block receipts", "err", err)
panic(err)
}
if err := m.database.Put([]byte(receiptsk), dreceipts); err != nil {
log.Error("save block receipts", "err", err)
} else {
// remove from cache.
m.cache.Delete(receiptsk)
}
}
if data.receipts != nil && len(data.receipts.Receipts) > 0 {
type itemData struct {
hash common.Hash
entry txEntry
}
t1 := time.Now()
batch := m.database.NewBatch()
number := block.Header.Height
{
sequence := func(receipts []*nebulav1.TransactionReceipt) []interface{} {
s := make([]interface{}, len(receipts))
for i, r := range receipts {
hash := common.BytesToHash(r.Hash)
s[i] = &itemData{hash: hash, entry: txEntry{
BlockNumber: number,
Index: int64(i),
}}
}
return s
}
handler := func(item interface{}, chanIdx uint) {
idata := item.(*itemData)
batch.Put([]byte(txEntryKey(idata.hash)), idata.entry.Bytes())
m.txCache.Remove(idata.hash)
m.receiptCache.Remove(idata.hash)
}
{
t1 := time.Now()
seq := sequence(data.receipts.Receipts)
t2 := time.Now()
log.Debugf("save block txentry sequence cost %d ms", t2.Sub(t1).Milliseconds())
grouptask.DoMultiTasks(4, handler, seq...)
}
}
t2 := time.Now()
log.Debugf("save block txentry batch put cost %d ms", t2.Sub(t1).Milliseconds())
// save txhash -> entry with multi routine
if err := batch.Write(); err != nil {
log.Errorf("save block txentry error %s", err)
panic(err)
}
t3 := time.Now()
log.Debugf("save block txentry cost %d ms", t3.Sub(t1).Milliseconds())
}
{
// save latest height as string
k := chainHeightKey()
m.database.Put([]byte(k), []byte(blockHeight.String())) // ethdb save height string.
}
log.Infof("save block with block number %s finished", blockHeight.String())
case <-tm.C:
if m.startHeight != nil {
h := m.CurrentHeight()
for h.Cmp(m.startHeight.Add(m.startHeight, big10)) > 0 {
hk := blockHeaderKey(m.startHeight)
m.cache.Delete(hk)
m.startHeight = new(uint256.Int).AddUint64(m.startHeight, 1)
}
}
tm.Reset(duration)
}
}
}
func (m *chaindb) storeTask() {
go m.chainDataSaveTask()
}
func (m *chaindb) toStoreChainData(data chainData) {
m.toSaveData <- data
}
func (m *chaindb) blockNumberByHash(hash common.Hash) *uint256.Int {
k := blockNumKey(hash)
if number, exist := m.cache.Get(k); exist {
return number.(*uint256.Int)
} else {
d, err := m.database.Get([]byte(k))
if err != nil {
return nil
}
n := new(uint256.Int)
n.SetBytes(d)
return n
}
}
func (m *chaindb) getBlockTxs(num *uint256.Int) *nebulav1.TransactionList {
blockbody := m.GetBlockBody(num)
if blockbody == nil {
return nil
}
return blockbody.Transactions
}
func (m *chaindb) getTransaction(num *uint256.Int, index int) (*nebulav1.Transaction, error) {
blockTxs := m.getBlockTxs(num)
if blockTxs == nil {
return nil, errors.New("transaction not found")
}
if index >= len(blockTxs.Txs) || index < 0 {
return nil, errors.New("transaction not found")
}
return blockTxs.Txs[index], nil
}
func (m *chaindb) getBlockReceipts(num *uint256.Int) *nebulav1.TransactionReceiptList {
k := blockReceiptsKey(num)
d, err := m.database.Get([]byte(k))
if err != nil {
log.Error("GetBlockReceipts failed, ", err)
return nil
}
receipts := new(nebulav1.TransactionReceiptList)
if err := proto.Unmarshal(d, receipts); err != nil {
log.Error("GetBlockReceipts failed, ", err)
return nil
}
return receipts
}
func (m *chaindb) getReceipt(num *uint256.Int, index int) *nebulav1.TransactionReceipt {
blockReceipts := m.getBlockReceipts(num)
if blockReceipts == nil {
return nil
}
if index >= len(blockReceipts.Receipts) || index < 0 {
return nil
}
return blockReceipts.Receipts[index]
}
func (m *chaindb) cacheChainHeight(n *uint256.Int) {
if m.startHeight == nil {
m.startHeight = new(uint256.Int).Set(n)
}
m.height.Store(new(uint256.Int).Set(n)) // cache save uint256.Int
}
func (m *chaindb) cacheBlockHeader(header *nebulav1.BlockHeader) error {
k := blockHeaderKey(uint256.NewInt(header.Height))
m.cache.Set(k, header)
return nil
}
func (m *chaindb) cacheBlock(block *nebulav1.Block) error {
k := blockBodyKey(uint256.NewInt(block.Header.Height))
m.cache.Set(k, block)
return nil
}
func (m *chaindb) cacheBlockNumber(block *nebulav1.Block) error {
k := blockNumKey(common.BytesToHash(block.Header.Hash))
number := uint256.NewInt(block.Header.Height)
m.cache.Set(k, number)
return nil
}
func (m *chaindb) cacheBlockTxsInfo(txs *nebulav1.TransactionList, rs *nebulav1.TransactionReceiptList) error {
if txs == nil || rs == nil {
return nil
}
if len(txs.Txs) != len(rs.Receipts) {
return errors.New("txs and receipts not match")
}
for i, tx := range txs.Txs {
receipt := rs.Receipts[i]
txhash := common.BytesToHash(receipt.Hash)
m.txCache.Add(txhash, tx)
m.receiptCache.Add(txhash, receipt)
}
return nil
}
func (m *chaindb) cacheReceipts(rs []*nebulav1.TransactionReceipt) error {
for _, receipt := range rs {
m.receiptCache.Add(receipt.Hash, receipt)
}
return nil
}
func (m *chaindb) EmitChain(block *nebulav1.Block, hash common.Hash) {
var txCount = int64(0)
if block.Transactions != nil {
txCount = int64(len(block.Transactions.Txs))
}
event := exchain.ChainEvent{
Block: block,
BlockHash: hash,
TxCount: txCount,
}
m.chainFeed.Send(event)
}
func (m *chaindb) ResetHeight(height *uint256.Int, clear bool) error {
cur := m.CurrentHeight()
if cur.Cmp(height) < 0 {
return errors.New("given height too high")
}
if clear {
batch := m.database.NewBatch()
newh := uint256.NewInt(0).Set(&cur)
for ; newh.Cmp(height) > 0; newh = new(uint256.Int).SubUint64(newh, 1) {
receipts := m.GetBlockReceipts(newh)
if receipts != nil {
for _, r := range receipts.Receipts {
txhash := common.BytesToHash(r.Hash)
if err := batch.Delete([]byte(txEntryKey(txhash))); err != nil {
log.Debugf("delete tx entry %s failed, err:%s", txhash.String(), err)
}
if err := batch.Delete([]byte(transactionKey(txhash))); err != nil {
log.Debugf("delete tx %s failed, err:%s", txhash.String(), err)
}
if err := batch.Delete([]byte(receiptKey(txhash))); err != nil {
log.Debugf("delete tx receipt %s failed, err:%s", txhash.String(), err)
}
}
}
batch.Delete([]byte(blockHeaderKey(newh)))
batch.Delete([]byte(blockBodyKey(newh)))
log.Debugf("reset height delete data for height %s", newh.String())
batch.Put([]byte(chainHeightKey()), []byte(newh.String()))
}
batch.Put([]byte(chainHeightKey()), []byte(height.String()))
if err := batch.Write(); err != nil {
return errors.New(fmt.Sprintf("reset height failed:%v", err))
}
m.height.Store(new(uint256.Int).Set(height))
} else {
if err := m.database.Put([]byte(chainHeightKey()), []byte(height.String())); err != nil {
return errors.New(fmt.Sprintf("reset height failed:%v", err))
}
m.height.Store(height)
}
return nil
}
func (m *chaindb) SaveBlockHeader(header *nebulav1.BlockHeader) error {
hk := blockHeaderKey(uint256.NewInt(header.Height))
dh, err := proto.Marshal(header)
if err != nil {
return err
}
if err := m.database.Put([]byte(hk), dh); err != nil {
log.Error("database save block header", "err", err)
return err
}
return nil
}
func (m *chaindb) SaveBlockData(block *nebulav1.Block, rs *nebulav1.TransactionReceiptList) error {
m.cacheBlockHeader(block.Header)
m.cacheBlock(block)
wg := sync.WaitGroup{}
wg.Add(1)
go func() {
defer wg.Done()
// cache txs and receipt.
m.cacheBlockTxsInfo(block.Transactions, rs)
}()
height := uint256.NewInt(block.Header.Height)
m.cacheChainHeight(height)
wg.Wait()
data := chainData{
block: block,
receipts: rs,
}
m.toStoreChainData(data)
return nil
}
package chaindb
import (
"fmt"
"github.com/ethereum/go-ethereum/common"
"strconv"
"strings"
)
type txEntry struct {
BlockNumber uint64
Index int64
}
func (e *txEntry) Bytes() []byte {
d := fmt.Sprintf("%d,%d", e.BlockNumber, e.Index)
return []byte(d)
}
func (e *txEntry) SetBytes(data []byte) error {
splits := strings.Split(string(data), ",")
if len(splits) != 2 {
return fmt.Errorf("invalid txEntry bytes")
}
e.BlockNumber, _ = strconv.ParseUint(splits[0], 10, 0)
e.Index, _ = strconv.ParseInt(splits[1], 10, 0)
return nil
}
func (m *chaindb) storeTxEntry(hash common.Hash, entry txEntry) error {
k := txEntryKey(hash)
return m.database.Put([]byte(k), entry.Bytes())
}
func (m *chaindb) getTxEntry(hash common.Hash) (txEntry, error) {
k := txEntryKey(hash)
v, err := m.database.Get([]byte(k))
if err != nil {
return txEntry{}, err
}
var entry = new(txEntry)
if err := entry.SetBytes(v); err != nil {
return txEntry{}, err
}
return *entry, nil
}
package chaindb
import (
"github.com/ethereum/go-ethereum/common"
"github.com/holiman/uint256"
"strings"
)
const (
BlockNumKeyPrefix = "bn-"
BlockHeaderPrefix = "bh-"
BlockBodyPrefix = "bd-"
BlockReceiptsPrefix = "br-"
BlockAccountsPrefix = "bcc-"
TransactionEntryPrefix = "te"
TransactionKeyPrefix = "tr-"
ReceiptKeyPrefix = "re-"
HeightKey = "hei-chain"
ChainIdKey = "chain-id"
)
func chainIdKey() string {
return ChainIdKey
}
func chainHeightKey() string {
return HeightKey
}
func blockHeaderKey(number *uint256.Int) string {
n := number.String()
s := make([]byte, 0, len(n)+len(BlockHeaderPrefix))
s = append(s, BlockHeaderPrefix...)
s = append(s, n...)
return string(s)
}
func blockBodyKey(number *uint256.Int) string {
n := number.String()
s := make([]byte, 0, len(n)+len(BlockBodyPrefix))
s = append(s, BlockBodyPrefix...)
s = append(s, n...)
return string(s)
}
func blockReceiptsKey(number *uint256.Int) string {
n := number.String()
s := make([]byte, 0, len(n)+len(BlockReceiptsPrefix))
s = append(s, BlockReceiptsPrefix...)
s = append(s, n...)
return string(s)
}
func blockNumKey(hash common.Hash) string {
n := strings.ToLower(hash.String())
s := make([]byte, 0, len(n)+len(BlockNumKeyPrefix))
s = append(s, BlockNumKeyPrefix...)
s = append(s, n...)
return string(s)
}
func txEntryKey(hash common.Hash) string {
h := hash.String()
s := make([]byte, 0, len(h)+len(TransactionEntryPrefix))
s = append(s, TransactionEntryPrefix...)
s = append(s, h...)
return string(s)
}
func transactionKey(hash common.Hash) string {
h := hash.String()
s := make([]byte, 0, len(h)+len(TransactionKeyPrefix))
s = append(s, TransactionKeyPrefix...)
s = append(s, h...)
return string(s)
}
func receiptKey(hash common.Hash) string {
h := hash.String()
s := make([]byte, 0, len(h)+len(ReceiptKeyPrefix))
s = append(s, ReceiptKeyPrefix...)
s = append(s, h...)
return string(s)
}
package chaindb
import (
"github.com/ethereum/go-ethereum/common"
"testing"
)
func benchmark(b *testing.B, f func(hash common.Hash) string) {
var h = common.HexToHash("0x4daf6f018bdc33e7f5548382a995b89a6eb809d09fdf386a750d10ed5b88b134")
for i := 0; i < b.N; i++ {
f(h)
}
}
func BenchmarkMemChaindb_TransactionKey1(b *testing.B) {
benchmark(b, transactionKey)
}
package genesis
import (
"encoding/json"
"errors"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
)
// MarshalJSON marshals as JSON.
func (g *GenesisBlock) MarshalJSON() ([]byte, error) {
type Genesis struct {
ChainId uint64 `json:"chainId"`
Timestamp uint64 `json:"timestamp"`
ExtraData hexutil.Bytes `json:"extraData"`
Alloc map[string]GenesisAccount `json:"accounts"`
}
var enc Genesis
enc.Timestamp = g.Timestamp
enc.ExtraData = g.ExtraData
enc.ChainId = g.ChainId
if g.AllocInfo != nil {
enc.Alloc = make(map[string]GenesisAccount, len(g.AllocInfo))
for k, v := range g.AllocInfo {
enc.Alloc[k.String()] = v
}
}
return json.Marshal(&enc)
}
// UnmarshalJSON unmarshals from JSON.
func (g *GenesisBlock) UnmarshalJSON(input []byte) error {
type Genesis struct {
ChainId uint64 `json:"chainId"`
Timestamp uint64 `json:"timestamp"`
ExtraData *hexutil.Bytes `json:"extraData"`
Alloc map[common.Address]GenesisAccount `json:"accounts"`
}
var dec Genesis
if err := json.Unmarshal(input, &dec); err != nil {
return err
}
g.Timestamp = dec.Timestamp
if dec.ExtraData != nil {
g.ExtraData = *dec.ExtraData
}
g.ChainId = dec.ChainId
if dec.Alloc == nil {
return errors.New("missing required field 'alloc' for Genesis")
}
g.AllocInfo = make(GenesisAlloc, len(dec.Alloc))
for k, v := range dec.Alloc {
g.AllocInfo[k] = v
}
return nil
}
package genesis
import (
"encoding/json"
"github.com/ethereum/go-ethereum/common/hexutil"
"math/big"
)
// MarshalJSON marshals as JSON.
func (g GenesisAccount) MarshalJSON() ([]byte, error) {
type InnerWalletInfo struct {
Balance *hexutil.Big `json:"balance"`
Frozen *hexutil.Big `json:"frozen"`
}
type GenesisAccount struct {
SignerProxy *hexutil.Bytes `json:"signerProxy,omitempty"`
Assets map[string]InnerWalletInfo `json:"assets"`
}
var enc GenesisAccount
if len(g.SingerProxy) > 0 {
enc.SignerProxy = (*hexutil.Bytes)(&g.SingerProxy)
}
enc.Assets = make(map[string]InnerWalletInfo, len(g.Assets))
for k, v := range g.Assets {
wallet := InnerWalletInfo{}
if v.Balance != nil {
balance := hexutil.Big(*v.Balance)
wallet.Balance = &balance
}
if v.Frozen != nil {
frozen := hexutil.Big(*v.Frozen)
wallet.Frozen = &frozen
}
enc.Assets[k] = wallet
}
return json.Marshal(&enc)
}
// UnmarshalJSON unmarshals from JSON.
func (g *GenesisAccount) UnmarshalJSON(input []byte) error {
type InnerWalletInfo struct {
Balance *hexutil.Big `json:"balance"`
Frozen *hexutil.Big `json:"frozen"`
}
type GenesisAccount struct {
SignerProxy *hexutil.Bytes `json:"signerProxy,omitempty"`
Assets map[string]InnerWalletInfo `json:"assets"`
}
var dec GenesisAccount
if err := json.Unmarshal(input, &dec); err != nil {
return err
}
if dec.SignerProxy != nil {
g.SingerProxy = *dec.SignerProxy
}
if dec.Assets != nil {
g.Assets = make(map[string]WalletInfo, len(dec.Assets))
for coin, v := range dec.Assets {
wallet := WalletInfo{}
if v.Balance != nil {
wallet.Balance = new(big.Int).Set(v.Balance.ToInt())
}
if v.Frozen != nil {
wallet.Frozen = new(big.Int).Set(v.Frozen.ToInt())
}
g.Assets[coin] = wallet
}
}
return nil
}
package genesis
import (
"encoding/json"
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/exchain/go-exchain/exchain/chaindb"
nebulav1 "github.com/exchain/go-exchain/exchain/protocol/gen/go/nebula/v1"
"github.com/exchain/go-exchain/metadb"
"github.com/golang/protobuf/proto"
"github.com/holiman/uint256"
"math/big"
"os"
)
var (
genesisSigner = common.HexToAddress("0x5FbDB2315678afecb367f032d93F642f64180aa3")
genesisSignerKey, _ = crypto.HexToECDSA("329cb309edf3b9d0a5d16a16df6393da089f00f555a3290873a9daabc9e39711")
)
type WalletInfo struct {
Balance *big.Int `json:"balance"`
Frozen *big.Int `json:"frozen"`
}
type AssetsInfo map[string]WalletInfo
type GenesisAccount struct {
SingerProxy []byte `json:"signerProxy"`
Assets AssetsInfo `json:"assets"`
}
type GenesisBlock struct {
ChainId uint64 `json:"chainId"`
Timestamp uint64 `json:"timestamp"`
ExtraData []byte `json:"extraData"`
AllocInfo GenesisAlloc `json:"accounts"`
}
// GenesisAlloc specifies the initial state that is part of the genesis block.
type GenesisAlloc map[common.Address]GenesisAccount
func (ga *GenesisAlloc) UnmarshalJSON(data []byte) error {
m := make(map[common.UnprefixedAddress]GenesisAccount)
if err := json.Unmarshal(data, &m); err != nil {
return err
}
*ga = make(GenesisAlloc)
for addr, a := range m {
(*ga)[common.Address(addr)] = a
}
return nil
}
func (g *GenesisBlock) Commit(db metadb.Database) (err error) {
chain := chaindb.NewChainDB(db)
blk := g.ToBlock()
return chain.SaveBlockData(blk, nil)
}
func (g *GenesisBlock) ToBlock() *nebulav1.Block {
blk := &nebulav1.Block{
Header: &nebulav1.BlockHeader{
Height: 0,
ParentHash: common.Hash{}.Bytes(),
Timestamp: g.Timestamp,
Proposer: genesisSigner.Bytes(),
L1Hash: common.Hash{}.Bytes(),
AppRoot: common.Hash{}.Bytes(),
},
Transactions: &nebulav1.TransactionList{},
}
hash := BlockHash(blk)
signature, _ := crypto.Sign(hash.Bytes(), genesisSignerKey)
blk.Header.Signatures = signature
blk.Header.Hash = hash.Bytes()
return blk
}
func (g *GenesisBlock) AddAccount(addr common.Address, account GenesisAccount) {
g.AllocInfo[addr] = account
}
func NewGenesisBlock(genfile string) (*GenesisBlock, error) {
data, err := os.ReadFile(genfile)
if err != nil {
return nil, err
}
var genblock GenesisBlock
err = json.Unmarshal(data, &genblock)
if err != nil {
return nil, err
}
return &genblock, nil
}
func BlockHash(blk *nebulav1.Block) common.Hash {
data := make([]byte, 0)
data = append(data, uint256.NewInt(blk.Header.Height).Bytes()...)
data = append(data, uint256.NewInt(blk.Header.Timestamp).Bytes()...)
data = append(data, blk.Header.ParentHash...)
data = append(data, blk.Header.L1Hash...)
data = append(data, blk.Header.AppRoot...)
data = append(data, blk.Header.Proposer...)
txdata, _ := proto.Marshal(blk.Transactions)
data = append(data, txdata...)
return crypto.Keccak256Hash(data)
}
func LoadGenesisAllocs(allocsPath string) (GenesisAlloc, error) {
f, err := os.OpenFile(allocsPath, os.O_RDONLY, 0644)
if err != nil {
return nil, fmt.Errorf("failed to open forge allocs %q: %w", allocsPath, err)
}
defer f.Close()
var out = make(GenesisAlloc)
if err := json.NewDecoder(f).Decode(out); err != nil {
return nil, fmt.Errorf("failed to json-decode forge allocs %q: %w", allocsPath, err)
}
return out, nil
}
{
"chainId": 10086,
"timestamp": 1676518132,
"extraData": "0x",
"accounts": {
"0x905D5E8F7db76bCA91fdcA0990be7263dfD23335": {
"signerProxy": "0x",
"assets": {
"usdc": {
"balance": "0x20000000000",
"frozen": "0x10"
}
}
}
}
}
package genesis
import (
"encoding/json"
"fmt"
"testing"
)
var testGenesis = `
{
"chainId": 10086,
"timestamp": 1676518132,
"extraData": "0x",
"accounts": {
"0x905D5E8F7db76bCA91fdcA0990be7263dfD23335": {
"signerProxy": "0x",
"assets": {
"usdc": {
"balance": "0x20000000000",
"frozen": "10"
}
}
}
}
}
`
func TestGenesis_UnmarshalJSON(t *testing.T) {
var gen GenesisBlock
err := json.Unmarshal([]byte(testGenesis), &gen)
if err != nil {
t.Fatal("unmarshal json failed", "err", err)
}
for addr, info := range gen.AllocInfo {
data, _ := info.MarshalJSON()
fmt.Printf("user[%s]={%s}\n", addr.String(), string(data))
}
}
func TestGenesis_MarshalJSON(t *testing.T) {
var gen GenesisBlock
err := json.Unmarshal([]byte(testGenesis), &gen)
if err != nil {
t.Fatal("unmarshal json failed", "err", err)
}
data, err := json.Marshal(gen)
if err != nil {
t.Fatal("marshal genesis block failed", "err", err)
}
fmt.Println("get genesis block marshal", string(data))
}
func TestGenesisBlock_ToBlock(t *testing.T) {
var gen GenesisBlock
err := json.Unmarshal([]byte(testGenesis), &gen)
if err != nil {
t.Fatal("unmarshal json failed", "err", err)
}
block := gen.ToBlock()
fmt.Println("genesis block is ", block.String())
}
package exchain package exchain
import (
"github.com/ethereum/go-ethereum/common"
nebulav1 "github.com/exchain/go-exchain/exchain/protocol/gen/go/nebula/v1"
)
// todo: define some structs here. // todo: define some structs here.
type ChainEvent struct {
Block *nebulav1.Block
BlockHash common.Hash
TxCount int64
}
...@@ -2,13 +2,12 @@ package genesis ...@@ -2,13 +2,12 @@ package genesis
import ( import (
"errors" "errors"
"github.com/exchain/go-exchain/exchain/genesis"
"math/big" "math/big"
"time" "time"
"github.com/exchain/go-exchain/op-service/predeploys"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
...@@ -17,104 +16,25 @@ import ( ...@@ -17,104 +16,25 @@ import (
// defaultGasLimit represents the default gas limit for a genesis block. // defaultGasLimit represents the default gas limit for a genesis block.
const defaultGasLimit = 30_000_000 const defaultGasLimit = 30_000_000
// HoloceneExtraData represents the default extra data for Holocene-genesis chains.
var HoloceneExtraData = eip1559.EncodeHoloceneExtraData(250, 6)
// NewL2Genesis will create a new L2 genesis // NewL2Genesis will create a new L2 genesis
func NewL2Genesis(config *DeployConfig, l1StartHeader *types.Header) (*core.Genesis, error) { func NewL2Genesis(config *DeployConfig, l1StartHeader *types.Header) (*genesis.GenesisBlock, error) {
if config.L2ChainID == 0 { if config.L2ChainID == 0 {
return nil, errors.New("must define L2 ChainID") return nil, errors.New("must define L2 ChainID")
} }
eip1559Denom := config.EIP1559Denominator
if eip1559Denom == 0 {
eip1559Denom = 50
}
eip1559DenomCanyon := config.EIP1559DenominatorCanyon
if eip1559DenomCanyon == 0 {
eip1559DenomCanyon = 250
}
eip1559Elasticity := config.EIP1559Elasticity
if eip1559Elasticity == 0 {
eip1559Elasticity = 10
}
l1StartTime := l1StartHeader.Time l1StartTime := l1StartHeader.Time
optimismChainConfig := params.ChainConfig{
ChainID: new(big.Int).SetUint64(config.L2ChainID),
HomesteadBlock: big.NewInt(0),
DAOForkBlock: nil,
DAOForkSupport: false,
EIP150Block: big.NewInt(0),
EIP155Block: big.NewInt(0),
EIP158Block: big.NewInt(0),
ByzantiumBlock: big.NewInt(0),
ConstantinopleBlock: big.NewInt(0),
PetersburgBlock: big.NewInt(0),
IstanbulBlock: big.NewInt(0),
MuirGlacierBlock: big.NewInt(0),
BerlinBlock: big.NewInt(0),
LondonBlock: big.NewInt(0),
ArrowGlacierBlock: big.NewInt(0),
GrayGlacierBlock: big.NewInt(0),
MergeNetsplitBlock: big.NewInt(0),
TerminalTotalDifficulty: big.NewInt(0),
BedrockBlock: new(big.Int).SetUint64(uint64(config.L2GenesisBlockNumber)),
RegolithTime: config.RegolithTime(l1StartTime),
CanyonTime: config.CanyonTime(l1StartTime),
ShanghaiTime: config.CanyonTime(l1StartTime),
CancunTime: config.EcotoneTime(l1StartTime),
EcotoneTime: config.EcotoneTime(l1StartTime),
FjordTime: config.FjordTime(l1StartTime),
GraniteTime: config.GraniteTime(l1StartTime),
HoloceneTime: config.HoloceneTime(l1StartTime),
IsthmusTime: config.IsthmusTime(l1StartTime),
InteropTime: config.InteropTime(l1StartTime),
Optimism: &params.OptimismConfig{
EIP1559Denominator: eip1559Denom,
EIP1559Elasticity: eip1559Elasticity,
EIP1559DenominatorCanyon: &eip1559DenomCanyon,
},
}
gasLimit := config.L2GenesisBlockGasLimit
if gasLimit == 0 {
gasLimit = defaultGasLimit
}
baseFee := config.L2GenesisBlockBaseFeePerGas
if baseFee == nil {
baseFee = newHexBig(params.InitialBaseFee)
}
difficulty := config.L2GenesisBlockDifficulty difficulty := config.L2GenesisBlockDifficulty
if difficulty == nil { if difficulty == nil {
difficulty = newHexBig(0) difficulty = newHexBig(0)
} }
genesis := &core.Genesis{ blk := &genesis.GenesisBlock{
Config: &optimismChainConfig, ChainId: config.L2ChainID,
Nonce: uint64(config.L2GenesisBlockNonce), Timestamp: l1StartTime,
Timestamp: l1StartTime, AllocInfo: make(genesis.GenesisAlloc),
GasLimit: uint64(gasLimit),
Difficulty: difficulty.ToInt(),
Mixhash: config.L2GenesisBlockMixHash,
Coinbase: predeploys.SequencerFeeVaultAddr,
Number: uint64(config.L2GenesisBlockNumber),
GasUsed: uint64(config.L2GenesisBlockGasUsed),
ParentHash: config.L2GenesisBlockParentHash,
BaseFee: baseFee.ToInt(),
Alloc: map[common.Address]types.Account{},
}
if optimismChainConfig.IsEcotone(genesis.Timestamp) {
genesis.BlobGasUsed = u64ptr(0)
genesis.ExcessBlobGas = u64ptr(0)
}
if optimismChainConfig.IsHolocene(genesis.Timestamp) {
genesis.ExtraData = HoloceneExtraData
} }
return genesis, nil return blk, nil
} }
// NewL1Genesis will create a new L1 genesis config // NewL1Genesis will create a new L1 genesis config
......
package genesis package genesis
import ( import (
"fmt"
"math/big"
hdwallet "github.com/ethereum-optimism/go-ethereum-hdwallet"
"github.com/holiman/uint256"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/exchain/go-exchain/exchain/genesis"
"github.com/exchain/go-exchain/op-chain-ops/foundry" "github.com/exchain/go-exchain/op-chain-ops/foundry"
"github.com/exchain/go-exchain/op-service/predeploys"
) )
type L2AllocsMode string type L2AllocsMode string
...@@ -40,61 +31,12 @@ var ( ...@@ -40,61 +31,12 @@ var (
type AllocsLoader func(mode L2AllocsMode) *foundry.ForgeAllocs type AllocsLoader func(mode L2AllocsMode) *foundry.ForgeAllocs
// BuildL2Genesis will build the L2 genesis block. // BuildL2Genesis will build the L2 genesis block.
func BuildL2Genesis(config *DeployConfig, dump *foundry.ForgeAllocs, l1StartBlock *types.Header) (*core.Genesis, error) { func BuildL2Genesis(config *DeployConfig, dump genesis.GenesisAlloc, l1StartBlock *types.Header) (*genesis.GenesisBlock, error) {
genspec, err := NewL2Genesis(config, l1StartBlock) genspec, err := NewL2Genesis(config, l1StartBlock)
if err != nil { if err != nil {
return nil, err return nil, err
} }
genspec.Alloc = dump.Copy().Accounts genspec.AllocInfo = dump
// ensure the dev accounts are not funded unintentionally
if devAccounts, err := RetrieveDevAccounts(genspec.Alloc); err != nil {
return nil, fmt.Errorf("failed to check dev accounts: %w", err)
} else if (len(devAccounts) > 0) != config.FundDevAccounts {
return nil, fmt.Errorf("deploy config mismatch with allocs. Deploy config fundDevAccounts: %v, actual allocs: %v", config.FundDevAccounts, devAccounts)
}
// sanity check the permit2 immutable, to verify we using the allocs for the right chain.
if permit2 := genspec.Alloc[predeploys.Permit2Addr].Code; len(permit2) != 0 {
if len(permit2) < 6945+32 {
return nil, fmt.Errorf("permit2 code is too short (%d)", len(permit2))
}
chainID := [32]byte(permit2[6945 : 6945+32])
expected := uint256.MustFromBig(genspec.Config.ChainID).Bytes32()
if chainID != expected {
return nil, fmt.Errorf("allocs were generated for chain ID %x, but expected chain %x (%d)", chainID, expected, genspec.Config.ChainID)
}
}
// sanity check that all predeploys are present
for i := 0; i < 2048; i++ {
addr := common.BigToAddress(new(big.Int).Or(l2PredeployNamespace.Big(), big.NewInt(int64(i))))
if !config.GovernanceEnabled() && addr == predeploys.GovernanceTokenAddr {
continue
}
if len(genspec.Alloc[addr].Code) == 0 {
return nil, fmt.Errorf("predeploy %x is missing from L2 genesis allocs", addr)
}
}
return genspec, nil return genspec, nil
} }
func RetrieveDevAccounts(allocs types.GenesisAlloc) ([]common.Address, error) {
wallet, err := hdwallet.NewFromMnemonic(testMnemonic)
if err != nil {
return nil, fmt.Errorf("failed to create wallet: %w", err)
}
account := func(path string) accounts.Account {
return accounts.Account{URL: accounts.URL{Path: path}}
}
var devAccounts []common.Address
for i := 0; i < 30; i++ {
key, err := wallet.PrivateKey(account(fmt.Sprintf("m/44'/60'/0'/0/%d", i)))
if err != nil {
return nil, err
}
addr := crypto.PubkeyToAddress(key.PublicKey)
if _, ok := allocs[addr]; ok {
devAccounts = append(devAccounts, addr)
}
}
return devAccounts, nil
}
...@@ -320,10 +320,11 @@ func CompleteL2(l2Host *script.Host, cfg *L2Config, l1Block *types.Block, deploy ...@@ -320,10 +320,11 @@ func CompleteL2(l2Host *script.Host, cfg *L2Config, l1Block *types.Block, deploy
allocs.Accounts[addr] = acc allocs.Accounts[addr] = acc
} }
l2Genesis.Alloc = allocs.Accounts // todo: vicotor add the allocs to the genesis block.
//l2Genesis.AllocInfo = allocs.Accounts
l2GenesisBlock := l2Genesis.ToBlock() l2GenesisBlock := l2Genesis.ToBlock()
rollupCfg, err := deployCfg.RollupConfig(l1Block.Header(), l2GenesisBlock.Hash(), l2GenesisBlock.NumberU64()) rollupCfg, err := deployCfg.RollupConfig(l1Block.Header(), common.BytesToHash(l2GenesisBlock.Header.Hash), l2GenesisBlock.Header.Height)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to build L2 rollup config: %w", err) return nil, fmt.Errorf("failed to build L2 rollup config: %w", err)
} }
......
...@@ -2,6 +2,7 @@ package interopgen ...@@ -2,6 +2,7 @@ package interopgen
import ( import (
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/exchain/go-exchain/exchain/genesis"
"github.com/exchain/go-exchain/op-node/rollup" "github.com/exchain/go-exchain/op-node/rollup"
) )
...@@ -11,7 +12,7 @@ type L1Output struct { ...@@ -11,7 +12,7 @@ type L1Output struct {
} }
type L2Output struct { type L2Output struct {
Genesis *core.Genesis Genesis *genesis.GenesisBlock
RollupCfg *rollup.Config RollupCfg *rollup.Config
} }
......
...@@ -3,6 +3,9 @@ package genesis ...@@ -3,6 +3,9 @@ package genesis
import ( import (
"errors" "errors"
"fmt" "fmt"
"github.com/ethereum/go-ethereum/common"
chaingenesis "github.com/exchain/go-exchain/exchain/genesis"
"github.com/exchain/go-exchain/metadb/groupdb"
"time" "time"
"github.com/exchain/go-exchain/op-service/ioutil" "github.com/exchain/go-exchain/op-service/ioutil"
...@@ -10,12 +13,12 @@ import ( ...@@ -10,12 +13,12 @@ import (
"github.com/exchain/go-exchain/op-service/sources/batching" "github.com/exchain/go-exchain/op-service/sources/batching"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/exchain/go-exchain/op-chain-ops/foundry" "github.com/exchain/go-exchain/op-chain-ops/foundry"
"github.com/exchain/go-exchain/op-chain-ops/genesis" "github.com/exchain/go-exchain/op-chain-ops/genesis"
"github.com/exchain/go-exchain/op-service/jsonutil" "github.com/exchain/go-exchain/op-service/jsonutil"
oplog "github.com/exchain/go-exchain/op-service/log" oplog "github.com/exchain/go-exchain/op-service/log"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient"
) )
var ( var (
...@@ -42,6 +45,15 @@ var ( ...@@ -42,6 +45,15 @@ var (
Name: "outfile.rollup", Name: "outfile.rollup",
Usage: "Path to rollup output file", Usage: "Path to rollup output file",
} }
genesisFileFlag = &cli.PathFlag{
Name: "genesis",
Usage: "Path to L2 genesis file",
}
dataDirFlag = &cli.PathFlag{
Name: "data-dir",
Usage: "Path to the directory where the node will store its data",
Value: "node",
}
l1AllocsFlag = &cli.StringFlag{ l1AllocsFlag = &cli.StringFlag{
Name: "l1-allocs", Name: "l1-allocs",
...@@ -71,6 +83,10 @@ var ( ...@@ -71,6 +83,10 @@ var (
outfileL2Flag, outfileL2Flag,
outfileRollupFlag, outfileRollupFlag,
} }
initFlags = []cli.Flag{
genesisFileFlag,
dataDirFlag,
}
) )
var Subcommands = cli.Commands{ var Subcommands = cli.Commands{
...@@ -153,9 +169,9 @@ var Subcommands = cli.Commands{ ...@@ -153,9 +169,9 @@ var Subcommands = cli.Commands{
} }
config.SetDeployments(deployments) config.SetDeployments(deployments)
var l2Allocs *foundry.ForgeAllocs var l2Allocs chaingenesis.GenesisAlloc
if l2AllocsPath := ctx.String(l2AllocsFlag.Name); l2AllocsPath != "" { if l2AllocsPath := ctx.String(l2AllocsFlag.Name); l2AllocsPath != "" {
l2Allocs, err = foundry.LoadForgeAllocs(l2AllocsPath) l2Allocs, err = chaingenesis.LoadGenesisAllocs(l2AllocsPath)
if err != nil { if err != nil {
return err return err
} }
...@@ -197,7 +213,7 @@ var Subcommands = cli.Commands{ ...@@ -197,7 +213,7 @@ var Subcommands = cli.Commands{
} }
l2GenesisBlock := l2Genesis.ToBlock() l2GenesisBlock := l2Genesis.ToBlock()
rollupConfig, err := config.RollupConfig(l1StartBlock.Header(), l2GenesisBlock.Hash(), l2GenesisBlock.Number().Uint64()) rollupConfig, err := config.RollupConfig(l1StartBlock.Header(), common.BytesToHash(l2GenesisBlock.Header.Hash), l2GenesisBlock.Header.Height)
if err != nil { if err != nil {
return err return err
} }
...@@ -211,4 +227,32 @@ var Subcommands = cli.Commands{ ...@@ -211,4 +227,32 @@ var Subcommands = cli.Commands{
return jsonutil.WriteJSON(rollupConfig, ioutil.ToAtomicFile(ctx.String(outfileRollupFlag.Name), 0o666)) return jsonutil.WriteJSON(rollupConfig, ioutil.ToAtomicFile(ctx.String(outfileRollupFlag.Name), 0o666))
}, },
}, },
{
Name: "init",
Usage: "Init the L2 chain with the genesis.json",
Description: "Generating the L2 genesis block with genesis.json and write it to node data.",
Flags: initFlags,
Action: func(ctx *cli.Context) error {
cfg := oplog.DefaultCLIConfig()
logger := oplog.NewLogger(ctx.App.Writer, cfg)
genesisFile := ctx.Path(genesisFileFlag.Name)
dataDir := ctx.Path(dataDirFlag.Name)
logger.Info("Genesis file", "path", genesisFile)
logger.Info("Data directory", "path", dataDir)
database := groupdb.NewGroupDB(dataDir, "chain")
genblk, err := chaingenesis.NewGenesisBlock(genesisFile)
if err != nil {
logger.Error("Failed to create genesis block", "err", err)
return err
}
if err = genblk.Commit(database); err != nil {
logger.Error("Failed to commit genesis block", "err", err)
}
// wait db write to disk.
time.Sleep(2 * time.Second)
return nil
},
},
} }
...@@ -6,9 +6,11 @@ import ( ...@@ -6,9 +6,11 @@ import (
"fmt" "fmt"
"github.com/exchain/go-exchain/engine" "github.com/exchain/go-exchain/engine"
"github.com/exchain/go-exchain/exchain" "github.com/exchain/go-exchain/exchain"
"github.com/exchain/go-exchain/exchain/chaindb"
"github.com/exchain/go-exchain/exchain/mockengine" "github.com/exchain/go-exchain/exchain/mockengine"
"github.com/exchain/go-exchain/metadb" "github.com/exchain/go-exchain/metadb"
"github.com/exchain/go-exchain/metadb/groupdb" "github.com/exchain/go-exchain/metadb/groupdb"
"github.com/holiman/uint256"
"io" "io"
gosync "sync" gosync "sync"
"sync/atomic" "sync/atomic"
...@@ -67,13 +69,13 @@ type OpNode struct { ...@@ -67,13 +69,13 @@ type OpNode struct {
l1Source *sources.L1Client // L1 Client to fetch data from l1Source *sources.L1Client // L1 Client to fetch data from
l2Driver *driver.Driver // L2 Engine to Sync l2Driver *driver.Driver // L2 Engine to Sync
//l2Source *sources.EngineClient // L2 Execution Engine RPC bindings //l2Source *sources.EngineClient // L2 Execution Engine RPC bindings
l2Source *engine.EngineAPI // L2 Execution Engine RPC bindings l2Source *engine.ExChainAPI // L2 Execution Engine RPC bindings
server *rpcServer // RPC server hosting the rollup-node API server *rpcServer // RPC server hosting the rollup-node API
p2pNode *p2p.NodeP2P // P2P node functionality p2pNode *p2p.NodeP2P // P2P node functionality
p2pMu gosync.Mutex // protects p2pNode p2pMu gosync.Mutex // protects p2pNode
p2pSigner p2p.Signer // p2p gossip application messages will be signed with this signer p2pSigner p2p.Signer // p2p gossip application messages will be signed with this signer
tracer Tracer // tracer to get events for testing/debugging tracer Tracer // tracer to get events for testing/debugging
runCfg *RuntimeConfig // runtime configurables runCfg *RuntimeConfig // runtime configurables
safeDB closableSafeDB safeDB closableSafeDB
...@@ -396,11 +398,18 @@ func (n *OpNode) initL1BeaconAPI(ctx context.Context, cfg *Config) error { ...@@ -396,11 +398,18 @@ func (n *OpNode) initL1BeaconAPI(ctx context.Context, cfg *Config) error {
func (n *OpNode) initL2(ctx context.Context, cfg *Config) error { func (n *OpNode) initL2(ctx context.Context, cfg *Config) error {
var err error var err error
n.db = groupdb.NewGroupDB(n.cfg.NodeDataPath, "engine") n.db = groupdb.NewGroupDB(n.cfg.NodeDataPath, "engine")
chain := chaindb.NewChainDB(n.db)
n.engineIns = mockengine.NewEngine(n.db) n.engineIns = mockengine.NewEngine(n.db)
n.l2Source = engine.NewEngineAPI(n.db) n.l2Source = engine.NewEngineAPI(chain)
if n.engineIns == nil { if n.engineIns == nil {
return errors.New("failed to create L2 engine") return errors.New("failed to create L2 engine")
} }
// check genesis block.
if blk := chain.GetBlock(uint256.NewInt(0)); blk == nil {
return fmt.Errorf("node is not initialized, missing genesis block")
}
if err = n.engineIns.Start(); err != nil { if err = n.engineIns.Start(); err != nil {
return fmt.Errorf("failed to start L2 engine: %w", err) return fmt.Errorf("failed to start L2 engine: %w", err)
} }
......
package grouptask
import (
"errors"
"sync"
)
var (
ErrTaskPoolIsFull = errors.New("task pool is full")
)
type TaskHandle func(interface{}, uint)
type Tasks struct {
tasknum uint
handler TaskHandle
taskpool chan interface{}
wg sync.WaitGroup
}
func NewTasks(routine uint, handle TaskHandle) *Tasks {
return &Tasks{
tasknum: routine,
handler: handle,
taskpool: make(chan interface{}, 1000000),
}
}
func (t *Tasks) AddTask(task interface{}) error {
select {
case t.taskpool <- task:
return nil
default:
return ErrTaskPoolIsFull
}
}
func (t *Tasks) WaitFinish() {
close(t.taskpool)
t.wg.Wait()
}
func (t *Tasks) Run() {
for i := uint(0); i < t.tasknum; i++ {
t.wg.Add(1)
go func(chanIdx uint) {
defer t.wg.Done()
for {
select {
case task, ok := <-t.taskpool:
if !ok {
return
}
t.handler(task, chanIdx)
}
}
}(i)
}
}
func DoMultiTasks(routine int, handler TaskHandle, items ...interface{}) {
tasks := NewTasks(uint(routine), handler)
tasks.Run()
for _, item := range items {
tasks.AddTask(item)
}
tasks.WaitFinish()
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment