diff --git a/cmd/geth/main.go b/cmd/geth/main.go index a3e965109274..272ab2e7b6fa 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -69,6 +69,7 @@ var ( utils.NoUSBFlag, utils.USBFlag, utils.SmartCardDaemonPathFlag, + utils.IssuanceFlag, utils.OverrideArrowGlacierFlag, utils.OverrideTerminalTotalDifficulty, utils.EthashCacheDirFlag, diff --git a/cmd/geth/snapshot.go b/cmd/geth/snapshot.go index 7582b4c4d18c..79ec4b1a285d 100644 --- a/cmd/geth/snapshot.go +++ b/cmd/geth/snapshot.go @@ -26,6 +26,7 @@ import ( "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/issuance" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state/pruner" @@ -156,6 +157,18 @@ as the backend data source, making this command a lot faster. The argument is interpreted as block number or hash. If none is provided, the latest block is used. +`, + }, + { + Name: "crawl-supply", + Usage: "Calculate the Ether supply at a specific block", + Action: utils.MigrateFlags(crawlSupply), + Category: "MISCELLANEOUS COMMANDS", + Flags: utils.GroupFlags(utils.NetworkFlags, utils.DatabasePathFlags), + Description: ` +geth snapshot crawl-supply +will traverse the whole state from the given root and accumulate all the Ether +balances to calculate the total supply. `, }, }, @@ -576,3 +589,29 @@ func dumpState(ctx *cli.Context) error { "elapsed", common.PrettyDuration(time.Since(start))) return nil } + +func crawlSupply(ctx *cli.Context) error { + stack, _ := makeConfigNode(ctx) + defer stack.Close() + + chaindb := utils.MakeChainDatabase(ctx, stack, true) + headBlock := rawdb.ReadHeadBlock(chaindb) + if headBlock == nil { + log.Error("Failed to load head block") + return errors.New("no head block") + } + if ctx.NArg() > 1 { + log.Error("Too many arguments given") + return errors.New("too many arguments") + } + snaptree, err := snapshot.New(chaindb, trie.NewDatabase(chaindb), 256, headBlock.Root(), false, false, false) + if err != nil { + log.Error("Failed to open snapshot tree", "err", err) + return err + } + if _, err = issuance.Supply(headBlock.Header(), snaptree); err != nil { + log.Error("Failed to calculate current supply", "err", err) + return err + } + return nil +} diff --git a/cmd/geth/usage.go b/cmd/geth/usage.go index ff13ee4688fe..64802b270a5d 100644 --- a/cmd/geth/usage.go +++ b/cmd/geth/usage.go @@ -47,6 +47,7 @@ var AppHelpFlagGroups = []flags.FlagGroup{ utils.IdentityFlag, utils.LightKDFFlag, utils.EthPeerRequiredBlocksFlag, + utils.IssuanceFlag, }, utils.NetworkFlags, utils.DatabasePathFlags), }, { diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 515942be8978..caa47357bb0b 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -181,11 +181,6 @@ var ( Name: "identity", Usage: "Custom node name", } - DocRootFlag = DirectoryFlag{ - Name: "docroot", - Usage: "Document Root for HTTPClient file scheme", - Value: DirectoryString(HomeDir()), - } ExitWhenSyncedFlag = cli.BoolFlag{ Name: "exitwhensynced", Usage: "Exits after block synchronisation completes", @@ -821,6 +816,10 @@ var ( Usage: "InfluxDB organization name (v2 only)", Value: metrics.DefaultConfig.InfluxDBOrganization, } + IssuanceFlag = cli.BoolFlag{ + Name: "issuance", + Usage: "Track Ether issuance (don't use in production)", + } ) var ( @@ -1673,9 +1672,6 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { cfg.SnapshotCache = 0 // Disabled } } - if ctx.GlobalIsSet(DocRootFlag.Name) { - cfg.DocRoot = ctx.GlobalString(DocRootFlag.Name) - } if ctx.GlobalIsSet(VMEnableDebugFlag.Name) { // TODO(fjl): force-enable this in --dev mode cfg.EnablePreimageRecording = ctx.GlobalBool(VMEnableDebugFlag.Name) @@ -1705,6 +1701,9 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { cfg.EthDiscoveryURLs = SplitAndTrim(urls) } } + if ctx.GlobalIsSet(IssuanceFlag.Name) { + cfg.EnableIssuanceRecording = ctx.GlobalBool(IssuanceFlag.Name) + } // Override any default configs for hard coded networks. switch { case ctx.GlobalBool(MainnetFlag.Name): diff --git a/core/blockchain.go b/core/blockchain.go index c6c845350349..0f33b76d864a 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -31,6 +31,7 @@ import ( "github.com/ethereum/go-ethereum/common/mclock" "github.com/ethereum/go-ethereum/common/prque" "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/core/issuance" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state/snapshot" @@ -1185,7 +1186,7 @@ func (bc *BlockChain) writeKnownBlock(block *types.Block) error { // writeBlockWithState writes block, metadata and corresponding state data to the // database. -func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB) error { +func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.Receipt, state *state.StateDB) error { // Calculate the total difficulty of the block ptd := bc.GetTd(block.ParentHash(), block.NumberU64()-1) if ptd == nil { @@ -1263,6 +1264,22 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types. } } } + // If Ether issuance tracking is enabled, do it before emitting events + if bc.vmConfig.EnableIssuanceRecording { + // Note, this code path is opt-in for data analysis nodes, so speed + // is not really relevant, simplicity and containment much more so. + parent := rawdb.ReadHeader(bc.db, block.ParentHash(), block.NumberU64()-1) + if parent == nil { + log.Error("Failed to retrieve parent for issuance", "err", err) + } else { + issuance, err := issuance.Issuance(block, parent, bc.stateCache.TrieDB(), bc.chainConfig) + if err != nil { + log.Error("Failed to record Ether issuance", "err", err) + } else { + rawdb.WriteIssuance(bc.db, block.NumberU64(), block.Hash(), issuance) + } + } + } return nil } @@ -1280,7 +1297,7 @@ func (bc *BlockChain) WriteBlockAndSetHead(block *types.Block, receipts []*types // and also it applies the given block as the new chain head. This function expects // the chain mutex to be held. func (bc *BlockChain) writeBlockAndSetHead(block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB, emitHeadEvent bool) (status WriteStatus, err error) { - if err := bc.writeBlockWithState(block, receipts, logs, state); err != nil { + if err := bc.writeBlockWithState(block, receipts, state); err != nil { return NonStatTy, err } currentBlock := bc.CurrentBlock() @@ -1643,7 +1660,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals, setHead bool) var status WriteStatus if !setHead { // Don't set the head, only insert the block - err = bc.writeBlockWithState(block, receipts, logs, statedb) + err = bc.writeBlockWithState(block, receipts, statedb) } else { status, err = bc.writeBlockAndSetHead(block, receipts, logs, statedb, false) } diff --git a/core/issuance/issuance.go b/core/issuance/issuance.go new file mode 100644 index 000000000000..67f75b87a959 --- /dev/null +++ b/core/issuance/issuance.go @@ -0,0 +1,169 @@ +// Copyright 2022 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package issuance + +import ( + "fmt" + "math/big" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/ethash" + "github.com/ethereum/go-ethereum/core/state/snapshot" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/trie" +) + +// Issuance calculates the Ether issuance (or burn) across two state tries. In +// normal mode of operation, the expectation is to calculate the issuance between +// two consecutive blocks. +func Issuance(block *types.Block, parent *types.Header, db *trie.Database, config *params.ChainConfig) (*big.Int, error) { + var ( + issuance = new(big.Int) + start = time.Now() + ) + // Open the two tries + if block.ParentHash() != parent.Hash() { + return nil, fmt.Errorf("parent hash mismatch: have %s, want %s", block.ParentHash().Hex(), parent.Hash().Hex()) + } + src, err := trie.New(parent.Root, db) + if err != nil { + return nil, fmt.Errorf("failed to open source trie: %v", err) + } + dst, err := trie.New(block.Root(), db) + if err != nil { + return nil, fmt.Errorf("failed to open destination trie: %v", err) + } + // Gather all the changes across from source to destination + fwdDiffIt, _ := trie.NewDifferenceIterator(src.NodeIterator(nil), dst.NodeIterator(nil)) + fwdIt := trie.NewIterator(fwdDiffIt) + + for fwdIt.Next() { + acc := new(types.StateAccount) + if err := rlp.DecodeBytes(fwdIt.Value, acc); err != nil { + panic(err) + } + issuance.Add(issuance, acc.Balance) + } + // Gather all the changes across from destination to source + rewDiffIt, _ := trie.NewDifferenceIterator(dst.NodeIterator(nil), src.NodeIterator(nil)) + rewIt := trie.NewIterator(rewDiffIt) + + for rewIt.Next() { + acc := new(types.StateAccount) + if err := rlp.DecodeBytes(rewIt.Value, acc); err != nil { + panic(err) + } + issuance.Sub(issuance, acc.Balance) + } + // Calculate the block subsidy based on chain rules and progression + subsidy, uncles, burn := Subsidy(block, config) + + // Calculate the difference between the "calculated" and "crawled" issuance + diff := new(big.Int).Set(issuance) + diff.Sub(diff, subsidy) + diff.Sub(diff, uncles) + diff.Add(diff, burn) + + log.Info("Calculated issuance for block", "number", block.Number(), "hash", block.Hash(), "state", issuance, "subsidy", subsidy, "uncles", uncles, "burn", burn, "diff", diff, "elapsed", time.Since(start)) + return issuance, nil +} + +// Subsidy calculates the block mining and uncle subsidy as well as the 1559 burn +// solely based on header fields. This method is a very accurate approximation of +// the true issuance, but cannot take into account Ether burns via selfdestructs, +// so it will always be ever so slightly off. +func Subsidy(block *types.Block, config *params.ChainConfig) (subsidy *big.Int, uncles *big.Int, burn *big.Int) { + // Calculate the block subsidy based on chain rules and progression + subsidy = new(big.Int) + uncles = new(big.Int) + + // Select the correct block reward based on chain progression + if config.Ethash != nil { + if block.Difficulty().BitLen() != 0 { + subsidy = ethash.FrontierBlockReward + if config.IsByzantium(block.Number()) { + subsidy = ethash.ByzantiumBlockReward + } + if config.IsConstantinople(block.Number()) { + subsidy = ethash.ConstantinopleBlockReward + } + } + // Accumulate the rewards for inclded uncles + var ( + big8 = big.NewInt(8) + big32 = big.NewInt(32) + r = new(big.Int) + ) + for _, uncle := range block.Uncles() { + // Add the reward for the side blocks + r.Add(uncle.Number, big8) + r.Sub(r, block.Number()) + r.Mul(r, subsidy) + r.Div(r, big8) + uncles.Add(uncles, r) + + // Add the reward for accumulating the side blocks + r.Div(subsidy, big32) + uncles.Add(uncles, r) + } + } + // Calculate the burn based on chain rules and progression + burn = new(big.Int) + if block.BaseFee() != nil { + burn = new(big.Int).Mul(new(big.Int).SetUint64(block.GasUsed()), block.BaseFee()) + } + return subsidy, uncles, burn +} + +// Supply crawls the state snapshot at a given header and gatheres all the account +// balances to sum into the total Ether supply. +func Supply(header *types.Header, snaptree *snapshot.Tree) (*big.Int, error) { + accIt, err := snaptree.AccountIterator(header.Root, common.Hash{}) + if err != nil { + return nil, err + } + defer accIt.Release() + + log.Info("Ether supply counting started", "block", header.Number, "hash", header.Hash(), "root", header.Root) + var ( + start = time.Now() + logged = time.Now() + accounts uint64 + ) + supply := big.NewInt(0) + for accIt.Next() { + account, err := snapshot.FullAccount(accIt.Account()) + if err != nil { + return nil, err + } + supply.Add(supply, account.Balance) + accounts++ + if time.Since(logged) > 8*time.Second { + log.Info("Ether supply counting in progress", "at", accIt.Hash(), + "accounts", accounts, "supply", supply, "elapsed", common.PrettyDuration(time.Since(start))) + logged = time.Now() + } + } + log.Info("Ether supply counting complete", "block", header.Number, "hash", header.Hash(), "root", header.Root, + "accounts", accounts, "supply", supply, "elapsed", common.PrettyDuration(time.Since(start))) + + return supply, nil +} diff --git a/core/rawdb/accessors_metadata.go b/core/rawdb/accessors_metadata.go index f5a161adb688..4db9d95ec127 100644 --- a/core/rawdb/accessors_metadata.go +++ b/core/rawdb/accessors_metadata.go @@ -18,6 +18,7 @@ package rawdb import ( "encoding/json" + "math/big" "time" "github.com/ethereum/go-ethereum/common" @@ -186,3 +187,38 @@ func WriteTransitionStatus(db ethdb.KeyValueWriter, data []byte) { log.Crit("Failed to store the eth2 transition status", "err", err) } } + +// ReadIssuance retrieves the amount of Ether (in Wei) issued (or burnt) in a +// specific block. If unavailable for the specific block (non full synced node), +// nil will be returned. +func ReadIssuance(db ethdb.KeyValueReader, number uint64, hash common.Hash) *big.Int { + blob, _ := db.Get(issuanceKey(number, hash)) + if len(blob) < 2 { + return nil + } + // Since negative big ints can't be encoded to bytes directly, use a dirty + // hack to store the negativift flag in the first byte (0 == positive, + // 1 == negative) + issuance := new(big.Int).SetBytes(blob[1:]) + if blob[0] == 1 { + issuance.Neg(issuance) + } + return issuance +} + +// WriteIssuance stores the amount of Ether (in wei) issued (or burnt) in a +// specific block. +func WriteIssuance(db ethdb.KeyValueWriter, number uint64, hash common.Hash, issuance *big.Int) { + // Since negative big ints can't be encoded to bytes directly, use a dirty + // hack to store the negativift flag in the first byte (0 == positive, + // 1 == negative) + blob := []byte{0} + if issuance.Sign() < 0 { + blob[0] = 1 + } + blob = append(blob, issuance.Bytes()...) + + if err := db.Put(issuanceKey(number, hash), blob); err != nil { + log.Crit("Failed to store block issuance", "err", err) + } +} diff --git a/core/rawdb/database.go b/core/rawdb/database.go index 54107dbc8015..16a5063229a2 100644 --- a/core/rawdb/database.go +++ b/core/rawdb/database.go @@ -339,6 +339,7 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error { bloomBits stat beaconHeaders stat cliqueSnaps stat + issuanceDiffs stat // Ancient store statistics ancientHeadersSize common.StorageSize @@ -400,6 +401,8 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error { bloomBits.Add(size) case bytes.HasPrefix(key, skeletonHeaderPrefix) && len(key) == (len(skeletonHeaderPrefix)+8): beaconHeaders.Add(size) + case bytes.HasPrefix(key, issuancePrefix) && len(key) == (len(issuancePrefix)+8+common.HashLength): + issuanceDiffs.Add(size) case bytes.HasPrefix(key, []byte("clique-")) && len(key) == 7+common.HashLength: cliqueSnaps.Add(size) case bytes.HasPrefix(key, []byte("cht-")) || @@ -464,6 +467,7 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error { {"Key-Value store", "Storage snapshot", storageSnaps.Size(), storageSnaps.Count()}, {"Key-Value store", "Beacon sync headers", beaconHeaders.Size(), beaconHeaders.Count()}, {"Key-Value store", "Clique snapshots", cliqueSnaps.Size(), cliqueSnaps.Count()}, + {"Key-Value store", "Issuance counters", issuanceDiffs.Size(), issuanceDiffs.Count()}, {"Key-Value store", "Singleton metadata", metadata.Size(), metadata.Count()}, {"Ancient store", "Headers", ancientHeadersSize.String(), ancients.String()}, {"Ancient store", "Bodies", ancientBodiesSize.String(), ancients.String()}, diff --git a/core/rawdb/schema.go b/core/rawdb/schema.go index 08f373488056..f48e80542701 100644 --- a/core/rawdb/schema.go +++ b/core/rawdb/schema.go @@ -97,6 +97,8 @@ var ( CodePrefix = []byte("c") // CodePrefix + code hash -> account code skeletonHeaderPrefix = []byte("S") // skeletonHeaderPrefix + num (uint64 big endian) -> header + issuancePrefix = []byte("e") // issuancePrefix + num (uint64 big endian) + hash -> wei diff + PreimagePrefix = []byte("secure-key-") // PreimagePrefix + hash -> preimage configPrefix = []byte("ethereum-config-") // config prefix for the db genesisPrefix = []byte("ethereum-genesis-") // genesis state prefix for the db @@ -248,3 +250,8 @@ func configKey(hash common.Hash) []byte { func genesisKey(hash common.Hash) []byte { return append(genesisPrefix, hash.Bytes()...) } + +// issuanceKey = issuancePrefix + num (uint64 big endian) + hash +func issuanceKey(number uint64, hash common.Hash) []byte { + return append(append(issuancePrefix, encodeBlockNumber(number)...), hash.Bytes()...) +} diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 2b26c044af18..d495835c7b08 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -30,6 +30,7 @@ type Config struct { Tracer EVMLogger // Opcode logger NoBaseFee bool // Forces the EIP-1559 baseFee to 0 (needed for 0 price calls) EnablePreimageRecording bool // Enables recording of SHA3/keccak preimages + EnableIssuanceRecording bool // Enables recording Ether issuance counters JumpTable *JumpTable // EVM instruction table, automatically populated if unset diff --git a/eth/api.go b/eth/api.go index f81dfa922b7a..06df05f5fd5e 100644 --- a/eth/api.go +++ b/eth/api.go @@ -31,6 +31,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/issuance" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" @@ -67,6 +68,101 @@ func (api *PublicEthereumAPI) Hashrate() hexutil.Uint64 { return hexutil.Uint64(api.e.Miner().Hashrate()) } +// Issuance send a notification each time a new block is appended to the chain +// with various counters about Ether issuance: the state diff (if available), +// block and uncle subsidy, 1559 burn. +func (api *PublicEthereumAPI) Issuance(ctx context.Context, from uint64) (*rpc.Subscription, error) { + // If issuance tracking is not explcitly enabled, refuse to service this + // endpoint. Although we could enable the simple calculations, it might + // end up as an unexpected load on RPC providers, so let's not surprise. + if !api.e.config.EnableIssuanceRecording { + return nil, errors.New("issuance recording not enabled") + } + config := api.e.blockchain.Config() + + // Issuance recording enabled, create a subscription to stream through + notifier, supported := rpc.NotifierFromContext(ctx) + if !supported { + return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported + } + rpcSub := notifier.CreateSubscription() + + // Define an internal type for issuance notifications + type issuanceNotification struct { + Number uint64 `json:"block"` + Hash common.Hash `json:"hash"` + Issuance *big.Int `json:"issuance"` + Subsidy *big.Int `json:"subsidy"` + Uncles *big.Int `json:"uncles"` + Burn *big.Int `json:"burn"` + Destruct *big.Int `json:"destruct"` + } + // Define a method to convert a block into an issuance notification + service := func(block *types.Block) { + // Retrieve the state-crawled issuance - if available + crawled := rawdb.ReadIssuance(api.e.chainDb, block.NumberU64(), block.Hash()) + + // Calculate the subsidy from the block's contents + subsidy, uncles, burn := issuance.Subsidy(block, config) + + // Calculate the difference between the "calculated" and "crawled" issuance + var diff *big.Int + if crawled != nil { + diff = new(big.Int).Set(crawled) + diff.Sub(diff, subsidy) + diff.Sub(diff, uncles) + diff.Add(diff, burn) + } + // Push the issuance to the user + notifier.Notify(rpcSub.ID, &issuanceNotification{ + Number: block.NumberU64(), + Hash: block.Hash(), + Issuance: crawled, + Subsidy: subsidy, + Uncles: uncles, + Burn: burn, + Destruct: diff, + }) + } + go func() { + // Iterate over all blocks from the requested source up to head and push + // out historical issuance values to the user. Checking the head after + // each iteration is a bit heavy, but it's not really relevant compared + // to pulling blocks from disk, so this keeps thing simpler to switch + // from historicla blocks to live blocks. + for number := from; number <= api.e.blockchain.CurrentBlock().NumberU64(); number++ { + block := rawdb.ReadBlock(api.e.chainDb, rawdb.ReadCanonicalHash(api.e.chainDb, number), number) + if block == nil { + log.Error("Missing block for issuane reporting", "number", number) + return + } + service(block) + } + // Subscribe to chain events and keep emitting issuances on all branches + canonBlocks := make(chan core.ChainEvent) + canonBlocksSub := api.e.blockchain.SubscribeChainEvent(canonBlocks) + defer canonBlocksSub.Unsubscribe() + + sideBlocks := make(chan core.ChainSideEvent) + sideBlocksSub := api.e.blockchain.SubscribeChainSideEvent(sideBlocks) + defer sideBlocksSub.Unsubscribe() + + for { + select { + case event := <-canonBlocks: + service(event.Block) + case event := <-sideBlocks: + service(event.Block) + case <-rpcSub.Err(): + return + case <-notifier.Closed(): + return + } + } + }() + return rpcSub, nil +} + // PublicMinerAPI provides an API to control the miner. // It offers only methods that operate on data that pose no security risk when it is publicly accessible. type PublicMinerAPI struct { diff --git a/eth/backend.go b/eth/backend.go index 4e6d71523531..0b96e0680a3c 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -183,6 +183,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { var ( vmConfig = vm.Config{ EnablePreimageRecording: config.EnablePreimageRecording, + EnableIssuanceRecording: config.EnableIssuanceRecording, } cacheConfig = &core.CacheConfig{ TrieCleanLimit: config.TrieCleanCache, diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index c8de3c9d144e..5598688bfcd4 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -186,8 +186,8 @@ type Config struct { // Enables tracking of SHA3 preimages in the VM EnablePreimageRecording bool - // Miscellaneous options - DocRoot string `toml:"-"` + // Enables tracking Ether issuance during block processing + EnableIssuanceRecording bool // RPCGasCap is the global gas cap for eth-call variants. RPCGasCap uint64 diff --git a/eth/ethconfig/gen_config.go b/eth/ethconfig/gen_config.go index 874e30dffdac..9c1d6f76d8cb 100644 --- a/eth/ethconfig/gen_config.go +++ b/eth/ethconfig/gen_config.go @@ -53,7 +53,7 @@ func (c Config) MarshalTOML() (interface{}, error) { TxPool core.TxPoolConfig GPO gasprice.Config EnablePreimageRecording bool - DocRoot string `toml:"-"` + EnableIssuanceRecording bool RPCGasCap uint64 RPCEVMTimeout time.Duration RPCTxFeeCap float64 @@ -98,7 +98,7 @@ func (c Config) MarshalTOML() (interface{}, error) { enc.TxPool = c.TxPool enc.GPO = c.GPO enc.EnablePreimageRecording = c.EnablePreimageRecording - enc.DocRoot = c.DocRoot + enc.EnableIssuanceRecording = c.EnableIssuanceRecording enc.RPCGasCap = c.RPCGasCap enc.RPCEVMTimeout = c.RPCEVMTimeout enc.RPCTxFeeCap = c.RPCTxFeeCap @@ -147,7 +147,7 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { TxPool *core.TxPoolConfig GPO *gasprice.Config EnablePreimageRecording *bool - DocRoot *string `toml:"-"` + EnableIssuanceRecording *bool RPCGasCap *uint64 RPCEVMTimeout *time.Duration RPCTxFeeCap *float64 @@ -265,8 +265,8 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { if dec.EnablePreimageRecording != nil { c.EnablePreimageRecording = *dec.EnablePreimageRecording } - if dec.DocRoot != nil { - c.DocRoot = *dec.DocRoot + if dec.EnableIssuanceRecording != nil { + c.EnableIssuanceRecording = *dec.EnableIssuanceRecording } if dec.RPCGasCap != nil { c.RPCGasCap = *dec.RPCGasCap