Skip to content

Commit 0957146

Browse files
committed
blockchain: isolate tx cache during prefetch
1 parent 27c9215 commit 0957146

2 files changed

Lines changed: 101 additions & 0 deletions

File tree

blockchain/state_prefetcher.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ func (p *statePrefetcher) Prefetch(block *types.Block, stateDB *state.StateDB, c
5858
}
5959
// Block precaching permitted to continue, execute the transaction
6060
stateDB.SetTxContext(tx.Hash(), block.Hash(), i)
61+
tx = copyTxForPrefetch(tx)
6162
if err := precacheTransaction(p.chain, nil, stateDB, header, tx, cfg); err != nil {
6263
return // Ugh, something went horribly wrong, bail out
6364
}
@@ -81,11 +82,18 @@ func (p *statePrefetcher) PrefetchTx(block *types.Block, ti int, stateDB *state.
8182

8283
// Block precaching permitted to continue, execute the transaction
8384
stateDB.SetTxContext(tx.Hash(), block.Hash(), ti)
85+
tx = copyTxForPrefetch(tx)
8486
if err := precacheTransaction(p.chain, nil, stateDB, header, tx, cfg); err != nil {
8587
return // Ugh, something went horribly wrong, bail out
8688
}
8789
}
8890

91+
// Prefetch runs against speculative state, so it must not overwrite the
92+
// state-dependent execution caches on the transaction imported by the block.
93+
func copyTxForPrefetch(tx *types.Transaction) *types.Transaction {
94+
return types.NewTx(tx.GetTxInternalData())
95+
}
96+
8997
// precacheTransaction attempts to apply a transaction to the given state database
9098
// and uses the input parameters for its environment. The goal is not to execute
9199
// the transaction successfully, rather to warm up touched data slots.
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// Copyright 2026 The Kaia Authors
2+
// This file is part of the Kaia library.
3+
//
4+
// The Kaia library is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Lesser General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// The Kaia library is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Lesser General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Lesser General Public License
15+
// along with the Kaia library. If not, see <http://www.gnu.org/licenses/>.
16+
17+
package blockchain
18+
19+
import (
20+
"crypto/ecdsa"
21+
"math/big"
22+
"testing"
23+
24+
"github.com/kaiachain/kaia/blockchain/types"
25+
"github.com/kaiachain/kaia/blockchain/types/accountkey"
26+
"github.com/kaiachain/kaia/common"
27+
"github.com/kaiachain/kaia/crypto"
28+
"github.com/kaiachain/kaia/fork"
29+
"github.com/kaiachain/kaia/params"
30+
"github.com/stretchr/testify/require"
31+
)
32+
33+
type prefetchTestKeyPicker map[common.Address]accountkey.AccountKey
34+
35+
func (p prefetchTestKeyPicker) GetKey(addr common.Address) accountkey.AccountKey {
36+
return p[addr]
37+
}
38+
39+
func (p prefetchTestKeyPicker) Exist(addr common.Address) bool {
40+
return p[addr] != nil
41+
}
42+
43+
// TestCopyTxForPrefetchDoesNotOverwriteOriginalValidatedGas verifies that
44+
// prefetch cannot overwrite state-dependent validatedGas cached on the original
45+
// block transaction.
46+
func TestCopyTxForPrefetchDoesNotOverwriteOriginalValidatedGas(t *testing.T) {
47+
require.NoError(t, fork.SetHardForkBlockNumberConfig(&params.ChainConfig{
48+
IstanbulCompatibleBlock: big.NewInt(100),
49+
}))
50+
51+
const blockNumber = uint64(0)
52+
signer := types.LatestSignerForChainID(big.NewInt(1))
53+
senderKey, err := crypto.GenerateKey()
54+
require.NoError(t, err)
55+
extraKey, err := crypto.GenerateKey()
56+
require.NoError(t, err)
57+
58+
from := crypto.PubkeyToAddress(senderKey.PublicKey)
59+
to := common.HexToAddress("0x0000000000000000000000000000000000000100")
60+
txData, err := types.NewTxInternalDataWithMap(types.TxTypeValueTransfer, map[types.TxValueKeyType]interface{}{
61+
types.TxValueKeyNonce: uint64(0),
62+
types.TxValueKeyTo: to,
63+
types.TxValueKeyAmount: big.NewInt(1),
64+
types.TxValueKeyGasLimit: uint64(100000),
65+
types.TxValueKeyGasPrice: big.NewInt(1),
66+
types.TxValueKeyFrom: from,
67+
})
68+
require.NoError(t, err)
69+
70+
tx := types.NewTx(txData)
71+
require.NoError(t, tx.SignWithKeys(signer, []*ecdsa.PrivateKey{senderKey}))
72+
73+
stalePicker := prefetchTestKeyPicker{
74+
from: accountkey.NewAccountKeyPublicWithValue(&senderKey.PublicKey),
75+
}
76+
currentPicker := prefetchTestKeyPicker{
77+
from: accountkey.NewAccountKeyWeightedMultiSigWithValues(1, accountkey.WeightedPublicKeys{
78+
accountkey.NewWeightedPublicKey(1, (*accountkey.PublicKeySerializable)(&senderKey.PublicKey)),
79+
accountkey.NewWeightedPublicKey(1, (*accountkey.PublicKeySerializable)(&extraKey.PublicKey)),
80+
}),
81+
}
82+
83+
_, err = tx.AsMessageWithAccountKeyPicker(signer, currentPicker, blockNumber)
84+
require.NoError(t, err)
85+
originalGas := tx.ValidatedGas().IntrinsicGas
86+
87+
prefetchTx := copyTxForPrefetch(tx)
88+
_, err = prefetchTx.AsMessageWithAccountKeyPicker(signer, stalePicker, blockNumber)
89+
require.NoError(t, err)
90+
91+
require.Equal(t, params.TxValidationGasPerKey, originalGas-prefetchTx.ValidatedGas().IntrinsicGas)
92+
require.Equal(t, originalGas, tx.ValidatedGas().IntrinsicGas)
93+
}

0 commit comments

Comments
 (0)