From ef5e75d4d97ba804dc72d0145003303d2362b5c6 Mon Sep 17 00:00:00 2001 From: Matt Curtis Date: Mon, 11 Sep 2023 22:35:35 +0100 Subject: [PATCH] Filter for tx with any obscuro interactions --- go/common/host/services.go | 2 +- go/ethadapter/geth_rpc_client.go | 7 +++++++ go/ethadapter/interface.go | 1 + go/host/enclave/guardian.go | 5 ++++- go/host/l1/blockrepository.go | 20 ++++++++++++++++---- integration/ethereummock/node.go | 21 +++++++++++++++++++++ 6 files changed, 50 insertions(+), 6 deletions(-) diff --git a/go/common/host/services.go b/go/common/host/services.go index fb61454599..af2e8e63a1 100644 --- a/go/common/host/services.go +++ b/go/common/host/services.go @@ -86,7 +86,7 @@ type L1BlockRepository interface { // 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) // FetchObscuroReceipts returns the receipts for a given L1 block - FetchObscuroReceipts(block *common.L1Block) types.Receipts + FetchObscuroReceipts(block *common.L1Block) (types.Receipts, error) } // L1BlockHandler is an interface for receiving new blocks from the repository as they arrive diff --git a/go/ethadapter/geth_rpc_client.go b/go/ethadapter/geth_rpc_client.go index 67349af88e..156d143d9c 100644 --- a/go/ethadapter/geth_rpc_client.go +++ b/go/ethadapter/geth_rpc_client.go @@ -215,6 +215,13 @@ func (e *gethRPCClient) BalanceAt(address gethcommon.Address, blockNum *big.Int) return e.client.BalanceAt(ctx, address, blockNum) } +func (e *gethRPCClient) GetLogs(q ethereum.FilterQuery) ([]types.Log, error) { + ctx, cancel := context.WithTimeout(context.Background(), e.timeout) + defer cancel() + + return e.client.FilterLogs(ctx, q) +} + func (e *gethRPCClient) Stop() { e.client.Close() } diff --git a/go/ethadapter/interface.go b/go/ethadapter/interface.go index 3e8efc6863..8e8a5690bb 100644 --- a/go/ethadapter/interface.go +++ b/go/ethadapter/interface.go @@ -26,6 +26,7 @@ type EthClient interface { TransactionReceipt(hash gethcommon.Hash) (*types.Receipt, error) // fetches the ethereum transaction receipt Nonce(address gethcommon.Address) (uint64, error) // fetches the account nonce to use in the next transaction 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 diff --git a/go/host/enclave/guardian.go b/go/host/enclave/guardian.go index a2bfa86b5f..1223ee9ece 100644 --- a/go/host/enclave/guardian.go +++ b/go/host/enclave/guardian.go @@ -381,12 +381,15 @@ func (g *Guardian) catchupWithL2() error { // 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()) - receipts := g.sl.L1Repo().FetchObscuroReceipts(block) if !g.submitDataLock.TryLock() { g.logger.Info("Unable to submit block, already submitting another block") // we are already submitting a block, and we don't want to leak goroutines, we wil catch up with the block later return false, nil } + receipts, err := g.sl.L1Repo().FetchObscuroReceipts(block) + if err != nil { + return false, fmt.Errorf("could not fetch obscuro receipts for block=%s - %w", block.Hash(), err) + } resp, err := g.enclaveClient.SubmitL1Block(*block, receipts, isLatest) g.submitDataLock.Unlock() if err != nil { diff --git a/go/host/l1/blockrepository.go b/go/host/l1/blockrepository.go index cac26d7db6..ae6ebac168 100644 --- a/go/host/l1/blockrepository.go +++ b/go/host/l1/blockrepository.go @@ -128,11 +128,23 @@ func (r *Repository) latestCanonAncestor(blkHash gethcommon.Hash) (*types.Block, } // FetchObscuroReceipts returns all obscuro-relevant receipts for an L1 block -func (r *Repository) FetchObscuroReceipts(block *common.L1Block) types.Receipts { +func (r *Repository) FetchObscuroReceipts(block *common.L1Block) (types.Receipts, error) { receipts := make([]*types.Receipt, len(block.Transactions())) + blkHash := block.Hash() + // we want to send receipts for any transactions that produced obscuro-relevant log events + logs, err := r.ethClient.GetLogs(ethereum.FilterQuery{BlockHash: &blkHash, Addresses: r.obscuroRelevantContracts}) + if err != nil { + return nil, fmt.Errorf("unable to fetch logs for L1 block - %w", err) + } + // make a lookup map of the relevant tx hashes which need receipts + relevantTx := make(map[gethcommon.Hash]bool) + for _, l := range logs { + relevantTx[l.TxHash] = true + } + for idx, transaction := range block.Transactions() { - if !r.isObscuroTransaction(transaction) { + if !relevantTx[transaction.Hash()] && !r.isObscuroTransaction(transaction) { // put in a dummy receipt so that the index matches the transaction index // (the receipts list maintains the indexes of the transactions, it is a sparse list) receipts[idx] = &types.Receipt{Status: types.ReceiptStatusFailed} @@ -146,12 +158,12 @@ func (r *Repository) FetchObscuroReceipts(block *common.L1Block) types.Receipts } r.logger.Trace("Adding receipt", "status", receipt.Status, log.TxKey, transaction.Hash(), - log.BlockHashKey, block.Hash(), log.CmpKey, log.CrossChainCmp) + log.BlockHashKey, blkHash, log.CmpKey, log.CrossChainCmp) receipts[idx] = receipt } - return receipts + return receipts, nil } // stream blocks from L1 as they arrive and forward them to subscribers, no guarantee of perfect ordering or that there won't be gaps. diff --git a/integration/ethereummock/node.go b/integration/ethereummock/node.go index 7926ada297..feb4626d06 100644 --- a/integration/ethereummock/node.go +++ b/integration/ethereummock/node.go @@ -219,6 +219,27 @@ func (m *Node) BalanceAt(gethcommon.Address, *big.Int) (*big.Int, error) { panic("not implemented") } +// GetLogs is a mock method - we don't really have logs on the mock transactions, so it returns a basic log for every tx +// so the host recognises them as relevant +func (m *Node) GetLogs(fq ethereum.FilterQuery) ([]types.Log, error) { + logs := make([]types.Log, 0) + if fq.BlockHash == nil { + return logs, nil + } + blk, err := m.BlockByHash(*fq.BlockHash) + if err != nil { + return nil, fmt.Errorf("could not retrieve block. Cause: %w", err) + } + for _, tx := range blk.Transactions() { + dummyLog := types.Log{ + BlockHash: blk.Hash(), + TxHash: tx.Hash(), + } + logs = append(logs, dummyLog) + } + return logs, nil +} + // Start runs an infinite loop that listens to the two block producing channels and processes them. func (m *Node) Start() { if m.mining {