compute block hash based on header

This commit is contained in:
Sina Mahmoodi 2023-10-05 17:50:36 +03:00
parent d5c0ff4eae
commit 45ba37ecc9
2 changed files with 115 additions and 57 deletions

View file

@ -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. // ChainContextBackend provides methods required to implement ChainContext.
type ChainContextBackend interface { type ChainContextBackend interface {
Engine() consensus.Engine Engine() consensus.Engine
@ -1213,6 +1241,20 @@ type blockResult struct {
Calls []callResult `json:"calls"` 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 { type callResult struct {
ReturnValue hexutil.Bytes `json:"returnData"` ReturnValue hexutil.Bytes `json:"returnData"`
Logs []*types.Log `json:"logs"` 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 // Make sure the context is cancelled when the call has completed
// this makes sure resources are cleaned up. // this makes sure resources are cleaned up.
defer cancel() defer cancel()
blockContexts, err := makeBlockContexts(ctx, s.b, blocks, header) headers, err := makeHeaders(ctx, s.b.ChainConfig(), blocks, header)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -1281,27 +1323,25 @@ func (s *BlockChainAPI) MulticallV1(ctx context.Context, opts multicallOpts, blo
precompiles = vm.ActivePrecompiledContracts(rules).Copy() precompiles = vm.ActivePrecompiledContracts(rules).Copy()
) )
for bi, block := range blocks { 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()) hash := crypto.Keccak256Hash(blockContext.BlockNumber.Bytes())
// State overrides are applied prior to execution of a block // State overrides are applied prior to execution of a block
if err := block.StateOverrides.Apply(state, precompiles); err != nil { if err := block.StateOverrides.Apply(state, precompiles); err != nil {
return nil, err return nil, err
} }
results[bi] = blockResult{
Number: hexutil.Uint64(blockContext.BlockNumber.Uint64()), var (
Hash: hash, gasUsed uint64
Time: hexutil.Uint64(blockContext.Time), root common.Hash
GasLimit: hexutil.Uint64(blockContext.GasLimit), txes = make([]*types.Transaction, len(block.Calls))
FeeRecipient: blockContext.Coinbase, callResults = make([]callResult, len(block.Calls))
BaseFee: (*hexutil.Big)(blockContext.BaseFee), )
Calls: make([]callResult, len(block.Calls)),
}
if blockContext.Random != nil {
results[bi].PrevRandao = *blockContext.Random
}
var gasUsed uint64
for i, call := range 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 { if call.Nonce == nil {
nonce := state.GetNonce(call.from()) nonce := state.GetNonce(call.from())
call.Nonce = (*hexutil.Uint64)(&nonce) call.Nonce = (*hexutil.Uint64)(&nonce)
@ -1320,6 +1360,8 @@ func (s *BlockChainAPI) MulticallV1(ctx context.Context, opts multicallOpts, blo
return nil, err return nil, err
} }
tx := call.ToTransaction(true) tx := call.ToTransaction(true)
txes[i] = tx
// TODO: repair log block hashes post execution.
vmConfig := &vm.Config{ vmConfig := &vm.Config{
NoBaseFee: true, NoBaseFee: true,
Tracer: newTracer(opts.TraceTransfers, blockContext.BlockNumber.Uint64(), hash, tx.Hash(), uint(i)), 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) result, err := applyMessage(ctx, s.b, call, state, header, timeout, gp, &blockContext, vmConfig, precompiles, opts.Validation)
if err != nil { if err != nil {
callErr := callErrorFromError(err) 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 continue
} }
// If the result contains a revert reason, try to unpack it. // 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 { } else {
callRes.Status = hexutil.Uint64(types.ReceiptStatusSuccessful) callRes.Status = hexutil.Uint64(types.ReceiptStatusSuccessful)
} }
results[bi].Calls[i] = callRes callResults[i] = callRes
gasUsed += result.UsedGas 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 return results, nil
} }
func makeBlockContexts(ctx context.Context, b Backend, blocks []CallBatch, header *types.Header) ([]vm.BlockContext, error) { func makeHeaders(ctx context.Context, config *params.ChainConfig, blocks []CallBatch, base *types.Header) ([]*types.Header, error) {
res := make([]vm.BlockContext, len(blocks)) res := make([]*types.Header, len(blocks))
var ( var (
prevNumber = header.Number.Uint64() prevNumber = base.Number.Uint64()
prevTimestamp = header.Time prevTimestamp = base.Time
header = base
) )
for bi, block := range blocks { for bi, block := range blocks {
blockContext := core.NewEVMBlockContext(header, NewChainContext(ctx, b), nil)
if block.BlockOverrides == nil { if block.BlockOverrides == nil {
block.BlockOverrides = new(BlockOverrides) block.BlockOverrides = new(BlockOverrides)
} }
// Sanitize block number and timestamp
if block.BlockOverrides.Number == nil { if block.BlockOverrides.Number == nil {
n := new(big.Int).Add(big.NewInt(int64(prevNumber)), big.NewInt(1)) n := new(big.Int).Add(big.NewInt(int64(prevNumber)), big.NewInt(1))
block.BlockOverrides.Number = (*hexutil.Big)(n) 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 { if block.BlockOverrides.Time == nil {
t := prevTimestamp + 1 t := prevTimestamp + 1
block.BlockOverrides.Time = (*hexutil.Uint64)(&t) block.BlockOverrides.Time = (*hexutil.Uint64)(&t)
} } else if time := (*uint64)(block.BlockOverrides.Time); *time <= prevTimestamp {
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 {
return nil, fmt.Errorf("timestamps must be in order") return nil, fmt.Errorf("timestamps must be in order")
} }
prevTimestamp = blockContext.Time prevTimestamp = uint64(*block.BlockOverrides.Time)
res[bi] = blockContext
// 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 return res, nil
} }

View file

@ -956,7 +956,6 @@ func TestMulticallV1(t *testing.T) {
}, },
}, },
} }
n11hash = crypto.Keccak256Hash([]byte{0xb}).Hex()
sha256Address = common.BytesToAddress([]byte{0x02}) sha256Address = common.BytesToAddress([]byte{0x02})
) )
api := NewBlockChainAPI(newTestBackend(t, genBlocks, genesis, ethash.NewFaker(), func(i int, b *core.BlockGen) { 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"` BlockNumber hexutil.Uint64 `json:"blockNumber"`
// Skip txHash // Skip txHash
//TxHash common.Hash `json:"transactionHash" gencodec:"required"` //TxHash common.Hash `json:"transactionHash" gencodec:"required"`
TxIndex hexutil.Uint `json:"transactionIndex"` TxIndex hexutil.Uint `json:"transactionIndex"`
BlockHash common.Hash `json:"blockHash"` //BlockHash common.Hash `json:"blockHash"`
Index hexutil.Uint `json:"logIndex"` Index hexutil.Uint `json:"logIndex"`
} }
type callErr struct { type callErr struct {
Message string Message string
@ -1004,7 +1003,7 @@ func TestMulticallV1(t *testing.T) {
} }
type blockRes struct { type blockRes struct {
Number string Number string
Hash string //Hash string
// Ignore timestamp // Ignore timestamp
GasLimit string GasLimit string
GasUsed string GasUsed string
@ -1045,7 +1044,6 @@ func TestMulticallV1(t *testing.T) {
}}, }},
want: []blockRes{{ want: []blockRes{{
Number: "0xb", Number: "0xb",
Hash: n11hash,
GasLimit: "0x47e7c4", GasLimit: "0x47e7c4",
GasUsed: "0xf618", GasUsed: "0xf618",
FeeRecipient: coinbase, FeeRecipient: coinbase,
@ -1103,7 +1101,6 @@ func TestMulticallV1(t *testing.T) {
}}, }},
want: []blockRes{{ want: []blockRes{{
Number: "0xb", Number: "0xb",
Hash: n11hash,
GasLimit: "0x47e7c4", GasLimit: "0x47e7c4",
GasUsed: "0xa410", GasUsed: "0xa410",
FeeRecipient: coinbase, FeeRecipient: coinbase,
@ -1120,7 +1117,6 @@ func TestMulticallV1(t *testing.T) {
}}, }},
}, { }, {
Number: "0xc", Number: "0xc",
Hash: crypto.Keccak256Hash([]byte{0xc}).Hex(),
GasLimit: "0x47e7c4", GasLimit: "0x47e7c4",
GasUsed: "0x5208", GasUsed: "0x5208",
FeeRecipient: coinbase, FeeRecipient: coinbase,
@ -1171,7 +1167,6 @@ func TestMulticallV1(t *testing.T) {
}}, }},
want: []blockRes{{ want: []blockRes{{
Number: "0xb", Number: "0xb",
Hash: n11hash,
GasLimit: "0x47e7c4", GasLimit: "0x47e7c4",
GasUsed: "0xe891", GasUsed: "0xe891",
FeeRecipient: strings.ToLower(cac.String()), FeeRecipient: strings.ToLower(cac.String()),
@ -1183,7 +1178,6 @@ func TestMulticallV1(t *testing.T) {
}}, }},
}, { }, {
Number: "0xc", Number: "0xc",
Hash: crypto.Keccak256Hash([]byte{0xc}).Hex(),
GasLimit: "0x47e7c4", GasLimit: "0x47e7c4",
GasUsed: "0xe891", GasUsed: "0xe891",
FeeRecipient: coinbase, FeeRecipient: coinbase,
@ -1252,7 +1246,6 @@ func TestMulticallV1(t *testing.T) {
}}, }},
want: []blockRes{{ want: []blockRes{{
Number: "0xb", Number: "0xb",
Hash: n11hash,
GasLimit: "0x47e7c4", GasLimit: "0x47e7c4",
GasUsed: "0x10683", GasUsed: "0x10683",
FeeRecipient: coinbase, FeeRecipient: coinbase,
@ -1294,7 +1287,6 @@ func TestMulticallV1(t *testing.T) {
}}, }},
want: []blockRes{{ want: []blockRes{{
Number: "0xb", Number: "0xb",
Hash: n11hash,
GasLimit: "0x47e7c4", GasLimit: "0x47e7c4",
GasUsed: "0x5508", GasUsed: "0x5508",
FeeRecipient: coinbase, FeeRecipient: coinbase,
@ -1305,7 +1297,6 @@ func TestMulticallV1(t *testing.T) {
Topics: []common.Hash{common.HexToHash("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")}, Topics: []common.Hash{common.HexToHash("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")},
BlockNumber: hexutil.Uint64(11), BlockNumber: hexutil.Uint64(11),
Data: hexutil.Bytes{}, Data: hexutil.Bytes{},
BlockHash: hex2Hash(n11hash),
}}, }},
GasUsed: "0x5508", GasUsed: "0x5508",
Status: "0x1", Status: "0x1",
@ -1364,7 +1355,6 @@ func TestMulticallV1(t *testing.T) {
}}, }},
want: []blockRes{{ want: []blockRes{{
Number: "0xb", Number: "0xb",
Hash: n11hash,
GasLimit: "0x47e7c4", GasLimit: "0x47e7c4",
GasUsed: "0x52f6", GasUsed: "0x52f6",
FeeRecipient: coinbase, FeeRecipient: coinbase,
@ -1415,7 +1405,6 @@ func TestMulticallV1(t *testing.T) {
}}, }},
want: []blockRes{{ want: []blockRes{{
Number: "0xb", Number: "0xb",
Hash: n11hash,
GasLimit: "0x47e7c4", GasLimit: "0x47e7c4",
GasUsed: "0xa58c", GasUsed: "0xa58c",
FeeRecipient: coinbase, FeeRecipient: coinbase,
@ -1464,7 +1453,6 @@ func TestMulticallV1(t *testing.T) {
includeTransfers: &includeTransfers, includeTransfers: &includeTransfers,
want: []blockRes{{ want: []blockRes{{
Number: "0xb", Number: "0xb",
Hash: n11hash,
GasLimit: "0x47e7c4", GasLimit: "0x47e7c4",
GasUsed: "0xd984", GasUsed: "0xd984",
FeeRecipient: coinbase, FeeRecipient: coinbase,
@ -1480,7 +1468,6 @@ func TestMulticallV1(t *testing.T) {
}, },
Data: hexutil.Bytes(common.BigToHash(big.NewInt(50)).Bytes()), Data: hexutil.Bytes(common.BigToHash(big.NewInt(50)).Bytes()),
BlockNumber: hexutil.Uint64(11), BlockNumber: hexutil.Uint64(11),
BlockHash: hex2Hash(n11hash),
}, { }, {
Address: common.Address{}, Address: common.Address{},
Topics: []common.Hash{ Topics: []common.Hash{
@ -1490,7 +1477,6 @@ func TestMulticallV1(t *testing.T) {
}, },
Data: hexutil.Bytes(common.BigToHash(big.NewInt(100)).Bytes()), Data: hexutil.Bytes(common.BigToHash(big.NewInt(100)).Bytes()),
BlockNumber: hexutil.Uint64(11), BlockNumber: hexutil.Uint64(11),
BlockHash: hex2Hash(n11hash),
Index: hexutil.Uint(1), Index: hexutil.Uint(1),
}}, }},
Status: "0x1", Status: "0x1",
@ -1533,7 +1519,6 @@ func TestMulticallV1(t *testing.T) {
}}, }},
want: []blockRes{{ want: []blockRes{{
Number: "0xb", Number: "0xb",
Hash: n11hash,
GasLimit: "0x47e7c4", GasLimit: "0x47e7c4",
GasUsed: "0x1b83f", GasUsed: "0x1b83f",
FeeRecipient: coinbase, FeeRecipient: coinbase,
@ -1550,7 +1535,6 @@ func TestMulticallV1(t *testing.T) {
}}, }},
}, { }, {
Number: "0xc", Number: "0xc",
Hash: crypto.Keccak256Hash([]byte{0xc}).Hex(),
GasLimit: "0x47e7c4", GasLimit: "0x47e7c4",
GasUsed: "0xe6d9", GasUsed: "0xe6d9",
FeeRecipient: coinbase, FeeRecipient: coinbase,
@ -1576,7 +1560,6 @@ func TestMulticallV1(t *testing.T) {
validation: &validation, validation: &validation,
want: []blockRes{{ want: []blockRes{{
Number: "0xb", Number: "0xb",
Hash: n11hash,
GasLimit: "0x47e7c4", GasLimit: "0x47e7c4",
GasUsed: "0x0", GasUsed: "0x0",
FeeRecipient: coinbase, FeeRecipient: coinbase,
@ -1630,7 +1613,6 @@ func TestMulticallV1(t *testing.T) {
}}, }},
want: []blockRes{{ want: []blockRes{{
Number: "0xb", Number: "0xb",
Hash: n11hash,
GasLimit: "0x47e7c4", GasLimit: "0x47e7c4",
GasUsed: "0xc542", GasUsed: "0xc542",
FeeRecipient: coinbase, FeeRecipient: coinbase,
@ -1647,7 +1629,6 @@ func TestMulticallV1(t *testing.T) {
}}, }},
}, { }, {
Number: "0xc", Number: "0xc",
Hash: crypto.Keccak256Hash([]byte{0xc}).Hex(),
GasLimit: "0x47e7c4", GasLimit: "0x47e7c4",
GasUsed: "0x62a1", GasUsed: "0x62a1",
FeeRecipient: coinbase, FeeRecipient: coinbase,