From e1f9a5e6f191d01fc36a544f464a8f9aeae231e7 Mon Sep 17 00:00:00 2001 From: Tudor Malene Date: Wed, 8 Jan 2025 15:13:15 +0200 Subject: [PATCH 1/8] remove l1 block, close resources --- go/common/encoding.go | 22 ----- go/common/host/host.go | 6 -- go/common/host/services.go | 8 +- go/common/types.go | 1 - go/common/utils.go | 22 ----- go/ethadapter/geth_rpc_client.go | 58 ++++++------ go/ethadapter/interface.go | 13 +-- go/host/enclave/guardian.go | 18 ++-- go/host/l1/dataservice.go | 64 +++++++------ go/host/storage/hostdb/rollup.go | 4 +- go/host/storage/interfaces.go | 2 +- go/host/storage/storage.go | 2 +- integration/ethereummock/db.go | 18 ++-- integration/ethereummock/gethutil.go | 38 ++++---- integration/ethereummock/mock_l1_network.go | 16 ++-- integration/ethereummock/node.go | 99 +++++++++++---------- integration/ethereummock/utils.go | 21 +++++ integration/simulation/output_stats.go | 8 +- integration/simulation/simulation.go | 8 +- integration/simulation/utils.go | 2 +- integration/simulation/validate_chain.go | 20 ++--- 21 files changed, 226 insertions(+), 224 deletions(-) diff --git a/go/common/encoding.go b/go/common/encoding.go index 8fc9f0337a..a9fd0f51a4 100644 --- a/go/common/encoding.go +++ b/go/common/encoding.go @@ -1,31 +1,9 @@ package common import ( - "fmt" - - "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rlp" ) -// EncodedL1Block the encoded version of an L1 block. -type EncodedL1Block []byte - -func EncodeBlock(b *types.Block) (EncodedL1Block, error) { - encoded, err := rlp.EncodeToBytes(b) - if err != nil { - return nil, fmt.Errorf("could not encode block to bytes. Cause: %w", err) - } - return encoded, nil -} - -func (eb EncodedL1Block) DecodeBlock() (*types.Block, error) { - b := types.Block{} - if err := rlp.DecodeBytes(eb, &b); err != nil { - return nil, fmt.Errorf("could not decode block from bytes. Cause: %w", err) - } - return &b, nil -} - func EncodeRollup(r *ExtRollup) (EncodedRollup, error) { return rlp.EncodeToBytes(r) } diff --git a/go/common/host/host.go b/go/common/host/host.go index d1479049d3..0b64dbf813 100644 --- a/go/common/host/host.go +++ b/go/common/host/host.go @@ -3,7 +3,6 @@ package host import ( "context" - "github.com/ethereum/go-ethereum/core/types" "github.com/ten-protocol/go-ten/go/common" hostconfig "github.com/ten-protocol/go-ten/go/host/config" "github.com/ten-protocol/go-ten/go/host/storage" @@ -38,11 +37,6 @@ type Host interface { NewHeadsChan() chan *common.BatchHeader } -type BlockStream struct { - Stream <-chan *types.Block // the channel which will receive the consecutive, canonical blocks - Stop func() // function to permanently stop the stream and clean up any associated processes/resources -} - type BatchMsg struct { Batches []*common.ExtBatch // The batches being sent. IsLive bool // true if these batches are being sent as new, false if in response to a p2p request diff --git a/go/common/host/services.go b/go/common/host/services.go index 9b1dcfbe43..f0464d2b9e 100644 --- a/go/common/host/services.go +++ b/go/common/host/services.go @@ -80,18 +80,18 @@ type L1DataService interface { // Subscribe will register a block handler to receive new blocks as they arrive, returns unsubscribe func Subscribe(handler L1BlockHandler) func() - FetchBlockByHeight(height *big.Int) (*types.Block, error) + FetchBlockByHeight(height *big.Int) (*types.Header, error) // FetchNextBlock returns the next canonical block after a given block hash // It returns the new block, a bool which is true if the block is the current L1 head and a bool if the block is on a different fork to prevBlock - FetchNextBlock(prevBlock gethcommon.Hash) (*types.Block, bool, error) + FetchNextBlock(prevBlock gethcommon.Hash) (*types.Header, bool, error) // GetTenRelevantTransactions returns the events and transactions relevant to Ten - GetTenRelevantTransactions(block *common.L1Block) (*common.ProcessedL1Data, error) + GetTenRelevantTransactions(block *types.Header) (*common.ProcessedL1Data, error) } // L1BlockHandler is an interface for receiving new blocks from the repository as they arrive type L1BlockHandler interface { // HandleBlock will be called in a new goroutine for each new block as it arrives - HandleBlock(block *types.Block) + HandleBlock(block *types.Header) } // L1Publisher provides an interface for the host to interact with Ten data (management contract etc.) on L1 diff --git a/go/common/types.go b/go/common/types.go index 1f4001fc5d..09e1756c81 100644 --- a/go/common/types.go +++ b/go/common/types.go @@ -33,7 +33,6 @@ type ( // MainNet aliases L1Address = common.Address L1BlockHash = common.Hash - L1Block = types.Block L1Transaction = types.Transaction L1Receipt = types.Receipt L1Receipts = types.Receipts diff --git a/go/common/utils.go b/go/common/utils.go index b4a3bc2fcb..2ea26798e3 100644 --- a/go/common/utils.go +++ b/go/common/utils.go @@ -1,19 +1,12 @@ package common import ( - "math/big" "math/rand" "time" - "github.com/ethereum/go-ethereum/core/types" - gethcommon "github.com/ethereum/go-ethereum/common" ) -type ( - Latency func() time.Duration -) - func MaxInt(x, y uint32) uint32 { if x < y { return y @@ -21,21 +14,6 @@ func MaxInt(x, y uint32) uint32 { return x } -// ShortHash converts the hash to a shorter uint64 for printing. -func ShortHash(hash gethcommon.Hash) uint64 { - return hash.Big().Uint64() -} - -// ShortAddress converts the address to a shorter uint64 for printing. -func ShortAddress(address gethcommon.Address) uint64 { - return address.Big().Uint64() -} - -// ShortNonce converts the nonce to a shorter uint64 for printing. -func ShortNonce(nonce types.BlockNonce) uint64 { - return new(big.Int).SetBytes(nonce[4:]).Uint64() -} - // ExtractPotentialAddress - given a 32 byte hash , it checks whether it can be an address and extracts that func ExtractPotentialAddress(hash gethcommon.Hash) *gethcommon.Address { bitlen := hash.Big().BitLen() diff --git a/go/ethadapter/geth_rpc_client.go b/go/ethadapter/geth_rpc_client.go index b2705c8170..f4e6b2981e 100644 --- a/go/ethadapter/geth_rpc_client.go +++ b/go/ethadapter/geth_rpc_client.go @@ -45,7 +45,7 @@ type gethRPCClient struct { timeout time.Duration // the timeout for connecting to, or communicating with, the L1 node logger gethlog.Logger rpcURL string - blockCache *lru.Cache[gethcommon.Hash, *types.Block] + blockCache *lru.Cache[gethcommon.Hash, *types.Header] } // NewEthClientFromURL instantiates a new ethadapter.EthClient that connects to an ethereum node @@ -58,7 +58,7 @@ func NewEthClientFromURL(rpcURL string, timeout time.Duration, logger gethlog.Lo logger.Trace(fmt.Sprintf("Initialized eth node connection - addr: %s", rpcURL)) // cache recent blocks to avoid re-fetching them (they are often re-used for checking for forks etc.) - blkCache, err := lru.New[gethcommon.Hash, *types.Block](_defaultBlockCacheSize) + blkCache, err := lru.New[gethcommon.Hash, *types.Header](_defaultBlockCacheSize) if err != nil { return nil, fmt.Errorf("unable to initialize block cache - %w", err) } @@ -77,25 +77,26 @@ func NewEthClient(ipaddress string, port uint, timeout time.Duration, logger get return NewEthClientFromURL(fmt.Sprintf("ws://%s:%d", ipaddress, port), timeout, logger) } -func (e *gethRPCClient) FetchHeadBlock() (*types.Block, error) { +func (e *gethRPCClient) FetchHeadBlock() (*types.Header, error) { ctx, cancel := context.WithTimeout(context.Background(), e.timeout) defer cancel() - return e.client.BlockByNumber(ctx, nil) + return e.client.HeaderByNumber(ctx, nil) } func (e *gethRPCClient) Info() Info { return Info{} } -func (e *gethRPCClient) BlocksBetween(startingBlock *types.Header, lastBlock *types.Block) []*types.Block { - var blocksBetween []*types.Block +func (e *gethRPCClient) BlocksBetween(startingBlock *types.Header, lastBlock *types.Header) []*types.Header { + var blocksBetween []*types.Header var err error - for currentBlk := lastBlock; currentBlk != nil && !bytes.Equal(currentBlk.Hash().Bytes(), startingBlock.Hash().Bytes()) && !bytes.Equal(currentBlk.ParentHash().Bytes(), gethcommon.HexToHash("").Bytes()); { - currentBlk, err = e.BlockByHash(currentBlk.ParentHash()) + for currentBlk := lastBlock; currentBlk != nil && !bytes.Equal(currentBlk.Hash().Bytes(), startingBlock.Hash().Bytes()) && !bytes.Equal(currentBlk.ParentHash.Bytes(), gethcommon.HexToHash("").Bytes()); { + c := currentBlk.ParentHash + currentBlk, err = e.HeaderByHash(currentBlk.ParentHash) if err != nil { - e.logger.Crit(fmt.Sprintf("could not fetch parent block with hash %s.", currentBlk.ParentHash().String()), log.ErrKey, err) + e.logger.Crit(fmt.Sprintf("could not fetch parent block with hash %s.", c.String()), log.ErrKey, err) } blocksBetween = append(blocksBetween, currentBlk) } @@ -103,28 +104,26 @@ func (e *gethRPCClient) BlocksBetween(startingBlock *types.Header, lastBlock *ty return blocksBetween } -func (e *gethRPCClient) IsBlockAncestor(block *types.Block, maybeAncestor common.L1BlockHash) bool { +func (e *gethRPCClient) IsBlockAncestor(block *types.Header, maybeAncestor common.L1BlockHash) bool { if bytes.Equal(maybeAncestor.Bytes(), block.Hash().Bytes()) || bytes.Equal(maybeAncestor.Bytes(), (common.L1BlockHash{}).Bytes()) { return true } - if block.Number().Int64() == int64(common.L1GenesisHeight) { + if block.Number.Int64() == int64(common.L1GenesisHeight) { return false } - resolvedBlock, err := e.BlockByHash(maybeAncestor) + resolvedAncestorBlock, err := e.HeaderByHash(maybeAncestor) if err != nil { e.logger.Crit(fmt.Sprintf("could not fetch parent block with hash %s.", maybeAncestor.String()), log.ErrKey, err) } - if resolvedBlock == nil { - if resolvedBlock.Number().Int64() >= block.Number().Int64() { - return false - } + if resolvedAncestorBlock.Number.Int64() >= block.Number.Int64() { + return false } - p, err := e.BlockByHash(block.ParentHash()) + p, err := e.HeaderByHash(block.ParentHash) if err != nil { - e.logger.Crit(fmt.Sprintf("could not fetch parent block with hash %s", block.ParentHash().String()), log.ErrKey, err) + e.logger.Crit(fmt.Sprintf("could not fetch parent block with hash %s", block.ParentHash.String()), log.ErrKey, err) } if p == nil { return false @@ -166,7 +165,7 @@ func (e *gethRPCClient) BlockListener() (chan *types.Header, ethereum.Subscripti defer cancel() // we do not buffer here, we expect the consumer to always be ready to receive new blocks and not fall behind - ch := make(chan *types.Header, 1) + ch := make(chan *types.Header) var sub ethereum.Subscription var err error err = retry.Do(func() error { @@ -192,29 +191,38 @@ func (e *gethRPCClient) BlockNumber() (uint64, error) { return e.client.BlockNumber(ctx) } -func (e *gethRPCClient) BlockByNumber(n *big.Int) (*types.Block, error) { +func (e *gethRPCClient) HeaderByNumber(n *big.Int) (*types.Header, error) { ctx, cancel := context.WithTimeout(context.Background(), e.timeout) defer cancel() - return e.client.BlockByNumber(ctx, n) + return e.client.HeaderByNumber(ctx, n) } -func (e *gethRPCClient) BlockByHash(hash gethcommon.Hash) (*types.Block, error) { +func (e *gethRPCClient) HeaderByHash(hash gethcommon.Hash) (*types.Header, error) { block, found := e.blockCache.Get(hash) if found { - return block, nil + cp := *block + return &cp, nil } // not in cache, fetch from RPC ctx, cancel := context.WithTimeout(context.Background(), e.timeout) defer cancel() - block, err := e.client.BlockByHash(ctx, hash) + block, err := e.client.HeaderByHash(ctx, hash) if err != nil { return nil, err } e.blockCache.Add(hash, block) - return block, nil + cp := *block + return &cp, nil +} + +func (e *gethRPCClient) BlockByHash(hash gethcommon.Hash) (*types.Block, error) { + ctx, cancel := context.WithTimeout(context.Background(), e.timeout) + defer cancel() + + return e.client.BlockByHash(ctx, hash) } func (e *gethRPCClient) CallContract(msg ethereum.CallMsg) ([]byte, error) { diff --git a/go/ethadapter/interface.go b/go/ethadapter/interface.go index 59e4daa770..20db0da9c2 100644 --- a/go/ethadapter/interface.go +++ b/go/ethadapter/interface.go @@ -17,8 +17,9 @@ import ( // todo (#1617) - some of these methods are composed calls that should be decoupled in the future (ie: BlocksBetween or IsBlockAncestor) type EthClient interface { BlockNumber() (uint64, error) // retrieves the number of the head block + HeaderByHash(id gethcommon.Hash) (*types.Header, error) // retrieves a block header given a hash BlockByHash(id gethcommon.Hash) (*types.Block, error) // retrieves a block given a hash - BlockByNumber(n *big.Int) (*types.Block, error) // retrieves a block given a number - returns head block if n is nil + HeaderByNumber(n *big.Int) (*types.Header, error) // retrieves a block given a number - returns head block if n is nil SendTransaction(signedTx *types.Transaction) error // issues an ethereum transaction (expects signed tx) TransactionReceipt(hash gethcommon.Hash) (*types.Receipt, error) // fetches the ethereum transaction receipt TransactionByHash(hash gethcommon.Hash) (*types.Transaction, bool, error) // fetches the ethereum tx @@ -26,11 +27,11 @@ type EthClient interface { BalanceAt(account gethcommon.Address, blockNumber *big.Int) (*big.Int, error) // fetches the balance of the account GetLogs(q ethereum.FilterQuery) ([]types.Log, error) // fetches the logs for a given query - Info() Info // retrieves the node Info - FetchHeadBlock() (*types.Block, error) // retrieves the block at head height - BlocksBetween(block *types.Header, head *types.Block) []*types.Block // returns the blocks between two blocks - IsBlockAncestor(block *types.Block, proof common.L1BlockHash) bool // returns if the node considers a block the ancestor - BlockListener() (chan *types.Header, ethereum.Subscription) // subscribes to new blocks and returns a listener with the blocks heads and the subscription handler + Info() Info // retrieves the node Info + FetchHeadBlock() (*types.Header, error) // retrieves the block at head height + BlocksBetween(block *types.Header, head *types.Header) []*types.Header // returns the blocks between two blocks + IsBlockAncestor(block *types.Header, maybeAncestor common.L1BlockHash) bool // returns if the node considers a block the ancestor + BlockListener() (chan *types.Header, ethereum.Subscription) // subscribes to new blocks and returns a listener with the blocks heads and the subscription handler CallContract(msg ethereum.CallMsg) ([]byte, error) // Runs the provided call message on the latest block. diff --git a/go/host/enclave/guardian.go b/go/host/enclave/guardian.go index 27e212bd71..18837ec2b8 100644 --- a/go/host/enclave/guardian.go +++ b/go/host/enclave/guardian.go @@ -213,8 +213,8 @@ func (g *Guardian) PromoteToActiveSequencer() error { // Note: The L1 processing behaviour has two modes based on the state, either // - enclave is behind: lookup blocks to feed it 1-by-1 (see `catchupWithL1()`), ignore new live blocks that arrive here // - enclave is up-to-date: feed it these live blocks as they arrive, no need to lookup blocks -func (g *Guardian) HandleBlock(block *types.Block) { - g.logger.Debug("Received L1 block", log.BlockHashKey, block.Hash(), log.BlockHeightKey, block.Number()) +func (g *Guardian) HandleBlock(block *types.Header) { + g.logger.Debug("Received L1 block", log.BlockHashKey, block.Hash(), log.BlockHeightKey, block.Number) // record the newest block we've seen g.state.OnReceivedBlock(block.Hash()) if !g.state.InSyncWithL1() { @@ -463,8 +463,8 @@ func (g *Guardian) catchupWithL2() error { // returns false if the block was not processed // todo - @matt - think about removing the TryLock -func (g *Guardian) submitL1Block(block *common.L1Block, isLatest bool) (bool, error) { - g.logger.Trace("submitting L1 block", log.BlockHashKey, block.Hash(), log.BlockHeightKey, block.Number()) +func (g *Guardian) submitL1Block(block *types.Header, isLatest bool) (bool, error) { + g.logger.Trace("submitting L1 block", log.BlockHashKey, block.Hash(), log.BlockHeightKey, block.Number) if !g.submitDataLock.TryLock() { g.logger.Debug("Unable to submit block, enclave is busy processing data") return false, nil @@ -485,7 +485,7 @@ func (g *Guardian) submitL1Block(block *common.L1Block, isLatest bool) (bool, er // this is most common when we are returning to a previous fork and the enclave has already seen some of the blocks on it // note: logging this because we don't expect it to happen often and would like visibility on that. g.logger.Info("L1 block already processed by enclave, trying the next block", "block", block.Hash()) - nextHeight := big.NewInt(0).Add(block.Number(), big.NewInt(1)) + nextHeight := big.NewInt(0).Add(block.Number, big.NewInt(1)) nextCanonicalBlock, err := g.sl.L1Data().FetchBlockByHeight(nextHeight) if err != nil { return false, fmt.Errorf("failed to fetch next block after forking block=%s: %w", block.Hash(), err) @@ -499,10 +499,6 @@ func (g *Guardian) submitL1Block(block *common.L1Block, isLatest bool) (bool, er g.state.OnProcessedBlock(block.Hash()) g.processL1BlockTransactions(block, rollupTxs, syncContracts) - if err != nil { - return false, fmt.Errorf("submitted block to enclave but could not store the block processing result. Cause: %w", err) - } - // todo: make sure this doesn't respond to old requests (once we have a proper protocol for that) err = g.publishSharedSecretResponses(resp.ProducedSecretResponses) if err != nil { @@ -511,9 +507,9 @@ func (g *Guardian) submitL1Block(block *common.L1Block, isLatest bool) (bool, er return true, nil } -func (g *Guardian) processL1BlockTransactions(block *common.L1Block, rollupTxs []*common.L1RollupTx, syncContracts bool) { +func (g *Guardian) processL1BlockTransactions(block *types.Header, rollupTxs []*common.L1RollupTx, syncContracts bool) { // TODO (@will) this should be removed and pulled from the L1 - err := g.storage.AddBlock(block.Header()) + err := g.storage.AddBlock(block) if err != nil { g.logger.Error("Could not add block to host db.", log.ErrKey, err) } diff --git a/go/host/l1/dataservice.go b/go/host/l1/dataservice.go index 67f888047b..5fc65f7a8d 100644 --- a/go/host/l1/dataservice.go +++ b/go/host/l1/dataservice.go @@ -26,8 +26,8 @@ import ( ) var ( - // todo (@matt) make this configurable? - _timeoutNoBlocks = 30 * time.Second + l1BlockTime = 12 * time.Second + _timeoutNoBlocks = 2 * l1BlockTime // after this timeout we assume the subscription to the L1 node is not working one = big.NewInt(1) ErrNoNextBlock = errors.New("no next block") ) @@ -100,7 +100,7 @@ func (r *DataService) Subscribe(handler host.L1BlockHandler) func() { // FetchNextBlock calculates the next canonical block that should be sent to requester after a given hash. // It returns the block and a bool for whether it is the latest known head -func (r *DataService) FetchNextBlock(prevBlockHash gethcommon.Hash) (*types.Block, bool, error) { +func (r *DataService) FetchNextBlock(prevBlockHash gethcommon.Hash) (*types.Header, bool, error) { if prevBlockHash == r.head { // prevBlock is the latest known head return nil, false, ErrNoNextBlock @@ -108,7 +108,7 @@ func (r *DataService) FetchNextBlock(prevBlockHash gethcommon.Hash) (*types.Bloc if prevBlockHash == (gethcommon.Hash{}) { // prevBlock is empty, so we are starting from genesis - blk, err := r.ethClient.BlockByNumber(big.NewInt(0)) + blk, err := r.ethClient.HeaderByNumber(big.NewInt(0)) if err != nil { return nil, false, fmt.Errorf("could not find genesis block - %w", err) } @@ -122,36 +122,40 @@ func (r *DataService) FetchNextBlock(prevBlockHash gethcommon.Hash) (*types.Bloc } // and send the canonical block at the height after that // (which may be a fork, or it may just be the next on the same branch if we are catching-up) - blk, err := r.ethClient.BlockByNumber(increment(lca.Number())) + blk, err := r.ethClient.HeaderByNumber(increment(lca.Number)) if err != nil { if errors.Is(err, ethereum.NotFound) { return nil, false, ErrNoNextBlock } - return nil, false, fmt.Errorf("could not find block after latest canon ancestor, height=%s - %w", increment(lca.Number()), err) + return nil, false, fmt.Errorf("could not find block after latest canon ancestor, height=%s - %w", increment(lca.Number), err) } return blk, blk.Hash() == r.head, nil } -func (r *DataService) latestCanonAncestor(blkHash gethcommon.Hash) (*types.Block, error) { - blk, err := r.ethClient.BlockByHash(blkHash) +func (r *DataService) latestCanonAncestor(blkHash gethcommon.Hash) (*types.Header, error) { + blk, err := r.ethClient.HeaderByHash(blkHash) if err != nil { return nil, fmt.Errorf("unable to fetch L1 block with hash=%s - %w", blkHash, err) } - canonAtSameHeight, err := r.ethClient.BlockByNumber(blk.Number()) + canonAtSameHeight, err := r.ethClient.HeaderByNumber(blk.Number) if err != nil { - return nil, fmt.Errorf("unable to fetch L1 block at height=%d - %w", blk.Number(), err) + return nil, fmt.Errorf("unable to fetch L1 block at height=%d - %w", blk.Number, err) } if blk.Hash() != canonAtSameHeight.Hash() { - return r.latestCanonAncestor(blk.ParentHash()) + empty := gethcommon.Hash{} + if blk.ParentHash == empty { + return blk, nil + } + return r.latestCanonAncestor(blk.ParentHash) } return blk, nil } // GetTenRelevantTransactions processes logs in their natural order without grouping by transaction hash. -func (r *DataService) GetTenRelevantTransactions(block *common.L1Block) (*common.ProcessedL1Data, error) { +func (r *DataService) GetTenRelevantTransactions(block *types.Header) (*common.ProcessedL1Data, error) { processed := &common.ProcessedL1Data{ - BlockHeader: block.Header(), + BlockHeader: block, Events: []common.L1Event{}, } @@ -192,7 +196,8 @@ func (r *DataService) GetTenRelevantTransactions(block *common.L1Block) (*common case crosschain.NetworkSecretRespondedID: processed.AddEvent(common.SecretResponseTx, txData) default: - r.logger.Warn("Unknown log topic", "topic", l.Topics[0], "txHash", l.TxHash) + // there are known events that we don't care about here + r.logger.Debug("Unknown log topic", "topic", l.Topics[0], "txHash", l.TxHash) } } @@ -200,7 +205,7 @@ func (r *DataService) GetTenRelevantTransactions(block *common.L1Block) (*common } // fetchMessageBusMgmtContractLogs retrieves all logs from management contract and message bus addresses -func (r *DataService) fetchMessageBusMgmtContractLogs(block *common.L1Block) ([]types.Log, error) { +func (r *DataService) fetchMessageBusMgmtContractLogs(block *types.Header) ([]types.Log, error) { blkHash := block.Hash() var allAddresses []gethcommon.Address allAddresses = append(allAddresses, r.contractAddresses[MgmtContract]...) @@ -284,27 +289,30 @@ func (r *DataService) streamLiveBlocks() { liveStream, streamSub := r.resetLiveStream() for r.running.Load() { select { - case header := <-liveStream: - r.head = header.Hash() - block, err := r.ethClient.BlockByHash(header.Hash()) - if err != nil { - r.logger.Error("Error fetching new block", log.BlockHashKey, header.Hash(), - log.BlockHeightKey, header.Number, log.ErrKey, err) - continue - } + case blockHeader := <-liveStream: + r.head = blockHeader.Hash() for _, handler := range r.blockSubscribers.Subscribers() { - go handler.HandleBlock(block) + go handler.HandleBlock(blockHeader) } case <-time.After(_timeoutNoBlocks): - r.logger.Warn("no new blocks received since timeout", "timeout", _timeoutNoBlocks) + r.logger.Warn("no new blocks received since timeout. Reconnecting..", "timeout", _timeoutNoBlocks) + if streamSub != nil { + streamSub.Unsubscribe() + } + if liveStream != nil { + close(liveStream) + } // reset stream to ensure it has not died liveStream, streamSub = r.resetLiveStream() } } - + r.logger.Info("block streaming stopped") if streamSub != nil { streamSub.Unsubscribe() } + if liveStream != nil { + close(liveStream) + } } func (r *DataService) resetLiveStream() (chan *types.Header, ethereum.Subscription) { @@ -328,8 +336,8 @@ func (r *DataService) resetLiveStream() (chan *types.Header, ethereum.Subscripti return r.ethClient.BlockListener() } -func (r *DataService) FetchBlockByHeight(height *big.Int) (*types.Block, error) { - return r.ethClient.BlockByNumber(height) +func (r *DataService) FetchBlockByHeight(height *big.Int) (*types.Header, error) { + return r.ethClient.HeaderByNumber(height) } // getEnclaveIdFromLog gets the enclave ID from the log topic diff --git a/go/host/storage/hostdb/rollup.go b/go/host/storage/hostdb/rollup.go index a1fdd1b85d..bc996ffefa 100644 --- a/go/host/storage/hostdb/rollup.go +++ b/go/host/storage/hostdb/rollup.go @@ -5,6 +5,8 @@ import ( "fmt" "math/big" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rlp" "github.com/pkg/errors" "github.com/ten-protocol/go-ten/go/common" @@ -22,7 +24,7 @@ const ( ) // AddRollup adds a rollup to the DB -func AddRollup(dbtx *dbTransaction, statements *SQLStatements, rollup *common.ExtRollup, metadata *common.PublicRollupMetadata, block *common.L1Block) error { +func AddRollup(dbtx *dbTransaction, statements *SQLStatements, rollup *common.ExtRollup, metadata *common.PublicRollupMetadata, block *types.Header) error { extRollup, err := rlp.EncodeToBytes(rollup) if err != nil { return fmt.Errorf("could not encode rollup: %w", err) diff --git a/go/host/storage/interfaces.go b/go/host/storage/interfaces.go index 7abbf4fc9e..a218f3af80 100644 --- a/go/host/storage/interfaces.go +++ b/go/host/storage/interfaces.go @@ -58,7 +58,7 @@ type BlockResolver interface { // AddBlock stores block data containing rollups in the host DB AddBlock(b *types.Header) error // AddRollup stores a rollup in the host DB - AddRollup(rollup *common.ExtRollup, metadata *common.PublicRollupMetadata, block *common.L1Block) error + AddRollup(rollup *common.ExtRollup, metadata *common.PublicRollupMetadata, block *types.Header) error // FetchLatestRollupHeader returns the head `RollupHeader` FetchLatestRollupHeader() (*common.RollupHeader, error) // FetchRollupListing returns a paginated list of rollups diff --git a/go/host/storage/storage.go b/go/host/storage/storage.go index fe896e755d..44a68dd2bc 100644 --- a/go/host/storage/storage.go +++ b/go/host/storage/storage.go @@ -52,7 +52,7 @@ func (s *storageImpl) AddBatch(batch *common.ExtBatch) error { return nil } -func (s *storageImpl) AddRollup(rollup *common.ExtRollup, metadata *common.PublicRollupMetadata, block *common.L1Block) error { +func (s *storageImpl) AddRollup(rollup *common.ExtRollup, metadata *common.PublicRollupMetadata, block *types.Header) error { // Check if the Header is already stored _, err := hostdb.GetRollupHeader(s.db, rollup.Header.Hash()) if err == nil { diff --git a/integration/ethereummock/db.go b/integration/ethereummock/db.go index 4a034fa387..fbced97ddb 100644 --- a/integration/ethereummock/db.go +++ b/integration/ethereummock/db.go @@ -79,16 +79,16 @@ func (n *blockResolverInMem) FetchHeadBlock(_ context.Context) (*types.Block, er return max, nil } -func (n *blockResolverInMem) ParentBlock(ctx context.Context, b *types.Block) (*types.Block, error) { - return n.FetchBlock(ctx, b.Header().ParentHash) +func (n *blockResolverInMem) ParentBlock(ctx context.Context, b *types.Header) (*types.Block, error) { + return n.FetchBlock(ctx, b.ParentHash) } -func (n *blockResolverInMem) IsAncestor(ctx context.Context, block *types.Block, maybeAncestor *types.Block) bool { +func (n *blockResolverInMem) IsAncestor(ctx context.Context, block *types.Header, maybeAncestor *types.Header) bool { if bytes.Equal(maybeAncestor.Hash().Bytes(), block.Hash().Bytes()) { return true } - if maybeAncestor.NumberU64() >= block.NumberU64() { + if maybeAncestor.Number.Uint64() >= block.Number.Uint64() { return false } @@ -97,10 +97,10 @@ func (n *blockResolverInMem) IsAncestor(ctx context.Context, block *types.Block, return false } - return n.IsAncestor(ctx, p, maybeAncestor) + return n.IsAncestor(ctx, p.Header(), maybeAncestor) } -func (n *blockResolverInMem) IsBlockAncestor(ctx context.Context, block *types.Block, maybeAncestor common.L1BlockHash) bool { +func (n *blockResolverInMem) IsBlockAncestor(ctx context.Context, block *types.Header, maybeAncestor common.L1BlockHash) bool { if bytes.Equal(maybeAncestor.Bytes(), block.Hash().Bytes()) { return true } @@ -109,13 +109,13 @@ func (n *blockResolverInMem) IsBlockAncestor(ctx context.Context, block *types.B return true } - if block.NumberU64() == common.L1GenesisHeight { + if block.Number.Uint64() == common.L1GenesisHeight { return false } resolvedBlock, err := n.FetchBlock(ctx, maybeAncestor) if err == nil { - if resolvedBlock.NumberU64() >= block.NumberU64() { + if resolvedBlock.NumberU64() >= block.Number.Uint64() { return false } } @@ -126,7 +126,7 @@ func (n *blockResolverInMem) IsBlockAncestor(ctx context.Context, block *types.B return false } - return n.IsBlockAncestor(ctx, p, maybeAncestor) + return n.IsBlockAncestor(ctx, p.Header(), maybeAncestor) } // The cache of included transactions diff --git a/integration/ethereummock/gethutil.go b/integration/ethereummock/gethutil.go index ac22b5f816..f2a30b6ee1 100644 --- a/integration/ethereummock/gethutil.go +++ b/integration/ethereummock/gethutil.go @@ -16,52 +16,52 @@ var EmptyHash = gethcommon.Hash{} // LCA - returns the latest common ancestor of the 2 blocks or an error if no common ancestor is found // it also returns the blocks that became canonical, and the once that are now the fork -func LCA(ctx context.Context, newCanonical *types.Block, oldCanonical *types.Block, resolver *blockResolverInMem) (*common.ChainFork, error) { +func LCA(ctx context.Context, newCanonical *types.Header, oldCanonical *types.Header, resolver *blockResolverInMem) (*common.ChainFork, error) { b, cp, ncp, err := internalLCA(ctx, newCanonical, oldCanonical, resolver, []common.L1BlockHash{}, []common.L1BlockHash{}) if err != nil { return nil, fmt.Errorf("could not calculate LCA. Cause: %w", err) } return &common.ChainFork{ - NewCanonical: newCanonical.Header(), - OldCanonical: oldCanonical.Header(), - CommonAncestor: b.Header(), + NewCanonical: newCanonical, + OldCanonical: oldCanonical, + CommonAncestor: b, CanonicalPath: cp, NonCanonicalPath: ncp, }, err } -func internalLCA(ctx context.Context, newCanonical *types.Block, oldCanonical *types.Block, resolver *blockResolverInMem, canonicalPath []common.L1BlockHash, nonCanonicalPath []common.L1BlockHash) (*types.Block, []common.L1BlockHash, []common.L1BlockHash, error) { - if newCanonical.NumberU64() == common.L1GenesisHeight || oldCanonical.NumberU64() == common.L1GenesisHeight { +func internalLCA(ctx context.Context, newCanonical *types.Header, oldCanonical *types.Header, resolver *blockResolverInMem, canonicalPath []common.L1BlockHash, nonCanonicalPath []common.L1BlockHash) (*types.Header, []common.L1BlockHash, []common.L1BlockHash, error) { + if newCanonical.Number.Uint64() == common.L1GenesisHeight || oldCanonical.Number.Uint64() == common.L1GenesisHeight { return newCanonical, canonicalPath, nonCanonicalPath, nil } if newCanonical.Hash() == oldCanonical.Hash() { // this is where we reach the common ancestor, which we add to the canonical path return newCanonical, append(canonicalPath, newCanonical.Hash()), nonCanonicalPath, nil } - if newCanonical.NumberU64() > oldCanonical.NumberU64() { - p, err := resolver.FetchBlock(ctx, newCanonical.ParentHash()) + if newCanonical.Number.Uint64() > oldCanonical.Number.Uint64() { + p, err := resolver.FetchBlock(ctx, newCanonical.ParentHash) if err != nil { - return nil, nil, nil, fmt.Errorf("could not retrieve parent block %s. Cause: %w", newCanonical.ParentHash(), err) + return nil, nil, nil, fmt.Errorf("could not retrieve parent block %s. Cause: %w", newCanonical.ParentHash, err) } - return internalLCA(ctx, p, oldCanonical, resolver, append(canonicalPath, newCanonical.Hash()), nonCanonicalPath) + return internalLCA(ctx, p.Header(), oldCanonical, resolver, append(canonicalPath, newCanonical.Hash()), nonCanonicalPath) } - if oldCanonical.NumberU64() > newCanonical.NumberU64() { - p, err := resolver.FetchBlock(ctx, oldCanonical.ParentHash()) + if oldCanonical.Number.Uint64() > newCanonical.Number.Uint64() { + p, err := resolver.FetchBlock(ctx, oldCanonical.ParentHash) if err != nil { - return nil, nil, nil, fmt.Errorf("could not retrieve parent block %s. Cause: %w", oldCanonical.ParentHash(), err) + return nil, nil, nil, fmt.Errorf("could not retrieve parent block %s. Cause: %w", oldCanonical.ParentHash, err) } - return internalLCA(ctx, newCanonical, p, resolver, canonicalPath, append(nonCanonicalPath, oldCanonical.Hash())) + return internalLCA(ctx, newCanonical, p.Header(), resolver, canonicalPath, append(nonCanonicalPath, oldCanonical.Hash())) } - parentBlockA, err := resolver.FetchBlock(ctx, newCanonical.ParentHash()) + parentBlockA, err := resolver.FetchBlock(ctx, newCanonical.ParentHash) if err != nil { - return nil, nil, nil, fmt.Errorf("could not retrieve parent block %s. Cause: %w", newCanonical.ParentHash(), err) + return nil, nil, nil, fmt.Errorf("could not retrieve parent block %s. Cause: %w", newCanonical.ParentHash, err) } - parentBlockB, err := resolver.FetchBlock(ctx, oldCanonical.ParentHash()) + parentBlockB, err := resolver.FetchBlock(ctx, oldCanonical.ParentHash) if err != nil { - return nil, nil, nil, fmt.Errorf("could not retrieve parent block %s. Cause: %w", oldCanonical.ParentHash(), err) + return nil, nil, nil, fmt.Errorf("could not retrieve parent block %s. Cause: %w", oldCanonical.ParentHash, err) } - return internalLCA(ctx, parentBlockA, parentBlockB, resolver, append(canonicalPath, newCanonical.Hash()), append(nonCanonicalPath, oldCanonical.Hash())) + return internalLCA(ctx, parentBlockA.Header(), parentBlockB.Header(), resolver, append(canonicalPath, newCanonical.Hash()), append(nonCanonicalPath, oldCanonical.Hash())) } diff --git a/integration/ethereummock/mock_l1_network.go b/integration/ethereummock/mock_l1_network.go index 5c35d2fc14..93987a2973 100644 --- a/integration/ethereummock/mock_l1_network.go +++ b/integration/ethereummock/mock_l1_network.go @@ -5,6 +5,8 @@ import ( "fmt" "time" + gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ten-protocol/go-ten/go/common/async" "github.com/ten-protocol/go-ten/go/common/log" @@ -44,7 +46,7 @@ func NewMockEthNetwork(avgBlockDuration time.Duration, avgLatency time.Duration, } // BroadcastBlock broadcast a block to the l1 nodes -func (n *MockEthNetwork) BroadcastBlock(b common.EncodedL1Block, p common.EncodedL1Block) { +func (n *MockEthNetwork) BroadcastBlock(b EncodedL1Block, p EncodedL1Block) { bl, _ := b.DecodeBlock() for _, m := range n.AllNodes { if m.Info().L2ID != n.CurrentNode.Info().L2ID { @@ -95,14 +97,14 @@ func printBlock(b *types.Block, m *Node) string { if err != nil { testlog.Logger().Crit("failed to decode rollup") } - txs = append(txs, fmt.Sprintf("r_%d(nonce=%d)", common.ShortHash(r.Hash()), tx.Nonce())) + txs = append(txs, fmt.Sprintf("r_%s(nonce=%s)", r.Hash(), tx.Nonce())) case *common.L1DepositTx: - var to uint64 + var to gethcommon.Address if l1Tx.To != nil { - to = common.ShortAddress(*l1Tx.To) + to = *l1Tx.To } - txs = append(txs, fmt.Sprintf("deposit(%d=%d)", to, l1Tx.Amount)) + txs = append(txs, fmt.Sprintf("deposit(%s=%d)", to, l1Tx.Amount)) } } p, err := m.BlockResolver.FetchBlock(context.Background(), b.ParentHash()) @@ -110,6 +112,6 @@ func printBlock(b *types.Block, m *Node) string { testlog.Logger().Crit("Should not happen. Could not retrieve parent", log.ErrKey, err) } - return fmt.Sprintf(" create b_%d(Height=%d, RollupNonce=%d)[parent=b_%d]. Txs: %v", - common.ShortHash(b.Hash()), b.NumberU64(), common.ShortNonce(b.Header().Nonce), common.ShortHash(p.Hash()), txs) + return fmt.Sprintf(" create b_%s(Height=%d, RollupNonce=%s)[parent=b_%s]. Txs: %v", + b.Hash(), b.NumberU64(), b.Header().Nonce, p.Hash(), txs) } diff --git a/integration/ethereummock/node.go b/integration/ethereummock/node.go index d97f968c9f..e51319c0dd 100644 --- a/integration/ethereummock/node.go +++ b/integration/ethereummock/node.go @@ -36,16 +36,18 @@ import ( "github.com/ten-protocol/go-ten/go/ethadapter/mgmtcontractlib" ) -const SecondsPerSlot = uint64(12) +type ( + Latency func() time.Duration +) type L1Network interface { // BroadcastBlock - send the block and the parent to make sure there are no gaps - BroadcastBlock(b common.EncodedL1Block, p common.EncodedL1Block) + BroadcastBlock(b EncodedL1Block, p EncodedL1Block) BroadcastTx(tx *types.Transaction) } type MiningConfig struct { - PowTime common.Latency + PowTime Latency LogFile string L1BeaconPort int } @@ -84,12 +86,10 @@ type Node struct { p2pCh chan *types.Block // this is where blocks received from peers are dropped miningCh chan *types.Block // this is where blocks created by the mining setup of the current node are dropped - canonicalCh chan *types.Block // this is where the main processing routine drops blocks that are canonical + canonicalCh chan *types.Header // this is where the main processing routine drops blocks that are canonical mempoolCh chan *types.Transaction // where l1 transactions to be published in the next block are added // internal - headInCh chan bool - headOutCh chan *types.Block erc20ContractLib erc20contractlib.ERC20ContractLib mgmtContractLib mgmtcontractlib.MgmtContractLib @@ -226,16 +226,17 @@ func (m *Node) FetchLastBatchSeqNo(gethcommon.Address) (*big.Int, error) { return nil, err } - for currentBlock := startingBlock; currentBlock.NumberU64() != 0; { - currentBlock, err = m.BlockByHash(currentBlock.Header().ParentHash) + for currentBlock := startingBlock; currentBlock.Number.Uint64() != 0; { + cb, err := m.BlockByHash(currentBlock.ParentHash) if err != nil { m.logger.Error("Error fetching block by hash", "error", err) break } - rollup := m.getRollupFromBlock(currentBlock) + rollup := m.getRollupFromBlock(cb) if rollup != nil { return big.NewInt(int64(rollup.Header.LastBatchSeqNo)), nil } + currentBlock = cb.Header() } // the first batch is number 1 return big.NewInt(int64(common.L2GenesisSeqNo)), nil @@ -266,9 +267,9 @@ func (m *Node) BlockNumber() (uint64, error) { return blk.NumberU64(), nil } -func (m *Node) BlockByNumber(n *big.Int) (*types.Block, error) { +func (m *Node) HeaderByNumber(n *big.Int) (*types.Header, error) { if n.Int64() == 0 { - return MockGenesisBlock, nil + return MockGenesisBlock.Header(), nil } // TODO this should be a method in the resolver blk, err := m.BlockResolver.FetchHeadBlock(context.Background()) @@ -280,7 +281,7 @@ func (m *Node) BlockByNumber(n *big.Int) (*types.Block, error) { } for !bytes.Equal(blk.ParentHash().Bytes(), (common.L1BlockHash{}).Bytes()) { if blk.NumberU64() == n.Uint64() { - return blk, nil + return blk.Header(), nil } blk, err = m.BlockResolver.FetchBlock(context.Background(), blk.ParentHash()) @@ -291,6 +292,14 @@ func (m *Node) BlockByNumber(n *big.Int) (*types.Block, error) { return nil, ethereum.NotFound } +func (m *Node) HeaderByHash(id gethcommon.Hash) (*types.Header, error) { + blk, err := m.BlockResolver.FetchBlock(context.Background(), id) + if err != nil { + return nil, fmt.Errorf("block could not be retrieved. Cause: %w", err) + } + return blk.Header(), nil +} + func (m *Node) BlockByHash(id gethcommon.Hash) (*types.Block, error) { blk, err := m.BlockResolver.FetchBlock(context.Background(), id) if err != nil { @@ -299,12 +308,12 @@ func (m *Node) BlockByHash(id gethcommon.Hash) (*types.Block, error) { return blk, nil } -func (m *Node) FetchHeadBlock() (*types.Block, error) { +func (m *Node) FetchHeadBlock() (*types.Header, error) { block, err := m.BlockResolver.FetchHeadBlock(context.Background()) if err != nil { return nil, fmt.Errorf("could not retrieve head block. Cause: %w", err) } - return block, nil + return block.Header(), nil } func (m *Node) Info() ethadapter.Info { @@ -313,7 +322,7 @@ func (m *Node) Info() ethadapter.Info { } } -func (m *Node) IsBlockAncestor(block *types.Block, proof common.L1BlockHash) bool { +func (m *Node) IsBlockAncestor(block *types.Header, proof common.L1BlockHash) bool { return m.BlockResolver.IsBlockAncestor(context.Background(), block, proof) } @@ -387,7 +396,7 @@ func (m *Node) Start() { if err != nil { m.logger.Crit("Failed to store block") } - head := m.setHead(MockGenesisBlock) + head := m.setHead(MockGenesisBlock.Header()) for { select { @@ -409,25 +418,23 @@ func (m *Node) Start() { if err != nil { panic(fmt.Errorf("could not retrieve parent. Cause: %w", err)) } - encodedBlock, err := common.EncodeBlock(mb) + encodedBlock, err := EncodeBlock(mb) if err != nil { panic(fmt.Errorf("could not encode block. Cause: %w", err)) } - encodedParentBlock, err := common.EncodeBlock(p) + encodedParentBlock, err := EncodeBlock(p) if err != nil { panic(fmt.Errorf("could not encode parent block. Cause: %w", err)) } m.Network.BroadcastBlock(encodedBlock, encodedParentBlock) } - case <-m.headInCh: - m.headOutCh <- head case <-m.exitCh: return } } } -func (m *Node) processBlock(b *types.Block, head *types.Block) *types.Block { +func (m *Node) processBlock(b *types.Block, head *types.Header) *types.Header { err := m.BlockResolver.StoreBlock(context.Background(), b, nil) if err != nil { m.logger.Crit("Failed to store block. Cause: %w", err) @@ -437,38 +444,38 @@ func (m *Node) processBlock(b *types.Block, head *types.Block) *types.Block { // only proceed if the parent is available if err != nil { if errors.Is(err, errutil.ErrNotFound) { - m.logger.Info(fmt.Sprintf("Parent block not found=b_%d", common.ShortHash(b.Header().ParentHash))) + m.logger.Info(fmt.Sprintf("Parent block not found=b_%s", b.Header().ParentHash)) return head } m.logger.Crit("Could not fetch block parent. Cause: %w", err) } // Ignore superseded blocks - if b.NumberU64() <= head.NumberU64() { + if b.NumberU64() <= head.Number.Uint64() { return head } // Check for Reorgs - if !m.BlockResolver.IsAncestor(context.Background(), b, head) { + if !m.BlockResolver.IsAncestor(context.Background(), b.Header(), head) { m.stats.L1Reorg(m.l2ID) - fork, err := LCA(context.Background(), head, b, m.BlockResolver) + fork, err := LCA(context.Background(), head, b.Header(), m.BlockResolver) if err != nil { m.logger.Error("Should not happen.", log.ErrKey, err) return head } m.logger.Info( - fmt.Sprintf("L1Reorg new=b_%d(%d), old=b_%d(%d), fork=b_%d(%d)", common.ShortHash(b.Hash()), b.NumberU64(), common.ShortHash(head.Hash()), head.NumberU64(), common.ShortHash(fork.CommonAncestor.Hash()), fork.CommonAncestor.Number.Uint64())) - return m.setFork(m.BlocksBetween(fork.CommonAncestor, b)) + fmt.Sprintf("L1Reorg new=b_%s(%d), old=b_%s(%d), fork=b_%s(%d)", b.Hash(), b.NumberU64(), head.Hash(), head.Number.Uint64(), fork.CommonAncestor.Hash(), fork.CommonAncestor.Number.Uint64())) + return m.setFork(m.BlocksBetween(fork.CommonAncestor, b.Header())) } - if b.NumberU64() > (head.NumberU64() + 1) { + if b.NumberU64() > (head.Number.Uint64() + 1) { m.logger.Error("Should not happen. Blocks are skewed") } - return m.setHead(b) + return m.setHead(b.Header()) } // Notifies the Miner to start mining on the new block and the aggregator to produce rollups -func (m *Node) setHead(b *types.Block) *types.Block { +func (m *Node) setHead(b *types.Header) *types.Header { if atomic.LoadInt32(m.interrupt) == 1 { return b } @@ -485,7 +492,7 @@ func (m *Node) setHead(b *types.Block) *types.Block { return b } -func (m *Node) setFork(blocks []*types.Block) *types.Block { +func (m *Node) setFork(blocks []*types.Header) *types.Header { head := blocks[len(blocks)-1] if atomic.LoadInt32(m.interrupt) == 1 { return head @@ -506,7 +513,7 @@ func (m *Node) setFork(blocks []*types.Block) *types.Block { // P2PReceiveBlock is called by counterparties when there is a block to broadcast // All it does is drop the blocks in a channel for processing. -func (m *Node) P2PReceiveBlock(b common.EncodedL1Block, p common.EncodedL1Block) { +func (m *Node) P2PReceiveBlock(b EncodedL1Block, p EncodedL1Block) { if atomic.LoadInt32(m.interrupt) == 1 { return } @@ -537,8 +544,12 @@ func (m *Node) startMining() { case tx := <-m.mempoolCh: mempool = append(mempool, tx) - case canonicalBlock := <-m.canonicalCh: + case canonicalBlockHeader := <-m.canonicalCh: // A new canonical block was found. Start a new round based on that block. + canonicalBlock, err := m.BlockResolver.FetchBlock(context.Background(), canonicalBlockHeader.Hash()) + if err != nil { + panic(fmt.Errorf("could not fetch block. Cause: %w", err)) + } // remove transactions that are already considered committed mempool = m.removeCommittedTransactions(context.Background(), canonicalBlock, mempool, m.BlockResolver, m.db) @@ -590,25 +601,25 @@ func (m *Node) Stop() { m.exitCh <- true } -func (m *Node) BlocksBetween(blockA *types.Header, blockB *types.Block) []*types.Block { +func (m *Node) BlocksBetween(blockA *types.Header, blockB *types.Header) []*types.Header { if bytes.Equal(blockA.Hash().Bytes(), blockB.Hash().Bytes()) { - return []*types.Block{blockB} + return []*types.Header{blockB} } - blocks := make([]*types.Block, 0) + blocks := make([]*types.Header, 0) tempBlock := blockB - var err error for { blocks = append(blocks, tempBlock) if bytes.Equal(tempBlock.Hash().Bytes(), blockA.Hash().Bytes()) { break } - tempBlock, err = m.BlockResolver.FetchBlock(context.Background(), tempBlock.ParentHash()) + tb, err := m.BlockResolver.FetchBlock(context.Background(), tempBlock.ParentHash) if err != nil { panic(fmt.Errorf("could not retrieve parent block. Cause: %w", err)) } + tempBlock = tb.Header() } n := len(blocks) - result := make([]*types.Block, n) + result := make([]*types.Header, n) for i, block := range blocks { result[n-i-1] = block } @@ -687,10 +698,8 @@ func NewMiner( interrupt: new(int32), p2pCh: make(chan *types.Block), miningCh: make(chan *types.Block), - canonicalCh: make(chan *types.Block), + canonicalCh: make(chan *types.Header), mempoolCh: make(chan *types.Transaction), - headInCh: make(chan bool), - headOutCh: make(chan *types.Block), erc20ContractLib: NewERC20ContractLibMock(), mgmtContractLib: NewMgmtContractLibMock(), logger: logger, @@ -714,11 +723,11 @@ func (sub *mockSubscription) Unsubscribe() { sub.node.RemoveSubscription(sub.id) } -func (sub *mockSubscription) publish(b *types.Block) { - sub.headCh <- b.Header() +func (sub *mockSubscription) publish(b *types.Header) { + sub.headCh <- b } -func (sub *mockSubscription) publishAll(blocks []*types.Block) { +func (sub *mockSubscription) publishAll(blocks []*types.Header) { for _, b := range blocks { sub.publish(b) } diff --git a/integration/ethereummock/utils.go b/integration/ethereummock/utils.go index 3bad980648..5097de506e 100644 --- a/integration/ethereummock/utils.go +++ b/integration/ethereummock/utils.go @@ -4,6 +4,8 @@ import ( "context" "fmt" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ten-protocol/go-ten/go/common" "github.com/ethereum/go-ethereum/core/types" @@ -56,3 +58,22 @@ func makeMap(txs types.Transactions) map[common.TxHash]*types.Transaction { } return m } + +// EncodedL1Block the encoded version of an L1 block. +type EncodedL1Block []byte + +func EncodeBlock(b *types.Block) (EncodedL1Block, error) { + encoded, err := rlp.EncodeToBytes(b) + if err != nil { + return nil, fmt.Errorf("could not encode block to bytes. Cause: %w", err) + } + return encoded, nil +} + +func (eb EncodedL1Block) DecodeBlock() (*types.Block, error) { + b := types.Block{} + if err := rlp.DecodeBytes(eb, &b); err != nil { + return nil, fmt.Errorf("could not decode block from bytes. Cause: %w", err) + } + return &b, nil +} diff --git a/integration/simulation/output_stats.go b/integration/simulation/output_stats.go index 441388f89e..cb48e731bb 100644 --- a/integration/simulation/output_stats.go +++ b/integration/simulation/output_stats.go @@ -80,8 +80,12 @@ func (o *OutputStats) countBlockChain() { } // iterate the L1 Blocks and get the rollups - for block, _ := l1Node.FetchHeadBlock(); block != nil && !bytes.Equal(block.Hash().Bytes(), (common.L1BlockHash{}).Bytes()); block, _ = l1Node.BlockByHash(block.ParentHash()) { - o.incrementStats(block, l1Node) + for block, _ := l1Node.FetchHeadBlock(); block != nil && !bytes.Equal(block.Hash().Bytes(), (common.L1BlockHash{}).Bytes()); block, _ = l1Node.HeaderByHash(block.ParentHash) { + b, err := l1Node.BlockByHash(block.Hash()) + if err != nil { + panic(err) + } + o.incrementStats(b, l1Node) } } diff --git a/integration/simulation/simulation.go b/integration/simulation/simulation.go index 4ce3f4c32e..97c1973f07 100644 --- a/integration/simulation/simulation.go +++ b/integration/simulation/simulation.go @@ -52,7 +52,7 @@ type Simulation struct { // Start executes the simulation given all the Params. Injects transactions. func (s *Simulation) Start() { - testlog.Logger().Info(fmt.Sprintf("Genesis block: b_%d.", common.ShortHash(ethereummock.MockGenesisBlock.Hash()))) + testlog.Logger().Info(fmt.Sprintf("Genesis block: b_%s.", ethereummock.MockGenesisBlock.Hash())) s.ctx = context.Background() // use injected context for graceful shutdowns fmt.Printf("Waiting for TEN genesis on L1\n") @@ -132,7 +132,11 @@ func (s *Simulation) waitForTenGenesisOnL1() { panic(fmt.Errorf("could not fetch head block. Cause: %w", err)) } if err == nil { - for _, b := range client.BlocksBetween(ethereummock.MockGenesisBlock.Header(), head) { + for _, h := range client.BlocksBetween(ethereummock.MockGenesisBlock.Header(), head) { + b, err := client.BlockByHash(h.Hash()) + if err != nil { + panic(err) + } for _, tx := range b.Transactions() { t := s.Params.MgmtContractLib.DecodeTx(tx) if t == nil { diff --git a/integration/simulation/utils.go b/integration/simulation/utils.go index 9445b9c7be..7b9829d04e 100644 --- a/integration/simulation/utils.go +++ b/integration/simulation/utils.go @@ -119,7 +119,7 @@ func findRollupDups(list []*common.ExtRollup) map[common.L2RollupHash]int { for u, i := range elementCount { if i > 1 { dups[u] = i - fmt.Printf("Dup: r_%d\n", common.ShortHash(u)) + fmt.Printf("Dup: r_%s\n", u.Hex()) } } return dups diff --git a/integration/simulation/validate_chain.go b/integration/simulation/validate_chain.go index 4b061e1cc7..625d16e46f 100644 --- a/integration/simulation/validate_chain.go +++ b/integration/simulation/validate_chain.go @@ -176,13 +176,13 @@ func checkBlockchainOfEthereumNode(t *testing.T, node ethadapter.EthClient, minH if err != nil { t.Errorf("Node %d: Could not find head block. Cause: %s", nodeIdx, err) } - height := head.NumberU64() + height := head.Number.Uint64() if height < minHeight { t.Errorf("Node %d: There were only %d blocks mined. Expected at least: %d.", nodeIdx, height, minHeight) } - deposits, rollups, _, blockCount, _, rollupReceipts := ExtractDataFromEthereumChain(ethereummock.MockGenesisBlock, head, node, s, nodeIdx) + deposits, rollups, _, blockCount, _, rollupReceipts := ExtractDataFromEthereumChain(ethereummock.MockGenesisBlock.Header(), head, node, s, nodeIdx) s.Stats.TotalL1Blocks = uint64(blockCount) checkCollectedL1Fees(t, node, s, nodeIdx, rollupReceipts) @@ -247,21 +247,19 @@ func checkRollups(t *testing.T, _ *Simulation, nodeIdx int, rollups []*common.Ex // ExtractDataFromEthereumChain returns the deposits, rollups, total amount deposited and length of the blockchain // between the start block and the end block. -func ExtractDataFromEthereumChain( - startBlock *types.Block, - endBlock *types.Block, - node ethadapter.EthClient, - s *Simulation, - nodeIdx int, -) ([]gethcommon.Hash, []*common.ExtRollup, *big.Int, int, uint64, types.Receipts) { +func ExtractDataFromEthereumChain(startBlock *types.Header, endBlock *types.Header, node ethadapter.EthClient, s *Simulation, nodeIdx int) ([]gethcommon.Hash, []*common.ExtRollup, *big.Int, int, uint64, types.Receipts) { deposits := make([]gethcommon.Hash, 0) rollups := make([]*common.ExtRollup, 0) rollupReceipts := make(types.Receipts, 0) totalDeposited := big.NewInt(0) - blockchain := node.BlocksBetween(startBlock.Header(), endBlock) + blockchain := node.BlocksBetween(startBlock, endBlock) successfulDeposits := uint64(0) - for _, block := range blockchain { + for _, header := range blockchain { + block, err := node.BlockByHash(header.Hash()) + if err != nil { + panic(err) + } for _, tx := range block.Transactions() { t := s.Params.ERC20ContractLib.DecodeTx(tx) if t == nil { From 3951c468a0fed33d482d9a18d2c17b46f6891217 Mon Sep 17 00:00:00 2001 From: Tudor Malene Date: Thu, 9 Jan 2025 09:46:28 +0200 Subject: [PATCH 2/8] fix --- integration/datagenerator/rollup.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration/datagenerator/rollup.go b/integration/datagenerator/rollup.go index 36d6e8b8c8..7f17c4e063 100644 --- a/integration/datagenerator/rollup.go +++ b/integration/datagenerator/rollup.go @@ -8,7 +8,7 @@ import ( // RandomRollup - block is needed in order to pass the smart contract check // when submitting cross chain messages. -func RandomRollup(_ *types.Block) common.ExtRollup { +func RandomRollup(_ *types.Header) common.ExtRollup { extRollup := common.ExtRollup{ Header: &common.RollupHeader{}, } From e2f590fa92c6e9772851a57406cf75b3c27ad028 Mon Sep 17 00:00:00 2001 From: Tudor Malene Date: Thu, 9 Jan 2025 09:52:29 +0200 Subject: [PATCH 3/8] fix --- go/host/storage/hostdb/block_test.go | 2 +- go/host/storage/hostdb/rollup_test.go | 26 +++++++++++++------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/go/host/storage/hostdb/block_test.go b/go/host/storage/hostdb/block_test.go index fb098c6c2f..e127883aae 100644 --- a/go/host/storage/hostdb/block_test.go +++ b/go/host/storage/hostdb/block_test.go @@ -77,7 +77,7 @@ func TestAddBlockWithForeignKeyConstraint(t *testing.T) { dbtx, _ = db.NewDBTransaction() // add rollup referencing block - err = AddRollup(dbtx, db.GetSQLStatement(), &rollup, &metadata, block) + err = AddRollup(dbtx, db.GetSQLStatement(), &rollup, &metadata, block.Header()) if err != nil { t.Errorf("could not store rollup. Cause: %s", err) } diff --git a/go/host/storage/hostdb/rollup_test.go b/go/host/storage/hostdb/rollup_test.go index d7bb16727b..ff869092e6 100644 --- a/go/host/storage/hostdb/rollup_test.go +++ b/go/host/storage/hostdb/rollup_test.go @@ -28,7 +28,7 @@ func TestCanStoreAndRetrieveRollup(t *testing.T) { } dbtx.Write() dbtx, _ = db.NewDBTransaction() - err = AddRollup(dbtx, db.GetSQLStatement(), &rollup, &metadata, block) + err = AddRollup(dbtx, db.GetSQLStatement(), &rollup, &metadata, block.Header()) if err != nil { t.Errorf("could not store rollup. Cause: %s", err) } @@ -68,7 +68,7 @@ func TestGetRollupByBlockHash(t *testing.T) { } dbtx.Write() dbtx, _ = db.NewDBTransaction() - err = AddRollup(dbtx, db.GetSQLStatement(), &rollup, &metadata, block) + err = AddRollup(dbtx, db.GetSQLStatement(), &rollup, &metadata, block.Header()) if err != nil { t.Errorf("could not store rollup. Cause: %s", err) } @@ -100,7 +100,7 @@ func TestGetLatestRollup(t *testing.T) { } dbtx.Write() dbtx, _ = db.NewDBTransaction() - err = AddRollup(dbtx, db.GetSQLStatement(), &rollup1, &metadata1, block) + err = AddRollup(dbtx, db.GetSQLStatement(), &rollup1, &metadata1, block.Header()) if err != nil { t.Errorf("could not store rollup. Cause: %s", err) } @@ -111,7 +111,7 @@ func TestGetLatestRollup(t *testing.T) { rollup2LastSeq := int64(batchNumber + 10) metadata2 := createRollupMetadata(rollup2FirstSeq) rollup2 := createRollup(rollup2LastSeq) - err = AddRollup(dbtx, db.GetSQLStatement(), &rollup2, &metadata2, block) + err = AddRollup(dbtx, db.GetSQLStatement(), &rollup2, &metadata2, block.Header()) if err != nil { t.Errorf("could not store rollup 2. Cause: %s", err) } @@ -145,7 +145,7 @@ func TestGetRollupBySeqNo(t *testing.T) { } dbtx.Write() dbtx, _ = db.NewDBTransaction() - err = AddRollup(dbtx, db.GetSQLStatement(), &rollup1, &metadata1, block) + err = AddRollup(dbtx, db.GetSQLStatement(), &rollup1, &metadata1, block.Header()) if err != nil { t.Errorf("could not store rollup. Cause: %s", err) } @@ -156,7 +156,7 @@ func TestGetRollupBySeqNo(t *testing.T) { rollup2LastSeq := int64(batchNumber + 10) // 787 metadata2 := createRollupMetadata(rollup2FirstSeq) rollup2 := createRollup(rollup2LastSeq) - err = AddRollup(dbtx, db.GetSQLStatement(), &rollup2, &metadata2, block) + err = AddRollup(dbtx, db.GetSQLStatement(), &rollup2, &metadata2, block.Header()) if err != nil { t.Errorf("could not store rollup 2. Cause: %s", err) } @@ -200,7 +200,7 @@ func TestGetRollupListing(t *testing.T) { } dbtx.Write() dbtx, _ = db.NewDBTransaction() - err = AddRollup(dbtx, db.GetSQLStatement(), &rollup1, &metadata1, block) + err = AddRollup(dbtx, db.GetSQLStatement(), &rollup1, &metadata1, block.Header()) if err != nil { t.Errorf("could not store rollup. Cause: %s", err) } @@ -209,7 +209,7 @@ func TestGetRollupListing(t *testing.T) { rollup2LastSeq := int64(batchNumber + 10) metadata2 := createRollupMetadata(rollup2FirstSeq) rollup2 := createRollup(rollup2LastSeq) - err = AddRollup(dbtx, db.GetSQLStatement(), &rollup2, &metadata2, block) + err = AddRollup(dbtx, db.GetSQLStatement(), &rollup2, &metadata2, block.Header()) if err != nil { t.Errorf("could not store rollup 2. Cause: %s", err) } @@ -218,7 +218,7 @@ func TestGetRollupListing(t *testing.T) { rollup3LastSeq := int64(batchNumber + 20) metadata3 := createRollupMetadata(rollup3FirstSeq) rollup3 := createRollup(rollup3LastSeq) - err = AddRollup(dbtx, db.GetSQLStatement(), &rollup3, &metadata3, block) + err = AddRollup(dbtx, db.GetSQLStatement(), &rollup3, &metadata3, block.Header()) dbtx.Write() if err != nil { t.Errorf("could not store rollup 3. Cause: %s", err) @@ -303,7 +303,7 @@ func TestGetRollupByHash(t *testing.T) { } dbtx.Write() dbtx, _ = db.NewDBTransaction() - err = AddRollup(dbtx, db.GetSQLStatement(), &rollup1, &metadata1, block) + err = AddRollup(dbtx, db.GetSQLStatement(), &rollup1, &metadata1, block.Header()) if err != nil { t.Errorf("could not store rollup. Cause: %s", err) } @@ -312,7 +312,7 @@ func TestGetRollupByHash(t *testing.T) { rollup2LastSeq := int64(batchNumber + 10) metadata2 := createRollupMetadata(rollup2FirstSeq) rollup2 := createRollup(rollup2LastSeq) - err = AddRollup(dbtx, db.GetSQLStatement(), &rollup2, &metadata2, block) + err = AddRollup(dbtx, db.GetSQLStatement(), &rollup2, &metadata2, block.Header()) if err != nil { t.Errorf("could not store rollup 2. Cause: %s", err) } @@ -377,7 +377,7 @@ func TestGetRollupBatches(t *testing.T) { rollup1LastSeq := int64(batchNumber + 1) metadata1 := createRollupMetadata(rollup1FirstSeq) rollup1 := createRollup(rollup1LastSeq) - err = AddRollup(dbtx, db.GetSQLStatement(), &rollup1, &metadata1, block) + err = AddRollup(dbtx, db.GetSQLStatement(), &rollup1, &metadata1, block.Header()) if err != nil { t.Errorf("could not store rollup. Cause: %s", err) } @@ -386,7 +386,7 @@ func TestGetRollupBatches(t *testing.T) { rollup2LastSeq := int64(batchNumber + 3) metadata2 := createRollupMetadata(rollup2FirstSeq) rollup2 := createRollup(rollup2LastSeq) - err = AddRollup(dbtx, db.GetSQLStatement(), &rollup2, &metadata2, block) + err = AddRollup(dbtx, db.GetSQLStatement(), &rollup2, &metadata2, block.Header()) if err != nil { t.Errorf("could not store rollup 2. Cause: %s", err) } From 098a05a43d3bb0516373550d0a4e75a9ef76fa5e Mon Sep 17 00:00:00 2001 From: Tudor Malene Date: Thu, 9 Jan 2025 09:55:33 +0200 Subject: [PATCH 4/8] fix --- integration/ethereummock/mock_l1_network.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration/ethereummock/mock_l1_network.go b/integration/ethereummock/mock_l1_network.go index 93987a2973..6020f284f4 100644 --- a/integration/ethereummock/mock_l1_network.go +++ b/integration/ethereummock/mock_l1_network.go @@ -97,7 +97,7 @@ func printBlock(b *types.Block, m *Node) string { if err != nil { testlog.Logger().Crit("failed to decode rollup") } - txs = append(txs, fmt.Sprintf("r_%s(nonce=%s)", r.Hash(), tx.Nonce())) + txs = append(txs, fmt.Sprintf("r_%s(nonce=%d)", r.Hash(), tx.Nonce())) case *common.L1DepositTx: var to gethcommon.Address From 957caac6797bdf144f0840b298d2613d6169c0b5 Mon Sep 17 00:00:00 2001 From: Tudor Malene Date: Mon, 13 Jan 2025 12:14:23 +0200 Subject: [PATCH 5/8] fix --- go/common/gethencoding/geth_encoding.go | 4 +- go/common/gethutil/gethutil.go | 12 ++-- go/common/log/log.go | 3 +- go/common/subscription/new_heads_manager.go | 5 +- go/enclave/components/batch_executor.go | 4 +- go/enclave/enclave_admin_service.go | 2 +- go/enclave/rpc/GetTransaction.go | 6 +- go/enclave/storage/storage.go | 2 +- go/ethadapter/geth_rpc_client.go | 46 ++++++++------ go/host/container/host_container.go | 8 +-- go/host/l1/dataservice.go | 46 ++++++++------ go/host/l1/publisher.go | 6 +- go/host/l1/statemachine.go | 4 +- integration/common/testlog/testlog.go | 6 +- integration/eth2network/config.yml | 4 +- integration/ethereummock/db.go | 19 ++++-- integration/ethereummock/gethutil.go | 67 --------------------- integration/ethereummock/mock_l1_network.go | 2 +- integration/ethereummock/node.go | 26 ++++---- integration/ethereummock/utils.go | 2 +- integration/simulation/devnetwork/config.go | 2 +- 21 files changed, 125 insertions(+), 151 deletions(-) delete mode 100644 integration/ethereummock/gethutil.go diff --git a/go/common/gethencoding/geth_encoding.go b/go/common/gethencoding/geth_encoding.go index 88e6f3885f..edfb675db6 100644 --- a/go/common/gethencoding/geth_encoding.go +++ b/go/common/gethencoding/geth_encoding.go @@ -11,6 +11,8 @@ import ( "time" "unsafe" + "github.com/ten-protocol/go-ten/go/common/gethutil" + gethlog "github.com/ethereum/go-ethereum/log" "github.com/ten-protocol/go-ten/go/common/log" "github.com/ten-protocol/go-ten/go/enclave/storage" @@ -288,7 +290,7 @@ func (enc *gethEncodingServiceImpl) CreateEthHeaderForBatch(ctx context.Context, gethHeader := types.Header{ ParentHash: convertedParentHash, - UncleHash: gethcommon.Hash{}, + UncleHash: gethutil.EmptyHash, Root: h.Root, TxHash: h.TxHash, ReceiptHash: h.ReceiptHash, diff --git a/go/common/gethutil/gethutil.go b/go/common/gethutil/gethutil.go index 514f313004..1cb8a0596c 100644 --- a/go/common/gethutil/gethutil.go +++ b/go/common/gethutil/gethutil.go @@ -4,8 +4,6 @@ import ( "context" "fmt" - "github.com/ten-protocol/go-ten/go/enclave/storage" - gethcommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ten-protocol/go-ten/go/common" @@ -13,12 +11,16 @@ import ( // Utilities for working with geth structures +type BlockResolver interface { + FetchBlock(ctx context.Context, blockHash common.L1BlockHash) (*types.Header, error) +} + // EmptyHash is useful for comparisons to check if hash has been set var EmptyHash = gethcommon.Hash{} // LCA - returns the latest common ancestor of the 2 blocks or an error if no common ancestor is found // it also returns the blocks that became canonical, and the once that are now the fork -func LCA(ctx context.Context, newCanonical *types.Header, oldCanonical *types.Header, resolver storage.BlockResolver) (*common.ChainFork, error) { +func LCA(ctx context.Context, newCanonical *types.Header, oldCanonical *types.Header, resolver BlockResolver) (*common.ChainFork, error) { b, cp, ncp, err := internalLCA(ctx, newCanonical, oldCanonical, resolver, []common.L1BlockHash{}, []common.L1BlockHash{}) return &common.ChainFork{ NewCanonical: newCanonical, @@ -29,9 +31,9 @@ func LCA(ctx context.Context, newCanonical *types.Header, oldCanonical *types.He }, err } -func internalLCA(ctx context.Context, newCanonical *types.Header, oldCanonical *types.Header, resolver storage.BlockResolver, canonicalPath []common.L1BlockHash, nonCanonicalPath []common.L1BlockHash) (*types.Header, []common.L1BlockHash, []common.L1BlockHash, error) { +func internalLCA(ctx context.Context, newCanonical *types.Header, oldCanonical *types.Header, resolver BlockResolver, canonicalPath []common.L1BlockHash, nonCanonicalPath []common.L1BlockHash) (*types.Header, []common.L1BlockHash, []common.L1BlockHash, error) { if newCanonical.Number.Uint64() == common.L1GenesisHeight || oldCanonical.Number.Uint64() == common.L1GenesisHeight { - return newCanonical, canonicalPath, nonCanonicalPath, nil + return oldCanonical, canonicalPath, nonCanonicalPath, nil } if newCanonical.Hash() == oldCanonical.Hash() { // this is where we reach the common ancestor, which we add to the canonical path diff --git a/go/common/log/log.go b/go/common/log/log.go index 86f10876b4..69649ac5f6 100644 --- a/go/common/log/log.go +++ b/go/common/log/log.go @@ -19,7 +19,7 @@ const ( BatchHeightKey = "batch_height" BatchSeqNoKey = "batch_seq_num" RollupHashKey = "rollup" - CmpKey = "component" + CmpKey = "cmp" NodeIDKey = "node_id" EnclaveIDKey = "enclave_id" NetworkIDKey = "network_id" @@ -34,7 +34,6 @@ const ( HostCmp = "host" HostRPCCmp = "host_rpc" TxInjectCmp = "tx_inject" - TestLogCmp = "test_log" P2PCmp = "p2p" RPCClientCmp = "rpc_client" DeployerCmp = "deployer" diff --git a/go/common/subscription/new_heads_manager.go b/go/common/subscription/new_heads_manager.go index 4ebbb8ce02..5ec51050a8 100644 --- a/go/common/subscription/new_heads_manager.go +++ b/go/common/subscription/new_heads_manager.go @@ -7,7 +7,8 @@ import ( "sync/atomic" "time" - gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ten-protocol/go-ten/go/common/gethutil" + "github.com/ethereum/go-ethereum/core/types" gethlog "github.com/ethereum/go-ethereum/log" @@ -133,7 +134,7 @@ func (nhs *NewHeadsService) HealthStatus(context.Context) host.HealthStatus { func ConvertBatchHeader(head *common.BatchHeader) *types.Header { return &types.Header{ ParentHash: head.ParentHash, - UncleHash: gethcommon.Hash{}, + UncleHash: gethutil.EmptyHash, Coinbase: head.Coinbase, Root: head.Root, TxHash: head.TxHash, diff --git a/go/enclave/components/batch_executor.go b/go/enclave/components/batch_executor.go index 7fc18033c4..e49cb32bf2 100644 --- a/go/enclave/components/batch_executor.go +++ b/go/enclave/components/batch_executor.go @@ -8,6 +8,8 @@ import ( "math/big" "sync" + "github.com/ten-protocol/go-ten/go/common/gethutil" + "github.com/ten-protocol/go-ten/go/common/compression" gethcore "github.com/ethereum/go-ethereum/core" @@ -515,7 +517,7 @@ func (executor *batchExecutor) execResult(ec *BatchExecutionContext) (*ComputedB defer executor.stateDBMutex.Unlock() h, err := ec.stateDB.Commit(batch.Number().Uint64(), deleteEmptyObjects) if err != nil { - return gethcommon.Hash{}, fmt.Errorf("commit failure for batch %d. Cause: %w", ec.currentBatch.SeqNo(), err) + return gethutil.EmptyHash, fmt.Errorf("commit failure for batch %d. Cause: %w", ec.currentBatch.SeqNo(), err) } trieDB := executor.storage.TrieDB() err = trieDB.Commit(h, false) diff --git a/go/enclave/enclave_admin_service.go b/go/enclave/enclave_admin_service.go index b5e5143535..0a5bea6b62 100644 --- a/go/enclave/enclave_admin_service.go +++ b/go/enclave/enclave_admin_service.go @@ -550,7 +550,7 @@ func (e *enclaveAdminService) getNodeType(ctx context.Context) common.NodeType { id := e.enclaveKeyService.EnclaveID() attestedEnclave, err := e.storage.GetEnclavePubKey(ctx, id) if err != nil { - e.logger.Warn("could not read enclave pub key. Defaulting to validator type", log.ErrKey, err) + e.logger.Info("could not read enclave pub key. Defaulting to validator type", log.ErrKey, err) return common.Validator } return attestedEnclave.Type diff --git a/go/enclave/rpc/GetTransaction.go b/go/enclave/rpc/GetTransaction.go index 767c6d525c..ad374725a9 100644 --- a/go/enclave/rpc/GetTransaction.go +++ b/go/enclave/rpc/GetTransaction.go @@ -5,6 +5,8 @@ import ( "fmt" "math/big" + "github.com/ten-protocol/go-ten/go/common/gethutil" + "github.com/ten-protocol/go-ten/go/enclave/storage" "github.com/ten-protocol/go-ten/go/common/log" @@ -121,7 +123,7 @@ func newRPCTransaction(tx *types.Transaction, blockHash gethcommon.Hash, blockNu R: (*hexutil.Big)(r), S: (*hexutil.Big)(s), } - if blockHash != (gethcommon.Hash{}) { + if blockHash != gethutil.EmptyHash { result.BlockHash = &blockHash result.BlockNumber = (*hexutil.Big)(new(big.Int).SetUint64(blockNumber)) result.TransactionIndex = (*hexutil.Uint64)(&index) @@ -138,7 +140,7 @@ func newRPCTransaction(tx *types.Transaction, blockHash gethcommon.Hash, blockNu result.GasFeeCap = (*hexutil.Big)(tx.GasFeeCap()) result.GasTipCap = (*hexutil.Big)(tx.GasTipCap()) // if the transaction has been mined, compute the effective gas price - if baseFee != nil && blockHash != (gethcommon.Hash{}) { + if baseFee != nil && blockHash != gethutil.EmptyHash { // price = min(tip, gasFeeCap - baseFee) + baseFee price := math.BigMin(new(big.Int).Add(tx.GasTipCap(), baseFee), tx.GasFeeCap()) result.GasPrice = (*hexutil.Big)(price) diff --git a/go/enclave/storage/storage.go b/go/enclave/storage/storage.go index 0fa66316cd..392b3cd167 100644 --- a/go/enclave/storage/storage.go +++ b/go/enclave/storage/storage.go @@ -449,7 +449,7 @@ func (s *storageImpl) GetEnclavePubKey(ctx context.Context, enclaveId common.Enc return s.cachingService.ReadEnclavePubKey(ctx, enclaveId, func() (*AttestedEnclave, error) { key, nodeType, err := enclavedb.FetchAttestation(ctx, s.db.GetSQLDB(), enclaveId) if err != nil { - return nil, fmt.Errorf("could not retrieve attestation key for address %s. Cause: %w", enclaveId, err) + return nil, fmt.Errorf("could not retrieve attestation key for enclave %s. Cause: %w", enclaveId, err) } publicKey, err := gethcrypto.DecompressPubkey(key) diff --git a/go/ethadapter/geth_rpc_client.go b/go/ethadapter/geth_rpc_client.go index f4e6b2981e..5b9fa2566f 100644 --- a/go/ethadapter/geth_rpc_client.go +++ b/go/ethadapter/geth_rpc_client.go @@ -8,6 +8,8 @@ import ( "math/big" "time" + "github.com/TwiN/gocache/v2" + "github.com/ethereum/go-ethereum/consensus/misc/eip4844" "github.com/ethereum/go-ethereum/params" "github.com/holiman/uint256" @@ -19,8 +21,6 @@ import ( "github.com/ethereum/go-ethereum/ethclient" gethlog "github.com/ethereum/go-ethereum/log" - lru "github.com/hashicorp/golang-lru/v2" - "github.com/ten-protocol/go-ten/contracts/generated/ManagementContract" "github.com/ten-protocol/go-ten/go/common" "github.com/ten-protocol/go-ten/go/common/log" @@ -45,7 +45,7 @@ type gethRPCClient struct { timeout time.Duration // the timeout for connecting to, or communicating with, the L1 node logger gethlog.Logger rpcURL string - blockCache *lru.Cache[gethcommon.Hash, *types.Header] + blockCache *gocache.Cache } // NewEthClientFromURL instantiates a new ethadapter.EthClient that connects to an ethereum node @@ -57,21 +57,24 @@ func NewEthClientFromURL(rpcURL string, timeout time.Duration, logger gethlog.Lo logger.Trace(fmt.Sprintf("Initialized eth node connection - addr: %s", rpcURL)) - // cache recent blocks to avoid re-fetching them (they are often re-used for checking for forks etc.) - blkCache, err := lru.New[gethcommon.Hash, *types.Header](_defaultBlockCacheSize) - if err != nil { - return nil, fmt.Errorf("unable to initialize block cache - %w", err) - } - return &gethRPCClient{ client: client, timeout: timeout, logger: logger, rpcURL: rpcURL, - blockCache: blkCache, + blockCache: newFifoCache(_defaultBlockCacheSize, 5*time.Minute), }, nil } +func newFifoCache(nrElem int, ttl time.Duration) *gocache.Cache { + cache := gocache.NewCache().WithMaxSize(nrElem).WithEvictionPolicy(gocache.FirstInFirstOut).WithDefaultTTL(ttl) + err := cache.StartJanitor() + if err != nil { + panic("failed to start cache.") + } + return cache +} + // NewEthClient instantiates a new ethadapter.EthClient that connects to an ethereum node func NewEthClient(ipaddress string, port uint, timeout time.Duration, logger gethlog.Logger) (EthClient, error) { return NewEthClientFromURL(fmt.Sprintf("ws://%s:%d", ipaddress, port), timeout, logger) @@ -195,27 +198,33 @@ func (e *gethRPCClient) HeaderByNumber(n *big.Int) (*types.Header, error) { ctx, cancel := context.WithTimeout(context.Background(), e.timeout) defer cancel() - return e.client.HeaderByNumber(ctx, n) + b, err := e.client.BlockByNumber(ctx, n) + if err != nil { + return nil, err + } + return b.Header(), nil } func (e *gethRPCClient) HeaderByHash(hash gethcommon.Hash) (*types.Header, error) { - block, found := e.blockCache.Get(hash) + cachedBlock, found := e.blockCache.Get(hash.Hex()) if found { - cp := *block - return &cp, nil + h, ok := cachedBlock.(types.Header) + if !ok { + return nil, fmt.Errorf("should not happen. could not cast cached block to header") + } + return &h, nil } // not in cache, fetch from RPC ctx, cancel := context.WithTimeout(context.Background(), e.timeout) defer cancel() - block, err := e.client.HeaderByHash(ctx, hash) + block, err := e.client.BlockByHash(ctx, hash) if err != nil { return nil, err } - e.blockCache.Add(hash, block) - cp := *block - return &cp, nil + e.blockCache.Set(hash.Hex(), *block.Header()) + return block.Header(), nil } func (e *gethRPCClient) BlockByHash(hash gethcommon.Hash) (*types.Block, error) { @@ -251,6 +260,7 @@ func (e *gethRPCClient) GetLogs(q ethereum.FilterQuery) ([]types.Log, error) { } func (e *gethRPCClient) Stop() { + e.blockCache.StopJanitor() e.client.Close() } diff --git a/go/host/container/host_container.go b/go/host/container/host_container.go index 41be97eb29..4c7f8a6356 100644 --- a/go/host/container/host_container.go +++ b/go/host/container/host_container.go @@ -50,8 +50,8 @@ func (h *HostContainer) Start() error { if err != nil { return err } - h.logger.Info("Started Obscuro host...") - fmt.Println("Started Obscuro host...") + h.logger.Info("Started TEN host...") + fmt.Println("Started TEN host...") if h.rpcServer != nil { err = h.rpcServer.Start() @@ -59,8 +59,8 @@ func (h *HostContainer) Start() error { return err } - h.logger.Info("Started Obscuro host RPC Server...") - fmt.Println("Started Obscuro host RPC Server...") + h.logger.Info("Started TEN host RPC Server...") + fmt.Println("Started TEN host RPC Server...") } return nil diff --git a/go/host/l1/dataservice.go b/go/host/l1/dataservice.go index 5fc65f7a8d..888d092c12 100644 --- a/go/host/l1/dataservice.go +++ b/go/host/l1/dataservice.go @@ -8,6 +8,8 @@ import ( "sync/atomic" "time" + "github.com/ten-protocol/go-ten/go/common/gethutil" + "github.com/ten-protocol/go-ten/go/enclave/crosschain" "github.com/ten-protocol/go-ten/go/ethadapter/mgmtcontractlib" @@ -100,14 +102,14 @@ func (r *DataService) Subscribe(handler host.L1BlockHandler) func() { // FetchNextBlock calculates the next canonical block that should be sent to requester after a given hash. // It returns the block and a bool for whether it is the latest known head -func (r *DataService) FetchNextBlock(prevBlockHash gethcommon.Hash) (*types.Header, bool, error) { - if prevBlockHash == r.head { - // prevBlock is the latest known head +func (r *DataService) FetchNextBlock(remoteHead gethcommon.Hash) (*types.Header, bool, error) { + if remoteHead == r.head { + // remoteHead is the latest known head return nil, false, ErrNoNextBlock } - if prevBlockHash == (gethcommon.Hash{}) { - // prevBlock is empty, so we are starting from genesis + if remoteHead == gethutil.EmptyHash { + // remoteHead is empty, so we are starting from genesis blk, err := r.ethClient.HeaderByNumber(big.NewInt(0)) if err != nil { return nil, false, fmt.Errorf("could not find genesis block - %w", err) @@ -115,41 +117,45 @@ func (r *DataService) FetchNextBlock(prevBlockHash gethcommon.Hash) (*types.Head return blk, false, nil } - // the latestCanonAncestor will usually return the prevBlock itself but this step is necessary to walk back if there was a fork - lca, err := r.latestCanonAncestor(prevBlockHash) + // the latestCanonAncestor will usually return the remoteHead itself but this step is necessary to walk back if there was a fork + fork, err := r.latestCanonAncestor(remoteHead) if err != nil { return nil, false, err } + // and send the canonical block at the height after that // (which may be a fork, or it may just be the next on the same branch if we are catching-up) - blk, err := r.ethClient.HeaderByNumber(increment(lca.Number)) + blk, err := r.ethClient.HeaderByNumber(increment(fork.CommonAncestor.Number)) if err != nil { if errors.Is(err, ethereum.NotFound) { return nil, false, ErrNoNextBlock } - return nil, false, fmt.Errorf("could not find block after latest canon ancestor, height=%s - %w", increment(lca.Number), err) + return nil, false, fmt.Errorf("could not find block after latest canon ancestor, height=%s - %w", increment(fork.CommonAncestor.Number), err) } return blk, blk.Hash() == r.head, nil } -func (r *DataService) latestCanonAncestor(blkHash gethcommon.Hash) (*types.Header, error) { +func (r *DataService) FetchBlock(_ context.Context, blockHash common.L1BlockHash) (*types.Header, error) { + return r.ethClient.HeaderByHash(blockHash) +} + +func (r *DataService) latestCanonAncestor(blkHash gethcommon.Hash) (*common.ChainFork, error) { + ctx := context.Background() + currentHead, err := r.ethClient.HeaderByNumber(nil) + if err != nil { + return nil, fmt.Errorf("unable to fetch L1 head- %w", err) + } blk, err := r.ethClient.HeaderByHash(blkHash) if err != nil { return nil, fmt.Errorf("unable to fetch L1 block with hash=%s - %w", blkHash, err) } - canonAtSameHeight, err := r.ethClient.HeaderByNumber(blk.Number) + + fork, err := gethutil.LCA(ctx, currentHead, blk, r) if err != nil { - return nil, fmt.Errorf("unable to fetch L1 block at height=%d - %w", blk.Number, err) - } - if blk.Hash() != canonAtSameHeight.Hash() { - empty := gethcommon.Hash{} - if blk.ParentHash == empty { - return blk, nil - } - return r.latestCanonAncestor(blk.ParentHash) + return nil, fmt.Errorf("unable to calculate LCA - %w", err) } - return blk, nil + return fork, nil } // GetTenRelevantTransactions processes logs in their natural order without grouping by transaction hash. diff --git a/go/host/l1/publisher.go b/go/host/l1/publisher.go index c7d38bec19..223d660b18 100644 --- a/go/host/l1/publisher.go +++ b/go/host/l1/publisher.go @@ -8,6 +8,8 @@ import ( "sync" "time" + "github.com/ten-protocol/go-ten/go/common/gethutil" + "github.com/ten-protocol/go-ten/contracts/generated/ManagementContract" "github.com/ten-protocol/go-ten/go/common/errutil" "github.com/ten-protocol/go-ten/go/common/stopcontrol" @@ -176,7 +178,7 @@ func (p *Publisher) InitializeSecret(attestation *common.AttestationReport, encS func (p *Publisher) RequestSecret(attestation *common.AttestationReport) (gethcommon.Hash, error) { encodedAttestation, err := common.EncodeAttestation(attestation) if err != nil { - return gethcommon.Hash{}, errors.Wrap(err, "could not encode attestation") + return gethutil.EmptyHash, errors.Wrap(err, "could not encode attestation") } l1tx := &common.L1RequestSecretTx{ Attestation: encodedAttestation, @@ -197,7 +199,7 @@ func (p *Publisher) RequestSecret(attestation *common.AttestationReport) (gethco // we wait until the secret req transaction has succeeded before we start polling for the secret err = p.publishTransaction(requestSecretTx) if err != nil { - return gethcommon.Hash{}, err + return gethutil.EmptyHash, err } return l1Head.Hash(), nil diff --git a/go/host/l1/statemachine.go b/go/host/l1/statemachine.go index 438336a594..8e4f630dc8 100644 --- a/go/host/l1/statemachine.go +++ b/go/host/l1/statemachine.go @@ -5,6 +5,8 @@ import ( "errors" "math/big" + "github.com/ten-protocol/go-ten/go/common/gethutil" + "github.com/ethereum/go-ethereum/accounts/abi/bind" gethcommon "github.com/ethereum/go-ethereum/common" gethlog "github.com/ethereum/go-ethereum/log" @@ -61,7 +63,7 @@ func NewCrossChainStateMachine( ) CrossChainStateMachine { return &crossChainStateMachine{ latestRollup: RollupInfo{ - ForkUID: gethcommon.Hash{}, + ForkUID: gethutil.EmptyHash, Number: 0, }, rollupHistory: make(map[RollupNumber]RollupInfo), diff --git a/integration/common/testlog/testlog.go b/integration/common/testlog/testlog.go index 9d12e2ab36..248d1b91f5 100644 --- a/integration/common/testlog/testlog.go +++ b/integration/common/testlog/testlog.go @@ -6,8 +6,6 @@ import ( "os" "time" - "github.com/ten-protocol/go-ten/go/common/log" - "github.com/ten-protocol/go-ten/lib/gethfork/debug" gethlog "github.com/ethereum/go-ethereum/log" @@ -53,7 +51,7 @@ func Setup(cfg *Cfg) *os.File { panic(err) } - testlog = gethlog.New(log.CmpKey, log.TestLogCmp) + testlog = gethlog.New() return f } @@ -63,5 +61,5 @@ func SetupSysOut() { if err != nil { panic(err) } - testlog = gethlog.New(log.CmpKey, log.TestLogCmp) + testlog = gethlog.New() } diff --git a/integration/eth2network/config.yml b/integration/eth2network/config.yml index 210e3c1ea4..a20da14531 100644 --- a/integration/eth2network/config.yml +++ b/integration/eth2network/config.yml @@ -28,8 +28,8 @@ DENEB_FORK_VERSION: 0x20000093 ELECTRA_FORK_VERSION: 0x20000094 # Time parameters -SECONDS_PER_SLOT: 12 -SLOTS_PER_EPOCH: 6 +SECONDS_PER_SLOT: 2 +SLOTS_PER_EPOCH: 3 # Deposit contract DEPOSIT_CONTRACT_ADDRESS: 0x4242424242424242424242424242424242424242 \ No newline at end of file diff --git a/integration/ethereummock/db.go b/integration/ethereummock/db.go index fbced97ddb..252795742b 100644 --- a/integration/ethereummock/db.go +++ b/integration/ethereummock/db.go @@ -52,7 +52,7 @@ func (n *blockResolverInMem) StoreBlock(_ context.Context, block *types.Block, _ return nil } -func (n *blockResolverInMem) FetchBlock(_ context.Context, hash common.L1BlockHash) (*types.Block, error) { +func (n *blockResolverInMem) FetchFullBlock(_ context.Context, hash common.L1BlockHash) (*types.Block, error) { n.m.RLock() defer n.m.RUnlock() block, f := n.blockCache[hash] @@ -63,6 +63,17 @@ func (n *blockResolverInMem) FetchBlock(_ context.Context, hash common.L1BlockHa return block, nil } +func (n *blockResolverInMem) FetchBlock(_ context.Context, hash common.L1BlockHash) (*types.Header, error) { + n.m.RLock() + defer n.m.RUnlock() + block, f := n.blockCache[hash] + + if !f { + return nil, errutil.ErrNotFound + } + return block.Header(), nil +} + func (n *blockResolverInMem) FetchHeadBlock(_ context.Context) (*types.Block, error) { n.m.RLock() defer n.m.RUnlock() @@ -80,7 +91,7 @@ func (n *blockResolverInMem) FetchHeadBlock(_ context.Context) (*types.Block, er } func (n *blockResolverInMem) ParentBlock(ctx context.Context, b *types.Header) (*types.Block, error) { - return n.FetchBlock(ctx, b.ParentHash) + return n.FetchFullBlock(ctx, b.ParentHash) } func (n *blockResolverInMem) IsAncestor(ctx context.Context, block *types.Header, maybeAncestor *types.Header) bool { @@ -113,7 +124,7 @@ func (n *blockResolverInMem) IsBlockAncestor(ctx context.Context, block *types.H return false } - resolvedBlock, err := n.FetchBlock(ctx, maybeAncestor) + resolvedBlock, err := n.FetchFullBlock(ctx, maybeAncestor) if err == nil { if resolvedBlock.NumberU64() >= block.Number.Uint64() { return false @@ -177,7 +188,7 @@ func (m *Node) removeCommittedTransactions( break } - p, err := resolver.FetchBlock(ctx, b.ParentHash()) + p, err := resolver.FetchFullBlock(ctx, b.ParentHash()) if err != nil { m.logger.Crit("Could not retrieve parent block.", log.ErrKey, err) } diff --git a/integration/ethereummock/gethutil.go b/integration/ethereummock/gethutil.go deleted file mode 100644 index f2a30b6ee1..0000000000 --- a/integration/ethereummock/gethutil.go +++ /dev/null @@ -1,67 +0,0 @@ -package ethereummock - -import ( - "context" - "fmt" - - gethcommon "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ten-protocol/go-ten/go/common" -) - -// Utilities for working with geth structures - -// EmptyHash is useful for comparisons to check if hash has been set -var EmptyHash = gethcommon.Hash{} - -// LCA - returns the latest common ancestor of the 2 blocks or an error if no common ancestor is found -// it also returns the blocks that became canonical, and the once that are now the fork -func LCA(ctx context.Context, newCanonical *types.Header, oldCanonical *types.Header, resolver *blockResolverInMem) (*common.ChainFork, error) { - b, cp, ncp, err := internalLCA(ctx, newCanonical, oldCanonical, resolver, []common.L1BlockHash{}, []common.L1BlockHash{}) - if err != nil { - return nil, fmt.Errorf("could not calculate LCA. Cause: %w", err) - } - return &common.ChainFork{ - NewCanonical: newCanonical, - OldCanonical: oldCanonical, - CommonAncestor: b, - CanonicalPath: cp, - NonCanonicalPath: ncp, - }, err -} - -func internalLCA(ctx context.Context, newCanonical *types.Header, oldCanonical *types.Header, resolver *blockResolverInMem, canonicalPath []common.L1BlockHash, nonCanonicalPath []common.L1BlockHash) (*types.Header, []common.L1BlockHash, []common.L1BlockHash, error) { - if newCanonical.Number.Uint64() == common.L1GenesisHeight || oldCanonical.Number.Uint64() == common.L1GenesisHeight { - return newCanonical, canonicalPath, nonCanonicalPath, nil - } - if newCanonical.Hash() == oldCanonical.Hash() { - // this is where we reach the common ancestor, which we add to the canonical path - return newCanonical, append(canonicalPath, newCanonical.Hash()), nonCanonicalPath, nil - } - if newCanonical.Number.Uint64() > oldCanonical.Number.Uint64() { - p, err := resolver.FetchBlock(ctx, newCanonical.ParentHash) - if err != nil { - return nil, nil, nil, fmt.Errorf("could not retrieve parent block %s. Cause: %w", newCanonical.ParentHash, err) - } - - return internalLCA(ctx, p.Header(), oldCanonical, resolver, append(canonicalPath, newCanonical.Hash()), nonCanonicalPath) - } - if oldCanonical.Number.Uint64() > newCanonical.Number.Uint64() { - p, err := resolver.FetchBlock(ctx, oldCanonical.ParentHash) - if err != nil { - return nil, nil, nil, fmt.Errorf("could not retrieve parent block %s. Cause: %w", oldCanonical.ParentHash, err) - } - - return internalLCA(ctx, newCanonical, p.Header(), resolver, canonicalPath, append(nonCanonicalPath, oldCanonical.Hash())) - } - parentBlockA, err := resolver.FetchBlock(ctx, newCanonical.ParentHash) - if err != nil { - return nil, nil, nil, fmt.Errorf("could not retrieve parent block %s. Cause: %w", newCanonical.ParentHash, err) - } - parentBlockB, err := resolver.FetchBlock(ctx, oldCanonical.ParentHash) - if err != nil { - return nil, nil, nil, fmt.Errorf("could not retrieve parent block %s. Cause: %w", oldCanonical.ParentHash, err) - } - - return internalLCA(ctx, parentBlockA.Header(), parentBlockB.Header(), resolver, append(canonicalPath, newCanonical.Hash()), append(nonCanonicalPath, oldCanonical.Hash())) -} diff --git a/integration/ethereummock/mock_l1_network.go b/integration/ethereummock/mock_l1_network.go index 6020f284f4..b475d28d6d 100644 --- a/integration/ethereummock/mock_l1_network.go +++ b/integration/ethereummock/mock_l1_network.go @@ -107,7 +107,7 @@ func printBlock(b *types.Block, m *Node) string { txs = append(txs, fmt.Sprintf("deposit(%s=%d)", to, l1Tx.Amount)) } } - p, err := m.BlockResolver.FetchBlock(context.Background(), b.ParentHash()) + p, err := m.BlockResolver.FetchFullBlock(context.Background(), b.ParentHash()) if err != nil { testlog.Logger().Crit("Should not happen. Could not retrieve parent", log.ErrKey, err) } diff --git a/integration/ethereummock/node.go b/integration/ethereummock/node.go index e51319c0dd..1af1994da7 100644 --- a/integration/ethereummock/node.go +++ b/integration/ethereummock/node.go @@ -10,6 +10,8 @@ import ( "sync/atomic" "time" + "github.com/ten-protocol/go-ten/go/common/gethutil" + "github.com/ten-protocol/go-ten/go/common/log" "github.com/ethereum/go-ethereum/crypto/kzg4844" @@ -178,7 +180,7 @@ func (m *Node) TransactionByHash(hash gethcommon.Hash) (*types.Transaction, bool } } - blk, err = m.BlockResolver.FetchBlock(context.Background(), blk.ParentHash()) + blk, err = m.BlockResolver.FetchFullBlock(context.Background(), blk.ParentHash()) if err != nil { return nil, false, fmt.Errorf("could not retrieve parent block. Cause: %w", err) } @@ -268,10 +270,12 @@ func (m *Node) BlockNumber() (uint64, error) { } func (m *Node) HeaderByNumber(n *big.Int) (*types.Header, error) { + if n == nil { + return m.FetchHeadBlock() + } if n.Int64() == 0 { return MockGenesisBlock.Header(), nil } - // TODO this should be a method in the resolver blk, err := m.BlockResolver.FetchHeadBlock(context.Background()) if err != nil { if errors.Is(err, errutil.ErrNotFound) { @@ -284,7 +288,7 @@ func (m *Node) HeaderByNumber(n *big.Int) (*types.Header, error) { return blk.Header(), nil } - blk, err = m.BlockResolver.FetchBlock(context.Background(), blk.ParentHash()) + blk, err = m.BlockResolver.FetchFullBlock(context.Background(), blk.ParentHash()) if err != nil { return nil, fmt.Errorf("could not retrieve parent for block in chain. Cause: %w", err) } @@ -293,7 +297,7 @@ func (m *Node) HeaderByNumber(n *big.Int) (*types.Header, error) { } func (m *Node) HeaderByHash(id gethcommon.Hash) (*types.Header, error) { - blk, err := m.BlockResolver.FetchBlock(context.Background(), id) + blk, err := m.BlockResolver.FetchFullBlock(context.Background(), id) if err != nil { return nil, fmt.Errorf("block could not be retrieved. Cause: %w", err) } @@ -301,7 +305,7 @@ func (m *Node) HeaderByHash(id gethcommon.Hash) (*types.Header, error) { } func (m *Node) BlockByHash(id gethcommon.Hash) (*types.Block, error) { - blk, err := m.BlockResolver.FetchBlock(context.Background(), id) + blk, err := m.BlockResolver.FetchFullBlock(context.Background(), id) if err != nil { return nil, fmt.Errorf("block could not be retrieved. Cause: %w", err) } @@ -401,7 +405,7 @@ func (m *Node) Start() { for { select { case p2pb := <-m.p2pCh: // Received from peers - _, err := m.BlockResolver.FetchBlock(context.Background(), p2pb.Hash()) + _, err := m.BlockResolver.FetchFullBlock(context.Background(), p2pb.Hash()) // only process blocks if they haven't been processed before if err != nil { if errors.Is(err, errutil.ErrNotFound) { @@ -414,7 +418,7 @@ func (m *Node) Start() { case mb := <-m.miningCh: // Received from the local mining head = m.processBlock(mb, head) if bytes.Equal(head.Hash().Bytes(), mb.Hash().Bytes()) { // Only broadcast if it's the new head - p, err := m.BlockResolver.FetchBlock(context.Background(), mb.ParentHash()) + p, err := m.BlockResolver.FetchFullBlock(context.Background(), mb.ParentHash()) if err != nil { panic(fmt.Errorf("could not retrieve parent. Cause: %w", err)) } @@ -440,7 +444,7 @@ func (m *Node) processBlock(b *types.Block, head *types.Header) *types.Header { m.logger.Crit("Failed to store block. Cause: %w", err) } - _, err = m.BlockResolver.FetchBlock(context.Background(), b.Header().ParentHash) + _, err = m.BlockResolver.FetchFullBlock(context.Background(), b.Header().ParentHash) // only proceed if the parent is available if err != nil { if errors.Is(err, errutil.ErrNotFound) { @@ -458,7 +462,7 @@ func (m *Node) processBlock(b *types.Block, head *types.Header) *types.Header { // Check for Reorgs if !m.BlockResolver.IsAncestor(context.Background(), b.Header(), head) { m.stats.L1Reorg(m.l2ID) - fork, err := LCA(context.Background(), head, b.Header(), m.BlockResolver) + fork, err := gethutil.LCA(context.Background(), head, b.Header(), m.BlockResolver) if err != nil { m.logger.Error("Should not happen.", log.ErrKey, err) return head @@ -546,7 +550,7 @@ func (m *Node) startMining() { case canonicalBlockHeader := <-m.canonicalCh: // A new canonical block was found. Start a new round based on that block. - canonicalBlock, err := m.BlockResolver.FetchBlock(context.Background(), canonicalBlockHeader.Hash()) + canonicalBlock, err := m.BlockResolver.FetchFullBlock(context.Background(), canonicalBlockHeader.Hash()) if err != nil { panic(fmt.Errorf("could not fetch block. Cause: %w", err)) } @@ -612,7 +616,7 @@ func (m *Node) BlocksBetween(blockA *types.Header, blockB *types.Header) []*type if bytes.Equal(tempBlock.Hash().Bytes(), blockA.Hash().Bytes()) { break } - tb, err := m.BlockResolver.FetchBlock(context.Background(), tempBlock.ParentHash) + tb, err := m.BlockResolver.FetchFullBlock(context.Background(), tempBlock.ParentHash) if err != nil { panic(fmt.Errorf("could not retrieve parent block. Cause: %w", err)) } diff --git a/integration/ethereummock/utils.go b/integration/ethereummock/utils.go index 5097de506e..dcd0ae769e 100644 --- a/integration/ethereummock/utils.go +++ b/integration/ethereummock/utils.go @@ -27,7 +27,7 @@ func allIncludedTransactions(b *types.Block, r *blockResolverInMem, db TxDB) map return makeMap(b.Transactions()) } newMap := make(map[common.TxHash]*types.Transaction) - p, err := r.FetchBlock(context.Background(), b.ParentHash()) + p, err := r.FetchFullBlock(context.Background(), b.ParentHash()) if err != nil { panic(fmt.Errorf("should not happen. Could not retrieve parent. Cause: %w", err)) } diff --git a/integration/simulation/devnetwork/config.go b/integration/simulation/devnetwork/config.go index 248c4b2ce7..cee6810819 100644 --- a/integration/simulation/devnetwork/config.go +++ b/integration/simulation/devnetwork/config.go @@ -74,7 +74,7 @@ func LocalDevNetwork(tenConfigOpts ...TenConfigOption) *InMemDevNetwork { l1Config := &L1Config{ PortStart: integration.TestPorts.NetworkTestsPort, NumNodes: tenConfig.NumNodes, // we'll have 1 L1 node per L2 node - AvgBlockDuration: 1 * time.Second, + AvgBlockDuration: 2 * time.Second, } l1Network := NewGethNetwork(nodeOpL1Wallets, l1Config) From 65c4165b102aea97aa17857e3030814da4806553 Mon Sep 17 00:00:00 2001 From: Tudor Malene Date: Mon, 13 Jan 2025 12:34:56 +0200 Subject: [PATCH 6/8] fix --- go/ethadapter/geth_rpc_client.go | 5 ++--- go/host/l1/dataservice.go | 6 +++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/go/ethadapter/geth_rpc_client.go b/go/ethadapter/geth_rpc_client.go index 5b9fa2566f..b0d90dc9f3 100644 --- a/go/ethadapter/geth_rpc_client.go +++ b/go/ethadapter/geth_rpc_client.go @@ -164,14 +164,13 @@ func (e *gethRPCClient) Nonce(account gethcommon.Address) (uint64, error) { } func (e *gethRPCClient) BlockListener() (chan *types.Header, ethereum.Subscription) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - // we do not buffer here, we expect the consumer to always be ready to receive new blocks and not fall behind ch := make(chan *types.Header) var sub ethereum.Subscription var err error err = retry.Do(func() error { + ctx, cancel := context.WithTimeout(context.Background(), e.timeout) + defer cancel() sub, err = e.client.SubscribeNewHead(ctx, ch) if err != nil { e.logger.Warn("could not subscribe for new head blocks", log.ErrKey, err) diff --git a/go/host/l1/dataservice.go b/go/host/l1/dataservice.go index 888d092c12..c3bf535430 100644 --- a/go/host/l1/dataservice.go +++ b/go/host/l1/dataservice.go @@ -322,6 +322,7 @@ func (r *DataService) streamLiveBlocks() { } func (r *DataService) resetLiveStream() (chan *types.Header, ethereum.Subscription) { + r.logger.Info("reconnecting to L1 new Heads") err := retry.Do(func() error { if !r.running.Load() { // break out of the loop if repository has stopped @@ -339,7 +340,10 @@ func (r *DataService) resetLiveStream() (chan *types.Header, ethereum.Subscripti r.logger.Warn("unable to reconnect to L1", log.ErrKey, err) return nil, nil } - return r.ethClient.BlockListener() + + ch, s := r.ethClient.BlockListener() + r.logger.Info("successfully reconnected to L1 new Heads") + return ch, s } func (r *DataService) FetchBlockByHeight(height *big.Int) (*types.Header, error) { From 3ce05ab83aecb494f5fd12ecaec6d76e7b1cdb1b Mon Sep 17 00:00:00 2001 From: Tudor Malene Date: Mon, 13 Jan 2025 12:48:27 +0200 Subject: [PATCH 7/8] fix --- integration/ethereummock/node.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/integration/ethereummock/node.go b/integration/ethereummock/node.go index 1af1994da7..1b805d4c17 100644 --- a/integration/ethereummock/node.go +++ b/integration/ethereummock/node.go @@ -728,7 +728,9 @@ func (sub *mockSubscription) Unsubscribe() { } func (sub *mockSubscription) publish(b *types.Header) { - sub.headCh <- b + if sub.headCh != nil { + sub.headCh <- b + } } func (sub *mockSubscription) publishAll(blocks []*types.Header) { From 3fd19328988eb5f982eaea18a2a980e4fd6190d8 Mon Sep 17 00:00:00 2001 From: Tudor Malene Date: Mon, 13 Jan 2025 14:06:59 +0200 Subject: [PATCH 8/8] fix --- integration/contractdeployer/contract_deployer_test.go | 2 +- integration/faucet/faucet_test.go | 2 +- integration/simulation/simulation_full_network_test.go | 2 +- integration/simulation/simulation_geth_in_mem_test.go | 2 +- integration/tengateway/tengateway_test.go | 2 +- integration/tenscan/tenscan_test.go | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/integration/contractdeployer/contract_deployer_test.go b/integration/contractdeployer/contract_deployer_test.go index 30d2e00794..6328c072c1 100644 --- a/integration/contractdeployer/contract_deployer_test.go +++ b/integration/contractdeployer/contract_deployer_test.go @@ -137,7 +137,7 @@ func creatTenNetwork(t *testing.T, startPort int) { wallets := params.NewSimWallets(1, numberOfNodes, integration.EthereumChainID, integration.TenChainID) simParams := params.SimParams{ NumberOfNodes: numberOfNodes, - AvgBlockDuration: 1 * time.Second, + AvgBlockDuration: 2 * time.Second, MgmtContractLib: ethereummock.NewMgmtContractLibMock(), ERC20ContractLib: ethereummock.NewERC20ContractLibMock(), Wallets: wallets, diff --git a/integration/faucet/faucet_test.go b/integration/faucet/faucet_test.go index 5245877d0d..b02789eaba 100644 --- a/integration/faucet/faucet_test.go +++ b/integration/faucet/faucet_test.go @@ -101,7 +101,7 @@ func createObscuroNetwork(t *testing.T, startPort int) { wallets := params.NewSimWallets(1, numberOfNodes, integration.EthereumChainID, integration.TenChainID) simParams := params.SimParams{ NumberOfNodes: numberOfNodes, - AvgBlockDuration: 1 * time.Second, + AvgBlockDuration: 2 * time.Second, MgmtContractLib: ethereummock.NewMgmtContractLibMock(), ERC20ContractLib: ethereummock.NewERC20ContractLibMock(), Wallets: wallets, diff --git a/integration/simulation/simulation_full_network_test.go b/integration/simulation/simulation_full_network_test.go index 2323bf2bbc..d5a58b59d3 100644 --- a/integration/simulation/simulation_full_network_test.go +++ b/integration/simulation/simulation_full_network_test.go @@ -24,7 +24,7 @@ func TestFullNetworkMonteCarloSimulation(t *testing.T) { simParams := ¶ms.SimParams{ NumberOfNodes: numberOfNodes, - AvgBlockDuration: 1 * time.Second, + AvgBlockDuration: 2 * time.Second, SimulationTime: 120 * time.Second, L1EfficiencyThreshold: 0.2, Wallets: wallets, diff --git a/integration/simulation/simulation_geth_in_mem_test.go b/integration/simulation/simulation_geth_in_mem_test.go index c7a8ad8fea..cb891f4041 100644 --- a/integration/simulation/simulation_geth_in_mem_test.go +++ b/integration/simulation/simulation_geth_in_mem_test.go @@ -29,7 +29,7 @@ func TestGethSimulation(t *testing.T) { simParams := ¶ms.SimParams{ NumberOfNodes: numberOfNodes, - AvgBlockDuration: 1 * time.Second, + AvgBlockDuration: 2 * time.Second, SimulationTime: 35 * time.Second, L1EfficiencyThreshold: 0.2, Wallets: wallets, diff --git a/integration/tengateway/tengateway_test.go b/integration/tengateway/tengateway_test.go index 87ea7e4ee2..93324a655b 100644 --- a/integration/tengateway/tengateway_test.go +++ b/integration/tengateway/tengateway_test.go @@ -969,7 +969,7 @@ func createTenNetwork(t *testing.T, startPort int) { wallets := params.NewSimWallets(1, numberOfNodes, integration.EthereumChainID, integration.TenChainID) simParams := params.SimParams{ NumberOfNodes: numberOfNodes, - AvgBlockDuration: 1 * time.Second, + AvgBlockDuration: 2 * time.Second, MgmtContractLib: ethereummock.NewMgmtContractLibMock(), ERC20ContractLib: ethereummock.NewERC20ContractLibMock(), Wallets: wallets, diff --git a/integration/tenscan/tenscan_test.go b/integration/tenscan/tenscan_test.go index 9f3bd040b3..4d595a1480 100644 --- a/integration/tenscan/tenscan_test.go +++ b/integration/tenscan/tenscan_test.go @@ -300,7 +300,7 @@ func createTenNetwork(t *testing.T, startPort int) { wallets := params.NewSimWallets(1, 1, integration.EthereumChainID, integration.TenChainID) simParams := params.SimParams{ NumberOfNodes: 1, - AvgBlockDuration: 1 * time.Second, + AvgBlockDuration: 2 * time.Second, MgmtContractLib: ethereummock.NewMgmtContractLibMock(), ERC20ContractLib: ethereummock.NewERC20ContractLibMock(), Wallets: wallets,