From 45ba37ecc9aad81d65aa47b82eb82b15abdfdeaf Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Thu, 5 Oct 2023 17:50:36 +0300 Subject: [PATCH] compute block hash based on header --- internal/ethapi/api.go | 145 +++++++++++++++++++++++++++--------- internal/ethapi/api_test.go | 27 +------ 2 files changed, 115 insertions(+), 57 deletions(-) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index bcdf357a99..fd02923de0 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1044,6 +1044,34 @@ func (diff *BlockOverrides) Apply(blockCtx *vm.BlockContext) { } } +// ApplyToHeader overrides the given fields into a header. +func (diff *BlockOverrides) ApplyToHeader(header *types.Header) { + if diff == nil { + return + } + if diff.Number != nil { + header.Number = diff.Number.ToInt() + } + if diff.Difficulty != nil { + header.Difficulty = diff.Difficulty.ToInt() + } + if diff.Time != nil { + header.Time = uint64(*diff.Time) + } + if diff.GasLimit != nil { + header.GasLimit = uint64(*diff.GasLimit) + } + if diff.FeeRecipient != nil { + header.Coinbase = *diff.FeeRecipient + } + if diff.PrevRandao != nil { + header.MixDigest = *diff.PrevRandao + } + if diff.BaseFeePerGas != nil { + header.BaseFee = diff.BaseFeePerGas.ToInt() + } +} + // ChainContextBackend provides methods required to implement ChainContext. type ChainContextBackend interface { Engine() consensus.Engine @@ -1213,6 +1241,20 @@ type blockResult struct { Calls []callResult `json:"calls"` } +func mcBlockResultFromHeader(header *types.Header, callResults []callResult) blockResult { + return blockResult{ + Number: hexutil.Uint64(header.Number.Uint64()), + Hash: header.Hash(), + Time: hexutil.Uint64(header.Time), + GasLimit: hexutil.Uint64(header.GasLimit), + GasUsed: hexutil.Uint64(header.GasUsed), + FeeRecipient: header.Coinbase, + BaseFee: (*hexutil.Big)(header.BaseFee), + PrevRandao: header.MixDigest, + Calls: callResults, + } +} + type callResult struct { ReturnValue hexutil.Bytes `json:"returnData"` Logs []*types.Log `json:"logs"` @@ -1267,7 +1309,7 @@ func (s *BlockChainAPI) MulticallV1(ctx context.Context, opts multicallOpts, blo // Make sure the context is cancelled when the call has completed // this makes sure resources are cleaned up. defer cancel() - blockContexts, err := makeBlockContexts(ctx, s.b, blocks, header) + headers, err := makeHeaders(ctx, s.b.ChainConfig(), blocks, header) if err != nil { return nil, err } @@ -1281,27 +1323,25 @@ func (s *BlockChainAPI) MulticallV1(ctx context.Context, opts multicallOpts, blo precompiles = vm.ActivePrecompiledContracts(rules).Copy() ) for bi, block := range blocks { - blockContext = blockContexts[bi] + prevHeader := header + header = headers[bi] + blockContext = core.NewEVMBlockContext(header, NewChainContext(ctx, s.b), nil) + // TODO: GetHashFn hash := crypto.Keccak256Hash(blockContext.BlockNumber.Bytes()) // State overrides are applied prior to execution of a block if err := block.StateOverrides.Apply(state, precompiles); err != nil { return nil, err } - results[bi] = blockResult{ - Number: hexutil.Uint64(blockContext.BlockNumber.Uint64()), - Hash: hash, - Time: hexutil.Uint64(blockContext.Time), - GasLimit: hexutil.Uint64(blockContext.GasLimit), - FeeRecipient: blockContext.Coinbase, - BaseFee: (*hexutil.Big)(blockContext.BaseFee), - Calls: make([]callResult, len(block.Calls)), - } - if blockContext.Random != nil { - results[bi].PrevRandao = *blockContext.Random - } - var gasUsed uint64 + + var ( + gasUsed uint64 + root common.Hash + txes = make([]*types.Transaction, len(block.Calls)) + callResults = make([]callResult, len(block.Calls)) + ) for i, call := range block.Calls { - // setDefaults will consult txpool's nonce tracker. Work around that. + // TODO: Track nonce by counting txes from sender + // Because then we can pre-populate the tx object. if call.Nonce == nil { nonce := state.GetNonce(call.from()) call.Nonce = (*hexutil.Uint64)(&nonce) @@ -1320,6 +1360,8 @@ func (s *BlockChainAPI) MulticallV1(ctx context.Context, opts multicallOpts, blo return nil, err } tx := call.ToTransaction(true) + txes[i] = tx + // TODO: repair log block hashes post execution. vmConfig := &vm.Config{ NoBaseFee: true, Tracer: newTracer(opts.TraceTransfers, blockContext.BlockNumber.Uint64(), hash, tx.Hash(), uint(i)), @@ -1327,7 +1369,7 @@ func (s *BlockChainAPI) MulticallV1(ctx context.Context, opts multicallOpts, blo result, err := applyMessage(ctx, s.b, call, state, header, timeout, gp, &blockContext, vmConfig, precompiles, opts.Validation) if err != nil { callErr := callErrorFromError(err) - results[bi].Calls[i] = callResult{Error: callErr, Status: hexutil.Uint64(types.ReceiptStatusFailed)} + callResults[i] = callResult{Error: callErr, Status: hexutil.Uint64(types.ReceiptStatusFailed)} continue } // If the result contains a revert reason, try to unpack it. @@ -1346,44 +1388,79 @@ func (s *BlockChainAPI) MulticallV1(ctx context.Context, opts multicallOpts, blo } else { callRes.Status = hexutil.Uint64(types.ReceiptStatusSuccessful) } - results[bi].Calls[i] = callRes + callResults[i] = callRes gasUsed += result.UsedGas - state.Finalise(true) + root = state.IntermediateRoot(true) } - results[bi].GasUsed = hexutil.Uint64(gasUsed) + // If last non-phantom block is parent of current block, set parent hash. + if header.Number.Uint64()-prevHeader.Number.Uint64() == 1 { + header.ParentHash = prevHeader.Hash() + } + header.Root = root + header.GasUsed = gasUsed + header.TxHash = types.DeriveSha(types.Transactions(txes), trie.NewStackTrie(nil)) + results[bi] = mcBlockResultFromHeader(header, callResults) } return results, nil } -func makeBlockContexts(ctx context.Context, b Backend, blocks []CallBatch, header *types.Header) ([]vm.BlockContext, error) { - res := make([]vm.BlockContext, len(blocks)) +func makeHeaders(ctx context.Context, config *params.ChainConfig, blocks []CallBatch, base *types.Header) ([]*types.Header, error) { + res := make([]*types.Header, len(blocks)) var ( - prevNumber = header.Number.Uint64() - prevTimestamp = header.Time + prevNumber = base.Number.Uint64() + prevTimestamp = base.Time + header = base ) for bi, block := range blocks { - blockContext := core.NewEVMBlockContext(header, NewChainContext(ctx, b), nil) if block.BlockOverrides == nil { block.BlockOverrides = new(BlockOverrides) } + // Sanitize block number and timestamp if block.BlockOverrides.Number == nil { n := new(big.Int).Add(big.NewInt(int64(prevNumber)), big.NewInt(1)) block.BlockOverrides.Number = (*hexutil.Big)(n) + } else if block.BlockOverrides.Number.ToInt().Uint64() <= prevNumber { + return nil, fmt.Errorf("block numbers must be in order") } + prevNumber = block.BlockOverrides.Number.ToInt().Uint64() + if block.BlockOverrides.Time == nil { t := prevTimestamp + 1 block.BlockOverrides.Time = (*hexutil.Uint64)(&t) - } - block.BlockOverrides.Apply(&blockContext) - if blockContext.BlockNumber.Uint64() <= prevNumber { - return nil, fmt.Errorf("block numbers must be in order") - } - prevNumber = blockContext.BlockNumber.Uint64() - if blockContext.Time <= prevTimestamp { + } else if time := (*uint64)(block.BlockOverrides.Time); *time <= prevTimestamp { return nil, fmt.Errorf("timestamps must be in order") } - prevTimestamp = blockContext.Time - res[bi] = blockContext + prevTimestamp = uint64(*block.BlockOverrides.Time) + + // ParentHash for non-phantom blocks can only be computed + // after the previous block is executed. + var ( + parentHash = common.Hash{} + baseFee *big.Int + ) + // Calculate parentHash for phantom blocks. + if block.BlockOverrides.Number.ToInt().Uint64()-header.Number.Uint64() > 1 { + // keccak(rlp(lastNonPhantomBlockHash, blockNumber)) + hashData, err := rlp.EncodeToBytes([][]byte{header.Hash().Bytes(), block.BlockOverrides.Number.ToInt().Bytes()}) + if err != nil { + return nil, err + } + parentHash = crypto.Keccak256Hash(hashData) + } + if config.IsLondon(block.BlockOverrides.Number.ToInt()) { + baseFee = eip1559.CalcBaseFee(config, header) + } + header = &types.Header{ + ParentHash: parentHash, + UncleHash: types.EmptyUncleHash, + Coinbase: base.Coinbase, + Difficulty: base.Difficulty, + GasLimit: base.GasLimit, + //MixDigest: header.MixDigest, + BaseFee: baseFee, + } + block.BlockOverrides.ApplyToHeader(header) + res[bi] = header } return res, nil } diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index 9b61c84e4e..cd0e912a76 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -956,7 +956,6 @@ func TestMulticallV1(t *testing.T) { }, }, } - n11hash = crypto.Keccak256Hash([]byte{0xb}).Hex() sha256Address = common.BytesToAddress([]byte{0x02}) ) api := NewBlockChainAPI(newTestBackend(t, genBlocks, genesis, ethash.NewFaker(), func(i int, b *core.BlockGen) { @@ -987,9 +986,9 @@ func TestMulticallV1(t *testing.T) { BlockNumber hexutil.Uint64 `json:"blockNumber"` // Skip txHash //TxHash common.Hash `json:"transactionHash" gencodec:"required"` - TxIndex hexutil.Uint `json:"transactionIndex"` - BlockHash common.Hash `json:"blockHash"` - Index hexutil.Uint `json:"logIndex"` + TxIndex hexutil.Uint `json:"transactionIndex"` + //BlockHash common.Hash `json:"blockHash"` + Index hexutil.Uint `json:"logIndex"` } type callErr struct { Message string @@ -1004,7 +1003,7 @@ func TestMulticallV1(t *testing.T) { } type blockRes struct { Number string - Hash string + //Hash string // Ignore timestamp GasLimit string GasUsed string @@ -1045,7 +1044,6 @@ func TestMulticallV1(t *testing.T) { }}, want: []blockRes{{ Number: "0xb", - Hash: n11hash, GasLimit: "0x47e7c4", GasUsed: "0xf618", FeeRecipient: coinbase, @@ -1103,7 +1101,6 @@ func TestMulticallV1(t *testing.T) { }}, want: []blockRes{{ Number: "0xb", - Hash: n11hash, GasLimit: "0x47e7c4", GasUsed: "0xa410", FeeRecipient: coinbase, @@ -1120,7 +1117,6 @@ func TestMulticallV1(t *testing.T) { }}, }, { Number: "0xc", - Hash: crypto.Keccak256Hash([]byte{0xc}).Hex(), GasLimit: "0x47e7c4", GasUsed: "0x5208", FeeRecipient: coinbase, @@ -1171,7 +1167,6 @@ func TestMulticallV1(t *testing.T) { }}, want: []blockRes{{ Number: "0xb", - Hash: n11hash, GasLimit: "0x47e7c4", GasUsed: "0xe891", FeeRecipient: strings.ToLower(cac.String()), @@ -1183,7 +1178,6 @@ func TestMulticallV1(t *testing.T) { }}, }, { Number: "0xc", - Hash: crypto.Keccak256Hash([]byte{0xc}).Hex(), GasLimit: "0x47e7c4", GasUsed: "0xe891", FeeRecipient: coinbase, @@ -1252,7 +1246,6 @@ func TestMulticallV1(t *testing.T) { }}, want: []blockRes{{ Number: "0xb", - Hash: n11hash, GasLimit: "0x47e7c4", GasUsed: "0x10683", FeeRecipient: coinbase, @@ -1294,7 +1287,6 @@ func TestMulticallV1(t *testing.T) { }}, want: []blockRes{{ Number: "0xb", - Hash: n11hash, GasLimit: "0x47e7c4", GasUsed: "0x5508", FeeRecipient: coinbase, @@ -1305,7 +1297,6 @@ func TestMulticallV1(t *testing.T) { Topics: []common.Hash{common.HexToHash("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")}, BlockNumber: hexutil.Uint64(11), Data: hexutil.Bytes{}, - BlockHash: hex2Hash(n11hash), }}, GasUsed: "0x5508", Status: "0x1", @@ -1364,7 +1355,6 @@ func TestMulticallV1(t *testing.T) { }}, want: []blockRes{{ Number: "0xb", - Hash: n11hash, GasLimit: "0x47e7c4", GasUsed: "0x52f6", FeeRecipient: coinbase, @@ -1415,7 +1405,6 @@ func TestMulticallV1(t *testing.T) { }}, want: []blockRes{{ Number: "0xb", - Hash: n11hash, GasLimit: "0x47e7c4", GasUsed: "0xa58c", FeeRecipient: coinbase, @@ -1464,7 +1453,6 @@ func TestMulticallV1(t *testing.T) { includeTransfers: &includeTransfers, want: []blockRes{{ Number: "0xb", - Hash: n11hash, GasLimit: "0x47e7c4", GasUsed: "0xd984", FeeRecipient: coinbase, @@ -1480,7 +1468,6 @@ func TestMulticallV1(t *testing.T) { }, Data: hexutil.Bytes(common.BigToHash(big.NewInt(50)).Bytes()), BlockNumber: hexutil.Uint64(11), - BlockHash: hex2Hash(n11hash), }, { Address: common.Address{}, Topics: []common.Hash{ @@ -1490,7 +1477,6 @@ func TestMulticallV1(t *testing.T) { }, Data: hexutil.Bytes(common.BigToHash(big.NewInt(100)).Bytes()), BlockNumber: hexutil.Uint64(11), - BlockHash: hex2Hash(n11hash), Index: hexutil.Uint(1), }}, Status: "0x1", @@ -1533,7 +1519,6 @@ func TestMulticallV1(t *testing.T) { }}, want: []blockRes{{ Number: "0xb", - Hash: n11hash, GasLimit: "0x47e7c4", GasUsed: "0x1b83f", FeeRecipient: coinbase, @@ -1550,7 +1535,6 @@ func TestMulticallV1(t *testing.T) { }}, }, { Number: "0xc", - Hash: crypto.Keccak256Hash([]byte{0xc}).Hex(), GasLimit: "0x47e7c4", GasUsed: "0xe6d9", FeeRecipient: coinbase, @@ -1576,7 +1560,6 @@ func TestMulticallV1(t *testing.T) { validation: &validation, want: []blockRes{{ Number: "0xb", - Hash: n11hash, GasLimit: "0x47e7c4", GasUsed: "0x0", FeeRecipient: coinbase, @@ -1630,7 +1613,6 @@ func TestMulticallV1(t *testing.T) { }}, want: []blockRes{{ Number: "0xb", - Hash: n11hash, GasLimit: "0x47e7c4", GasUsed: "0xc542", FeeRecipient: coinbase, @@ -1647,7 +1629,6 @@ func TestMulticallV1(t *testing.T) { }}, }, { Number: "0xc", - Hash: crypto.Keccak256Hash([]byte{0xc}).Hex(), GasLimit: "0x47e7c4", GasUsed: "0x62a1", FeeRecipient: coinbase,