mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 18:32:23 +00:00
fix block hash cache
This commit is contained in:
parent
cc1158ae6d
commit
2690445207
2 changed files with 118 additions and 23 deletions
|
|
@ -1322,8 +1322,8 @@ func (s *BlockChainAPI) MulticallV1(ctx context.Context, opts multicallOpts, blo
|
||||||
blockContext = core.NewEVMBlockContext(header, NewChainContext(ctx, s.b), nil)
|
blockContext = core.NewEVMBlockContext(header, NewChainContext(ctx, s.b), nil)
|
||||||
rules = s.b.ChainConfig().Rules(blockContext.BlockNumber, blockContext.Random != nil, blockContext.Time)
|
rules = s.b.ChainConfig().Rules(blockContext.BlockNumber, blockContext.Random != nil, blockContext.Time)
|
||||||
precompiles = vm.ActivePrecompiledContracts(rules).Copy()
|
precompiles = vm.ActivePrecompiledContracts(rules).Copy()
|
||||||
numHashes = headers[len(headers)-1].Number.Uint64() - base.Number.Uint64()
|
numHashes = headers[len(headers)-1].Number.Uint64() - base.Number.Uint64() + 256
|
||||||
// Cache for simulated and phantom block hashes.
|
// Cache for the block hashes.
|
||||||
hashes = make([]common.Hash, numHashes)
|
hashes = make([]common.Hash, numHashes)
|
||||||
)
|
)
|
||||||
for bi, block := range blocks {
|
for bi, block := range blocks {
|
||||||
|
|
@ -1426,21 +1426,35 @@ func (s *BlockChainAPI) MulticallV1(ctx context.Context, opts multicallOpts, blo
|
||||||
// Note getHash assumes `n` is smaller than the last already simulated block
|
// Note getHash assumes `n` is smaller than the last already simulated block
|
||||||
// and smaller than the last block to be simulated.
|
// and smaller than the last block to be simulated.
|
||||||
func getHash(ctx context.Context, n uint64, base *types.Header, headers []*types.Header, backend Backend, hashes []common.Hash) (common.Hash, []common.Hash, error) {
|
func getHash(ctx context.Context, n uint64, base *types.Header, headers []*types.Header, backend Backend, hashes []common.Hash) (common.Hash, []common.Hash, error) {
|
||||||
|
// getIndex returns the index of the hash in the hashes cache.
|
||||||
|
// The cache potentially includes 255 blocks prior to the base.
|
||||||
getIndex := func(n uint64) int {
|
getIndex := func(n uint64) int {
|
||||||
return int(n - base.Number.Uint64())
|
first := base.Number.Uint64() - 255
|
||||||
|
return int(n - first)
|
||||||
}
|
}
|
||||||
index := getIndex(n)
|
index := getIndex(n)
|
||||||
if h := hashes[index]; h != (common.Hash{}) {
|
if h := hashes[index]; h != (common.Hash{}) {
|
||||||
return h, hashes, nil
|
return h, hashes, nil
|
||||||
}
|
}
|
||||||
|
h, err := computeHash(ctx, n, base, headers, backend, hashes)
|
||||||
|
if err != nil {
|
||||||
|
return common.Hash{}, hashes, err
|
||||||
|
}
|
||||||
|
if h != (common.Hash{}) {
|
||||||
|
hashes[index] = h
|
||||||
|
}
|
||||||
|
return h, hashes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func computeHash(ctx context.Context, n uint64, base *types.Header, headers []*types.Header, backend Backend, hashes []common.Hash) (common.Hash, error) {
|
||||||
if n == base.Number.Uint64() {
|
if n == base.Number.Uint64() {
|
||||||
return base.Hash(), hashes, nil
|
return base.Hash(), nil
|
||||||
} else if n < base.Number.Uint64() {
|
} else if n < base.Number.Uint64() {
|
||||||
h, err := backend.HeaderByNumber(ctx, rpc.BlockNumber(n))
|
h, err := backend.HeaderByNumber(ctx, rpc.BlockNumber(n))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Hash{}, hashes, fmt.Errorf("failed to load block hash for number %d. Err: %v\n", n, err)
|
return common.Hash{}, fmt.Errorf("failed to load block hash for number %d. Err: %v\n", n, err)
|
||||||
}
|
}
|
||||||
return h.Hash(), hashes, nil
|
return h.Hash(), nil
|
||||||
}
|
}
|
||||||
h := base
|
h := base
|
||||||
for i, _ := range headers {
|
for i, _ := range headers {
|
||||||
|
|
@ -1449,29 +1463,23 @@ func getHash(ctx context.Context, n uint64, base *types.Header, headers []*types
|
||||||
// so no need to check that condition.
|
// so no need to check that condition.
|
||||||
if tmp.Number.Uint64() == n {
|
if tmp.Number.Uint64() == n {
|
||||||
hash := tmp.Hash()
|
hash := tmp.Hash()
|
||||||
hashes[index] = hash
|
return hash, nil
|
||||||
return hash, hashes, nil
|
|
||||||
} else if tmp.Number.Uint64() > n {
|
} else if tmp.Number.Uint64() > n {
|
||||||
// Phantom block.
|
// Phantom block.
|
||||||
var lastNonPhantomHash common.Hash
|
lastNonPhantomHash, _, err := getHash(ctx, h.Number.Uint64(), base, headers, backend, hashes)
|
||||||
if hash := hashes[getIndex(h.Number.Uint64())]; hash != (common.Hash{}) {
|
if err != nil {
|
||||||
lastNonPhantomHash = hash
|
return common.Hash{}, err
|
||||||
} else {
|
|
||||||
lastNonPhantomHash = h.Hash()
|
|
||||||
hashes[getIndex(h.Number.Uint64())] = lastNonPhantomHash
|
|
||||||
}
|
}
|
||||||
// keccak(rlp(lastNonPhantomBlockHash, blockNumber))
|
// keccak(rlp(lastNonPhantomBlockHash, blockNumber))
|
||||||
hashData, err := rlp.EncodeToBytes([][]byte{lastNonPhantomHash.Bytes(), big.NewInt(int64(n)).Bytes()})
|
hashData, err := rlp.EncodeToBytes([][]byte{lastNonPhantomHash.Bytes(), big.NewInt(int64(n)).Bytes()})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Hash{}, hashes, err
|
return common.Hash{}, err
|
||||||
}
|
}
|
||||||
hash := crypto.Keccak256Hash(hashData)
|
return crypto.Keccak256Hash(hashData), nil
|
||||||
hashes[index] = hash
|
|
||||||
return hash, hashes, nil
|
|
||||||
}
|
}
|
||||||
h = tmp
|
h = tmp
|
||||||
}
|
}
|
||||||
return common.Hash{}, hashes, errors.New("requested block is in future")
|
return common.Hash{}, errors.New("requested block is in future")
|
||||||
}
|
}
|
||||||
|
|
||||||
// repairLogs updates the block hash in the logs present in a multicall
|
// repairLogs updates the block hash in the logs present in a multicall
|
||||||
|
|
|
||||||
|
|
@ -1640,6 +1640,91 @@ func TestMulticallV1(t *testing.T) {
|
||||||
}},
|
}},
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "blockhash-opcode",
|
||||||
|
tag: latest,
|
||||||
|
blocks: []CallBatch{{
|
||||||
|
BlockOverrides: &BlockOverrides{
|
||||||
|
Number: (*hexutil.Big)(big.NewInt(12)),
|
||||||
|
},
|
||||||
|
StateOverrides: &StateOverride{
|
||||||
|
randomAccounts[2].addr: {
|
||||||
|
Code: hex2Bytes("600035804060008103601057600080fd5b5050"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Calls: []TransactionArgs{{
|
||||||
|
From: &accounts[0].addr,
|
||||||
|
To: &randomAccounts[2].addr,
|
||||||
|
// Phantom block after base.
|
||||||
|
Input: uint256ToBytes(uint256.NewInt(11)),
|
||||||
|
}, {
|
||||||
|
From: &accounts[0].addr,
|
||||||
|
To: &randomAccounts[2].addr,
|
||||||
|
// Canonical block.
|
||||||
|
Input: uint256ToBytes(uint256.NewInt(8)),
|
||||||
|
}, {
|
||||||
|
From: &accounts[0].addr,
|
||||||
|
To: &randomAccounts[2].addr,
|
||||||
|
// base block.
|
||||||
|
Input: uint256ToBytes(uint256.NewInt(10)),
|
||||||
|
}},
|
||||||
|
}, {
|
||||||
|
BlockOverrides: &BlockOverrides{
|
||||||
|
Number: (*hexutil.Big)(big.NewInt(16)),
|
||||||
|
},
|
||||||
|
Calls: []TransactionArgs{{
|
||||||
|
From: &accounts[0].addr,
|
||||||
|
To: &randomAccounts[2].addr,
|
||||||
|
// blocks[0]
|
||||||
|
Input: uint256ToBytes(uint256.NewInt(12)),
|
||||||
|
}, {
|
||||||
|
From: &accounts[0].addr,
|
||||||
|
To: &randomAccounts[2].addr,
|
||||||
|
// Phantom after blocks[0]
|
||||||
|
Input: uint256ToBytes(uint256.NewInt(13)),
|
||||||
|
}},
|
||||||
|
}},
|
||||||
|
want: []blockRes{{
|
||||||
|
Number: "0xc",
|
||||||
|
GasLimit: "0x47e7c4",
|
||||||
|
GasUsed: "0xf864",
|
||||||
|
FeeRecipient: coinbase,
|
||||||
|
Calls: []callRes{{
|
||||||
|
ReturnValue: "0x",
|
||||||
|
GasUsed: "0x52cc",
|
||||||
|
Logs: []log{},
|
||||||
|
Status: "0x1",
|
||||||
|
}, {
|
||||||
|
ReturnValue: "0x",
|
||||||
|
GasUsed: "0x52cc",
|
||||||
|
Logs: []log{},
|
||||||
|
Status: "0x1",
|
||||||
|
}, {
|
||||||
|
|
||||||
|
ReturnValue: "0x",
|
||||||
|
GasUsed: "0x52cc",
|
||||||
|
Logs: []log{},
|
||||||
|
Status: "0x1",
|
||||||
|
}},
|
||||||
|
}, {
|
||||||
|
Number: "0x10",
|
||||||
|
GasLimit: "0x47e7c4",
|
||||||
|
GasUsed: "0xa598",
|
||||||
|
FeeRecipient: coinbase,
|
||||||
|
Calls: []callRes{{
|
||||||
|
ReturnValue: "0x",
|
||||||
|
GasUsed: "0x52cc",
|
||||||
|
Logs: []log{},
|
||||||
|
Status: "0x1",
|
||||||
|
}, {
|
||||||
|
|
||||||
|
ReturnValue: "0x",
|
||||||
|
GasUsed: "0x52cc",
|
||||||
|
Logs: []log{},
|
||||||
|
Status: "0x1",
|
||||||
|
}},
|
||||||
|
}},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testSuite {
|
for _, tc := range testSuite {
|
||||||
|
|
@ -1715,6 +1800,12 @@ func newBytes(b []byte) *hexutil.Bytes {
|
||||||
return &rpcBytes
|
return &rpcBytes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func uint256ToBytes(v *uint256.Int) *hexutil.Bytes {
|
||||||
|
b := v.Bytes32()
|
||||||
|
r := hexutil.Bytes(b[:])
|
||||||
|
return &r
|
||||||
|
}
|
||||||
|
|
||||||
func TestRPCMarshalBlock(t *testing.T) {
|
func TestRPCMarshalBlock(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
var (
|
var (
|
||||||
|
|
@ -1924,10 +2015,6 @@ func TestRPCMarshalBlock(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func hex2Hash(s string) common.Hash {
|
|
||||||
return common.BytesToHash(common.FromHex(s))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRPCGetBlockOrHeader(t *testing.T) {
|
func TestRPCGetBlockOrHeader(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue