Compare commits
32 Commits
merge-tx-p
...
support-da
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3a3eb1d34f | ||
|
|
a702225a27 | ||
|
|
78b0f2940a | ||
|
|
31a16b3fb8 | ||
|
|
adf9b91f8f | ||
|
|
312f1208f8 | ||
|
|
6957903492 | ||
|
|
2da9923d4f | ||
|
|
c6368a513c | ||
|
|
a55650056a | ||
|
|
71520fb59e | ||
|
|
7cb7800397 | ||
|
|
d97a34b0cb | ||
|
|
c20c0e680c | ||
|
|
b5194c043b | ||
|
|
6f8e9c570c | ||
|
|
68fa227b0c | ||
|
|
2d8613a296 | ||
|
|
533f592a05 | ||
|
|
6d1d2ddea5 | ||
|
|
bb578de461 | ||
|
|
2d0ddc1f67 | ||
|
|
eff9d23bd2 | ||
|
|
98fa1e6c44 | ||
|
|
eafdc65814 | ||
|
|
1a787b6081 | ||
|
|
1cb6989a30 | ||
|
|
35e71a769b | ||
|
|
6b02ac7ac5 | ||
|
|
8b9558bb4d | ||
|
|
63e7eac394 | ||
|
|
05543e558d |
@@ -36,7 +36,7 @@ To combine DPoS and PoA for consensus, BNB Smart Chain implement a novel consens
|
|||||||
2. Validators take turns to produce blocks in a PoA manner, similar to Ethereum's Clique consensus engine.
|
2. Validators take turns to produce blocks in a PoA manner, similar to Ethereum's Clique consensus engine.
|
||||||
3. Validator set are elected in and out based on a staking based governance on BNB Beacon Chain.
|
3. Validator set are elected in and out based on a staking based governance on BNB Beacon Chain.
|
||||||
4. The validator set change is relayed via a cross-chain communication mechanism.
|
4. The validator set change is relayed via a cross-chain communication mechanism.
|
||||||
5. Parlia consensus engine will interact with a set of [system contracts](https://docs.bnbchain.org/docs/learn/system-contract) to achieve liveness slash, revenue distributing and validator set renewing func.
|
5. Parlia consensus engine will interact with a set of [system contracts](https://docs.bnbchain.org/bnb-smart-chain/staking/overview/#system-contracts) to achieve liveness slash, revenue distributing and validator set renewing func.
|
||||||
|
|
||||||
|
|
||||||
### Light Client of BNB Beacon Chain
|
### Light Client of BNB Beacon Chain
|
||||||
@@ -183,7 +183,7 @@ This tool is optional and if you leave it out you can always attach to an alread
|
|||||||
|
|
||||||
#### 7. More
|
#### 7. More
|
||||||
|
|
||||||
More details about [running a node](https://docs.bnbchain.org/docs/validator/fullnode) and [becoming a validator](https://docs.bnbchain.org/docs/validator/create-val)
|
More details about [running a node](https://docs.bnbchain.org/bnb-smart-chain/developers/node_operators/full_node/) and [becoming a validator](https://docs.bnbchain.org/bnb-smart-chain/validator/create-val/)
|
||||||
|
|
||||||
*Note: Although some internal protective measures prevent transactions from
|
*Note: Although some internal protective measures prevent transactions from
|
||||||
crossing over between the main network and test network, you should always
|
crossing over between the main network and test network, you should always
|
||||||
|
|||||||
@@ -227,7 +227,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
|||||||
return nil, nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
vmConfig.Tracer = tracer
|
vmConfig.Tracer = tracer
|
||||||
statedb.SetTxContext(tx.Hash(), txIndex)
|
statedb.SetTxContext(tx.Hash(), txIndex, 0)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
txContext = core.NewEVMTxContext(msg)
|
txContext = core.NewEVMTxContext(msg)
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ var (
|
|||||||
utils.CachePreimagesFlag,
|
utils.CachePreimagesFlag,
|
||||||
utils.OverrideCancun,
|
utils.OverrideCancun,
|
||||||
utils.OverrideHaber,
|
utils.OverrideHaber,
|
||||||
|
utils.OverrideBohr,
|
||||||
utils.OverrideVerkle,
|
utils.OverrideVerkle,
|
||||||
}, utils.DatabaseFlags),
|
}, utils.DatabaseFlags),
|
||||||
Description: `
|
Description: `
|
||||||
@@ -261,6 +262,10 @@ func initGenesis(ctx *cli.Context) error {
|
|||||||
v := ctx.Uint64(utils.OverrideHaber.Name)
|
v := ctx.Uint64(utils.OverrideHaber.Name)
|
||||||
overrides.OverrideHaber = &v
|
overrides.OverrideHaber = &v
|
||||||
}
|
}
|
||||||
|
if ctx.IsSet(utils.OverrideBohr.Name) {
|
||||||
|
v := ctx.Uint64(utils.OverrideBohr.Name)
|
||||||
|
overrides.OverrideBohr = &v
|
||||||
|
}
|
||||||
if ctx.IsSet(utils.OverrideVerkle.Name) {
|
if ctx.IsSet(utils.OverrideVerkle.Name) {
|
||||||
v := ctx.Uint64(utils.OverrideVerkle.Name)
|
v := ctx.Uint64(utils.OverrideVerkle.Name)
|
||||||
overrides.OverrideVerkle = &v
|
overrides.OverrideVerkle = &v
|
||||||
|
|||||||
@@ -193,6 +193,10 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) {
|
|||||||
v := ctx.Uint64(utils.OverrideHaber.Name)
|
v := ctx.Uint64(utils.OverrideHaber.Name)
|
||||||
cfg.Eth.OverrideHaber = &v
|
cfg.Eth.OverrideHaber = &v
|
||||||
}
|
}
|
||||||
|
if ctx.IsSet(utils.OverrideBohr.Name) {
|
||||||
|
v := ctx.Uint64(utils.OverrideBohr.Name)
|
||||||
|
cfg.Eth.OverrideBohr = &v
|
||||||
|
}
|
||||||
if ctx.IsSet(utils.OverrideVerkle.Name) {
|
if ctx.IsSet(utils.OverrideVerkle.Name) {
|
||||||
v := ctx.Uint64(utils.OverrideVerkle.Name)
|
v := ctx.Uint64(utils.OverrideVerkle.Name)
|
||||||
cfg.Eth.OverrideVerkle = &v
|
cfg.Eth.OverrideVerkle = &v
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ var (
|
|||||||
utils.RialtoHash,
|
utils.RialtoHash,
|
||||||
utils.OverrideCancun,
|
utils.OverrideCancun,
|
||||||
utils.OverrideHaber,
|
utils.OverrideHaber,
|
||||||
|
utils.OverrideBohr,
|
||||||
utils.OverrideVerkle,
|
utils.OverrideVerkle,
|
||||||
utils.OverrideFullImmutabilityThreshold,
|
utils.OverrideFullImmutabilityThreshold,
|
||||||
utils.OverrideMinBlocksForBlobRequests,
|
utils.OverrideMinBlocksForBlobRequests,
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ func NewLevelDBDatabaseWithFreezer(file string, cache int, handles int, ancient
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
frdb, err := rawdb.NewDatabaseWithFreezer(kvdb, ancient, namespace, readonly, disableFreeze, isLastOffset, pruneAncientData)
|
frdb, err := rawdb.NewDatabaseWithFreezer(kvdb, ancient, namespace, readonly, disableFreeze, isLastOffset, pruneAncientData, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
kvdb.Close()
|
kvdb.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -178,11 +178,10 @@ func BlockchainCreator(t *testing.T, chaindbPath, AncientPath string, blockRemai
|
|||||||
|
|
||||||
// Force run a freeze cycle
|
// Force run a freeze cycle
|
||||||
type freezer interface {
|
type freezer interface {
|
||||||
Freeze() error
|
Freeze(threshold uint64) error
|
||||||
Ancients() (uint64, error)
|
Ancients() (uint64, error)
|
||||||
}
|
}
|
||||||
blockchain.SetFinalized(blocks[len(blocks)-1].Header())
|
db.(freezer).Freeze(10)
|
||||||
db.(freezer).Freeze()
|
|
||||||
|
|
||||||
frozen, err := db.Ancients()
|
frozen, err := db.Ancients()
|
||||||
//make sure there're frozen items
|
//make sure there're frozen items
|
||||||
|
|||||||
@@ -43,9 +43,11 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/metrics"
|
"github.com/ethereum/go-ethereum/metrics"
|
||||||
"github.com/ethereum/go-ethereum/node"
|
"github.com/ethereum/go-ethereum/node"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
"github.com/ethereum/go-ethereum/triedb"
|
"github.com/ethereum/go-ethereum/triedb"
|
||||||
|
"github.com/ethereum/go-ethereum/triedb/pathdb"
|
||||||
cli "github.com/urfave/cli/v2"
|
cli "github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -245,7 +247,16 @@ func accessDb(ctx *cli.Context, stack *node.Node) (ethdb.Database, error) {
|
|||||||
NoBuild: true,
|
NoBuild: true,
|
||||||
AsyncBuild: false,
|
AsyncBuild: false,
|
||||||
}
|
}
|
||||||
snaptree, err := snapshot.New(snapconfig, chaindb, triedb.NewDatabase(chaindb, nil), headBlock.Root(), TriesInMemory, false)
|
dbScheme := rawdb.ReadStateScheme(chaindb)
|
||||||
|
var config *triedb.Config
|
||||||
|
if dbScheme == rawdb.PathScheme {
|
||||||
|
config = &triedb.Config{
|
||||||
|
PathDB: utils.PathDBConfigAddJournalFilePath(stack, pathdb.ReadOnly),
|
||||||
|
}
|
||||||
|
} else if dbScheme == rawdb.HashScheme {
|
||||||
|
config = triedb.HashDefaults
|
||||||
|
}
|
||||||
|
snaptree, err := snapshot.New(snapconfig, chaindb, triedb.NewDatabase(chaindb, config), headBlock.Root(), TriesInMemory, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("snaptree error", "err", err)
|
log.Error("snaptree error", "err", err)
|
||||||
return nil, err // The relevant snapshot(s) might not exist
|
return nil, err // The relevant snapshot(s) might not exist
|
||||||
@@ -333,6 +344,9 @@ func pruneBlock(ctx *cli.Context) error {
|
|||||||
stack, config = makeConfigNode(ctx)
|
stack, config = makeConfigNode(ctx)
|
||||||
defer stack.Close()
|
defer stack.Close()
|
||||||
blockAmountReserved = ctx.Uint64(utils.BlockAmountReserved.Name)
|
blockAmountReserved = ctx.Uint64(utils.BlockAmountReserved.Name)
|
||||||
|
if blockAmountReserved < params.FullImmutabilityThreshold {
|
||||||
|
return fmt.Errorf("block-amount-reserved must be greater than or equal to %d", params.FullImmutabilityThreshold)
|
||||||
|
}
|
||||||
chaindb, err = accessDb(ctx, stack)
|
chaindb, err = accessDb(ctx, stack)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -315,6 +315,11 @@ var (
|
|||||||
Usage: "Manually specify the Haber fork timestamp, overriding the bundled setting",
|
Usage: "Manually specify the Haber fork timestamp, overriding the bundled setting",
|
||||||
Category: flags.EthCategory,
|
Category: flags.EthCategory,
|
||||||
}
|
}
|
||||||
|
OverrideBohr = &cli.Uint64Flag{
|
||||||
|
Name: "override.bohr",
|
||||||
|
Usage: "Manually specify the Bohr fork timestamp, overriding the bundled setting",
|
||||||
|
Category: flags.EthCategory,
|
||||||
|
}
|
||||||
OverrideVerkle = &cli.Uint64Flag{
|
OverrideVerkle = &cli.Uint64Flag{
|
||||||
Name: "override.verkle",
|
Name: "override.verkle",
|
||||||
Usage: "Manually specify the Verkle fork timestamp, overriding the bundled setting",
|
Usage: "Manually specify the Verkle fork timestamp, overriding the bundled setting",
|
||||||
@@ -1082,6 +1087,7 @@ Please note that --` + MetricsHTTPFlag.Name + ` must be set to start the server.
|
|||||||
Name: "block-amount-reserved",
|
Name: "block-amount-reserved",
|
||||||
Usage: "Sets the expected remained amount of blocks for offline block prune",
|
Usage: "Sets the expected remained amount of blocks for offline block prune",
|
||||||
Category: flags.BlockHistoryCategory,
|
Category: flags.BlockHistoryCategory,
|
||||||
|
Value: params.FullImmutabilityThreshold,
|
||||||
}
|
}
|
||||||
|
|
||||||
CheckSnapshotWithMPT = &cli.BoolFlag{
|
CheckSnapshotWithMPT = &cli.BoolFlag{
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ func TestHistoryImportAndExport(t *testing.T) {
|
|||||||
|
|
||||||
// Now import Era.
|
// Now import Era.
|
||||||
freezer := t.TempDir()
|
freezer := t.TempDir()
|
||||||
db2, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), freezer, "", false, false, false, false)
|
db2, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), freezer, "", false, false, false, false, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1824,7 +1824,7 @@ func (p *Parlia) applyTransaction(
|
|||||||
// move to next
|
// move to next
|
||||||
*receivedTxs = (*receivedTxs)[1:]
|
*receivedTxs = (*receivedTxs)[1:]
|
||||||
}
|
}
|
||||||
state.SetTxContext(expectedTx.Hash(), len(*txs))
|
state.SetTxContext(expectedTx.Hash(), len(*txs), 0)
|
||||||
gasUsed, err := applyMessage(msg, state, header, p.chainConfig, chainContext)
|
gasUsed, err := applyMessage(msg, state, header, p.chainConfig, chainContext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -2307,7 +2307,6 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
|
|||||||
|
|
||||||
blockWriteTimer.Update(time.Since(wstart) - statedb.AccountCommits - statedb.StorageCommits - statedb.SnapshotCommits - statedb.TrieDBCommits)
|
blockWriteTimer.Update(time.Since(wstart) - statedb.AccountCommits - statedb.StorageCommits - statedb.SnapshotCommits - statedb.TrieDBCommits)
|
||||||
blockInsertTimer.UpdateSince(start)
|
blockInsertTimer.UpdateSince(start)
|
||||||
|
|
||||||
// Report the import stats before returning the various results
|
// Report the import stats before returning the various results
|
||||||
stats.processed++
|
stats.processed++
|
||||||
stats.usedGas += usedGas
|
stats.usedGas += usedGas
|
||||||
|
|||||||
@@ -1832,14 +1832,10 @@ func testRepairWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme s
|
|||||||
}
|
}
|
||||||
// Force run a freeze cycle
|
// Force run a freeze cycle
|
||||||
type freezer interface {
|
type freezer interface {
|
||||||
Freeze() error
|
Freeze(threshold uint64) error
|
||||||
Ancients() (uint64, error)
|
Ancients() (uint64, error)
|
||||||
}
|
}
|
||||||
if tt.freezeThreshold < uint64(tt.canonicalBlocks) {
|
db.(freezer).Freeze(tt.freezeThreshold)
|
||||||
final := uint64(tt.canonicalBlocks) - tt.freezeThreshold
|
|
||||||
chain.SetFinalized(canonblocks[int(final)-1].Header())
|
|
||||||
}
|
|
||||||
db.(freezer).Freeze()
|
|
||||||
|
|
||||||
// Set the simulated pivot block
|
// Set the simulated pivot block
|
||||||
if tt.pivotBlock != nil {
|
if tt.pivotBlock != nil {
|
||||||
|
|||||||
@@ -2045,14 +2045,10 @@ func testSetHeadWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme
|
|||||||
|
|
||||||
// Force run a freeze cycle
|
// Force run a freeze cycle
|
||||||
type freezer interface {
|
type freezer interface {
|
||||||
Freeze() error
|
Freeze(threshold uint64) error
|
||||||
Ancients() (uint64, error)
|
Ancients() (uint64, error)
|
||||||
}
|
}
|
||||||
if tt.freezeThreshold < uint64(tt.canonicalBlocks) {
|
db.(freezer).Freeze(tt.freezeThreshold)
|
||||||
final := uint64(tt.canonicalBlocks) - tt.freezeThreshold
|
|
||||||
chain.SetFinalized(canonblocks[int(final)-1].Header())
|
|
||||||
}
|
|
||||||
db.(freezer).Freeze()
|
|
||||||
|
|
||||||
// Set the simulated pivot block
|
// Set the simulated pivot block
|
||||||
if tt.pivotBlock != nil {
|
if tt.pivotBlock != nil {
|
||||||
|
|||||||
@@ -974,7 +974,7 @@ func testFastVsFullChains(t *testing.T, scheme string) {
|
|||||||
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
||||||
}
|
}
|
||||||
// Freezer style fast import the chain.
|
// Freezer style fast import the chain.
|
||||||
ancientDb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false, false, false, false)
|
ancientDb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false, false, false, false, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create temp freezer db: %v", err)
|
t.Fatalf("failed to create temp freezer db: %v", err)
|
||||||
}
|
}
|
||||||
@@ -1069,7 +1069,7 @@ func testLightVsFastVsFullChainHeads(t *testing.T, scheme string) {
|
|||||||
|
|
||||||
// makeDb creates a db instance for testing.
|
// makeDb creates a db instance for testing.
|
||||||
makeDb := func() ethdb.Database {
|
makeDb := func() ethdb.Database {
|
||||||
db, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false, false, false, false)
|
db, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false, false, false, false, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create temp freezer db: %v", err)
|
t.Fatalf("failed to create temp freezer db: %v", err)
|
||||||
}
|
}
|
||||||
@@ -1957,7 +1957,7 @@ func testLargeReorgTrieGC(t *testing.T, scheme string) {
|
|||||||
competitor, _ := GenerateChain(genesis.Config, shared[len(shared)-1], engine, genDb, 2*TriesInMemory+1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{3}) })
|
competitor, _ := GenerateChain(genesis.Config, shared[len(shared)-1], engine, genDb, 2*TriesInMemory+1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{3}) })
|
||||||
|
|
||||||
// Import the shared chain and the original canonical one
|
// Import the shared chain and the original canonical one
|
||||||
db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false, false, false, false)
|
db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false, false, false, false, false)
|
||||||
defer db.Close()
|
defer db.Close()
|
||||||
|
|
||||||
chain, err := NewBlockChain(db, DefaultCacheConfigWithScheme(scheme), genesis, nil, engine, vm.Config{}, nil, nil)
|
chain, err := NewBlockChain(db, DefaultCacheConfigWithScheme(scheme), genesis, nil, engine, vm.Config{}, nil, nil)
|
||||||
@@ -2026,7 +2026,7 @@ func testBlockchainRecovery(t *testing.T, scheme string) {
|
|||||||
_, blocks, receipts := GenerateChainWithGenesis(gspec, ethash.NewFaker(), int(height), nil)
|
_, blocks, receipts := GenerateChainWithGenesis(gspec, ethash.NewFaker(), int(height), nil)
|
||||||
|
|
||||||
// Import the chain as a ancient-first node and ensure all pointers are updated
|
// Import the chain as a ancient-first node and ensure all pointers are updated
|
||||||
ancientDb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false, false, false, false)
|
ancientDb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false, false, false, false, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create temp freezer db: %v", err)
|
t.Fatalf("failed to create temp freezer db: %v", err)
|
||||||
}
|
}
|
||||||
@@ -2097,7 +2097,7 @@ func testInsertReceiptChainRollback(t *testing.T, scheme string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set up a BlockChain that uses the ancient store.
|
// Set up a BlockChain that uses the ancient store.
|
||||||
ancientDb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false, false, false, false)
|
ancientDb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false, false, false, false, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create temp freezer db: %v", err)
|
t.Fatalf("failed to create temp freezer db: %v", err)
|
||||||
}
|
}
|
||||||
@@ -2167,7 +2167,7 @@ func testLowDiffLongChain(t *testing.T, scheme string) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Import the canonical chain
|
// Import the canonical chain
|
||||||
diskdb, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false, false, false, false)
|
diskdb, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false, false, false, false, false)
|
||||||
defer diskdb.Close()
|
defer diskdb.Close()
|
||||||
|
|
||||||
chain, err := NewBlockChain(diskdb, DefaultCacheConfigWithScheme(scheme), genesis, nil, engine, vm.Config{}, nil, nil)
|
chain, err := NewBlockChain(diskdb, DefaultCacheConfigWithScheme(scheme), genesis, nil, engine, vm.Config{}, nil, nil)
|
||||||
@@ -2384,7 +2384,7 @@ func testInsertKnownChainData(t *testing.T, typ string, scheme string) {
|
|||||||
b.OffsetTime(-9) // A higher difficulty
|
b.OffsetTime(-9) // A higher difficulty
|
||||||
})
|
})
|
||||||
// Import the shared chain and the original canonical one
|
// Import the shared chain and the original canonical one
|
||||||
chaindb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false, false, false, false)
|
chaindb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false, false, false, false, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create temp freezer db: %v", err)
|
t.Fatalf("failed to create temp freezer db: %v", err)
|
||||||
}
|
}
|
||||||
@@ -2555,7 +2555,7 @@ func testInsertKnownChainDataWithMerging(t *testing.T, typ string, mergeHeight i
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
// Import the shared chain and the original canonical one
|
// Import the shared chain and the original canonical one
|
||||||
chaindb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false, false, false, false)
|
chaindb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false, false, false, false, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create temp freezer db: %v", err)
|
t.Fatalf("failed to create temp freezer db: %v", err)
|
||||||
}
|
}
|
||||||
@@ -3858,7 +3858,7 @@ func testSetCanonical(t *testing.T, scheme string) {
|
|||||||
}
|
}
|
||||||
gen.AddTx(tx)
|
gen.AddTx(tx)
|
||||||
})
|
})
|
||||||
diskdb, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false, false, false, false)
|
diskdb, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false, false, false, false, false)
|
||||||
defer diskdb.Close()
|
defer diskdb.Close()
|
||||||
|
|
||||||
chain, err := NewBlockChain(diskdb, DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{}, nil, nil)
|
chain, err := NewBlockChain(diskdb, DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{}, nil, nil)
|
||||||
@@ -4483,7 +4483,7 @@ func (c *mockParlia) CalcDifficulty(chain consensus.ChainHeaderReader, time uint
|
|||||||
func TestParliaBlobFeeReward(t *testing.T) {
|
func TestParliaBlobFeeReward(t *testing.T) {
|
||||||
// Have N headers in the freezer
|
// Have N headers in the freezer
|
||||||
frdir := t.TempDir()
|
frdir := t.TempDir()
|
||||||
db, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), frdir, "", false, false, false, false)
|
db, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), frdir, "", false, false, false, false, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create database with ancient backend")
|
t.Fatalf("failed to create database with ancient backend")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ func (b *BlockGen) addTx(bc *BlockChain, vmConfig vm.Config, tx *types.Transacti
|
|||||||
if b.gasPool == nil {
|
if b.gasPool == nil {
|
||||||
b.SetCoinbase(common.Address{})
|
b.SetCoinbase(common.Address{})
|
||||||
}
|
}
|
||||||
b.statedb.SetTxContext(tx.Hash(), len(b.txs))
|
b.statedb.SetTxContext(tx.Hash(), len(b.txs), 0)
|
||||||
receipt, err := ApplyTransaction(b.cm.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, vmConfig, NewReceiptBloomGenerator())
|
receipt, err := ApplyTransaction(b.cm.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, vmConfig, NewReceiptBloomGenerator())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|||||||
@@ -218,6 +218,7 @@ func (e *GenesisMismatchError) Error() string {
|
|||||||
type ChainOverrides struct {
|
type ChainOverrides struct {
|
||||||
OverrideCancun *uint64
|
OverrideCancun *uint64
|
||||||
OverrideHaber *uint64
|
OverrideHaber *uint64
|
||||||
|
OverrideBohr *uint64
|
||||||
OverrideVerkle *uint64
|
OverrideVerkle *uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,6 +251,9 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *triedb.Database, g
|
|||||||
if overrides != nil && overrides.OverrideHaber != nil {
|
if overrides != nil && overrides.OverrideHaber != nil {
|
||||||
config.HaberTime = overrides.OverrideHaber
|
config.HaberTime = overrides.OverrideHaber
|
||||||
}
|
}
|
||||||
|
if overrides != nil && overrides.OverrideBohr != nil {
|
||||||
|
config.BohrTime = overrides.OverrideBohr
|
||||||
|
}
|
||||||
if overrides != nil && overrides.OverrideVerkle != nil {
|
if overrides != nil && overrides.OverrideVerkle != nil {
|
||||||
config.VerkleTime = overrides.OverrideVerkle
|
config.VerkleTime = overrides.OverrideVerkle
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -518,7 +518,7 @@ func checkBlobSidecarsRLP(have, want types.BlobSidecars) error {
|
|||||||
func TestAncientStorage(t *testing.T) {
|
func TestAncientStorage(t *testing.T) {
|
||||||
// Freezer style fast import the chain.
|
// Freezer style fast import the chain.
|
||||||
frdir := t.TempDir()
|
frdir := t.TempDir()
|
||||||
db, err := NewDatabaseWithFreezer(NewMemoryDatabase(), frdir, "", false, false, false, false)
|
db, err := NewDatabaseWithFreezer(NewMemoryDatabase(), frdir, "", false, false, false, false, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create database with ancient backend")
|
t.Fatalf("failed to create database with ancient backend")
|
||||||
}
|
}
|
||||||
@@ -657,7 +657,7 @@ func TestHashesInRange(t *testing.T) {
|
|||||||
func BenchmarkWriteAncientBlocks(b *testing.B) {
|
func BenchmarkWriteAncientBlocks(b *testing.B) {
|
||||||
// Open freezer database.
|
// Open freezer database.
|
||||||
frdir := b.TempDir()
|
frdir := b.TempDir()
|
||||||
db, err := NewDatabaseWithFreezer(NewMemoryDatabase(), frdir, "", false, false, false, false)
|
db, err := NewDatabaseWithFreezer(NewMemoryDatabase(), frdir, "", false, false, false, false, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("failed to create database with ancient backend")
|
b.Fatalf("failed to create database with ancient backend")
|
||||||
}
|
}
|
||||||
@@ -1001,7 +1001,7 @@ func TestHeadersRLPStorage(t *testing.T) {
|
|||||||
// Have N headers in the freezer
|
// Have N headers in the freezer
|
||||||
frdir := t.TempDir()
|
frdir := t.TempDir()
|
||||||
|
|
||||||
db, err := NewDatabaseWithFreezer(NewMemoryDatabase(), frdir, "", false, false, false, false)
|
db, err := NewDatabaseWithFreezer(NewMemoryDatabase(), frdir, "", false, false, false, false, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create database with ancient backend")
|
t.Fatalf("failed to create database with ancient backend")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,12 +24,12 @@ import (
|
|||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -51,25 +51,31 @@ var (
|
|||||||
// The background thread will keep moving ancient chain segments from key-value
|
// The background thread will keep moving ancient chain segments from key-value
|
||||||
// database to flat files for saving space on live database.
|
// database to flat files for saving space on live database.
|
||||||
type chainFreezer struct {
|
type chainFreezer struct {
|
||||||
|
threshold atomic.Uint64 // Number of recent blocks not to freeze (params.FullImmutabilityThreshold apart from tests)
|
||||||
|
|
||||||
*Freezer
|
*Freezer
|
||||||
quit chan struct{}
|
quit chan struct{}
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
trigger chan chan struct{} // Manual blocking freeze trigger, test determinism
|
trigger chan chan struct{} // Manual blocking freeze trigger, test determinism
|
||||||
|
|
||||||
freezeEnv atomic.Value
|
freezeEnv atomic.Value
|
||||||
|
|
||||||
|
multiDatabase bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// newChainFreezer initializes the freezer for ancient chain data.
|
// newChainFreezer initializes the freezer for ancient chain data.
|
||||||
func newChainFreezer(datadir string, namespace string, readonly bool, offset uint64) (*chainFreezer, error) {
|
func newChainFreezer(datadir string, namespace string, readonly bool, offset uint64, multiDatabase bool) (*chainFreezer, error) {
|
||||||
freezer, err := NewChainFreezer(datadir, namespace, readonly, offset)
|
freezer, err := NewChainFreezer(datadir, namespace, readonly, offset)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &chainFreezer{
|
cf := chainFreezer{
|
||||||
Freezer: freezer,
|
Freezer: freezer,
|
||||||
quit: make(chan struct{}),
|
quit: make(chan struct{}),
|
||||||
trigger: make(chan chan struct{}),
|
trigger: make(chan chan struct{}),
|
||||||
}, nil
|
}
|
||||||
|
cf.threshold.Store(params.FullImmutabilityThreshold)
|
||||||
|
return &cf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close closes the chain freezer instance and terminates the background thread.
|
// Close closes the chain freezer instance and terminates the background thread.
|
||||||
@@ -185,29 +191,101 @@ func (f *chainFreezer) freeze(db ethdb.KeyValueStore) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
threshold, err := f.freezeThreshold(nfdb)
|
var (
|
||||||
if err != nil {
|
frozen uint64
|
||||||
backoff = true
|
threshold uint64
|
||||||
log.Debug("Current full block not old enough to freeze", "err", err)
|
first uint64 // the first block to freeze
|
||||||
continue
|
last uint64 // the last block to freeze
|
||||||
}
|
|
||||||
frozen := f.frozen.Load()
|
|
||||||
|
|
||||||
// Short circuit if the blocks below threshold are already frozen.
|
hash common.Hash
|
||||||
if frozen != 0 && frozen-1 >= threshold {
|
number *uint64
|
||||||
backoff = true
|
head *types.Header
|
||||||
log.Debug("Ancient blocks frozen already", "threshold", threshold, "frozen", frozen)
|
)
|
||||||
continue
|
|
||||||
|
// use finalized block as the chain freeze indicator was used for multiDatabase feature, if multiDatabase is false, keep 9W blocks in db
|
||||||
|
if f.multiDatabase {
|
||||||
|
threshold, err = f.freezeThreshold(nfdb)
|
||||||
|
if err != nil {
|
||||||
|
backoff = true
|
||||||
|
log.Debug("Current full block not old enough to freeze", "err", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
frozen = f.frozen.Load()
|
||||||
|
|
||||||
|
// Short circuit if the blocks below threshold are already frozen.
|
||||||
|
if frozen != 0 && frozen-1 >= threshold {
|
||||||
|
backoff = true
|
||||||
|
log.Debug("Ancient blocks frozen already", "threshold", threshold, "frozen", frozen)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
hash = ReadHeadBlockHash(nfdb)
|
||||||
|
if hash == (common.Hash{}) {
|
||||||
|
log.Debug("Current full block hash unavailable") // new chain, empty database
|
||||||
|
backoff = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
number = ReadHeaderNumber(nfdb, hash)
|
||||||
|
if number == nil {
|
||||||
|
log.Error("Current full block number unavailable", "hash", hash)
|
||||||
|
backoff = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
head = ReadHeader(nfdb, hash, *number)
|
||||||
|
if head == nil {
|
||||||
|
log.Error("Current full block unavailable", "number", *number, "hash", hash)
|
||||||
|
backoff = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
first = frozen
|
||||||
|
last = threshold
|
||||||
|
if last-first+1 > freezerBatchLimit {
|
||||||
|
last = freezerBatchLimit + first - 1
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Retrieve the freezing threshold.
|
||||||
|
hash = ReadHeadBlockHash(nfdb)
|
||||||
|
if hash == (common.Hash{}) {
|
||||||
|
log.Debug("Current full block hash unavailable") // new chain, empty database
|
||||||
|
backoff = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
number = ReadHeaderNumber(nfdb, hash)
|
||||||
|
threshold = f.threshold.Load()
|
||||||
|
frozen = f.frozen.Load()
|
||||||
|
switch {
|
||||||
|
case number == nil:
|
||||||
|
log.Error("Current full block number unavailable", "hash", hash)
|
||||||
|
backoff = true
|
||||||
|
continue
|
||||||
|
|
||||||
|
case *number < threshold:
|
||||||
|
log.Debug("Current full block not old enough to freeze", "number", *number, "hash", hash, "delay", threshold)
|
||||||
|
backoff = true
|
||||||
|
continue
|
||||||
|
|
||||||
|
case *number-threshold <= frozen:
|
||||||
|
log.Debug("Ancient blocks frozen already", "number", *number, "hash", hash, "frozen", frozen)
|
||||||
|
backoff = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
head = ReadHeader(nfdb, hash, *number)
|
||||||
|
if head == nil {
|
||||||
|
log.Error("Current full block unavailable", "number", *number, "hash", hash)
|
||||||
|
backoff = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
first, _ = f.Ancients()
|
||||||
|
last = *number - threshold
|
||||||
|
if last-first > freezerBatchLimit {
|
||||||
|
last = first + freezerBatchLimit
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Seems we have data ready to be frozen, process in usable batches
|
// Seems we have data ready to be frozen, process in usable batches
|
||||||
var (
|
var (
|
||||||
start = time.Now()
|
start = time.Now()
|
||||||
first = frozen // the first block to freeze
|
|
||||||
last = threshold // the last block to freeze
|
|
||||||
)
|
)
|
||||||
if last-first+1 > freezerBatchLimit {
|
|
||||||
last = freezerBatchLimit + first - 1
|
|
||||||
}
|
|
||||||
|
|
||||||
ancients, err := f.freezeRangeWithBlobs(nfdb, first, last)
|
ancients, err := f.freezeRangeWithBlobs(nfdb, first, last)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -295,24 +373,6 @@ func (f *chainFreezer) freeze(db ethdb.KeyValueStore) {
|
|||||||
log.Debug("Deep froze chain segment", context...)
|
log.Debug("Deep froze chain segment", context...)
|
||||||
|
|
||||||
env, _ := f.freezeEnv.Load().(*ethdb.FreezerEnv)
|
env, _ := f.freezeEnv.Load().(*ethdb.FreezerEnv)
|
||||||
hash := ReadHeadBlockHash(nfdb)
|
|
||||||
if hash == (common.Hash{}) {
|
|
||||||
log.Debug("Current full block hash unavailable") // new chain, empty database
|
|
||||||
backoff = true
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
number := ReadHeaderNumber(nfdb, hash)
|
|
||||||
if number == nil {
|
|
||||||
log.Error("Current full block number unavailable", "hash", hash)
|
|
||||||
backoff = true
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
head := ReadHeader(nfdb, hash, *number)
|
|
||||||
if head == nil {
|
|
||||||
log.Error("Current full block unavailable", "number", *number, "hash", hash)
|
|
||||||
backoff = true
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// try prune blob data after cancun fork
|
// try prune blob data after cancun fork
|
||||||
if isCancun(env, head.Number, head.Time) {
|
if isCancun(env, head.Number, head.Time) {
|
||||||
f.tryPruneBlobAncientTable(env, *number)
|
f.tryPruneBlobAncientTable(env, *number)
|
||||||
|
|||||||
@@ -138,13 +138,22 @@ func (frdb *freezerdb) SetBlockStore(block ethdb.Database) {
|
|||||||
frdb.blockStore = block
|
frdb.blockStore = block
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (frdb *freezerdb) HasSeparateBlockStore() bool {
|
||||||
|
return frdb.blockStore != nil
|
||||||
|
}
|
||||||
|
|
||||||
// Freeze is a helper method used for external testing to trigger and block until
|
// Freeze is a helper method used for external testing to trigger and block until
|
||||||
// a freeze cycle completes, without having to sleep for a minute to trigger the
|
// a freeze cycle completes, without having to sleep for a minute to trigger the
|
||||||
// automatic background run.
|
// automatic background run.
|
||||||
func (frdb *freezerdb) Freeze() error {
|
func (frdb *freezerdb) Freeze(threshold uint64) error {
|
||||||
if frdb.AncientStore.(*chainFreezer).readonly {
|
if frdb.AncientStore.(*chainFreezer).readonly {
|
||||||
return errReadOnly
|
return errReadOnly
|
||||||
}
|
}
|
||||||
|
// Set the freezer threshold to a temporary value
|
||||||
|
defer func(old uint64) {
|
||||||
|
frdb.AncientStore.(*chainFreezer).threshold.Store(old)
|
||||||
|
}(frdb.AncientStore.(*chainFreezer).threshold.Load())
|
||||||
|
frdb.AncientStore.(*chainFreezer).threshold.Store(threshold)
|
||||||
// Trigger a freeze cycle and block until it's done
|
// Trigger a freeze cycle and block until it's done
|
||||||
trigger := make(chan struct{}, 1)
|
trigger := make(chan struct{}, 1)
|
||||||
frdb.AncientStore.(*chainFreezer).trigger <- trigger
|
frdb.AncientStore.(*chainFreezer).trigger <- trigger
|
||||||
@@ -263,6 +272,10 @@ func (db *nofreezedb) SetBlockStore(block ethdb.Database) {
|
|||||||
db.blockStore = block
|
db.blockStore = block
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *nofreezedb) HasSeparateBlockStore() bool {
|
||||||
|
return db.blockStore != nil
|
||||||
|
}
|
||||||
|
|
||||||
func (db *nofreezedb) BlockStoreReader() ethdb.Reader {
|
func (db *nofreezedb) BlockStoreReader() ethdb.Reader {
|
||||||
if db.blockStore != nil {
|
if db.blockStore != nil {
|
||||||
return db.blockStore
|
return db.blockStore
|
||||||
@@ -358,7 +371,7 @@ func resolveChainFreezerDir(ancient string) string {
|
|||||||
// value data store with a freezer moving immutable chain segments into cold
|
// value data store with a freezer moving immutable chain segments into cold
|
||||||
// storage. The passed ancient indicates the path of root ancient directory
|
// storage. The passed ancient indicates the path of root ancient directory
|
||||||
// where the chain freezer can be opened.
|
// where the chain freezer can be opened.
|
||||||
func NewDatabaseWithFreezer(db ethdb.KeyValueStore, ancient string, namespace string, readonly, disableFreeze, isLastOffset, pruneAncientData bool) (ethdb.Database, error) {
|
func NewDatabaseWithFreezer(db ethdb.KeyValueStore, ancient string, namespace string, readonly, disableFreeze, isLastOffset, pruneAncientData, multiDatabase bool) (ethdb.Database, error) {
|
||||||
var offset uint64
|
var offset uint64
|
||||||
// The offset of ancientDB should be handled differently in different scenarios.
|
// The offset of ancientDB should be handled differently in different scenarios.
|
||||||
if isLastOffset {
|
if isLastOffset {
|
||||||
@@ -394,7 +407,7 @@ func NewDatabaseWithFreezer(db ethdb.KeyValueStore, ancient string, namespace st
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create the idle freezer instance
|
// Create the idle freezer instance
|
||||||
frdb, err := newChainFreezer(resolveChainFreezerDir(ancient), namespace, readonly, offset)
|
frdb, err := newChainFreezer(resolveChainFreezerDir(ancient), namespace, readonly, offset, multiDatabase)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
printChainMetadata(db)
|
printChainMetadata(db)
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -575,6 +588,8 @@ type OpenOptions struct {
|
|||||||
// Ephemeral means that filesystem sync operations should be avoided: data integrity in the face of
|
// Ephemeral means that filesystem sync operations should be avoided: data integrity in the face of
|
||||||
// a crash is not important. This option should typically be used in tests.
|
// a crash is not important. This option should typically be used in tests.
|
||||||
Ephemeral bool
|
Ephemeral bool
|
||||||
|
|
||||||
|
MultiDataBase bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// openKeyValueDatabase opens a disk-based key-value database, e.g. leveldb or pebble.
|
// openKeyValueDatabase opens a disk-based key-value database, e.g. leveldb or pebble.
|
||||||
@@ -625,7 +640,7 @@ func Open(o OpenOptions) (ethdb.Database, error) {
|
|||||||
if len(o.AncientsDirectory) == 0 {
|
if len(o.AncientsDirectory) == 0 {
|
||||||
return kvdb, nil
|
return kvdb, nil
|
||||||
}
|
}
|
||||||
frdb, err := NewDatabaseWithFreezer(kvdb, o.AncientsDirectory, o.Namespace, o.ReadOnly, o.DisableFreeze, o.IsLastOffset, o.PruneAncientData)
|
frdb, err := NewDatabaseWithFreezer(kvdb, o.AncientsDirectory, o.Namespace, o.ReadOnly, o.DisableFreeze, o.IsLastOffset, o.PruneAncientData, o.MultiDataBase)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
kvdb.Close()
|
kvdb.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -769,7 +784,7 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
|
|||||||
trieIter = db.StateStore().NewIterator(keyPrefix, nil)
|
trieIter = db.StateStore().NewIterator(keyPrefix, nil)
|
||||||
defer trieIter.Release()
|
defer trieIter.Release()
|
||||||
}
|
}
|
||||||
if db.BlockStore() != db {
|
if db.HasSeparateBlockStore() {
|
||||||
blockIter = db.BlockStore().NewIterator(keyPrefix, nil)
|
blockIter = db.BlockStore().NewIterator(keyPrefix, nil)
|
||||||
defer blockIter.Release()
|
defer blockIter.Release()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,10 @@ func (t *table) SetBlockStore(block ethdb.Database) {
|
|||||||
panic("not implement")
|
panic("not implement")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *table) HasSeparateBlockStore() bool {
|
||||||
|
panic("not implement")
|
||||||
|
}
|
||||||
|
|
||||||
// NewTable returns a database object that prefixes all keys with a given string.
|
// NewTable returns a database object that prefixes all keys with a given string.
|
||||||
func NewTable(db ethdb.Database, prefix string) ethdb.Database {
|
func NewTable(db ethdb.Database, prefix string) ethdb.Database {
|
||||||
return &table{
|
return &table{
|
||||||
|
|||||||
@@ -164,7 +164,7 @@ func (ch createObjectChange) dirtied() *common.Address {
|
|||||||
func (ch resetObjectChange) revert(s *StateDB) {
|
func (ch resetObjectChange) revert(s *StateDB) {
|
||||||
s.setStateObject(ch.prev)
|
s.setStateObject(ch.prev)
|
||||||
if !ch.prevdestruct {
|
if !ch.prevdestruct {
|
||||||
delete(s.stateObjectsDestruct, ch.prev.address)
|
s.deleteStateObjectsDestruct(ch.prev.address)
|
||||||
}
|
}
|
||||||
if ch.prevAccount != nil {
|
if ch.prevAccount != nil {
|
||||||
s.accounts[ch.prev.addrHash] = ch.prevAccount
|
s.accounts[ch.prev.addrHash] = ch.prevAccount
|
||||||
|
|||||||
@@ -382,7 +382,7 @@ func (p *BlockPruner) backUpOldDb(name string, cache, handles int, namespace str
|
|||||||
log.Info("chainDB opened successfully")
|
log.Info("chainDB opened successfully")
|
||||||
|
|
||||||
// Get the number of items in old ancient db.
|
// Get the number of items in old ancient db.
|
||||||
itemsOfAncient, err := chainDb.ItemAmountInAncient()
|
itemsOfAncient, err := chainDb.BlockStore().ItemAmountInAncient()
|
||||||
log.Info("the number of items in ancientDB is ", "itemsOfAncient", itemsOfAncient)
|
log.Info("the number of items in ancientDB is ", "itemsOfAncient", itemsOfAncient)
|
||||||
|
|
||||||
// If we can't access the freezer or it's empty, abort.
|
// If we can't access the freezer or it's empty, abort.
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ package state
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
"io"
|
"io"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@@ -68,6 +69,11 @@ type stateObject struct {
|
|||||||
origin *types.StateAccount // Account original data without any change applied, nil means it was not existent
|
origin *types.StateAccount // Account original data without any change applied, nil means it was not existent
|
||||||
data types.StateAccount // Account data with all mutations applied in the scope of block
|
data types.StateAccount // Account data with all mutations applied in the scope of block
|
||||||
|
|
||||||
|
// dirty account state
|
||||||
|
dirtyBalance *uint256.Int
|
||||||
|
dirtyNonce *uint64
|
||||||
|
dirtyCodeHash []byte
|
||||||
|
|
||||||
// Write caches.
|
// Write caches.
|
||||||
trie Trie // storage trie, which becomes non-nil on first access
|
trie Trie // storage trie, which becomes non-nil on first access
|
||||||
code Code // contract bytecode, which gets set when code is loaded
|
code Code // contract bytecode, which gets set when code is loaded
|
||||||
@@ -95,7 +101,7 @@ type stateObject struct {
|
|||||||
|
|
||||||
// empty returns whether the account is considered empty.
|
// empty returns whether the account is considered empty.
|
||||||
func (s *stateObject) empty() bool {
|
func (s *stateObject) empty() bool {
|
||||||
return s.data.Nonce == 0 && s.data.Balance.IsZero() && bytes.Equal(s.data.CodeHash, types.EmptyCodeHash.Bytes())
|
return s.Nonce() == 0 && s.Balance().IsZero() && bytes.Equal(s.CodeHash(), types.EmptyCodeHash.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
// newObject creates a state object.
|
// newObject creates a state object.
|
||||||
@@ -113,7 +119,7 @@ func newObject(db *StateDB, address common.Address, acct *types.StateAccount) *s
|
|||||||
storageMap = db.GetStorage(address)
|
storageMap = db.GetStorage(address)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &stateObject{
|
s := &stateObject{
|
||||||
db: db,
|
db: db,
|
||||||
address: address,
|
address: address,
|
||||||
addrHash: crypto.Keccak256Hash(address[:]),
|
addrHash: crypto.Keccak256Hash(address[:]),
|
||||||
@@ -125,6 +131,15 @@ func newObject(db *StateDB, address common.Address, acct *types.StateAccount) *s
|
|||||||
dirtyStorage: make(Storage),
|
dirtyStorage: make(Storage),
|
||||||
created: created,
|
created: created,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// dirty data when create a new account
|
||||||
|
if acct == nil {
|
||||||
|
s.dirtyBalance = acct.Balance.Clone()
|
||||||
|
s.dirtyNonce = new(uint64)
|
||||||
|
*s.dirtyNonce = acct.Nonce
|
||||||
|
s.dirtyCodeHash = acct.CodeHash
|
||||||
|
}
|
||||||
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
// EncodeRLP implements rlp.Encoder.
|
// EncodeRLP implements rlp.Encoder.
|
||||||
@@ -219,7 +234,7 @@ func (s *stateObject) GetCommittedState(key common.Hash) common.Hash {
|
|||||||
// 1) resurrect happened, and new slot values were set -- those should
|
// 1) resurrect happened, and new slot values were set -- those should
|
||||||
// have been handles via pendingStorage above.
|
// have been handles via pendingStorage above.
|
||||||
// 2) we don't have new values, and can deliver empty response back
|
// 2) we don't have new values, and can deliver empty response back
|
||||||
if _, destructed := s.db.stateObjectsDestruct[s.address]; destructed {
|
if _, destructed := s.db.queryStateObjectsDestruct(s.address); destructed {
|
||||||
return common.Hash{}
|
return common.Hash{}
|
||||||
}
|
}
|
||||||
// If no live objects are available, attempt to use snapshots
|
// If no live objects are available, attempt to use snapshots
|
||||||
@@ -294,6 +309,18 @@ func (s *stateObject) finalise(prefetch bool) {
|
|||||||
slotsToPrefetch = append(slotsToPrefetch, common.CopyBytes(key[:])) // Copy needed for closure
|
slotsToPrefetch = append(slotsToPrefetch, common.CopyBytes(key[:])) // Copy needed for closure
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if s.dirtyNonce != nil {
|
||||||
|
s.data.Nonce = *s.dirtyNonce
|
||||||
|
s.dirtyNonce = nil
|
||||||
|
}
|
||||||
|
if s.dirtyBalance != nil {
|
||||||
|
s.data.Balance = s.dirtyBalance
|
||||||
|
s.dirtyBalance = nil
|
||||||
|
}
|
||||||
|
if s.dirtyCodeHash != nil {
|
||||||
|
s.data.CodeHash = s.dirtyCodeHash
|
||||||
|
s.dirtyCodeHash = nil
|
||||||
|
}
|
||||||
if s.db.prefetcher != nil && prefetch && len(slotsToPrefetch) > 0 && s.data.Root != types.EmptyRootHash {
|
if s.db.prefetcher != nil && prefetch && len(slotsToPrefetch) > 0 && s.data.Root != types.EmptyRootHash {
|
||||||
s.db.prefetcher.prefetch(s.addrHash, s.data.Root, s.address, slotsToPrefetch)
|
s.db.prefetcher.prefetch(s.addrHash, s.data.Root, s.address, slotsToPrefetch)
|
||||||
}
|
}
|
||||||
@@ -302,6 +329,26 @@ func (s *stateObject) finalise(prefetch bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *stateObject) finaliseRWSet() {
|
||||||
|
for key, value := range s.dirtyStorage {
|
||||||
|
// three are some unclean dirtyStorage from previous reverted txs, it will skip finalise
|
||||||
|
// so add a new rule, if val has no change, then skip it
|
||||||
|
if value == s.GetCommittedState(key) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
s.db.RecordWrite(types.StorageStateKey(s.address, key), value)
|
||||||
|
}
|
||||||
|
if s.dirtyNonce != nil && *s.dirtyNonce != s.data.Nonce {
|
||||||
|
s.db.RecordWrite(types.AccountStateKey(s.address, types.AccountNonce), *s.dirtyNonce)
|
||||||
|
}
|
||||||
|
if s.dirtyBalance != nil && !s.dirtyBalance.Eq(s.data.Balance) {
|
||||||
|
s.db.RecordWrite(types.AccountStateKey(s.address, types.AccountBalance), s.dirtyBalance.Clone())
|
||||||
|
}
|
||||||
|
if s.dirtyCodeHash != nil && !slices.Equal(s.dirtyCodeHash, s.data.CodeHash) {
|
||||||
|
s.db.RecordWrite(types.AccountStateKey(s.address, types.AccountCodeHash), s.dirtyCodeHash)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// updateTrie is responsible for persisting cached storage changes into the
|
// updateTrie is responsible for persisting cached storage changes into the
|
||||||
// object's storage trie. In case the storage trie is not yet loaded, this
|
// object's storage trie. In case the storage trie is not yet loaded, this
|
||||||
// function will load the trie automatically. If any issues arise during the
|
// function will load the trie automatically. If any issues arise during the
|
||||||
@@ -501,13 +548,13 @@ func (s *stateObject) SubBalance(amount *uint256.Int) {
|
|||||||
func (s *stateObject) SetBalance(amount *uint256.Int) {
|
func (s *stateObject) SetBalance(amount *uint256.Int) {
|
||||||
s.db.journal.append(balanceChange{
|
s.db.journal.append(balanceChange{
|
||||||
account: &s.address,
|
account: &s.address,
|
||||||
prev: new(uint256.Int).Set(s.data.Balance),
|
prev: new(uint256.Int).Set(s.Balance()),
|
||||||
})
|
})
|
||||||
s.setBalance(amount)
|
s.setBalance(amount)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stateObject) setBalance(amount *uint256.Int) {
|
func (s *stateObject) setBalance(amount *uint256.Int) {
|
||||||
s.data.Balance = amount
|
s.dirtyBalance = amount
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stateObject) deepCopy(db *StateDB) *stateObject {
|
func (s *stateObject) deepCopy(db *StateDB) *stateObject {
|
||||||
@@ -528,6 +575,17 @@ func (s *stateObject) deepCopy(db *StateDB) *stateObject {
|
|||||||
obj.selfDestructed = s.selfDestructed
|
obj.selfDestructed = s.selfDestructed
|
||||||
obj.dirtyCode = s.dirtyCode
|
obj.dirtyCode = s.dirtyCode
|
||||||
obj.deleted = s.deleted
|
obj.deleted = s.deleted
|
||||||
|
|
||||||
|
// dirty states
|
||||||
|
if s.dirtyNonce != nil {
|
||||||
|
obj.dirtyNonce = new(uint64)
|
||||||
|
*obj.dirtyNonce = *s.dirtyNonce
|
||||||
|
}
|
||||||
|
if s.dirtyBalance != nil {
|
||||||
|
obj.dirtyBalance = s.dirtyBalance.Clone()
|
||||||
|
}
|
||||||
|
obj.dirtyCodeHash = s.dirtyCodeHash
|
||||||
|
|
||||||
return obj
|
return obj
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -585,32 +643,44 @@ func (s *stateObject) SetCode(codeHash common.Hash, code []byte) {
|
|||||||
|
|
||||||
func (s *stateObject) setCode(codeHash common.Hash, code []byte) {
|
func (s *stateObject) setCode(codeHash common.Hash, code []byte) {
|
||||||
s.code = code
|
s.code = code
|
||||||
s.data.CodeHash = codeHash[:]
|
s.dirtyCodeHash = codeHash[:]
|
||||||
s.dirtyCode = true
|
s.dirtyCode = true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stateObject) SetNonce(nonce uint64) {
|
func (s *stateObject) SetNonce(nonce uint64) {
|
||||||
s.db.journal.append(nonceChange{
|
s.db.journal.append(nonceChange{
|
||||||
account: &s.address,
|
account: &s.address,
|
||||||
prev: s.data.Nonce,
|
prev: s.Nonce(),
|
||||||
})
|
})
|
||||||
s.setNonce(nonce)
|
s.setNonce(nonce)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stateObject) setNonce(nonce uint64) {
|
func (s *stateObject) setNonce(nonce uint64) {
|
||||||
s.data.Nonce = nonce
|
s.dirtyNonce = &nonce
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stateObject) CodeHash() []byte {
|
func (s *stateObject) CodeHash() []byte {
|
||||||
return s.data.CodeHash
|
if len(s.dirtyCodeHash) > 0 {
|
||||||
|
return s.dirtyCodeHash
|
||||||
|
}
|
||||||
|
ret := s.data.CodeHash
|
||||||
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stateObject) Balance() *uint256.Int {
|
func (s *stateObject) Balance() *uint256.Int {
|
||||||
return s.data.Balance
|
if s.dirtyBalance != nil {
|
||||||
|
return s.dirtyBalance
|
||||||
|
}
|
||||||
|
ret := s.data.Balance
|
||||||
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stateObject) Nonce() uint64 {
|
func (s *stateObject) Nonce() uint64 {
|
||||||
return s.data.Nonce
|
if s.dirtyNonce != nil {
|
||||||
|
return *s.dirtyNonce
|
||||||
|
}
|
||||||
|
ret := s.data.Nonce
|
||||||
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stateObject) Root() common.Hash {
|
func (s *stateObject) Root() common.Hash {
|
||||||
|
|||||||
@@ -95,10 +95,11 @@ type StateDB struct {
|
|||||||
|
|
||||||
// This map holds 'live' objects, which will get modified while processing
|
// This map holds 'live' objects, which will get modified while processing
|
||||||
// a state transition.
|
// a state transition.
|
||||||
stateObjects map[common.Address]*stateObject
|
stateObjects map[common.Address]*stateObject
|
||||||
stateObjectsPending map[common.Address]struct{} // State objects finalized but not yet written to the trie
|
stateObjectsPending map[common.Address]struct{} // State objects finalized but not yet written to the trie
|
||||||
stateObjectsDirty map[common.Address]struct{} // State objects modified in the current execution
|
stateObjectsDirty map[common.Address]struct{} // State objects modified in the current execution
|
||||||
stateObjectsDestruct map[common.Address]*types.StateAccount // State objects destructed in the block along with its previous value
|
stateObjectsDestruct map[common.Address]*types.StateAccount // State objects destructed in the block along with its previous value
|
||||||
|
stateObjectsDestructDirty map[common.Address]*types.StateAccount
|
||||||
|
|
||||||
storagePool *StoragePool // sharedPool to store L1 originStorage of stateObjects
|
storagePool *StoragePool // sharedPool to store L1 originStorage of stateObjects
|
||||||
writeOnSharedStorage bool // Write to the shared origin storage of a stateObject while reading from the underlying storage layer.
|
writeOnSharedStorage bool // Write to the shared origin storage of a stateObject while reading from the underlying storage layer.
|
||||||
@@ -115,10 +116,16 @@ type StateDB struct {
|
|||||||
refund uint64
|
refund uint64
|
||||||
|
|
||||||
// The tx context and all occurred logs in the scope of transaction.
|
// The tx context and all occurred logs in the scope of transaction.
|
||||||
thash common.Hash
|
thash common.Hash
|
||||||
txIndex int
|
txIndex int
|
||||||
logs map[common.Hash][]*types.Log
|
txIncarnation int
|
||||||
logSize uint
|
logs map[common.Hash][]*types.Log
|
||||||
|
logSize uint
|
||||||
|
|
||||||
|
// parallel EVM related
|
||||||
|
rwSet *types.RWSet
|
||||||
|
mvStates *types.MVStates
|
||||||
|
es *types.ExeStat
|
||||||
|
|
||||||
// Preimages occurred seen by VM in the scope of block.
|
// Preimages occurred seen by VM in the scope of block.
|
||||||
preimages map[common.Hash][]byte
|
preimages map[common.Hash][]byte
|
||||||
@@ -173,23 +180,24 @@ func NewWithSharedPool(root common.Hash, db Database, snaps *snapshot.Tree) (*St
|
|||||||
// New creates a new state from a given trie.
|
// New creates a new state from a given trie.
|
||||||
func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error) {
|
func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error) {
|
||||||
sdb := &StateDB{
|
sdb := &StateDB{
|
||||||
db: db,
|
db: db,
|
||||||
originalRoot: root,
|
originalRoot: root,
|
||||||
snaps: snaps,
|
snaps: snaps,
|
||||||
accounts: make(map[common.Hash][]byte),
|
accounts: make(map[common.Hash][]byte),
|
||||||
storages: make(map[common.Hash]map[common.Hash][]byte),
|
storages: make(map[common.Hash]map[common.Hash][]byte),
|
||||||
accountsOrigin: make(map[common.Address][]byte),
|
accountsOrigin: make(map[common.Address][]byte),
|
||||||
storagesOrigin: make(map[common.Address]map[common.Hash][]byte),
|
storagesOrigin: make(map[common.Address]map[common.Hash][]byte),
|
||||||
stateObjects: make(map[common.Address]*stateObject, defaultNumOfSlots),
|
stateObjects: make(map[common.Address]*stateObject, defaultNumOfSlots),
|
||||||
stateObjectsPending: make(map[common.Address]struct{}, defaultNumOfSlots),
|
stateObjectsPending: make(map[common.Address]struct{}, defaultNumOfSlots),
|
||||||
stateObjectsDirty: make(map[common.Address]struct{}, defaultNumOfSlots),
|
stateObjectsDirty: make(map[common.Address]struct{}, defaultNumOfSlots),
|
||||||
stateObjectsDestruct: make(map[common.Address]*types.StateAccount, defaultNumOfSlots),
|
stateObjectsDestruct: make(map[common.Address]*types.StateAccount, defaultNumOfSlots),
|
||||||
logs: make(map[common.Hash][]*types.Log),
|
stateObjectsDestructDirty: make(map[common.Address]*types.StateAccount, defaultNumOfSlots),
|
||||||
preimages: make(map[common.Hash][]byte),
|
logs: make(map[common.Hash][]*types.Log),
|
||||||
journal: newJournal(),
|
preimages: make(map[common.Hash][]byte),
|
||||||
accessList: newAccessList(),
|
journal: newJournal(),
|
||||||
transientStorage: newTransientStorage(),
|
accessList: newAccessList(),
|
||||||
hasher: crypto.NewKeccakState(),
|
transientStorage: newTransientStorage(),
|
||||||
|
hasher: crypto.NewKeccakState(),
|
||||||
}
|
}
|
||||||
|
|
||||||
if sdb.snaps != nil {
|
if sdb.snaps != nil {
|
||||||
@@ -425,7 +433,10 @@ func (s *StateDB) Empty(addr common.Address) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetBalance retrieves the balance from the given address or 0 if object not found
|
// GetBalance retrieves the balance from the given address or 0 if object not found
|
||||||
func (s *StateDB) GetBalance(addr common.Address) *uint256.Int {
|
func (s *StateDB) GetBalance(addr common.Address) (ret *uint256.Int) {
|
||||||
|
defer func() {
|
||||||
|
s.RecordRead(types.AccountStateKey(addr, types.AccountBalance), ret)
|
||||||
|
}()
|
||||||
stateObject := s.getStateObject(addr)
|
stateObject := s.getStateObject(addr)
|
||||||
if stateObject != nil {
|
if stateObject != nil {
|
||||||
return stateObject.Balance()
|
return stateObject.Balance()
|
||||||
@@ -434,7 +445,10 @@ func (s *StateDB) GetBalance(addr common.Address) *uint256.Int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetNonce retrieves the nonce from the given address or 0 if object not found
|
// GetNonce retrieves the nonce from the given address or 0 if object not found
|
||||||
func (s *StateDB) GetNonce(addr common.Address) uint64 {
|
func (s *StateDB) GetNonce(addr common.Address) (ret uint64) {
|
||||||
|
defer func() {
|
||||||
|
s.RecordRead(types.AccountStateKey(addr, types.AccountNonce), ret)
|
||||||
|
}()
|
||||||
stateObject := s.getStateObject(addr)
|
stateObject := s.getStateObject(addr)
|
||||||
if stateObject != nil {
|
if stateObject != nil {
|
||||||
return stateObject.Nonce()
|
return stateObject.Nonce()
|
||||||
@@ -482,7 +496,10 @@ func (s *StateDB) GetCodeSize(addr common.Address) int {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StateDB) GetCodeHash(addr common.Address) common.Hash {
|
func (s *StateDB) GetCodeHash(addr common.Address) (ret common.Hash) {
|
||||||
|
defer func() {
|
||||||
|
s.RecordRead(types.AccountStateKey(addr, types.AccountCodeHash), ret.Bytes())
|
||||||
|
}()
|
||||||
stateObject := s.getStateObject(addr)
|
stateObject := s.getStateObject(addr)
|
||||||
if stateObject != nil {
|
if stateObject != nil {
|
||||||
return common.BytesToHash(stateObject.CodeHash())
|
return common.BytesToHash(stateObject.CodeHash())
|
||||||
@@ -491,7 +508,10 @@ func (s *StateDB) GetCodeHash(addr common.Address) common.Hash {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetState retrieves a value from the given account's storage trie.
|
// GetState retrieves a value from the given account's storage trie.
|
||||||
func (s *StateDB) GetState(addr common.Address, hash common.Hash) common.Hash {
|
func (s *StateDB) GetState(addr common.Address, hash common.Hash) (ret common.Hash) {
|
||||||
|
defer func() {
|
||||||
|
s.RecordRead(types.StorageStateKey(addr, hash), ret)
|
||||||
|
}()
|
||||||
stateObject := s.getStateObject(addr)
|
stateObject := s.getStateObject(addr)
|
||||||
if stateObject != nil {
|
if stateObject != nil {
|
||||||
return stateObject.GetState(hash)
|
return stateObject.GetState(hash)
|
||||||
@@ -500,7 +520,10 @@ func (s *StateDB) GetState(addr common.Address, hash common.Hash) common.Hash {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetCommittedState retrieves a value from the given account's committed storage trie.
|
// GetCommittedState retrieves a value from the given account's committed storage trie.
|
||||||
func (s *StateDB) GetCommittedState(addr common.Address, hash common.Hash) common.Hash {
|
func (s *StateDB) GetCommittedState(addr common.Address, hash common.Hash) (ret common.Hash) {
|
||||||
|
defer func() {
|
||||||
|
s.RecordRead(types.StorageStateKey(addr, hash), ret)
|
||||||
|
}()
|
||||||
stateObject := s.getStateObject(addr)
|
stateObject := s.getStateObject(addr)
|
||||||
if stateObject != nil {
|
if stateObject != nil {
|
||||||
return stateObject.GetCommittedState(hash)
|
return stateObject.GetCommittedState(hash)
|
||||||
@@ -529,6 +552,7 @@ func (s *StateDB) HasSelfDestructed(addr common.Address) bool {
|
|||||||
func (s *StateDB) AddBalance(addr common.Address, amount *uint256.Int) {
|
func (s *StateDB) AddBalance(addr common.Address, amount *uint256.Int) {
|
||||||
stateObject := s.getOrNewStateObject(addr)
|
stateObject := s.getOrNewStateObject(addr)
|
||||||
if stateObject != nil {
|
if stateObject != nil {
|
||||||
|
s.RecordRead(types.AccountStateKey(addr, types.AccountBalance), stateObject.Balance())
|
||||||
stateObject.AddBalance(amount)
|
stateObject.AddBalance(amount)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -537,6 +561,7 @@ func (s *StateDB) AddBalance(addr common.Address, amount *uint256.Int) {
|
|||||||
func (s *StateDB) SubBalance(addr common.Address, amount *uint256.Int) {
|
func (s *StateDB) SubBalance(addr common.Address, amount *uint256.Int) {
|
||||||
stateObject := s.getOrNewStateObject(addr)
|
stateObject := s.getOrNewStateObject(addr)
|
||||||
if stateObject != nil {
|
if stateObject != nil {
|
||||||
|
s.RecordRead(types.AccountStateKey(addr, types.AccountBalance), stateObject.Balance())
|
||||||
stateObject.SubBalance(amount)
|
stateObject.SubBalance(amount)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -581,8 +606,8 @@ func (s *StateDB) SetStorage(addr common.Address, storage map[common.Hash]common
|
|||||||
//
|
//
|
||||||
// TODO(rjl493456442) this function should only be supported by 'unwritable'
|
// TODO(rjl493456442) this function should only be supported by 'unwritable'
|
||||||
// state and all mutations made should all be discarded afterwards.
|
// state and all mutations made should all be discarded afterwards.
|
||||||
if _, ok := s.stateObjectsDestruct[addr]; !ok {
|
if _, ok := s.queryStateObjectsDestruct(addr); !ok {
|
||||||
s.stateObjectsDestruct[addr] = nil
|
s.tagStateObjectsDestruct(addr, nil)
|
||||||
}
|
}
|
||||||
stateObject := s.getOrNewStateObject(addr)
|
stateObject := s.getOrNewStateObject(addr)
|
||||||
for k, v := range storage {
|
for k, v := range storage {
|
||||||
@@ -606,7 +631,7 @@ func (s *StateDB) SelfDestruct(addr common.Address) {
|
|||||||
prevbalance: new(uint256.Int).Set(stateObject.Balance()),
|
prevbalance: new(uint256.Int).Set(stateObject.Balance()),
|
||||||
})
|
})
|
||||||
stateObject.markSelfdestructed()
|
stateObject.markSelfdestructed()
|
||||||
stateObject.data.Balance = new(uint256.Int)
|
stateObject.setBalance(new(uint256.Int))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StateDB) Selfdestruct6780(addr common.Address) {
|
func (s *StateDB) Selfdestruct6780(addr common.Address) {
|
||||||
@@ -712,6 +737,7 @@ func (s *StateDB) getStateObject(addr common.Address) *stateObject {
|
|||||||
// flag set. This is needed by the state journal to revert to the correct s-
|
// flag set. This is needed by the state journal to revert to the correct s-
|
||||||
// destructed object instead of wiping all knowledge about the state object.
|
// destructed object instead of wiping all knowledge about the state object.
|
||||||
func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject {
|
func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject {
|
||||||
|
s.RecordRead(types.AccountStateKey(addr, types.AccountSelf), struct{}{})
|
||||||
// Prefer live objects if any is available
|
// Prefer live objects if any is available
|
||||||
if obj := s.stateObjects[addr]; obj != nil {
|
if obj := s.stateObjects[addr]; obj != nil {
|
||||||
return obj
|
return obj
|
||||||
@@ -798,9 +824,9 @@ func (s *StateDB) createObject(addr common.Address) (newobj, prev *stateObject)
|
|||||||
// account and storage data should be cleared as well. Note, it must
|
// account and storage data should be cleared as well. Note, it must
|
||||||
// be done here, otherwise the destruction event of "original account"
|
// be done here, otherwise the destruction event of "original account"
|
||||||
// will be lost.
|
// will be lost.
|
||||||
_, prevdestruct := s.stateObjectsDestruct[prev.address]
|
_, prevdestruct := s.queryStateObjectsDestruct(prev.address)
|
||||||
if !prevdestruct {
|
if !prevdestruct {
|
||||||
s.stateObjectsDestruct[prev.address] = prev.origin
|
s.tagStateObjectsDestruct(prev.address, prev.origin)
|
||||||
}
|
}
|
||||||
// There may be some cached account/storage data already since IntermediateRoot
|
// There may be some cached account/storage data already since IntermediateRoot
|
||||||
// will be called for each transaction before byzantium fork which will always
|
// will be called for each transaction before byzantium fork which will always
|
||||||
@@ -841,7 +867,7 @@ func (s *StateDB) createObject(addr common.Address) (newobj, prev *stateObject)
|
|||||||
func (s *StateDB) CreateAccount(addr common.Address) {
|
func (s *StateDB) CreateAccount(addr common.Address) {
|
||||||
newObj, prev := s.createObject(addr)
|
newObj, prev := s.createObject(addr)
|
||||||
if prev != nil {
|
if prev != nil {
|
||||||
newObj.setBalance(prev.data.Balance)
|
newObj.setBalance(prev.Balance())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -869,15 +895,16 @@ func (s *StateDB) copyInternal(doPrefetch bool) *StateDB {
|
|||||||
originalRoot: s.originalRoot,
|
originalRoot: s.originalRoot,
|
||||||
// fullProcessed: s.fullProcessed,
|
// fullProcessed: s.fullProcessed,
|
||||||
// pipeCommit: s.pipeCommit,
|
// pipeCommit: s.pipeCommit,
|
||||||
accounts: make(map[common.Hash][]byte),
|
accounts: make(map[common.Hash][]byte),
|
||||||
storages: make(map[common.Hash]map[common.Hash][]byte),
|
storages: make(map[common.Hash]map[common.Hash][]byte),
|
||||||
accountsOrigin: make(map[common.Address][]byte),
|
accountsOrigin: make(map[common.Address][]byte),
|
||||||
storagesOrigin: make(map[common.Address]map[common.Hash][]byte),
|
storagesOrigin: make(map[common.Address]map[common.Hash][]byte),
|
||||||
stateObjects: make(map[common.Address]*stateObject, len(s.journal.dirties)),
|
stateObjects: make(map[common.Address]*stateObject, len(s.journal.dirties)),
|
||||||
stateObjectsPending: make(map[common.Address]struct{}, len(s.stateObjectsPending)),
|
stateObjectsPending: make(map[common.Address]struct{}, len(s.stateObjectsPending)),
|
||||||
stateObjectsDirty: make(map[common.Address]struct{}, len(s.journal.dirties)),
|
stateObjectsDirty: make(map[common.Address]struct{}, len(s.journal.dirties)),
|
||||||
stateObjectsDestruct: make(map[common.Address]*types.StateAccount, len(s.stateObjectsDestruct)),
|
stateObjectsDestruct: make(map[common.Address]*types.StateAccount, len(s.stateObjectsDestruct)),
|
||||||
storagePool: s.storagePool,
|
stateObjectsDestructDirty: make(map[common.Address]*types.StateAccount, len(s.stateObjectsDestructDirty)),
|
||||||
|
storagePool: s.storagePool,
|
||||||
// writeOnSharedStorage: s.writeOnSharedStorage,
|
// writeOnSharedStorage: s.writeOnSharedStorage,
|
||||||
refund: s.refund,
|
refund: s.refund,
|
||||||
logs: make(map[common.Hash][]*types.Log, len(s.logs)),
|
logs: make(map[common.Hash][]*types.Log, len(s.logs)),
|
||||||
@@ -929,6 +956,9 @@ func (s *StateDB) copyInternal(doPrefetch bool) *StateDB {
|
|||||||
for addr, value := range s.stateObjectsDestruct {
|
for addr, value := range s.stateObjectsDestruct {
|
||||||
state.stateObjectsDestruct[addr] = value
|
state.stateObjectsDestruct[addr] = value
|
||||||
}
|
}
|
||||||
|
for addr, value := range s.stateObjectsDestructDirty {
|
||||||
|
state.stateObjectsDestructDirty[addr] = value
|
||||||
|
}
|
||||||
// Deep copy the state changes made in the scope of block
|
// Deep copy the state changes made in the scope of block
|
||||||
// along with their original values.
|
// along with their original values.
|
||||||
state.accounts = copySet(s.accounts)
|
state.accounts = copySet(s.accounts)
|
||||||
@@ -967,6 +997,12 @@ func (s *StateDB) copyInternal(doPrefetch bool) *StateDB {
|
|||||||
// know that they need to explicitly terminate an active copy).
|
// know that they need to explicitly terminate an active copy).
|
||||||
state.prefetcher = state.prefetcher.copy()
|
state.prefetcher = state.prefetcher.copy()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parallel EVM related
|
||||||
|
if s.mvStates != nil {
|
||||||
|
state.mvStates = s.mvStates
|
||||||
|
}
|
||||||
|
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1015,6 +1051,11 @@ func (s *StateDB) WaitPipeVerification() error {
|
|||||||
// into the tries just yet. Only IntermediateRoot or Commit will do that.
|
// into the tries just yet. Only IntermediateRoot or Commit will do that.
|
||||||
func (s *StateDB) Finalise(deleteEmptyObjects bool) {
|
func (s *StateDB) Finalise(deleteEmptyObjects bool) {
|
||||||
addressesToPrefetch := make([][]byte, 0, len(s.journal.dirties))
|
addressesToPrefetch := make([][]byte, 0, len(s.journal.dirties))
|
||||||
|
// finalise stateObjectsDestruct
|
||||||
|
for addr, acc := range s.stateObjectsDestructDirty {
|
||||||
|
s.stateObjectsDestruct[addr] = acc
|
||||||
|
}
|
||||||
|
s.stateObjectsDestructDirty = make(map[common.Address]*types.StateAccount)
|
||||||
for addr := range s.journal.dirties {
|
for addr := range s.journal.dirties {
|
||||||
obj, exist := s.stateObjects[addr]
|
obj, exist := s.stateObjects[addr]
|
||||||
if !exist {
|
if !exist {
|
||||||
@@ -1249,9 +1290,10 @@ func (s *StateDB) StateIntermediateRoot() common.Hash {
|
|||||||
// SetTxContext sets the current transaction hash and index which are
|
// SetTxContext sets the current transaction hash and index which are
|
||||||
// used when the EVM emits new state logs. It should be invoked before
|
// used when the EVM emits new state logs. It should be invoked before
|
||||||
// transaction execution.
|
// transaction execution.
|
||||||
func (s *StateDB) SetTxContext(thash common.Hash, ti int) {
|
func (s *StateDB) SetTxContext(thash common.Hash, txIndex int, incarnation int) {
|
||||||
s.thash = thash
|
s.thash = thash
|
||||||
s.txIndex = ti
|
s.txIndex = txIndex
|
||||||
|
s.txIncarnation = incarnation
|
||||||
s.accessList = nil // can't delete this line now, because StateDB.Prepare is not called before processsing a system transaction
|
s.accessList = nil // can't delete this line now, because StateDB.Prepare is not called before processsing a system transaction
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1900,6 +1942,131 @@ func (s *StateDB) GetSnap() snapshot.Snapshot {
|
|||||||
return s.snap
|
return s.snap
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) BeforeTxTransition() {
|
||||||
|
log.Debug("BeforeTxTransition", "mvStates", s.mvStates == nil, "rwSet", s.rwSet == nil)
|
||||||
|
if s.mvStates == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.rwSet = types.NewRWSet(types.StateVersion{
|
||||||
|
TxIndex: s.txIndex,
|
||||||
|
TxIncarnation: s.txIncarnation,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) BeginTxStat(index int) {
|
||||||
|
if s.mvStates == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.es = types.NewExeStat(index).Begin()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) StopTxStat(usedGas uint64) {
|
||||||
|
if s.mvStates == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// record stat first
|
||||||
|
if s.es != nil {
|
||||||
|
s.es.Done().WithGas(usedGas).WithRead(len(s.rwSet.ReadSet()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) RecordRead(key types.RWKey, val interface{}) {
|
||||||
|
if s.mvStates == nil || s.rwSet == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// TODO: read from MVStates, record with ver
|
||||||
|
s.rwSet.RecordRead(key, types.StateVersion{
|
||||||
|
TxIndex: -1,
|
||||||
|
}, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) RecordWrite(key types.RWKey, val interface{}) {
|
||||||
|
if s.mvStates == nil || s.rwSet == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.rwSet.RecordWrite(key, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) ResetMVStates(txCount int) {
|
||||||
|
log.Debug("ResetMVStates", "mvStates", s.mvStates == nil, "rwSet", s.rwSet == nil)
|
||||||
|
s.mvStates = types.NewMVStates(txCount)
|
||||||
|
s.rwSet = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) FinaliseRWSet() error {
|
||||||
|
log.Debug("FinaliseRWSet", "mvStates", s.mvStates == nil, "rwSet", s.rwSet == nil)
|
||||||
|
if s.mvStates == nil || s.rwSet == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// finalise stateObjectsDestruct
|
||||||
|
for addr, acc := range s.stateObjectsDestructDirty {
|
||||||
|
s.stateObjectsDestruct[addr] = acc
|
||||||
|
s.RecordWrite(types.AccountStateKey(addr, types.AccountSuicide), struct{}{})
|
||||||
|
}
|
||||||
|
for addr := range s.journal.dirties {
|
||||||
|
obj, exist := s.stateObjects[addr]
|
||||||
|
if !exist {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if obj.selfDestructed || obj.empty() {
|
||||||
|
// We need to maintain account deletions explicitly (will remain
|
||||||
|
// set indefinitely). Note only the first occurred self-destruct
|
||||||
|
// event is tracked.
|
||||||
|
if _, ok := s.stateObjectsDestruct[obj.address]; !ok {
|
||||||
|
log.Debug("FinaliseRWSet find Destruct", "tx", s.txIndex, "addr", addr, "selfDestructed", obj.selfDestructed)
|
||||||
|
s.RecordWrite(types.AccountStateKey(addr, types.AccountSuicide), struct{}{})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// finalise account & storages
|
||||||
|
obj.finaliseRWSet()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ver := types.StateVersion{
|
||||||
|
TxIndex: s.txIndex,
|
||||||
|
TxIncarnation: s.txIncarnation,
|
||||||
|
}
|
||||||
|
if ver != s.rwSet.Version() {
|
||||||
|
return errors.New("you finalize a wrong ver of RWSet")
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug("FinaliseRWSet", "rwset", s.rwSet)
|
||||||
|
return s.mvStates.FulfillRWSet(s.rwSet, s.es)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) queryStateObjectsDestruct(addr common.Address) (*types.StateAccount, bool) {
|
||||||
|
if acc, ok := s.stateObjectsDestructDirty[addr]; ok {
|
||||||
|
return acc, ok
|
||||||
|
}
|
||||||
|
acc, ok := s.stateObjectsDestruct[addr]
|
||||||
|
return acc, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) tagStateObjectsDestruct(addr common.Address, acc *types.StateAccount) {
|
||||||
|
s.stateObjectsDestructDirty[addr] = acc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) deleteStateObjectsDestruct(addr common.Address) {
|
||||||
|
delete(s.stateObjectsDestructDirty, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) MVStates2TxDAG() (*types.TxDAG, []*types.ExeStat) {
|
||||||
|
if s.mvStates == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.mvStates.ResolveTxDAG(), s.mvStates.Stats()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) RecordSystemTxRWSet(index int) {
|
||||||
|
if s.mvStates == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.mvStates.FulfillRWSet(types.NewRWSet(types.StateVersion{
|
||||||
|
TxIndex: index,
|
||||||
|
TxIncarnation: 0,
|
||||||
|
}).WithSerialFlag(), types.NewExeStat(index).WithSerialFlag())
|
||||||
|
}
|
||||||
|
|
||||||
// copySet returns a deep-copied set.
|
// copySet returns a deep-copied set.
|
||||||
func copySet[k comparable](set map[k][]byte) map[k][]byte {
|
func copySet[k comparable](set map[k][]byte) map[k][]byte {
|
||||||
copied := make(map[k][]byte, len(set))
|
copied := make(map[k][]byte, len(set))
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return // Also invalid block, bail out
|
return // Also invalid block, bail out
|
||||||
}
|
}
|
||||||
newStatedb.SetTxContext(tx.Hash(), txIndex)
|
newStatedb.SetTxContext(tx.Hash(), txIndex, 0)
|
||||||
precacheTransaction(msg, p.config, gaspool, newStatedb, header, evm)
|
precacheTransaction(msg, p.config, gaspool, newStatedb, header, evm)
|
||||||
|
|
||||||
case <-interruptCh:
|
case <-interruptCh:
|
||||||
@@ -125,7 +125,7 @@ func (p *statePrefetcher) PrefetchMining(txs TransactionsByPriceAndNonce, header
|
|||||||
return // Also invalid block, bail out
|
return // Also invalid block, bail out
|
||||||
}
|
}
|
||||||
idx++
|
idx++
|
||||||
newStatedb.SetTxContext(tx.Hash(), idx)
|
newStatedb.SetTxContext(tx.Hash(), idx, 0)
|
||||||
precacheTransaction(msg, p.config, gaspool, newStatedb, header, evm)
|
precacheTransaction(msg, p.config, gaspool, newStatedb, header, evm)
|
||||||
gaspool = new(GasPool).AddGas(gasLimit)
|
gaspool = new(GasPool).AddGas(gasLimit)
|
||||||
case <-stopCh:
|
case <-stopCh:
|
||||||
|
|||||||
@@ -19,8 +19,6 @@ package core
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/consensus"
|
"github.com/ethereum/go-ethereum/consensus"
|
||||||
"github.com/ethereum/go-ethereum/consensus/misc"
|
"github.com/ethereum/go-ethereum/consensus/misc"
|
||||||
@@ -29,7 +27,11 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/ethereum/go-ethereum/metrics"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"math/big"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// StateProcessor is a basic Processor, which takes care of transitioning
|
// StateProcessor is a basic Processor, which takes care of transitioning
|
||||||
@@ -51,6 +53,12 @@ func NewStateProcessor(config *params.ChainConfig, bc *BlockChain, engine consen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
dagExecutionTimer = metrics.NewRegisteredTimer("dag/executiontime", nil)
|
||||||
|
dagAccountReadTimer = metrics.NewRegisteredTimer("dag/accountreadtime", nil)
|
||||||
|
dagStorageReadTimer = metrics.NewRegisteredTimer("dag/storagereadtime", nil)
|
||||||
|
)
|
||||||
|
|
||||||
// Process processes the state changes according to the Ethereum rules by running
|
// Process processes the state changes according to the Ethereum rules by running
|
||||||
// the transaction messages using the statedb and applying any rewards to both
|
// the transaction messages using the statedb and applying any rewards to both
|
||||||
// the processor (coinbase) and any included uncles.
|
// the processor (coinbase) and any included uncles.
|
||||||
@@ -59,6 +67,7 @@ func NewStateProcessor(config *params.ChainConfig, bc *BlockChain, engine consen
|
|||||||
// returns the amount of gas that was used in the process. If any of the
|
// returns the amount of gas that was used in the process. If any of the
|
||||||
// transactions failed to execute due to insufficient gas it will return an error.
|
// transactions failed to execute due to insufficient gas it will return an error.
|
||||||
func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (*state.StateDB, types.Receipts, []*types.Log, uint64, error) {
|
func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (*state.StateDB, types.Receipts, []*types.Log, uint64, error) {
|
||||||
|
|
||||||
var (
|
var (
|
||||||
usedGas = new(uint64)
|
usedGas = new(uint64)
|
||||||
header = block.Header()
|
header = block.Header()
|
||||||
@@ -99,16 +108,20 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
|
|||||||
// initialise bloom processors
|
// initialise bloom processors
|
||||||
bloomProcessors := NewAsyncReceiptBloomGenerator(txNum)
|
bloomProcessors := NewAsyncReceiptBloomGenerator(txNum)
|
||||||
statedb.MarkFullProcessed()
|
statedb.MarkFullProcessed()
|
||||||
|
statedb.ResetMVStates(len(block.Transactions()))
|
||||||
|
log.Debug("ResetMVStates", "block", block.NumberU64(), "txs", len(block.Transactions()))
|
||||||
|
|
||||||
// usually do have two tx, one for validator set contract, another for system reward contract.
|
// usually do have two tx, one for validator set contract, another for system reward contract.
|
||||||
systemTxs := make([]*types.Transaction, 0, 2)
|
systemTxs := make([]*types.Transaction, 0, 2)
|
||||||
|
start := time.Now()
|
||||||
for i, tx := range block.Transactions() {
|
for i, tx := range block.Transactions() {
|
||||||
|
statedb.BeginTxStat(i)
|
||||||
if isPoSA {
|
if isPoSA {
|
||||||
if isSystemTx, err := posa.IsSystemTransaction(tx, block.Header()); err != nil {
|
if isSystemTx, err := posa.IsSystemTransaction(tx, block.Header()); err != nil {
|
||||||
bloomProcessors.Close()
|
bloomProcessors.Close()
|
||||||
return statedb, nil, nil, 0, err
|
return statedb, nil, nil, 0, err
|
||||||
} else if isSystemTx {
|
} else if isSystemTx {
|
||||||
|
statedb.RecordSystemTxRWSet(i)
|
||||||
systemTxs = append(systemTxs, tx)
|
systemTxs = append(systemTxs, tx)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -125,7 +138,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
|
|||||||
bloomProcessors.Close()
|
bloomProcessors.Close()
|
||||||
return statedb, nil, nil, 0, err
|
return statedb, nil, nil, 0, err
|
||||||
}
|
}
|
||||||
statedb.SetTxContext(tx.Hash(), i)
|
statedb.SetTxContext(tx.Hash(), i, 0)
|
||||||
|
|
||||||
receipt, err := applyTransaction(msg, p.config, gp, statedb, blockNumber, blockHash, tx, usedGas, vmenv, bloomProcessors)
|
receipt, err := applyTransaction(msg, p.config, gp, statedb, blockNumber, blockHash, tx, usedGas, vmenv, bloomProcessors)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -134,7 +147,10 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
|
|||||||
}
|
}
|
||||||
commonTxs = append(commonTxs, tx)
|
commonTxs = append(commonTxs, tx)
|
||||||
receipts = append(receipts, receipt)
|
receipts = append(receipts, receipt)
|
||||||
|
statedb.StopTxStat(receipt.GasUsed)
|
||||||
}
|
}
|
||||||
|
eTime := time.Since(start)
|
||||||
|
// this bloomProcessors may take ~20ms
|
||||||
bloomProcessors.Close()
|
bloomProcessors.Close()
|
||||||
|
|
||||||
// Fail if Shanghai not enabled and len(withdrawals) is non-zero.
|
// Fail if Shanghai not enabled and len(withdrawals) is non-zero.
|
||||||
@@ -143,7 +159,20 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
|
|||||||
return nil, nil, nil, 0, errors.New("withdrawals before shanghai")
|
return nil, nil, nil, 0, errors.New("withdrawals before shanghai")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: temporary add time metrics
|
||||||
|
dag, exrStats := statedb.MVStates2TxDAG()
|
||||||
|
//log.Info("MVStates2TxDAG", "block", block.NumberU64(), "tx", len(block.Transactions()), "dag", dag)
|
||||||
|
fmt.Printf("MVStates2TxDAG, block: %v|%v, tx: %v, time: %v\n", block.NumberU64(), block.Hash(), len(block.Transactions()), time.Now().Format(time.DateTime))
|
||||||
|
fmt.Print(types.EvaluateTxDAGPerformance(dag, exrStats))
|
||||||
|
fmt.Printf("block: %v, execution: %.2fms, accountRead: %.2fms, storageRead: %.2fms\n",
|
||||||
|
block.NumberU64(), float64(eTime.Microseconds())/1000, float64((statedb.SnapshotAccountReads+statedb.AccountReads).Microseconds())/1000,
|
||||||
|
float64((statedb.SnapshotStorageReads+statedb.StorageReads).Microseconds())/1000)
|
||||||
|
|
||||||
|
dagExecutionTimer.Update(eTime)
|
||||||
|
dagAccountReadTimer.Update(statedb.SnapshotAccountReads + statedb.AccountReads)
|
||||||
|
dagStorageReadTimer.Update(statedb.SnapshotStorageReads + statedb.StorageReads)
|
||||||
// Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
|
// Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
|
||||||
|
// TODO: system txs must execute at last
|
||||||
err := p.engine.Finalize(p.bc, header, statedb, &commonTxs, block.Uncles(), withdrawals, &receipts, &systemTxs, usedGas)
|
err := p.engine.Finalize(p.bc, header, statedb, &commonTxs, block.Uncles(), withdrawals, &receipts, &systemTxs, usedGas)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return statedb, receipts, allLogs, *usedGas, err
|
return statedb, receipts, allLogs, *usedGas, err
|
||||||
|
|||||||
@@ -367,6 +367,8 @@ func (st *StateTransition) preCheck() error {
|
|||||||
// However if any consensus issue encountered, return the error directly with
|
// However if any consensus issue encountered, return the error directly with
|
||||||
// nil evm execution result.
|
// nil evm execution result.
|
||||||
func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
|
func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
|
||||||
|
// start record rw set in here
|
||||||
|
st.state.BeforeTxTransition()
|
||||||
// First check this message satisfies all consensus rules before
|
// First check this message satisfies all consensus rules before
|
||||||
// applying the message. The rules include these clauses
|
// applying the message. The rules include these clauses
|
||||||
//
|
//
|
||||||
@@ -446,6 +448,10 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
|
|||||||
ret, st.gasRemaining, vmerr = st.evm.Call(sender, st.to(), msg.Data, st.gasRemaining, value)
|
ret, st.gasRemaining, vmerr = st.evm.Call(sender, st.to(), msg.Data, st.gasRemaining, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// stop record rw set in here
|
||||||
|
if err := st.state.FinaliseRWSet(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
var gasRefund uint64
|
var gasRefund uint64
|
||||||
if !rules.IsLondon {
|
if !rules.IsLondon {
|
||||||
// Before EIP-3529: refunds were capped to gasUsed / 2
|
// Before EIP-3529: refunds were capped to gasUsed / 2
|
||||||
|
|||||||
@@ -212,7 +212,7 @@ func TestTxIndexer(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
frdir := t.TempDir()
|
frdir := t.TempDir()
|
||||||
db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), frdir, "", false, false, false, false)
|
db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), frdir, "", false, false, false, false, false)
|
||||||
rawdb.WriteAncientBlocks(db, append([]*types.Block{gspec.ToBlock()}, blocks...), append([]types.Receipts{{}}, receipts...), big.NewInt(0))
|
rawdb.WriteAncientBlocks(db, append([]*types.Block{gspec.ToBlock()}, blocks...), append([]types.Receipts{{}}, receipts...), big.NewInt(0))
|
||||||
|
|
||||||
// Index the initial blocks from ancient store
|
// Index the initial blocks from ancient store
|
||||||
|
|||||||
@@ -204,6 +204,8 @@ type Body struct {
|
|||||||
Transactions []*Transaction
|
Transactions []*Transaction
|
||||||
Uncles []*Header
|
Uncles []*Header
|
||||||
Withdrawals []*Withdrawal `rlp:"optional"`
|
Withdrawals []*Withdrawal `rlp:"optional"`
|
||||||
|
// TODO: add TxDAG in block body
|
||||||
|
//TxDAG *TxDAG `rlp:"optional"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Block represents an Ethereum block.
|
// Block represents an Ethereum block.
|
||||||
|
|||||||
698
core/types/dag.go
Normal file
698
core/types/dag.go
Normal file
@@ -0,0 +1,698 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/ethereum/go-ethereum/metrics"
|
||||||
|
"github.com/holiman/uint256"
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TxDep store the current tx dependency relation with other txs
|
||||||
|
type TxDep struct {
|
||||||
|
// It describes the Relation with below txs
|
||||||
|
// 0: this tx depends on below txs
|
||||||
|
// 1: this transaction does not depend on below txs, all other previous txs depend on
|
||||||
|
Relation uint8
|
||||||
|
TxIndexes []int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *TxDep) AppendDep(i int) {
|
||||||
|
d.TxIndexes = append(d.TxIndexes, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *TxDep) Exist(i int) bool {
|
||||||
|
for _, index := range d.TxIndexes {
|
||||||
|
if index == i {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// TxDAG indicate how to use the dependency of txs
|
||||||
|
type TxDAG struct {
|
||||||
|
// The TxDAG type
|
||||||
|
// 0: delay the distribution of GasFee, it will ignore all gas fee distribution when tx execute
|
||||||
|
// 1: timely distribution of transaction fees, it will keep partial serial execution when tx cannot delay the distribution
|
||||||
|
Type uint8
|
||||||
|
// Tx Dependency List, the list index is equal to TxIndex
|
||||||
|
TxDeps []TxDep
|
||||||
|
// It indicates the scheduling priority of the transactions
|
||||||
|
SchedulePriority []int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTxDAG(txLen int) *TxDAG {
|
||||||
|
return &TxDAG{
|
||||||
|
Type: 0,
|
||||||
|
TxDeps: make([]TxDep, txLen),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *TxDAG) String() string {
|
||||||
|
builder := strings.Builder{}
|
||||||
|
exePaths := d.travelExecutionPaths()
|
||||||
|
for _, path := range exePaths {
|
||||||
|
builder.WriteString(fmt.Sprintf("%v\n", path))
|
||||||
|
}
|
||||||
|
return builder.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *TxDAG) travelExecutionPaths() [][]int {
|
||||||
|
// regenerate TxDAG
|
||||||
|
nd := NewTxDAG(len(d.TxDeps))
|
||||||
|
for i, txDep := range d.TxDeps {
|
||||||
|
nd.TxDeps[i].Relation = 0
|
||||||
|
if txDep.Relation == 0 {
|
||||||
|
nd.TxDeps[i] = txDep
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// recover to relation 0
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if !txDep.Exist(j) {
|
||||||
|
nd.TxDeps[i].AppendDep(j)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exePaths := make([][]int, 0)
|
||||||
|
|
||||||
|
// travel tx deps with BFS
|
||||||
|
for i := 0; i < len(nd.TxDeps); i++ {
|
||||||
|
exePaths = append(exePaths, travelTargetPath(nd.TxDeps, i))
|
||||||
|
}
|
||||||
|
return exePaths
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
longestTimeTimer = metrics.NewRegisteredTimer("dag/longesttime", nil)
|
||||||
|
longestGasTimer = metrics.NewRegisteredTimer("dag/longestgas", nil)
|
||||||
|
serialTimeTimer = metrics.NewRegisteredTimer("dag/serialtime", nil)
|
||||||
|
totalTxMeter = metrics.NewRegisteredMeter("dag/txcnt", nil)
|
||||||
|
totalNoDepMeter = metrics.NewRegisteredMeter("dag/nodepcntcnt", nil)
|
||||||
|
total2DepMeter = metrics.NewRegisteredMeter("dag/2depcntcnt", nil)
|
||||||
|
total4DepMeter = metrics.NewRegisteredMeter("dag/4depcntcnt", nil)
|
||||||
|
total8DepMeter = metrics.NewRegisteredMeter("dag/8depcntcnt", nil)
|
||||||
|
total16DepMeter = metrics.NewRegisteredMeter("dag/16depcntcnt", nil)
|
||||||
|
total32DepMeter = metrics.NewRegisteredMeter("dag/32depcntcnt", nil)
|
||||||
|
)
|
||||||
|
|
||||||
|
func EvaluateTxDAGPerformance(dag *TxDAG, stats []*ExeStat) string {
|
||||||
|
if len(stats) != len(dag.TxDeps) || len(dag.TxDeps) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
sb := strings.Builder{}
|
||||||
|
sb.WriteString("TxDAG:\n")
|
||||||
|
for i, dep := range dag.TxDeps {
|
||||||
|
if stats[i].mustSerialFlag {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
sb.WriteString(fmt.Sprintf("%v: %v\n", i, dep.TxIndexes))
|
||||||
|
}
|
||||||
|
sb.WriteString("Parallel Execution Path:\n")
|
||||||
|
paths := dag.travelExecutionPaths()
|
||||||
|
// Attention: this is based on best schedule, it will reduce a lot by executing previous txs in parallel
|
||||||
|
// It assumes that there is no parallel thread limit
|
||||||
|
var (
|
||||||
|
maxGasIndex int
|
||||||
|
maxGas uint64
|
||||||
|
maxTimeIndex int
|
||||||
|
maxTime time.Duration
|
||||||
|
txTimes = make([]time.Duration, len(dag.TxDeps))
|
||||||
|
txGases = make([]uint64, len(dag.TxDeps))
|
||||||
|
txReads = make([]int, len(dag.TxDeps))
|
||||||
|
noDepdencyCount int
|
||||||
|
)
|
||||||
|
|
||||||
|
totalTxMeter.Mark(int64(len(dag.TxDeps)))
|
||||||
|
for i, path := range paths {
|
||||||
|
if stats[i].mustSerialFlag {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if len(path) <= 1 {
|
||||||
|
noDepdencyCount++
|
||||||
|
totalNoDepMeter.Mark(1)
|
||||||
|
}
|
||||||
|
if len(path) <= 3 {
|
||||||
|
total2DepMeter.Mark(1)
|
||||||
|
}
|
||||||
|
if len(path) <= 5 {
|
||||||
|
total4DepMeter.Mark(1)
|
||||||
|
}
|
||||||
|
if len(path) <= 9 {
|
||||||
|
total8DepMeter.Mark(1)
|
||||||
|
}
|
||||||
|
if len(path) <= 17 {
|
||||||
|
total16DepMeter.Mark(1)
|
||||||
|
}
|
||||||
|
if len(path) <= 33 {
|
||||||
|
total32DepMeter.Mark(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// find the biggest cost time from dependency txs
|
||||||
|
for j := 0; j < len(path)-1; j++ {
|
||||||
|
prev := path[j]
|
||||||
|
if txTimes[prev] > txTimes[i] {
|
||||||
|
txTimes[i] = txTimes[prev]
|
||||||
|
}
|
||||||
|
if txGases[prev] > txGases[i] {
|
||||||
|
txGases[i] = txGases[prev]
|
||||||
|
}
|
||||||
|
if txReads[prev] > txReads[i] {
|
||||||
|
txReads[i] = txReads[prev]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
txTimes[i] += stats[i].costTime
|
||||||
|
txGases[i] += stats[i].usedGas
|
||||||
|
txReads[i] += stats[i].readCount
|
||||||
|
|
||||||
|
//sb.WriteString(fmt.Sprintf("Tx%v, %.2fms|%vgas|%vreads\npath: %v\n", i, float64(txTimes[i].Microseconds())/1000, txGases[i], txReads[i], path))
|
||||||
|
sb.WriteString(fmt.Sprintf("%v: %v\n", i, path))
|
||||||
|
// try to find max gas
|
||||||
|
if txGases[i] > maxGas {
|
||||||
|
maxGas = txGases[i]
|
||||||
|
maxGasIndex = i
|
||||||
|
}
|
||||||
|
if txTimes[i] > maxTime {
|
||||||
|
maxTime = txTimes[i]
|
||||||
|
maxTimeIndex = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.WriteString(fmt.Sprintf("LargestGasPath: %.2fms|%vgas|%vreads\npath: %v\n", float64(txTimes[maxGasIndex].Microseconds())/1000, txGases[maxGasIndex], txReads[maxGasIndex], paths[maxGasIndex]))
|
||||||
|
sb.WriteString(fmt.Sprintf("LongestTimePath: %.2fms|%vgas|%vreads\npath: %v\n", float64(txTimes[maxTimeIndex].Microseconds())/1000, txGases[maxTimeIndex], txReads[maxTimeIndex], paths[maxTimeIndex]))
|
||||||
|
longestTimeTimer.Update(txTimes[maxTimeIndex])
|
||||||
|
longestGasTimer.Update(txTimes[maxGasIndex])
|
||||||
|
// serial path
|
||||||
|
var (
|
||||||
|
sTime time.Duration
|
||||||
|
sGas uint64
|
||||||
|
sRead int
|
||||||
|
sPath []int
|
||||||
|
)
|
||||||
|
for i, stat := range stats {
|
||||||
|
if stat.mustSerialFlag {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
sPath = append(sPath, i)
|
||||||
|
sTime += stat.costTime
|
||||||
|
sGas += stat.usedGas
|
||||||
|
sRead += stat.readCount
|
||||||
|
}
|
||||||
|
if sTime == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
sb.WriteString(fmt.Sprintf("SerialPath: %.2fms|%vgas|%vreads\npath: %v\n", float64(sTime.Microseconds())/1000, sGas, sRead, sPath))
|
||||||
|
maxParaTime := txTimes[maxTimeIndex]
|
||||||
|
sb.WriteString(fmt.Sprintf("Estimated saving: %.2fms, %.2f%%, %.2fX, noDepCnt: %v|%.2f%%\n",
|
||||||
|
float64((sTime-maxParaTime).Microseconds())/1000, float64(sTime-maxParaTime)/float64(sTime)*100,
|
||||||
|
float64(sTime)/float64(maxParaTime), noDepdencyCount, float64(noDepdencyCount)/float64(len(dag.TxDeps))*100))
|
||||||
|
serialTimeTimer.Update(sTime)
|
||||||
|
return sb.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func travelTargetPath(deps []TxDep, from int) []int {
|
||||||
|
q := make([]int, 0, len(deps))
|
||||||
|
path := make([]int, 0, len(deps))
|
||||||
|
|
||||||
|
q = append(q, from)
|
||||||
|
path = append(path, from)
|
||||||
|
for len(q) > 0 {
|
||||||
|
t := make([]int, 0, len(deps))
|
||||||
|
for _, i := range q {
|
||||||
|
for _, dep := range deps[i].TxIndexes {
|
||||||
|
if !slices.Contains(path, dep) {
|
||||||
|
path = append(path, dep)
|
||||||
|
t = append(t, dep)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
q = t
|
||||||
|
}
|
||||||
|
sort.Ints(path)
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
type ValidatorExtraItem struct {
|
||||||
|
ValidatorAddress common.Address
|
||||||
|
VoteAddress BLSPublicKey
|
||||||
|
}
|
||||||
|
|
||||||
|
type HeaderCustomExtra struct {
|
||||||
|
ValidatorSet ValidatorExtraItem
|
||||||
|
TxDAG TxDAG
|
||||||
|
}
|
||||||
|
|
||||||
|
// StateVersion record specific TxIndex & TxIncarnation
|
||||||
|
// if TxIndex equals to -1, it means the state read from DB.
|
||||||
|
type StateVersion struct {
|
||||||
|
TxIndex int
|
||||||
|
TxIncarnation int
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadRecord keep read value & its version
|
||||||
|
type ReadRecord struct {
|
||||||
|
StateVersion
|
||||||
|
Val interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteRecord keep latest state value & change count
|
||||||
|
type WriteRecord struct {
|
||||||
|
Val interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RWSet record all read & write set in txs
|
||||||
|
// Attention: this is not a concurrent safety structure
|
||||||
|
type RWSet struct {
|
||||||
|
ver StateVersion
|
||||||
|
readSet map[RWKey]*ReadRecord
|
||||||
|
writeSet map[RWKey]*WriteRecord
|
||||||
|
|
||||||
|
// some flags
|
||||||
|
mustSerial bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRWSet(ver StateVersion) *RWSet {
|
||||||
|
return &RWSet{
|
||||||
|
ver: ver,
|
||||||
|
readSet: make(map[RWKey]*ReadRecord),
|
||||||
|
writeSet: make(map[RWKey]*WriteRecord),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RWSet) RecordRead(key RWKey, ver StateVersion, val interface{}) {
|
||||||
|
// only record the first read version
|
||||||
|
if _, exist := s.readSet[key]; exist {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.readSet[key] = &ReadRecord{
|
||||||
|
StateVersion: ver,
|
||||||
|
Val: val,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RWSet) RecordWrite(key RWKey, val interface{}) {
|
||||||
|
wr, exist := s.writeSet[key]
|
||||||
|
if !exist {
|
||||||
|
s.writeSet[key] = &WriteRecord{
|
||||||
|
Val: val,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
wr.Val = val
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RWSet) Version() StateVersion {
|
||||||
|
return s.ver
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RWSet) ReadSet() map[RWKey]*ReadRecord {
|
||||||
|
return s.readSet
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RWSet) WriteSet() map[RWKey]*WriteRecord {
|
||||||
|
return s.writeSet
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RWSet) WithSerialFlag() *RWSet {
|
||||||
|
s.mustSerial = true
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RWSet) String() string {
|
||||||
|
builder := strings.Builder{}
|
||||||
|
builder.WriteString(fmt.Sprintf("tx: %v, inc: %v\nreadSet: [", s.ver.TxIndex, s.ver.TxIncarnation))
|
||||||
|
i := 0
|
||||||
|
for key, _ := range s.readSet {
|
||||||
|
if i > 0 {
|
||||||
|
builder.WriteString(fmt.Sprintf(", %v", key.String()))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
builder.WriteString(fmt.Sprintf("%v", key.String()))
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
builder.WriteString("]\nwriteSet: [")
|
||||||
|
i = 0
|
||||||
|
for key, _ := range s.writeSet {
|
||||||
|
if i > 0 {
|
||||||
|
builder.WriteString(fmt.Sprintf(", %v", key.String()))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
builder.WriteString(fmt.Sprintf("%v", key.String()))
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
builder.WriteString("]\n")
|
||||||
|
return builder.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
AccountStatePrefix = 'a'
|
||||||
|
StorageStatePrefix = 's'
|
||||||
|
)
|
||||||
|
|
||||||
|
type RWKey [1 + common.AddressLength + common.HashLength]byte
|
||||||
|
|
||||||
|
type AccountState byte
|
||||||
|
|
||||||
|
const (
|
||||||
|
AccountSelf AccountState = iota
|
||||||
|
AccountNonce
|
||||||
|
AccountBalance
|
||||||
|
AccountCodeHash
|
||||||
|
AccountSuicide
|
||||||
|
)
|
||||||
|
|
||||||
|
func AccountStateKey(account common.Address, state AccountState) RWKey {
|
||||||
|
var key RWKey
|
||||||
|
key[0] = AccountStatePrefix
|
||||||
|
copy(key[1:], account.Bytes())
|
||||||
|
key[1+common.AddressLength] = byte(state)
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
|
||||||
|
func StorageStateKey(account common.Address, state common.Hash) RWKey {
|
||||||
|
var key RWKey
|
||||||
|
key[0] = StorageStatePrefix
|
||||||
|
copy(key[1:], account.Bytes())
|
||||||
|
copy(key[1+common.AddressLength:], state.Bytes())
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
|
||||||
|
func (key *RWKey) IsAccountState() (bool, AccountState) {
|
||||||
|
return AccountStatePrefix == key[0], AccountState(key[1+common.AddressLength])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (key *RWKey) IsAccountSelf() bool {
|
||||||
|
ok, s := key.IsAccountState()
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return s == AccountSelf
|
||||||
|
}
|
||||||
|
|
||||||
|
func (key *RWKey) IsAccountSuicide() bool {
|
||||||
|
ok, s := key.IsAccountState()
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return s == AccountSuicide
|
||||||
|
}
|
||||||
|
|
||||||
|
func (key *RWKey) ToAccountSelf() RWKey {
|
||||||
|
return AccountStateKey(key.Addr(), AccountSelf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (key *RWKey) IsStorageState() bool {
|
||||||
|
return StorageStatePrefix == key[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (key *RWKey) String() string {
|
||||||
|
return hex.EncodeToString(key[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (key *RWKey) Addr() common.Address {
|
||||||
|
return common.BytesToAddress(key[1 : 1+common.AddressLength])
|
||||||
|
}
|
||||||
|
|
||||||
|
type PendingWrite struct {
|
||||||
|
Ver StateVersion
|
||||||
|
Val interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPendingWrite(ver StateVersion, wr *WriteRecord) *PendingWrite {
|
||||||
|
return &PendingWrite{
|
||||||
|
Ver: ver,
|
||||||
|
Val: wr.Val,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *PendingWrite) TxIndex() int {
|
||||||
|
return w.Ver.TxIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *PendingWrite) TxIncarnation() int {
|
||||||
|
return w.Ver.TxIncarnation
|
||||||
|
}
|
||||||
|
|
||||||
|
type PendingWrites struct {
|
||||||
|
list []*PendingWrite
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPendingWrites() *PendingWrites {
|
||||||
|
return &PendingWrites{
|
||||||
|
list: make([]*PendingWrite, 0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *PendingWrites) Append(pw *PendingWrite) {
|
||||||
|
if i, found := w.SearchTxIndex(pw.TxIndex()); found {
|
||||||
|
w.list[i] = pw
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.list = append(w.list, pw)
|
||||||
|
for i := len(w.list) - 1; i > 0; i-- {
|
||||||
|
if w.list[i].TxIndex() > w.list[i-1].TxIndex() {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
w.list[i-1], w.list[i] = w.list[i], w.list[i-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *PendingWrites) SearchTxIndex(txIndex int) (int, bool) {
|
||||||
|
n := len(w.list)
|
||||||
|
i, j := 0, n
|
||||||
|
for i < j {
|
||||||
|
h := int(uint(i+j) >> 1)
|
||||||
|
// i ≤ h < j
|
||||||
|
if w.list[h].TxIndex() < txIndex {
|
||||||
|
i = h + 1
|
||||||
|
} else {
|
||||||
|
j = h
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i, i < n && w.list[i].TxIndex() == txIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *PendingWrites) FindLastWrite(txIndex int) *PendingWrite {
|
||||||
|
var i, _ = w.SearchTxIndex(txIndex)
|
||||||
|
for j := i - 1; j >= 0; j-- {
|
||||||
|
if w.list[j].TxIndex() < txIndex {
|
||||||
|
return w.list[j]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type MVStates struct {
|
||||||
|
rwSets []*RWSet
|
||||||
|
stats []*ExeStat
|
||||||
|
pendingWriteSet map[RWKey]*PendingWrites
|
||||||
|
lock sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMVStates(txCount int) *MVStates {
|
||||||
|
return &MVStates{
|
||||||
|
rwSets: make([]*RWSet, txCount),
|
||||||
|
stats: make([]*ExeStat, txCount),
|
||||||
|
pendingWriteSet: make(map[RWKey]*PendingWrites, txCount*8),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MVStates) RWSets() []*RWSet {
|
||||||
|
s.lock.RLock()
|
||||||
|
defer s.lock.RUnlock()
|
||||||
|
return s.rwSets
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MVStates) Stats() []*ExeStat {
|
||||||
|
s.lock.RLock()
|
||||||
|
defer s.lock.RUnlock()
|
||||||
|
return s.stats
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MVStates) RWSet(index int) *RWSet {
|
||||||
|
s.lock.RLock()
|
||||||
|
defer s.lock.RUnlock()
|
||||||
|
return s.rwSets[index]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MVStates) FulfillRWSet(rwSet *RWSet, stat *ExeStat) error {
|
||||||
|
s.lock.Lock()
|
||||||
|
defer s.lock.Unlock()
|
||||||
|
index := rwSet.ver.TxIndex
|
||||||
|
if index >= len(s.rwSets) {
|
||||||
|
return errors.New("refill out of bound")
|
||||||
|
}
|
||||||
|
if s := s.rwSets[index]; s != nil {
|
||||||
|
return errors.New("refill a exist RWSet")
|
||||||
|
}
|
||||||
|
if stat != nil {
|
||||||
|
if stat.txIndex != index {
|
||||||
|
return errors.New("wrong execution stat")
|
||||||
|
}
|
||||||
|
s.stats[index] = stat
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range rwSet.writeSet {
|
||||||
|
// ignore no changed write record
|
||||||
|
checkRWSetInconsistent(index, k, rwSet.readSet, rwSet.writeSet)
|
||||||
|
// this will be handled by state object
|
||||||
|
//if rwSet.readSet[k] != nil && isEqualRWVal(k, rwSet.readSet[k].Val, v.Val) {
|
||||||
|
// delete(rwSet.writeSet, k)
|
||||||
|
// continue
|
||||||
|
//}
|
||||||
|
if _, exist := s.pendingWriteSet[k]; !exist {
|
||||||
|
s.pendingWriteSet[k] = NewPendingWrites()
|
||||||
|
}
|
||||||
|
s.pendingWriteSet[k].Append(NewPendingWrite(rwSet.ver, v))
|
||||||
|
}
|
||||||
|
s.rwSets[index] = rwSet
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkRWSetInconsistent(index int, k RWKey, readSet map[RWKey]*ReadRecord, writeSet map[RWKey]*WriteRecord) bool {
|
||||||
|
var (
|
||||||
|
readOk bool
|
||||||
|
writeOk bool
|
||||||
|
r *WriteRecord
|
||||||
|
)
|
||||||
|
|
||||||
|
if k.IsAccountSuicide() {
|
||||||
|
_, readOk = readSet[k.ToAccountSelf()]
|
||||||
|
} else {
|
||||||
|
_, readOk = readSet[k]
|
||||||
|
}
|
||||||
|
|
||||||
|
r, writeOk = writeSet[k]
|
||||||
|
if readOk != writeOk {
|
||||||
|
// check if it's correct? read nil, write non-nil
|
||||||
|
log.Info("checkRWSetInconsistent find inconsistent", "tx", index, "k", k.String(), "read", readOk, "write", writeOk, "val", r.Val)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func isEqualRWVal(key RWKey, src interface{}, compared interface{}) bool {
|
||||||
|
if ok, state := key.IsAccountState(); ok {
|
||||||
|
switch state {
|
||||||
|
case AccountBalance:
|
||||||
|
if src != nil && compared != nil {
|
||||||
|
return equalUint256(src.(*uint256.Int), compared.(*uint256.Int))
|
||||||
|
}
|
||||||
|
return src == compared
|
||||||
|
case AccountNonce:
|
||||||
|
return src.(uint64) == compared.(uint64)
|
||||||
|
case AccountCodeHash:
|
||||||
|
if src != nil && compared != nil {
|
||||||
|
return slices.Equal(src.([]byte), compared.([]byte))
|
||||||
|
}
|
||||||
|
return src == compared
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if src != nil && compared != nil {
|
||||||
|
return src.(common.Hash) == compared.(common.Hash)
|
||||||
|
}
|
||||||
|
return src == compared
|
||||||
|
}
|
||||||
|
|
||||||
|
func equalUint256(s, c *uint256.Int) bool {
|
||||||
|
if s != nil && c != nil {
|
||||||
|
return s.Eq(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
return s == c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MVStates) ResolveTxDAG() *TxDAG {
|
||||||
|
rwSets := s.RWSets()
|
||||||
|
txDAG := NewTxDAG(len(rwSets))
|
||||||
|
for i := len(rwSets) - 1; i >= 0; i-- {
|
||||||
|
txDAG.TxDeps[i].TxIndexes = []int{}
|
||||||
|
if rwSets[i].mustSerial {
|
||||||
|
txDAG.TxDeps[i].Relation = 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
readSet := rwSets[i].ReadSet()
|
||||||
|
// TODO: check if there are RW with system address
|
||||||
|
// check if there has written op before i
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if checkDependency(rwSets[j].writeSet, readSet) {
|
||||||
|
txDAG.TxDeps[i].AppendDep(j)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return txDAG
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkDependency(writeSet map[RWKey]*WriteRecord, readSet map[RWKey]*ReadRecord) bool {
|
||||||
|
// check tx dependency, only check key, skip version
|
||||||
|
for k, _ := range writeSet {
|
||||||
|
// check suicide, add read address flag, it only for check suicide quickly, and cannot for other scenarios.
|
||||||
|
if k.IsAccountSuicide() {
|
||||||
|
if _, ok := readSet[k.ToAccountSelf()]; ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, ok := readSet[k]; ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExeStat struct {
|
||||||
|
txIndex int
|
||||||
|
usedGas uint64
|
||||||
|
readCount int
|
||||||
|
startTime time.Time
|
||||||
|
costTime time.Duration
|
||||||
|
// TODO: consider system tx, gas fee issues, may need to use different flag
|
||||||
|
mustSerialFlag bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewExeStat(txIndex int) *ExeStat {
|
||||||
|
return &ExeStat{
|
||||||
|
txIndex: txIndex,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ExeStat) Begin() *ExeStat {
|
||||||
|
s.startTime = time.Now()
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ExeStat) Done() *ExeStat {
|
||||||
|
s.costTime = time.Since(s.startTime)
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ExeStat) WithSerialFlag() *ExeStat {
|
||||||
|
s.mustSerialFlag = true
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ExeStat) WithGas(gas uint64) *ExeStat {
|
||||||
|
s.usedGas = gas
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ExeStat) WithRead(rc int) *ExeStat {
|
||||||
|
s.readCount = rc
|
||||||
|
return s
|
||||||
|
}
|
||||||
239
core/types/dag_test.go
Normal file
239
core/types/dag_test.go
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/holiman/uint256"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
mockAddr = common.HexToAddress("0x482bA86399ab6Dcbe54071f8d22258688B4509b1")
|
||||||
|
mockHash = common.HexToHash("0xdc13f8d7bdb8ec4de02cd4a50a1aa2ab73ec8814e0cdb550341623be3dd8ab7a")
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTxDAG(t *testing.T) {
|
||||||
|
dag := mockSimpleDAG()
|
||||||
|
t.Log(dag.String())
|
||||||
|
dag = mockSystemTxDAG()
|
||||||
|
t.Log(dag.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEvaluateTxDAG(t *testing.T) {
|
||||||
|
dag := mockSystemTxDAG()
|
||||||
|
stats := make([]*ExeStat, len(dag.TxDeps))
|
||||||
|
for i, dep := range dag.TxDeps {
|
||||||
|
stats[i] = NewExeStat(i).WithGas(uint64(i)).WithRead(i)
|
||||||
|
stats[i].costTime = int64(i)
|
||||||
|
if dep.Relation == 1 {
|
||||||
|
stats[i].WithSerialFlag()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.Log(EvaluateTxDAGPerformance(dag, stats))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSimpleMVStates2TxDAG(t *testing.T) {
|
||||||
|
ms := NewMVStates(10)
|
||||||
|
|
||||||
|
ms.rwSets[0] = mockRWSet(0, []string{"0x00"}, []string{"0x00"})
|
||||||
|
ms.rwSets[1] = mockRWSet(1, []string{"0x01"}, []string{"0x01"})
|
||||||
|
ms.rwSets[2] = mockRWSet(2, []string{"0x02"}, []string{"0x02"})
|
||||||
|
ms.rwSets[3] = mockRWSet(3, []string{"0x00", "0x03"}, []string{"0x03"})
|
||||||
|
ms.rwSets[4] = mockRWSet(4, []string{"0x00", "0x04"}, []string{"0x04"})
|
||||||
|
ms.rwSets[5] = mockRWSet(5, []string{"0x01", "0x02", "0x05"}, []string{"0x05"})
|
||||||
|
ms.rwSets[6] = mockRWSet(6, []string{"0x02", "0x05", "0x06"}, []string{"0x06"})
|
||||||
|
ms.rwSets[7] = mockRWSet(7, []string{"0x06", "0x07"}, []string{"0x07"})
|
||||||
|
ms.rwSets[8] = mockRWSet(8, []string{"0x08"}, []string{"0x08"})
|
||||||
|
ms.rwSets[9] = mockRWSet(9, []string{"0x08", "0x09"}, []string{"0x09"})
|
||||||
|
|
||||||
|
dag := ms.ResolveTxDAG()
|
||||||
|
require.Equal(t, mockSimpleDAG(), dag)
|
||||||
|
t.Log(dag.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSystemTxMVStates2TxDAG(t *testing.T) {
|
||||||
|
ms := NewMVStates(12)
|
||||||
|
|
||||||
|
ms.rwSets[0] = mockRWSet(0, []string{"0x00"}, []string{"0x00"})
|
||||||
|
ms.rwSets[1] = mockRWSet(1, []string{"0x01"}, []string{"0x01"})
|
||||||
|
ms.rwSets[2] = mockRWSet(2, []string{"0x02"}, []string{"0x02"})
|
||||||
|
ms.rwSets[3] = mockRWSet(3, []string{"0x00", "0x03"}, []string{"0x03"})
|
||||||
|
ms.rwSets[4] = mockRWSet(4, []string{"0x00", "0x04"}, []string{"0x04"})
|
||||||
|
ms.rwSets[5] = mockRWSet(5, []string{"0x01", "0x02", "0x05"}, []string{"0x05"})
|
||||||
|
ms.rwSets[6] = mockRWSet(6, []string{"0x02", "0x05", "0x06"}, []string{"0x06"})
|
||||||
|
ms.rwSets[7] = mockRWSet(7, []string{"0x06", "0x07"}, []string{"0x07"})
|
||||||
|
ms.rwSets[8] = mockRWSet(8, []string{"0x08"}, []string{"0x08"})
|
||||||
|
ms.rwSets[9] = mockRWSet(9, []string{"0x08", "0x09"}, []string{"0x09"})
|
||||||
|
ms.rwSets[10] = mockRWSet(10, []string{"0x10"}, []string{"0x10"}).WithSerialFlag()
|
||||||
|
ms.rwSets[11] = mockRWSet(11, []string{"0x11"}, []string{"0x11"}).WithSerialFlag()
|
||||||
|
|
||||||
|
dag := ms.ResolveTxDAG()
|
||||||
|
require.Equal(t, mockSystemTxDAG(), dag)
|
||||||
|
t.Log(dag.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsEqualRWVal(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
key RWKey
|
||||||
|
src interface{}
|
||||||
|
compared interface{}
|
||||||
|
isEqual bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
key: AccountStateKey(mockAddr, AccountNonce),
|
||||||
|
src: uint64(0),
|
||||||
|
compared: uint64(0),
|
||||||
|
isEqual: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: AccountStateKey(mockAddr, AccountNonce),
|
||||||
|
src: uint64(0),
|
||||||
|
compared: uint64(1),
|
||||||
|
isEqual: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: AccountStateKey(mockAddr, AccountBalance),
|
||||||
|
src: new(uint256.Int).SetUint64(1),
|
||||||
|
compared: new(uint256.Int).SetUint64(1),
|
||||||
|
isEqual: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: AccountStateKey(mockAddr, AccountBalance),
|
||||||
|
src: nil,
|
||||||
|
compared: new(uint256.Int).SetUint64(1),
|
||||||
|
isEqual: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: AccountStateKey(mockAddr, AccountBalance),
|
||||||
|
src: (*uint256.Int)(nil),
|
||||||
|
compared: new(uint256.Int).SetUint64(1),
|
||||||
|
isEqual: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: AccountStateKey(mockAddr, AccountBalance),
|
||||||
|
src: (*uint256.Int)(nil),
|
||||||
|
compared: (*uint256.Int)(nil),
|
||||||
|
isEqual: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: AccountStateKey(mockAddr, AccountCodeHash),
|
||||||
|
src: []byte{1},
|
||||||
|
compared: []byte{1},
|
||||||
|
isEqual: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: AccountStateKey(mockAddr, AccountCodeHash),
|
||||||
|
src: nil,
|
||||||
|
compared: []byte{1},
|
||||||
|
isEqual: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: AccountStateKey(mockAddr, AccountCodeHash),
|
||||||
|
src: ([]byte)(nil),
|
||||||
|
compared: []byte{1},
|
||||||
|
isEqual: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: AccountStateKey(mockAddr, AccountCodeHash),
|
||||||
|
src: ([]byte)(nil),
|
||||||
|
compared: ([]byte)(nil),
|
||||||
|
isEqual: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: AccountStateKey(mockAddr, AccountSuicide),
|
||||||
|
src: struct{}{},
|
||||||
|
compared: struct{}{},
|
||||||
|
isEqual: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: AccountStateKey(mockAddr, AccountSuicide),
|
||||||
|
src: nil,
|
||||||
|
compared: struct{}{},
|
||||||
|
isEqual: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: StorageStateKey(mockAddr, mockHash),
|
||||||
|
src: mockHash,
|
||||||
|
compared: mockHash,
|
||||||
|
isEqual: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: StorageStateKey(mockAddr, mockHash),
|
||||||
|
src: nil,
|
||||||
|
compared: mockHash,
|
||||||
|
isEqual: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, item := range tests {
|
||||||
|
require.Equal(t, item.isEqual, isEqualRWVal(item.key, item.src, item.compared), i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func mockSimpleDAG() *TxDAG {
|
||||||
|
dag := NewTxDAG(10)
|
||||||
|
dag.TxDeps[0].TxIndexes = []int{}
|
||||||
|
dag.TxDeps[1].TxIndexes = []int{}
|
||||||
|
dag.TxDeps[2].TxIndexes = []int{}
|
||||||
|
dag.TxDeps[3].TxIndexes = []int{0}
|
||||||
|
dag.TxDeps[4].TxIndexes = []int{0}
|
||||||
|
dag.TxDeps[5].TxIndexes = []int{1, 2}
|
||||||
|
dag.TxDeps[6].TxIndexes = []int{2, 5}
|
||||||
|
dag.TxDeps[7].TxIndexes = []int{6}
|
||||||
|
dag.TxDeps[8].TxIndexes = []int{}
|
||||||
|
dag.TxDeps[9].TxIndexes = []int{8}
|
||||||
|
return dag
|
||||||
|
}
|
||||||
|
|
||||||
|
func mockSystemTxDAG() *TxDAG {
|
||||||
|
dag := NewTxDAG(12)
|
||||||
|
dag.TxDeps[0].TxIndexes = []int{}
|
||||||
|
dag.TxDeps[1].TxIndexes = []int{}
|
||||||
|
dag.TxDeps[2].TxIndexes = []int{}
|
||||||
|
dag.TxDeps[3].TxIndexes = []int{0}
|
||||||
|
dag.TxDeps[4].TxIndexes = []int{0}
|
||||||
|
dag.TxDeps[5].TxIndexes = []int{1, 2}
|
||||||
|
dag.TxDeps[6].TxIndexes = []int{2, 5}
|
||||||
|
dag.TxDeps[7].TxIndexes = []int{6}
|
||||||
|
dag.TxDeps[8].TxIndexes = []int{}
|
||||||
|
dag.TxDeps[9].TxIndexes = []int{8}
|
||||||
|
dag.TxDeps[10] = TxDep{
|
||||||
|
Relation: 1,
|
||||||
|
TxIndexes: []int{},
|
||||||
|
}
|
||||||
|
dag.TxDeps[11] = TxDep{
|
||||||
|
Relation: 1,
|
||||||
|
TxIndexes: []int{},
|
||||||
|
}
|
||||||
|
return dag
|
||||||
|
}
|
||||||
|
|
||||||
|
func mockRWSet(index int, read []string, write []string) *RWSet {
|
||||||
|
ver := StateVersion{
|
||||||
|
TxIndex: index,
|
||||||
|
}
|
||||||
|
set := NewRWSet(ver)
|
||||||
|
for _, k := range read {
|
||||||
|
key := RWKey{}
|
||||||
|
if len(k) > len(key) {
|
||||||
|
k = k[:len(key)]
|
||||||
|
}
|
||||||
|
copy(key[:], k)
|
||||||
|
set.readSet[key] = &ReadRecord{
|
||||||
|
StateVersion: ver,
|
||||||
|
Val: struct{}{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, k := range write {
|
||||||
|
key := RWKey{}
|
||||||
|
if len(k) > len(key) {
|
||||||
|
k = k[:len(key)]
|
||||||
|
}
|
||||||
|
copy(key[:], k)
|
||||||
|
set.writeSet[key] = &WriteRecord{
|
||||||
|
Val: struct{}{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return set
|
||||||
|
}
|
||||||
@@ -79,6 +79,10 @@ type StateDB interface {
|
|||||||
|
|
||||||
AddLog(*types.Log)
|
AddLog(*types.Log)
|
||||||
AddPreimage(common.Hash, []byte)
|
AddPreimage(common.Hash, []byte)
|
||||||
|
|
||||||
|
// parallel DAG related
|
||||||
|
BeforeTxTransition()
|
||||||
|
FinaliseRWSet() error
|
||||||
}
|
}
|
||||||
|
|
||||||
// CallContext provides a basic interface for the EVM calling conventions. The EVM
|
// CallContext provides a basic interface for the EVM calling conventions. The EVM
|
||||||
|
|||||||
@@ -187,6 +187,10 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
|
|||||||
chainConfig.HaberTime = config.OverrideHaber
|
chainConfig.HaberTime = config.OverrideHaber
|
||||||
overrides.OverrideHaber = config.OverrideHaber
|
overrides.OverrideHaber = config.OverrideHaber
|
||||||
}
|
}
|
||||||
|
if config.OverrideBohr != nil {
|
||||||
|
chainConfig.BohrTime = config.OverrideBohr
|
||||||
|
overrides.OverrideBohr = config.OverrideBohr
|
||||||
|
}
|
||||||
if config.OverrideVerkle != nil {
|
if config.OverrideVerkle != nil {
|
||||||
chainConfig.VerkleTime = config.OverrideVerkle
|
chainConfig.VerkleTime = config.OverrideVerkle
|
||||||
overrides.OverrideVerkle = config.OverrideVerkle
|
overrides.OverrideVerkle = config.OverrideVerkle
|
||||||
@@ -314,7 +318,6 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
|
|||||||
// Permit the downloader to use the trie cache allowance during fast sync
|
// Permit the downloader to use the trie cache allowance during fast sync
|
||||||
cacheLimit := cacheConfig.TrieCleanLimit + cacheConfig.TrieDirtyLimit + cacheConfig.SnapshotLimit
|
cacheLimit := cacheConfig.TrieCleanLimit + cacheConfig.TrieDirtyLimit + cacheConfig.SnapshotLimit
|
||||||
if eth.handler, err = newHandler(&handlerConfig{
|
if eth.handler, err = newHandler(&handlerConfig{
|
||||||
NodeID: eth.p2pServer.Self().ID(),
|
|
||||||
Database: chainDb,
|
Database: chainDb,
|
||||||
Chain: eth.blockchain,
|
Chain: eth.blockchain,
|
||||||
TxPool: eth.txPool,
|
TxPool: eth.txPool,
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ func newTester(t *testing.T) *downloadTester {
|
|||||||
// newTester creates a new downloader test mocker.
|
// newTester creates a new downloader test mocker.
|
||||||
func newTesterWithNotification(t *testing.T, success func()) *downloadTester {
|
func newTesterWithNotification(t *testing.T, success func()) *downloadTester {
|
||||||
freezer := t.TempDir()
|
freezer := t.TempDir()
|
||||||
db, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), freezer, "", false, false, false, false)
|
db, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), freezer, "", false, false, false, false, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -194,6 +194,9 @@ type Config struct {
|
|||||||
// OverrideHaber (TODO: remove after the fork)
|
// OverrideHaber (TODO: remove after the fork)
|
||||||
OverrideHaber *uint64 `toml:",omitempty"`
|
OverrideHaber *uint64 `toml:",omitempty"`
|
||||||
|
|
||||||
|
// OverrideBohr (TODO: remove after the fork)
|
||||||
|
OverrideBohr *uint64 `toml:",omitempty"`
|
||||||
|
|
||||||
// OverrideVerkle (TODO: remove after the fork)
|
// OverrideVerkle (TODO: remove after the fork)
|
||||||
OverrideVerkle *uint64 `toml:",omitempty"`
|
OverrideVerkle *uint64 `toml:",omitempty"`
|
||||||
|
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ func (c Config) MarshalTOML() (interface{}, error) {
|
|||||||
RPCTxFeeCap float64
|
RPCTxFeeCap float64
|
||||||
OverrideCancun *uint64 `toml:",omitempty"`
|
OverrideCancun *uint64 `toml:",omitempty"`
|
||||||
OverrideHaber *uint64 `toml:",omitempty"`
|
OverrideHaber *uint64 `toml:",omitempty"`
|
||||||
|
OverrideBohr *uint64 `toml:",omitempty"`
|
||||||
OverrideVerkle *uint64 `toml:",omitempty"`
|
OverrideVerkle *uint64 `toml:",omitempty"`
|
||||||
BlobExtraReserve uint64
|
BlobExtraReserve uint64
|
||||||
}
|
}
|
||||||
@@ -131,6 +132,7 @@ func (c Config) MarshalTOML() (interface{}, error) {
|
|||||||
enc.RPCTxFeeCap = c.RPCTxFeeCap
|
enc.RPCTxFeeCap = c.RPCTxFeeCap
|
||||||
enc.OverrideCancun = c.OverrideCancun
|
enc.OverrideCancun = c.OverrideCancun
|
||||||
enc.OverrideHaber = c.OverrideHaber
|
enc.OverrideHaber = c.OverrideHaber
|
||||||
|
enc.OverrideBohr = c.OverrideBohr
|
||||||
enc.OverrideVerkle = c.OverrideVerkle
|
enc.OverrideVerkle = c.OverrideVerkle
|
||||||
enc.BlobExtraReserve = c.BlobExtraReserve
|
enc.BlobExtraReserve = c.BlobExtraReserve
|
||||||
return &enc, nil
|
return &enc, nil
|
||||||
@@ -194,6 +196,7 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
|
|||||||
RPCTxFeeCap *float64
|
RPCTxFeeCap *float64
|
||||||
OverrideCancun *uint64 `toml:",omitempty"`
|
OverrideCancun *uint64 `toml:",omitempty"`
|
||||||
OverrideHaber *uint64 `toml:",omitempty"`
|
OverrideHaber *uint64 `toml:",omitempty"`
|
||||||
|
OverrideBohr *uint64 `toml:",omitempty"`
|
||||||
OverrideVerkle *uint64 `toml:",omitempty"`
|
OverrideVerkle *uint64 `toml:",omitempty"`
|
||||||
BlobExtraReserve *uint64
|
BlobExtraReserve *uint64
|
||||||
}
|
}
|
||||||
@@ -366,6 +369,9 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
|
|||||||
if dec.OverrideHaber != nil {
|
if dec.OverrideHaber != nil {
|
||||||
c.OverrideHaber = dec.OverrideHaber
|
c.OverrideHaber = dec.OverrideHaber
|
||||||
}
|
}
|
||||||
|
if dec.OverrideBohr != nil {
|
||||||
|
c.OverrideBohr = dec.OverrideBohr
|
||||||
|
}
|
||||||
if dec.OverrideVerkle != nil {
|
if dec.OverrideVerkle != nil {
|
||||||
c.OverrideVerkle = dec.OverrideVerkle
|
c.OverrideVerkle = dec.OverrideVerkle
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/txpool"
|
"github.com/ethereum/go-ethereum/core/txpool"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
|
||||||
"github.com/ethereum/go-ethereum/eth/downloader"
|
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||||
"github.com/ethereum/go-ethereum/eth/fetcher"
|
"github.com/ethereum/go-ethereum/eth/fetcher"
|
||||||
"github.com/ethereum/go-ethereum/eth/protocols/bsc"
|
"github.com/ethereum/go-ethereum/eth/protocols/bsc"
|
||||||
@@ -46,8 +45,6 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/metrics"
|
"github.com/ethereum/go-ethereum/metrics"
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
|
||||||
"golang.org/x/crypto/sha3"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -114,7 +111,6 @@ type votePool interface {
|
|||||||
// handlerConfig is the collection of initialization parameters to create a full
|
// handlerConfig is the collection of initialization parameters to create a full
|
||||||
// node network handler.
|
// node network handler.
|
||||||
type handlerConfig struct {
|
type handlerConfig struct {
|
||||||
NodeID enode.ID // P2P node ID used for tx propagation topology
|
|
||||||
Database ethdb.Database // Database for direct sync insertions
|
Database ethdb.Database // Database for direct sync insertions
|
||||||
Chain *core.BlockChain // Blockchain to serve data from
|
Chain *core.BlockChain // Blockchain to serve data from
|
||||||
TxPool txPool // Transaction pool to propagate from
|
TxPool txPool // Transaction pool to propagate from
|
||||||
@@ -131,7 +127,6 @@ type handlerConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type handler struct {
|
type handler struct {
|
||||||
nodeID enode.ID
|
|
||||||
networkID uint64
|
networkID uint64
|
||||||
forkFilter forkid.Filter // Fork ID filter, constant across the lifetime of the node
|
forkFilter forkid.Filter // Fork ID filter, constant across the lifetime of the node
|
||||||
disablePeerTxBroadcast bool
|
disablePeerTxBroadcast bool
|
||||||
@@ -189,7 +184,6 @@ func newHandler(config *handlerConfig) (*handler, error) {
|
|||||||
config.PeerSet = newPeerSet() // Nicety initialization for tests
|
config.PeerSet = newPeerSet() // Nicety initialization for tests
|
||||||
}
|
}
|
||||||
h := &handler{
|
h := &handler{
|
||||||
nodeID: config.NodeID,
|
|
||||||
networkID: config.Network,
|
networkID: config.Network,
|
||||||
forkFilter: forkid.NewFilter(config.Chain),
|
forkFilter: forkid.NewFilter(config.Chain),
|
||||||
disablePeerTxBroadcast: config.DisablePeerTxBroadcast,
|
disablePeerTxBroadcast: config.DisablePeerTxBroadcast,
|
||||||
@@ -858,54 +852,25 @@ func (h *handler) BroadcastTransactions(txs types.Transactions) {
|
|||||||
annos = make(map[*ethPeer][]common.Hash) // Set peer->hash to announce
|
annos = make(map[*ethPeer][]common.Hash) // Set peer->hash to announce
|
||||||
)
|
)
|
||||||
// Broadcast transactions to a batch of peers not knowing about it
|
// Broadcast transactions to a batch of peers not knowing about it
|
||||||
direct := big.NewInt(int64(math.Sqrt(float64(h.peers.len())))) // Approximate number of peers to broadcast to
|
|
||||||
if direct.BitLen() == 0 {
|
|
||||||
direct = big.NewInt(1)
|
|
||||||
}
|
|
||||||
total := new(big.Int).Exp(direct, big.NewInt(2), nil) // Stabilise total peer count a bit based on sqrt peers
|
|
||||||
|
|
||||||
var (
|
|
||||||
signer = types.LatestSignerForChainID(h.chain.Config().ChainID) // Don't care about chain status, we just need *a* sender
|
|
||||||
hasher = sha3.NewLegacyKeccak256().(crypto.KeccakState)
|
|
||||||
hash = make([]byte, 32)
|
|
||||||
)
|
|
||||||
for _, tx := range txs {
|
for _, tx := range txs {
|
||||||
var maybeDirect bool
|
peers := h.peers.peersWithoutTransaction(tx.Hash())
|
||||||
|
|
||||||
|
var numDirect int
|
||||||
switch {
|
switch {
|
||||||
case tx.Type() == types.BlobTxType:
|
case tx.Type() == types.BlobTxType:
|
||||||
blobTxs++
|
blobTxs++
|
||||||
case tx.Size() > txMaxBroadcastSize:
|
case tx.Size() > txMaxBroadcastSize:
|
||||||
largeTxs++
|
largeTxs++
|
||||||
default:
|
default:
|
||||||
maybeDirect = true
|
numDirect = int(math.Sqrt(float64(len(peers))))
|
||||||
}
|
}
|
||||||
// Send the transaction (if it's small enough) directly to a subset of
|
// Send the tx unconditionally to a subset of our peers
|
||||||
// the peers that have not received it yet, ensuring that the flow of
|
for _, peer := range peers[:numDirect] {
|
||||||
// transactions is groupped by account to (try and) avoid nonce gaps.
|
txset[peer] = append(txset[peer], tx.Hash())
|
||||||
//
|
}
|
||||||
// To do this, we hash the local enode IW with together with a peer's
|
// For the remaining peers, send announcement only
|
||||||
// enode ID together with the transaction sender and broadcast if
|
for _, peer := range peers[numDirect:] {
|
||||||
// `sha(self, peer, sender) mod peers < sqrt(peers)`.
|
annos[peer] = append(annos[peer], tx.Hash())
|
||||||
for _, peer := range h.peers.peersWithoutTransaction(tx.Hash()) {
|
|
||||||
var broadcast bool
|
|
||||||
if maybeDirect {
|
|
||||||
hasher.Reset()
|
|
||||||
hasher.Write(h.nodeID.Bytes())
|
|
||||||
hasher.Write(peer.Node().ID().Bytes())
|
|
||||||
|
|
||||||
from, _ := types.Sender(signer, tx) // Ignore error, we only use the addr as a propagation target splitter
|
|
||||||
hasher.Write(from.Bytes())
|
|
||||||
|
|
||||||
hasher.Read(hash)
|
|
||||||
if new(big.Int).Mod(new(big.Int).SetBytes(hash), total).Cmp(direct) < 0 {
|
|
||||||
broadcast = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if broadcast {
|
|
||||||
txset[peer] = append(txset[peer], tx.Hash())
|
|
||||||
} else {
|
|
||||||
annos[peer] = append(annos[peer], tx.Hash())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for peer, hashes := range txset {
|
for peer, hashes := range txset {
|
||||||
|
|||||||
@@ -258,7 +258,7 @@ func (c *mockParlia) CalcDifficulty(chain consensus.ChainHeaderReader, time uint
|
|||||||
func newTestParliaHandlerAfterCancun(t *testing.T, config *params.ChainConfig, mode downloader.SyncMode, preCancunBlks, postCancunBlks uint64) *testHandler {
|
func newTestParliaHandlerAfterCancun(t *testing.T, config *params.ChainConfig, mode downloader.SyncMode, preCancunBlks, postCancunBlks uint64) *testHandler {
|
||||||
// Have N headers in the freezer
|
// Have N headers in the freezer
|
||||||
frdir := t.TempDir()
|
frdir := t.TempDir()
|
||||||
db, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), frdir, "", false, false, false, false)
|
db, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), frdir, "", false, false, false, false, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create database with ancient backend")
|
t.Fatalf("failed to create database with ancient backend")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -281,7 +281,7 @@ func (eth *Ethereum) stateAtTransaction(ctx context.Context, block *types.Block,
|
|||||||
}
|
}
|
||||||
// Not yet the searched for transaction, execute on top of the current state
|
// Not yet the searched for transaction, execute on top of the current state
|
||||||
vmenv := vm.NewEVM(context, txContext, statedb, eth.blockchain.Config(), vm.Config{})
|
vmenv := vm.NewEVM(context, txContext, statedb, eth.blockchain.Config(), vm.Config{})
|
||||||
statedb.SetTxContext(tx.Hash(), idx)
|
statedb.SetTxContext(tx.Hash(), idx, 0)
|
||||||
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
|
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
|
||||||
return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
|
return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -587,7 +587,7 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
statedb.SetTxContext(tx.Hash(), i)
|
statedb.SetTxContext(tx.Hash(), i, 0)
|
||||||
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.GasLimit)); err != nil {
|
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.GasLimit)); err != nil {
|
||||||
log.Warn("Tracing intermediate roots did not complete", "txindex", i, "txhash", tx.Hash(), "err", err)
|
log.Warn("Tracing intermediate roots did not complete", "txindex", i, "txhash", tx.Hash(), "err", err)
|
||||||
// We intentionally don't return the error here: if we do, then the RPC server will not
|
// We intentionally don't return the error here: if we do, then the RPC server will not
|
||||||
@@ -786,7 +786,7 @@ txloop:
|
|||||||
|
|
||||||
// Generate the next state snapshot fast without tracing
|
// Generate the next state snapshot fast without tracing
|
||||||
msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee())
|
msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee())
|
||||||
statedb.SetTxContext(tx.Hash(), i)
|
statedb.SetTxContext(tx.Hash(), i, 0)
|
||||||
vmenv := vm.NewEVM(blockCtx, core.NewEVMTxContext(msg), statedb, api.backend.ChainConfig(), vm.Config{})
|
vmenv := vm.NewEVM(blockCtx, core.NewEVMTxContext(msg), statedb, api.backend.ChainConfig(), vm.Config{})
|
||||||
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.GasLimit)); err != nil {
|
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.GasLimit)); err != nil {
|
||||||
failed = err
|
failed = err
|
||||||
@@ -919,7 +919,7 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block
|
|||||||
}
|
}
|
||||||
// Execute the transaction and flush any traces to disk
|
// Execute the transaction and flush any traces to disk
|
||||||
vmenv := vm.NewEVM(vmctx, txContext, statedb, chainConfig, vmConf)
|
vmenv := vm.NewEVM(vmctx, txContext, statedb, chainConfig, vmConf)
|
||||||
statedb.SetTxContext(tx.Hash(), i)
|
statedb.SetTxContext(tx.Hash(), i, 0)
|
||||||
_, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.GasLimit))
|
_, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.GasLimit))
|
||||||
if writer != nil {
|
if writer != nil {
|
||||||
writer.Flush()
|
writer.Flush()
|
||||||
@@ -1127,7 +1127,7 @@ func (api *API) traceTx(ctx context.Context, message *core.Message, txctx *Conte
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Call Prepare to clear out the statedb access list
|
// Call Prepare to clear out the statedb access list
|
||||||
statedb.SetTxContext(txctx.TxHash, txctx.TxIndex)
|
statedb.SetTxContext(txctx.TxHash, txctx.TxIndex, 0)
|
||||||
if _, err = core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.GasLimit)); err != nil {
|
if _, err = core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.GasLimit)); err != nil {
|
||||||
return nil, fmt.Errorf("tracing failed: %w", err)
|
return nil, fmt.Errorf("tracing failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -183,6 +183,7 @@ type StateStoreReader interface {
|
|||||||
type BlockStore interface {
|
type BlockStore interface {
|
||||||
BlockStore() Database
|
BlockStore() Database
|
||||||
SetBlockStore(block Database)
|
SetBlockStore(block Database)
|
||||||
|
HasSeparateBlockStore() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type BlockStoreReader interface {
|
type BlockStoreReader interface {
|
||||||
|
|||||||
@@ -44,6 +44,10 @@ func (db *Database) BlockStore() ethdb.Database {
|
|||||||
return db
|
return db
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *Database) HasSeparateBlockStore() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (db *Database) SetBlockStore(block ethdb.Database) {
|
func (db *Database) SetBlockStore(block ethdb.Database) {
|
||||||
panic("not supported")
|
panic("not supported")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -756,7 +756,7 @@ func (r *BidRuntime) commitTransaction(chain *core.BlockChain, chainConfig *para
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Start executing the transaction
|
// Start executing the transaction
|
||||||
r.env.state.SetTxContext(tx.Hash(), r.env.tcount)
|
r.env.state.SetTxContext(tx.Hash(), r.env.tcount, 0)
|
||||||
|
|
||||||
if tx.Type() == types.BlobTxType {
|
if tx.Type() == types.BlobTxType {
|
||||||
sc = types.NewBlobSidecarFromTx(tx)
|
sc = types.NewBlobSidecarFromTx(tx)
|
||||||
|
|||||||
@@ -903,7 +903,7 @@ LOOP:
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Start executing the transaction
|
// Start executing the transaction
|
||||||
env.state.SetTxContext(tx.Hash(), env.tcount)
|
env.state.SetTxContext(tx.Hash(), env.tcount, 0)
|
||||||
|
|
||||||
logs, err := w.commitTransaction(env, tx, bloomProcessors)
|
logs, err := w.commitTransaction(env, tx, bloomProcessors)
|
||||||
switch {
|
switch {
|
||||||
|
|||||||
13
node/node.go
13
node/node.go
@@ -773,12 +773,13 @@ func (n *Node) OpenDatabase(name string, cache, handles int, namespace string, r
|
|||||||
db = rawdb.NewMemoryDatabase()
|
db = rawdb.NewMemoryDatabase()
|
||||||
} else {
|
} else {
|
||||||
db, err = rawdb.Open(rawdb.OpenOptions{
|
db, err = rawdb.Open(rawdb.OpenOptions{
|
||||||
Type: n.config.DBEngine,
|
Type: n.config.DBEngine,
|
||||||
Directory: n.ResolvePath(name),
|
Directory: n.ResolvePath(name),
|
||||||
Namespace: namespace,
|
Namespace: namespace,
|
||||||
Cache: cache,
|
Cache: cache,
|
||||||
Handles: handles,
|
Handles: handles,
|
||||||
ReadOnly: readonly,
|
ReadOnly: readonly,
|
||||||
|
MultiDataBase: n.CheckIfMultiDataBase(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -153,6 +153,7 @@ var (
|
|||||||
FeynmanFixTime: newUint64(1713419340), // 2024-04-18 05:49:00 AM UTC
|
FeynmanFixTime: newUint64(1713419340), // 2024-04-18 05:49:00 AM UTC
|
||||||
CancunTime: newUint64(1718863500), // 2024-06-20 06:05:00 AM UTC
|
CancunTime: newUint64(1718863500), // 2024-06-20 06:05:00 AM UTC
|
||||||
HaberTime: newUint64(1718863500), // 2024-06-20 06:05:00 AM UTC
|
HaberTime: newUint64(1718863500), // 2024-06-20 06:05:00 AM UTC
|
||||||
|
BohrTime: nil,
|
||||||
|
|
||||||
Parlia: &ParliaConfig{
|
Parlia: &ParliaConfig{
|
||||||
Period: 3,
|
Period: 3,
|
||||||
@@ -192,6 +193,7 @@ var (
|
|||||||
FeynmanFixTime: newUint64(1711342800), // 2024-03-25 5:00:00 AM UTC
|
FeynmanFixTime: newUint64(1711342800), // 2024-03-25 5:00:00 AM UTC
|
||||||
CancunTime: newUint64(1713330442), // 2024-04-17 05:07:22 AM UTC
|
CancunTime: newUint64(1713330442), // 2024-04-17 05:07:22 AM UTC
|
||||||
HaberTime: newUint64(1716962820), // 2024-05-29 06:07:00 AM UTC
|
HaberTime: newUint64(1716962820), // 2024-05-29 06:07:00 AM UTC
|
||||||
|
BohrTime: nil,
|
||||||
|
|
||||||
Parlia: &ParliaConfig{
|
Parlia: &ParliaConfig{
|
||||||
Period: 3,
|
Period: 3,
|
||||||
@@ -232,6 +234,7 @@ var (
|
|||||||
FeynmanFixTime: newUint64(0),
|
FeynmanFixTime: newUint64(0),
|
||||||
CancunTime: newUint64(0),
|
CancunTime: newUint64(0),
|
||||||
HaberTime: newUint64(0),
|
HaberTime: newUint64(0),
|
||||||
|
BohrTime: newUint64(0),
|
||||||
|
|
||||||
Parlia: &ParliaConfig{
|
Parlia: &ParliaConfig{
|
||||||
Period: 3,
|
Period: 3,
|
||||||
@@ -509,6 +512,7 @@ type ChainConfig struct {
|
|||||||
FeynmanFixTime *uint64 `json:"feynmanFixTime,omitempty"` // FeynmanFix switch time (nil = no fork, 0 = already activated)
|
FeynmanFixTime *uint64 `json:"feynmanFixTime,omitempty"` // FeynmanFix switch time (nil = no fork, 0 = already activated)
|
||||||
CancunTime *uint64 `json:"cancunTime,omitempty"` // Cancun switch time (nil = no fork, 0 = already on cancun)
|
CancunTime *uint64 `json:"cancunTime,omitempty"` // Cancun switch time (nil = no fork, 0 = already on cancun)
|
||||||
HaberTime *uint64 `json:"haberTime,omitempty"` // Haber switch time (nil = no fork, 0 = already on haber)
|
HaberTime *uint64 `json:"haberTime,omitempty"` // Haber switch time (nil = no fork, 0 = already on haber)
|
||||||
|
BohrTime *uint64 `json:"bohrTime,omitempty"` // Bohr switch time (nil = no fork, 0 = already on bohr)
|
||||||
PragueTime *uint64 `json:"pragueTime,omitempty"` // Prague switch time (nil = no fork, 0 = already on prague)
|
PragueTime *uint64 `json:"pragueTime,omitempty"` // Prague switch time (nil = no fork, 0 = already on prague)
|
||||||
VerkleTime *uint64 `json:"verkleTime,omitempty"` // Verkle switch time (nil = no fork, 0 = already on verkle)
|
VerkleTime *uint64 `json:"verkleTime,omitempty"` // Verkle switch time (nil = no fork, 0 = already on verkle)
|
||||||
|
|
||||||
@@ -619,7 +623,12 @@ func (c *ChainConfig) String() string {
|
|||||||
HaberTime = big.NewInt(0).SetUint64(*c.HaberTime)
|
HaberTime = big.NewInt(0).SetUint64(*c.HaberTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v Petersburg: %v Istanbul: %v, Muir Glacier: %v, Ramanujan: %v, Niels: %v, MirrorSync: %v, Bruno: %v, Berlin: %v, YOLO v3: %v, CatalystBlock: %v, London: %v, ArrowGlacier: %v, MergeFork:%v, Euler: %v, Gibbs: %v, Nano: %v, Moran: %v, Planck: %v,Luban: %v, Plato: %v, Hertz: %v, Hertzfix: %v, ShanghaiTime: %v, KeplerTime: %v, FeynmanTime: %v, FeynmanFixTime: %v, CancunTime: %v, HaberTime: %v, Engine: %v}",
|
var BohrTime *big.Int
|
||||||
|
if c.BohrTime != nil {
|
||||||
|
BohrTime = big.NewInt(0).SetUint64(*c.BohrTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v Petersburg: %v Istanbul: %v, Muir Glacier: %v, Ramanujan: %v, Niels: %v, MirrorSync: %v, Bruno: %v, Berlin: %v, YOLO v3: %v, CatalystBlock: %v, London: %v, ArrowGlacier: %v, MergeFork:%v, Euler: %v, Gibbs: %v, Nano: %v, Moran: %v, Planck: %v,Luban: %v, Plato: %v, Hertz: %v, Hertzfix: %v, ShanghaiTime: %v, KeplerTime: %v, FeynmanTime: %v, FeynmanFixTime: %v, CancunTime: %v, HaberTime: %v, BohrTime: %v, Engine: %v}",
|
||||||
c.ChainID,
|
c.ChainID,
|
||||||
c.HomesteadBlock,
|
c.HomesteadBlock,
|
||||||
c.DAOForkBlock,
|
c.DAOForkBlock,
|
||||||
@@ -657,6 +666,7 @@ func (c *ChainConfig) String() string {
|
|||||||
FeynmanFixTime,
|
FeynmanFixTime,
|
||||||
CancunTime,
|
CancunTime,
|
||||||
HaberTime,
|
HaberTime,
|
||||||
|
BohrTime,
|
||||||
engine,
|
engine,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -929,6 +939,20 @@ func (c *ChainConfig) IsHaber(num *big.Int, time uint64) bool {
|
|||||||
return c.IsLondon(num) && isTimestampForked(c.HaberTime, time)
|
return c.IsLondon(num) && isTimestampForked(c.HaberTime, time)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsBohr returns whether time is either equal to the Bohr fork time or greater.
|
||||||
|
func (c *ChainConfig) IsBohr(num *big.Int, time uint64) bool {
|
||||||
|
return c.IsLondon(num) && isTimestampForked(c.BohrTime, time)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsOnBohr returns whether currentBlockTime is either equal to the Bohr fork time or greater firstly.
|
||||||
|
func (c *ChainConfig) IsOnBohr(currentBlockNumber *big.Int, lastBlockTime uint64, currentBlockTime uint64) bool {
|
||||||
|
lastBlockNumber := new(big.Int)
|
||||||
|
if currentBlockNumber.Cmp(big.NewInt(1)) >= 0 {
|
||||||
|
lastBlockNumber.Sub(currentBlockNumber, big.NewInt(1))
|
||||||
|
}
|
||||||
|
return !c.IsBohr(lastBlockNumber, lastBlockTime) && c.IsBohr(currentBlockNumber, currentBlockTime)
|
||||||
|
}
|
||||||
|
|
||||||
// IsPrague returns whether num is either equal to the Prague fork time or greater.
|
// IsPrague returns whether num is either equal to the Prague fork time or greater.
|
||||||
func (c *ChainConfig) IsPrague(num *big.Int, time uint64) bool {
|
func (c *ChainConfig) IsPrague(num *big.Int, time uint64) bool {
|
||||||
return c.IsLondon(num) && isTimestampForked(c.PragueTime, time)
|
return c.IsLondon(num) && isTimestampForked(c.PragueTime, time)
|
||||||
@@ -993,6 +1017,7 @@ func (c *ChainConfig) CheckConfigForkOrder() error {
|
|||||||
{name: "feynmanFixTime", timestamp: c.FeynmanFixTime},
|
{name: "feynmanFixTime", timestamp: c.FeynmanFixTime},
|
||||||
{name: "cancunTime", timestamp: c.CancunTime},
|
{name: "cancunTime", timestamp: c.CancunTime},
|
||||||
{name: "haberTime", timestamp: c.HaberTime},
|
{name: "haberTime", timestamp: c.HaberTime},
|
||||||
|
{name: "bohrTime", timestamp: c.BohrTime},
|
||||||
{name: "pragueTime", timestamp: c.PragueTime, optional: true},
|
{name: "pragueTime", timestamp: c.PragueTime, optional: true},
|
||||||
{name: "verkleTime", timestamp: c.VerkleTime, optional: true},
|
{name: "verkleTime", timestamp: c.VerkleTime, optional: true},
|
||||||
} {
|
} {
|
||||||
@@ -1323,7 +1348,7 @@ type Rules struct {
|
|||||||
IsHertz bool
|
IsHertz bool
|
||||||
IsHertzfix bool
|
IsHertzfix bool
|
||||||
IsShanghai, IsKepler, IsFeynman, IsCancun, IsHaber bool
|
IsShanghai, IsKepler, IsFeynman, IsCancun, IsHaber bool
|
||||||
IsPrague, IsVerkle bool
|
IsBohr, IsPrague, IsVerkle bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rules ensures c's ChainID is not nil.
|
// Rules ensures c's ChainID is not nil.
|
||||||
@@ -1359,6 +1384,7 @@ func (c *ChainConfig) Rules(num *big.Int, isMerge bool, timestamp uint64) Rules
|
|||||||
IsFeynman: c.IsFeynman(num, timestamp),
|
IsFeynman: c.IsFeynman(num, timestamp),
|
||||||
IsCancun: c.IsCancun(num, timestamp),
|
IsCancun: c.IsCancun(num, timestamp),
|
||||||
IsHaber: c.IsHaber(num, timestamp),
|
IsHaber: c.IsHaber(num, timestamp),
|
||||||
|
IsBohr: c.IsBohr(num, timestamp),
|
||||||
IsPrague: c.IsPrague(num, timestamp),
|
IsPrague: c.IsPrague(num, timestamp),
|
||||||
IsVerkle: c.IsVerkle(num, timestamp),
|
IsVerkle: c.IsVerkle(num, timestamp),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ type tester struct {
|
|||||||
|
|
||||||
func newTester(t *testing.T, historyLimit uint64) *tester {
|
func newTester(t *testing.T, historyLimit uint64) *tester {
|
||||||
var (
|
var (
|
||||||
disk, _ = rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false, false, false, false)
|
disk, _ = rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false, false, false, false, false)
|
||||||
db = New(disk, &Config{
|
db = New(disk, &Config{
|
||||||
StateHistory: historyLimit,
|
StateHistory: historyLimit,
|
||||||
CleanCacheSize: 256 * 1024,
|
CleanCacheSize: 256 * 1024,
|
||||||
|
|||||||
@@ -152,12 +152,13 @@ func (kr *JournalKVReader) Close() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func newJournalWriter(file string, db ethdb.Database, journalType JournalType) JournalWriter {
|
func newJournalWriter(file string, db ethdb.Database, journalType JournalType) JournalWriter {
|
||||||
log.Info("New journal writer", "path", file, "journalType", journalType)
|
|
||||||
if journalType == JournalKVType {
|
if journalType == JournalKVType {
|
||||||
|
log.Info("New journal writer for journal kv")
|
||||||
return &JournalKVWriter{
|
return &JournalKVWriter{
|
||||||
diskdb: db,
|
diskdb: db,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
log.Info("New journal writer for journal file", "path", file)
|
||||||
fd, err := os.OpenFile(file, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
|
fd, err := os.OpenFile(file, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
@@ -169,8 +170,8 @@ func newJournalWriter(file string, db ethdb.Database, journalType JournalType) J
|
|||||||
}
|
}
|
||||||
|
|
||||||
func newJournalReader(file string, db ethdb.Database, journalType JournalType) (JournalReader, error) {
|
func newJournalReader(file string, db ethdb.Database, journalType JournalType) (JournalReader, error) {
|
||||||
log.Info("New journal reader", "path", file, "journalType", journalType)
|
|
||||||
if journalType == JournalKVType {
|
if journalType == JournalKVType {
|
||||||
|
log.Info("New journal reader for journal kv")
|
||||||
journal := rawdb.ReadTrieJournal(db)
|
journal := rawdb.ReadTrieJournal(db)
|
||||||
if len(journal) == 0 {
|
if len(journal) == 0 {
|
||||||
return nil, errMissJournal
|
return nil, errMissJournal
|
||||||
@@ -179,6 +180,7 @@ func newJournalReader(file string, db ethdb.Database, journalType JournalType) (
|
|||||||
journalBuf: bytes.NewBuffer(journal),
|
journalBuf: bytes.NewBuffer(journal),
|
||||||
}, nil
|
}, nil
|
||||||
} else {
|
} else {
|
||||||
|
log.Info("New journal reader for journal file", "path", file)
|
||||||
fd, err := os.Open(file)
|
fd, err := os.Open(file)
|
||||||
if errors.Is(err, fs.ErrNotExist) {
|
if errors.Is(err, fs.ErrNotExist) {
|
||||||
return nil, errMissJournal
|
return nil, errMissJournal
|
||||||
|
|||||||
Reference in New Issue
Block a user