diff --git a/CHANGELOG.md b/CHANGELOG.md index ec7c103f68..37d75695b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,23 @@ # Changelog +## v1.5.7 +v1.5.7 conduct small upstream code merge to follow the latest pectra hard fork and apply some bug fix. There are two PR for the code merge: +* [\#2897](https://github.com/bnb-chain/bsc/pull/2897) upstream: merge tag 'geth-v1.15.1' into bsc-develop +* [\#2926](https://github.com/bnb-chain/bsc/pull/2926) upstream: pick bug fix from latest geth + +Besides code merge, there are also several important bugfix/improvements, and setup mainnet Pascal hard fork time: +### FEATURE +* [\#2928](https://github.com/bnb-chain/bsc/pull/2928) config: update BSC Mainnet hardfork date: Pascal & Praque + +### BUGFIX +* [\#2907](https://github.com/bnb-chain/bsc/pull/0000) go.mod: downgrade bls-eth-go-binary to make it same as the prysm-v5.0.0 + +### IMPROVEMENT +* [\#2896](https://github.com/bnb-chain/bsc/pull/2896) consensus/parlia: estimate gas reserved for systemTxs +* [\#2912](https://github.com/bnb-chain/bsc/pull/2912) consensus/parlia: improve performance of func IsSystemTransaction +* [\#2916](https://github.com/bnb-chain/bsc/pull/2916) miner: avoid to collect requests when getting pending blocks +* [\#2913](https://github.com/bnb-chain/bsc/pull/2913) core/vm: add basic test cases for blsSignatureVerify +* [\#2918](https://github.com/bnb-chain/bsc/pull/2918) core/txpool/legacypool/legacypool.go: add gasTip check when reset + ## v1.5.6 v1.5.6 performed another small code sync with Geth upstream, mainly sync the 7702 tx type txpool update, refer: https://github.com/bnb-chain/bsc/pull/2888/ diff --git a/cmd/evm/testdata/evmrun/3.out.1.txt b/cmd/evm/testdata/evmrun/3.out.1.txt index 44956f54f6..25dd8da5a1 100644 --- a/cmd/evm/testdata/evmrun/3.out.1.txt +++ b/cmd/evm/testdata/evmrun/3.out.1.txt @@ -1,12 +1,13 @@ { "root": "b444481d1367188172f8c6091e948aaa68bae763fd26d6b9e994306a66bf69f9", "accounts": { - "pre(0x30d7a0694cb29af31b982480e11d7ebb003a3fca4026939149071f014689b142)": { + "0x0000000000000000000000007265636569766572": { "balance": "0", "nonce": 0, "root": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", "codeHash": "0x3e48ef54b89079a075f3b8fc253c657a86b110a7aed3568c1517b10edf2c3eb6", "code": "0x6040", + "address": "0x0000000000000000000000007265636569766572", "key": "0x30d7a0694cb29af31b982480e11d7ebb003a3fca4026939149071f014689b142" } } diff --git a/cmd/evm/testdata/evmrun/4.out.1.txt b/cmd/evm/testdata/evmrun/4.out.1.txt index 44956f54f6..25dd8da5a1 100644 --- a/cmd/evm/testdata/evmrun/4.out.1.txt +++ b/cmd/evm/testdata/evmrun/4.out.1.txt @@ -1,12 +1,13 @@ { "root": "b444481d1367188172f8c6091e948aaa68bae763fd26d6b9e994306a66bf69f9", "accounts": { - "pre(0x30d7a0694cb29af31b982480e11d7ebb003a3fca4026939149071f014689b142)": { + "0x0000000000000000000000007265636569766572": { "balance": "0", "nonce": 0, "root": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", "codeHash": "0x3e48ef54b89079a075f3b8fc253c657a86b110a7aed3568c1517b10edf2c3eb6", "code": "0x6040", + "address": "0x0000000000000000000000007265636569766572", "key": "0x30d7a0694cb29af31b982480e11d7ebb003a3fca4026939149071f014689b142" } } diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go index f7fd87d696..ebb5972b8f 100644 --- a/cmd/geth/chaincmd.go +++ b/cmd/geth/chaincmd.go @@ -64,6 +64,7 @@ var ( utils.OverridePassedForkTime, utils.OverridePascal, utils.OverridePrague, + utils.OverrideLorentz, utils.OverrideVerkle, utils.MultiDataBaseFlag, }, utils.DatabaseFlags), @@ -268,6 +269,10 @@ func initGenesis(ctx *cli.Context) error { v := ctx.Uint64(utils.OverridePrague.Name) overrides.OverridePrague = &v } + if ctx.IsSet(utils.OverrideLorentz.Name) { + v := ctx.Uint64(utils.OverrideLorentz.Name) + overrides.OverrideLorentz = &v + } if ctx.IsSet(utils.OverrideVerkle.Name) { v := ctx.Uint64(utils.OverrideVerkle.Name) overrides.OverrideVerkle = &v diff --git a/cmd/geth/config.go b/cmd/geth/config.go index d7ffc09e1d..eb9c0208f2 100644 --- a/cmd/geth/config.go +++ b/cmd/geth/config.go @@ -213,6 +213,10 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) { v := ctx.Uint64(utils.OverridePrague.Name) cfg.Eth.OverridePrague = &v } + if ctx.IsSet(utils.OverrideLorentz.Name) { + v := ctx.Uint64(utils.OverrideLorentz.Name) + cfg.Eth.OverrideLorentz = &v + } if ctx.IsSet(utils.OverrideVerkle.Name) { v := ctx.Uint64(utils.OverrideVerkle.Name) cfg.Eth.OverrideVerkle = &v diff --git a/cmd/geth/main.go b/cmd/geth/main.go index e7a61f8254..e8a84b6f1b 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -76,6 +76,7 @@ var ( utils.OverridePassedForkTime, utils.OverridePascal, utils.OverridePrague, + utils.OverrideLorentz, utils.OverrideVerkle, utils.OverrideFullImmutabilityThreshold, utils.OverrideMinBlocksForBlobRequests, @@ -466,8 +467,8 @@ func startNode(ctx *cli.Context, stack *node.Node, backend ethapi.Backend, isCon // Start auxiliary services if enabled ethBackend, ok := backend.(*eth.EthAPIBackend) gasCeil := ethBackend.Miner().GasCeil() - if gasCeil > params.SystemTxsGas { - ethBackend.TxPool().SetMaxGas(gasCeil - params.SystemTxsGas) + if gasCeil > params.SystemTxsGasSoftLimit { + ethBackend.TxPool().SetMaxGas(gasCeil - params.SystemTxsGasSoftLimit) } if ctx.Bool(utils.MiningEnabledFlag.Name) { // Mining only makes sense if a full Ethereum node is running diff --git a/cmd/jsutils/getchainstatus.js b/cmd/jsutils/getchainstatus.js index 98a9675242..cad7cd0508 100644 --- a/cmd/jsutils/getchainstatus.js +++ b/cmd/jsutils/getchainstatus.js @@ -6,6 +6,7 @@ program.option("--startNum ", "start num") program.option("--endNum ", "end num") program.option("--miner ", "miner", "") program.option("--num ", "validator num", 21) +program.option("--turnLength ", "the consecutive block length", 4) program.option("--topNum ", "top num of address to be displayed", 20) program.option("--blockNum ", "block num", 0) program.option("-h, --help", "") @@ -34,7 +35,7 @@ function printUsage() { console.log("\nExample:"); // mainnet https://bsc-mainnet.nodereal.io/v1/454e504917db4f82b756bd0cf6317dce console.log(" node getchainstatus.js GetMaxTxCountInBlockRange --rpc https://bsc-testnet-dataseed.bnbchain.org --startNum 40000001 --endNum 40000005") - console.log(" node getchainstatus.js GetBinaryVersion --rpc https://bsc-testnet-dataseed.bnbchain.org --num 21") + console.log(" node getchainstatus.js GetBinaryVersion --rpc https://bsc-testnet-dataseed.bnbchain.org --num 21 --turnLength 4") console.log(" node getchainstatus.js GetTopAddr --rpc https://bsc-testnet-dataseed.bnbchain.org --startNum 40000001 --endNum 40000010 --topNum 10") console.log(" node getchainstatus.js GetSlashCount --rpc https://bsc-testnet-dataseed.bnbchain.org --blockNum 40000001") // default: latest block console.log(" node getchainstatus.js GetPerformanceData --rpc https://bsc-testnet-dataseed.bnbchain.org --startNum 40000001 --endNum 40000010") @@ -193,12 +194,13 @@ async function getMaxTxCountInBlockRange() { // 2.cmd: "GetBinaryVersion", usage: // node getchainstatus.js GetBinaryVersion \ // --rpc https://bsc-testnet-dataseed.bnbchain.org \ -// --num(optional): defualt 21, the number of blocks that will be checked +// --num(optional): default 21, the number of blocks that will be checked +// --turnLength(optional): default 4, the consecutive block length async function getBinaryVersion() { const blockNum = await provider.getBlockNumber(); - console.log(blockNum); + let turnLength = program.turnLength for (let i = 0; i < program.num; i++) { - let blockData = await provider.getBlock(blockNum - i); + let blockData = await provider.getBlock(blockNum - i*turnLength); // 1.get Geth client version let major = ethers.toNumber(ethers.dataSlice(blockData.extraData, 2, 3)) let minor = ethers.toNumber(ethers.dataSlice(blockData.extraData, 3, 4)) @@ -215,7 +217,8 @@ async function getBinaryVersion() { lastGasPrice = txData.gasPrice break } - console.log(blockData.miner, "version =", major + "." + minor + "." + patch, " MinGasPrice = " + lastGasPrice) + var moniker = await getValidatorMoniker(blockData.miner, blockNum) + console.log(blockNum - i*turnLength, blockData.miner, "version =", major + "." + minor + "." + patch, " MinGasPrice = " + lastGasPrice, moniker) } }; diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index f7eedab80e..9feee1c293 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -311,6 +311,11 @@ var ( Usage: "Manually specify the Prague fork timestamp, overriding the bundled setting", Category: flags.EthCategory, } + OverrideLorentz = &cli.Uint64Flag{ + Name: "override.lorentz", + Usage: "Manually specify the Lorentz fork timestamp, overriding the bundled setting", + Category: flags.EthCategory, + } OverrideVerkle = &cli.Uint64Flag{ Name: "override.verkle", Usage: "Manually specify the Verkle fork timestamp, overriding the bundled setting", diff --git a/consensus/parlia/parlia.go b/consensus/parlia/parlia.go index 64b4b12a3c..815946597a 100644 --- a/consensus/parlia/parlia.go +++ b/consensus/parlia/parlia.go @@ -321,18 +321,17 @@ func (p *Parlia) Period() uint64 { } func (p *Parlia) IsSystemTransaction(tx *types.Transaction, header *types.Header) (bool, error) { - // deploy a contract - if tx.To() == nil { + if tx.To() == nil || !isToSystemContract(*tx.To()) { + return false, nil + } + if tx.GasPrice().Sign() != 0 { return false, nil } sender, err := types.Sender(p.signer, tx) if err != nil { return false, errors.New("UnAuthorized transaction") } - if sender == header.Coinbase && isToSystemContract(*tx.To()) && tx.GasPrice().Cmp(big.NewInt(0)) == 0 { - return true, nil - } - return false, nil + return sender == header.Coinbase, nil } func (p *Parlia) IsSystemContract(to *common.Address) bool { @@ -1258,6 +1257,44 @@ func (p *Parlia) distributeFinalityReward(chain consensus.ChainHeaderReader, sta return p.applyTransaction(msg, state, header, cx, txs, receipts, systemTxs, usedGas, mining, tracer) } +func (p *Parlia) EstimateGasReservedForSystemTxs(chain consensus.ChainHeaderReader, header *types.Header) uint64 { + parent := chain.GetHeaderByHash(header.ParentHash) + if parent != nil { + // Mainnet and Chapel have both passed Feynman. Now, simplify the logic before and during the Feynman hard fork. + if p.chainConfig.IsFeynman(header.Number, header.Time) && + !p.chainConfig.IsOnFeynman(header.Number, parent.Time, header.Time) { + // const ( + // the following values represent the maximum values found in the most recent blocks on the mainnet + // depositTxGas = uint64(60_000) + // slashTxGas = uint64(140_000) + // finalityRewardTxGas = uint64(350_000) + // updateValidatorTxGas = uint64(12_160_000) + // ) + // suggestReservedGas := depositTxGas + // if header.Difficulty.Cmp(diffInTurn) != 0 { + // snap, err := p.snapshot(chain, header.Number.Uint64()-1, header.ParentHash, nil) + // if err != nil || !snap.SignRecently(snap.inturnValidator()) { + // suggestReservedGas += slashTxGas + // } + // } + // if header.Number.Uint64()%p.config.Epoch == 0 { + // suggestReservedGas += finalityRewardTxGas + // } + // if isBreatheBlock(parent.Time, header.Time) { + // suggestReservedGas += updateValidatorTxGas + // } + // return suggestReservedGas * 150 / 100 + if !isBreatheBlock(parent.Time, header.Time) { + // params.SystemTxsGasSoftLimit > (depositTxGas+slashTxGas+finalityRewardTxGas)*150/100 + return params.SystemTxsGasSoftLimit + } + } + } + + // params.SystemTxsGasHardLimit > (depositTxGas+slashTxGas+finalityRewardTxGas+updateValidatorTxGas)*150/100 + return params.SystemTxsGasHardLimit +} + // Finalize implements consensus.Engine, ensuring no uncles are set, nor block // rewards given. func (p *Parlia) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state vm.StateDB, txs *[]*types.Transaction, diff --git a/core/genesis.go b/core/genesis.go index 8e5ee7216c..ce8c9ae1d4 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -266,6 +266,7 @@ type ChainOverrides struct { OverridePassedForkTime *uint64 OverridePascal *uint64 OverridePrague *uint64 + OverrideLorentz *uint64 OverrideVerkle *uint64 } @@ -290,6 +291,9 @@ func (o *ChainOverrides) apply(cfg *params.ChainConfig) error { if o.OverridePrague != nil { cfg.PragueTime = o.OverridePrague } + if o.OverrideLorentz != nil { + cfg.LorentzTime = o.OverrideLorentz + } if o.OverrideVerkle != nil { cfg.VerkleTime = o.OverrideVerkle } @@ -387,6 +391,11 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *triedb.Database, g } newCfg := genesis.chainConfigOrDefault(ghash, storedCfg) + // Sanity-check the new configuration. + if err := newCfg.CheckConfigForkOrder(); err != nil { + return nil, common.Hash{}, nil, err + } + // TODO(rjl493456442) better to define the comparator of chain config // and short circuit if the chain config is not changed. compatErr := storedCfg.CheckCompatible(newCfg, head.Number.Uint64(), head.Time) diff --git a/core/rawdb/freezer_table.go b/core/rawdb/freezer_table.go index f3eb860932..1891b225cb 100644 --- a/core/rawdb/freezer_table.go +++ b/core/rawdb/freezer_table.go @@ -420,6 +420,13 @@ func (t *freezerTable) repairIndex() error { // If legacy metadata is detected, attempt to recover the offset from the // index file to avoid clearing the entire table. if t.metadata.version == freezerTableV1 { + // Skip truncation if the legacy metadata is opened in read-only mode. + // Since all items in the legacy index file were forcibly synchronized, + // data integrity is guaranteed. Therefore, it's safe to leave any extra + // items untruncated in this special scenario. + if t.readonly { + return nil + } t.logger.Info("Recovering freezer flushOffset for legacy table", "offset", size) return t.metadata.setFlushOffset(size, true) } diff --git a/core/txpool/errors.go b/core/txpool/errors.go index 4b87137123..469b79b968 100644 --- a/core/txpool/errors.go +++ b/core/txpool/errors.go @@ -51,10 +51,6 @@ var ( // making the transaction invalid, rather a DOS protection. ErrOversizedData = errors.New("oversized data") - // ErrFutureReplacePending is returned if a future transaction replaces a pending - // one. Future transactions should only be able to replace other future transactions. - ErrFutureReplacePending = errors.New("future transaction tries to replace pending") - // ErrAlreadyReserved is returned if the sender address has a pending transaction // in a different subpool. For example, this error is returned in response to any // input transaction of non-blob type when a blob transaction from this sender @@ -63,13 +59,4 @@ var ( // ErrInBlackList is returned if the transaction send by banned address ErrInBlackList = errors.New("sender or to in black list") - - // ErrAuthorityReserved is returned if a transaction has an authorization - // signed by an address which already has in-flight transactions known to the - // pool. - ErrAuthorityReserved = errors.New("authority already reserved") - - // ErrAuthorityNonce is returned if a transaction has an authorization with - // a nonce that is not currently valid for the authority. - ErrAuthorityNonceTooLow = errors.New("authority nonce too low") ) diff --git a/core/txpool/legacypool/legacypool.go b/core/txpool/legacypool/legacypool.go index e6c599edf2..56b3fc7329 100644 --- a/core/txpool/legacypool/legacypool.go +++ b/core/txpool/legacypool/legacypool.go @@ -65,6 +65,19 @@ var ( // ErrTxPoolOverflow is returned if the transaction pool is full and can't accept // another remote transaction. ErrTxPoolOverflow = errors.New("txpool is full") + + // ErrInflightTxLimitReached is returned when the maximum number of in-flight + // transactions is reached for specific accounts. + ErrInflightTxLimitReached = errors.New("in-flight transaction limit reached for delegated accounts") + + // ErrAuthorityReserved is returned if a transaction has an authorization + // signed by an address which already has in-flight transactions known to the + // pool. + ErrAuthorityReserved = errors.New("authority already reserved") + + // ErrFutureReplacePending is returned if a future transaction replaces a pending + // one. Future transactions should only be able to replace other future transactions. + ErrFutureReplacePending = errors.New("future transaction tries to replace pending") ) var ( @@ -657,22 +670,8 @@ func (pool *LegacyPool) validateTx(tx *types.Transaction) error { opts := &txpool.ValidationOptionsWithState{ State: pool.currentState, - FirstNonceGap: nil, // Pool allows arbitrary arrival order, don't invalidate nonce gaps - UsedAndLeftSlots: func(addr common.Address) (int, int) { - var have int - if list := pool.pending[addr]; list != nil { - have += list.Len() - } - if list := pool.queue[addr]; list != nil { - have += list.Len() - } - if pool.currentState.GetCodeHash(addr) != types.EmptyCodeHash || len(pool.all.auths[addr]) != 0 { - // Allow at most one in-flight tx for delegated accounts or those with - // a pending authorization. - return have, max(0, 1-have) - } - return have, math.MaxInt - }, + FirstNonceGap: nil, // Pool allows arbitrary arrival order, don't invalidate nonce gaps + UsedAndLeftSlots: nil, // Pool has own mechanism to limit the number of transactions ExistingExpenditure: func(addr common.Address) *big.Int { if list := pool.pending[addr]; list != nil { return list.totalcost.ToBig() @@ -687,22 +686,49 @@ func (pool *LegacyPool) validateTx(tx *types.Transaction) error { } return nil }, - KnownConflicts: func(from common.Address, auths []common.Address) []common.Address { - var conflicts []common.Address - // Authorities cannot conflict with any pending or queued transactions. - for _, addr := range auths { - if list := pool.pending[addr]; list != nil { - conflicts = append(conflicts, addr) - } else if list := pool.queue[addr]; list != nil { - conflicts = append(conflicts, addr) - } - } - return conflicts - }, } if err := txpool.ValidateTransactionWithState(tx, pool.signer, opts); err != nil { return err } + return pool.validateAuth(tx) +} + +// validateAuth verifies that the transaction complies with code authorization +// restrictions brought by SetCode transaction type. +func (pool *LegacyPool) validateAuth(tx *types.Transaction) error { + from, _ := types.Sender(pool.signer, tx) // validated + + // Allow at most one in-flight tx for delegated accounts or those with a + // pending authorization. + if pool.currentState.GetCodeHash(from) != types.EmptyCodeHash || len(pool.all.auths[from]) != 0 { + var ( + count int + exists bool + ) + pending := pool.pending[from] + if pending != nil { + count += pending.Len() + exists = pending.Contains(tx.Nonce()) + } + queue := pool.queue[from] + if queue != nil { + count += queue.Len() + exists = exists || queue.Contains(tx.Nonce()) + } + // Replace the existing in-flight transaction for delegated accounts + // are still supported + if count >= 1 && !exists { + return ErrInflightTxLimitReached + } + } + // Authorities cannot conflict with any pending or queued transactions. + if auths := tx.SetCodeAuthorities(); len(auths) > 0 { + for _, auth := range auths { + if pool.pending[auth] != nil || pool.queue[auth] != nil { + return ErrAuthorityReserved + } + } + } return nil } @@ -794,7 +820,7 @@ func (pool *LegacyPool) add(tx *types.Transaction) (replaced bool, err error) { pool.priced.Put(dropTx) } log.Trace("Discarding future transaction replacing pending tx", "hash", hash) - return false, txpool.ErrFutureReplacePending + return false, ErrFutureReplacePending } } @@ -1423,8 +1449,9 @@ func (pool *LegacyPool) reset(oldHead, newHead *types.Header) { } } lost := make([]*types.Transaction, 0, len(discarded)) + gasTip := pool.gasTip.Load().ToBig() for _, tx := range types.TxDifference(discarded, included) { - if pool.Filter(tx) { + if pool.Filter(tx) && tx.GasTipCapIntCmp(gasTip) >= 0 { lost = append(lost, tx) } } @@ -1848,12 +1875,12 @@ func (t *lookup) Remove(hash common.Hash) { t.lock.Lock() defer t.lock.Unlock() - t.removeAuthorities(hash) tx, ok := t.txs[hash] if !ok { log.Error("No transaction found to be deleted", "hash", hash) return } + t.removeAuthorities(tx) t.slots -= numSlots(tx) slotsGauge.Update(int64(t.slots)) @@ -1891,8 +1918,9 @@ func (t *lookup) addAuthorities(tx *types.Transaction) { // removeAuthorities stops tracking the supplied tx in relation to its // authorities. -func (t *lookup) removeAuthorities(hash common.Hash) { - for addr := range t.auths { +func (t *lookup) removeAuthorities(tx *types.Transaction) { + hash := tx.Hash() + for _, addr := range tx.SetCodeAuthorities() { list := t.auths[addr] // Remove tx from tracker. if i := slices.Index(list, hash); i >= 0 { diff --git a/core/txpool/legacypool/legacypool_test.go b/core/txpool/legacypool/legacypool_test.go index 0818c28fb0..8e3b6dc45a 100644 --- a/core/txpool/legacypool/legacypool_test.go +++ b/core/txpool/legacypool/legacypool_test.go @@ -23,6 +23,7 @@ import ( "fmt" "math/big" "math/rand" + "slices" "sync" "sync/atomic" "testing" @@ -150,6 +151,10 @@ func pricedSetCodeTx(nonce uint64, gaslimit uint64, gasFee, tip *uint256.Int, ke }) authList = append(authList, auth) } + return pricedSetCodeTxWithAuth(nonce, gaslimit, gasFee, tip, key, authList) +} + +func pricedSetCodeTxWithAuth(nonce uint64, gaslimit uint64, gasFee, tip *uint256.Int, key *ecdsa.PrivateKey, authList []types.SetCodeAuthorization) *types.Transaction { return types.MustSignNewTx(key, types.LatestSignerForChainID(params.TestChainConfig.ChainID), &types.SetCodeTx{ ChainID: uint256.MustFromBig(params.TestChainConfig.ChainID), Nonce: nonce, @@ -235,6 +240,23 @@ func validatePoolInternals(pool *LegacyPool) error { return fmt.Errorf("pending nonce mismatch: have %v, want %v", nonce, last+1) } } + // Ensure all auths in pool are tracked + for _, tx := range pool.all.txs { + for _, addr := range tx.SetCodeAuthorities() { + list := pool.all.auths[addr] + if i := slices.Index(list, tx.Hash()); i < 0 { + return fmt.Errorf("authority not tracked: addr %s, tx %s", addr, tx.Hash()) + } + } + } + // Ensure all auths in pool have an associated tx. + for addr, hashes := range pool.all.auths { + for _, hash := range hashes { + if _, ok := pool.all.txs[hash]; !ok { + return fmt.Errorf("dangling authority, missing originating tx: addr %s, hash %s", addr, hash.Hex()) + } + } + } return nil } @@ -1643,8 +1665,8 @@ func TestUnderpricing(t *testing.T) { t.Fatalf("failed to add well priced transaction: %v", err) } // Ensure that replacing a pending transaction with a future transaction fails - if err := pool.addRemoteSync(pricedTransaction(5, 100000, big.NewInt(6), keys[1])); err != txpool.ErrFutureReplacePending { - t.Fatalf("adding future replace transaction error mismatch: have %v, want %v", err, txpool.ErrFutureReplacePending) + if err := pool.addRemoteSync(pricedTransaction(5, 100000, big.NewInt(6), keys[1])); err != ErrFutureReplacePending { + t.Fatalf("adding future replace transaction error mismatch: have %v, want %v", err, ErrFutureReplacePending) } pending, queued = pool.Stats() if pending != 4 { @@ -2345,12 +2367,12 @@ func TestSetCodeTransactions(t *testing.T) { if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1), keyA)); err != nil { t.Fatalf("%s: failed to add remote transaction: %v", name, err) } - if err := pool.addRemoteSync(pricedTransaction(1, 100000, big.NewInt(1), keyA)); !errors.Is(err, txpool.ErrAccountLimitExceeded) { - t.Fatalf("%s: error mismatch: want %v, have %v", name, txpool.ErrAccountLimitExceeded, err) + if err := pool.addRemoteSync(pricedTransaction(1, 100000, big.NewInt(1), keyA)); !errors.Is(err, ErrInflightTxLimitReached) { + t.Fatalf("%s: error mismatch: want %v, have %v", name, ErrInflightTxLimitReached, err) } // Also check gapped transaction. - if err := pool.addRemoteSync(pricedTransaction(2, 100000, big.NewInt(1), keyA)); !errors.Is(err, txpool.ErrAccountLimitExceeded) { - t.Fatalf("%s: error mismatch: want %v, have %v", name, txpool.ErrAccountLimitExceeded, err) + if err := pool.addRemoteSync(pricedTransaction(2, 100000, big.NewInt(1), keyA)); !errors.Is(err, ErrInflightTxLimitReached) { + t.Fatalf("%s: error mismatch: want %v, have %v", name, ErrInflightTxLimitReached, err) } // Replace by fee. if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(10), keyA)); err != nil { @@ -2384,8 +2406,8 @@ func TestSetCodeTransactions(t *testing.T) { t.Fatalf("%s: failed to add with pending delegatio: %v", name, err) } // Also check gapped transaction is rejected. - if err := pool.addRemoteSync(pricedTransaction(1, 100000, big.NewInt(1), keyC)); !errors.Is(err, txpool.ErrAccountLimitExceeded) { - t.Fatalf("%s: error mismatch: want %v, have %v", name, txpool.ErrAccountLimitExceeded, err) + if err := pool.addRemoteSync(pricedTransaction(1, 100000, big.NewInt(1), keyC)); !errors.Is(err, ErrInflightTxLimitReached) { + t.Fatalf("%s: error mismatch: want %v, have %v", name, ErrInflightTxLimitReached, err) } }, }, @@ -2460,7 +2482,7 @@ func TestSetCodeTransactions(t *testing.T) { if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1000), keyC)); err != nil { t.Fatalf("%s: failed to added single pooled for account with pending delegation: %v", name, err) } - if err, want := pool.addRemoteSync(pricedTransaction(1, 100000, big.NewInt(1000), keyC)), txpool.ErrAccountLimitExceeded; !errors.Is(err, want) { + if err, want := pool.addRemoteSync(pricedTransaction(1, 100000, big.NewInt(1000), keyC)), ErrInflightTxLimitReached; !errors.Is(err, want) { t.Fatalf("%s: error mismatch: want %v, have %v", name, want, err) } }, @@ -2473,11 +2495,37 @@ func TestSetCodeTransactions(t *testing.T) { if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1000), keyC)); err != nil { t.Fatalf("%s: failed to add with remote setcode transaction: %v", name, err) } - if err, want := pool.addRemoteSync(setCodeTx(0, keyA, []unsignedAuth{{1, keyC}})), txpool.ErrAuthorityReserved; !errors.Is(err, want) { + if err, want := pool.addRemoteSync(setCodeTx(0, keyA, []unsignedAuth{{1, keyC}})), ErrAuthorityReserved; !errors.Is(err, want) { t.Fatalf("%s: error mismatch: want %v, have %v", name, want, err) } }, }, + { + name: "remove-hash-from-authority-tracker", + pending: 10, + run: func(name string) { + var keys []*ecdsa.PrivateKey + for i := 0; i < 30; i++ { + key, _ := crypto.GenerateKey() + keys = append(keys, key) + addr := crypto.PubkeyToAddress(key.PublicKey) + testAddBalance(pool, addr, big.NewInt(params.Ether)) + } + // Create a transactions with 3 unique auths so the lookup's auth map is + // filled with addresses. + for i := 0; i < 30; i += 3 { + if err := pool.addRemoteSync(pricedSetCodeTx(0, 250000, uint256.NewInt(10), uint256.NewInt(3), keys[i], []unsignedAuth{{0, keys[i]}, {0, keys[i+1]}, {0, keys[i+2]}})); err != nil { + t.Fatalf("%s: failed to add with remote setcode transaction: %v", name, err) + } + } + // Replace one of the transactions with a normal transaction so that the + // original hash is removed from the tracker. The hash should be + // associated with 3 different authorities. + if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1000), keys[0])); err != nil { + t.Fatalf("%s: failed to replace with remote transaction: %v", name, err) + } + }, + }, } { tt.run(tt.name) pending, queued := pool.Stats() @@ -2494,6 +2542,65 @@ func TestSetCodeTransactions(t *testing.T) { } } +func TestSetCodeTransactionsReorg(t *testing.T) { + t.Parallel() + + // Create the pool to test the status retrievals with + statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting()) + blockchain := newTestBlockChain(params.MergedTestChainConfig, 1000000, statedb, new(event.Feed)) + + pool := New(testTxPoolConfig, blockchain) + pool.Init(testTxPoolConfig.PriceLimit, blockchain.CurrentBlock(), makeAddressReserver()) + defer pool.Close() + + // Create the test accounts + var ( + keyA, _ = crypto.GenerateKey() + addrA = crypto.PubkeyToAddress(keyA.PublicKey) + ) + testAddBalance(pool, addrA, big.NewInt(params.Ether)) + // Send an authorization for 0x42 + var authList []types.SetCodeAuthorization + auth, _ := types.SignSetCode(keyA, types.SetCodeAuthorization{ + ChainID: *uint256.MustFromBig(params.TestChainConfig.ChainID), + Address: common.Address{0x42}, + Nonce: 0, + }) + authList = append(authList, auth) + if err := pool.addRemoteSync(pricedSetCodeTxWithAuth(0, 250000, uint256.NewInt(10), uint256.NewInt(3), keyA, authList)); err != nil { + t.Fatalf("failed to add with remote setcode transaction: %v", err) + } + // Simulate the chain moving + blockchain.statedb.SetNonce(addrA, 1, tracing.NonceChangeAuthorization) + blockchain.statedb.SetCode(addrA, types.AddressToDelegation(auth.Address)) + <-pool.requestReset(nil, nil) + // Set an authorization for 0x00 + auth, _ = types.SignSetCode(keyA, types.SetCodeAuthorization{ + ChainID: *uint256.MustFromBig(params.TestChainConfig.ChainID), + Address: common.Address{}, + Nonce: 0, + }) + authList = append(authList, auth) + if err := pool.addRemoteSync(pricedSetCodeTxWithAuth(1, 250000, uint256.NewInt(10), uint256.NewInt(3), keyA, authList)); err != nil { + t.Fatalf("failed to add with remote setcode transaction: %v", err) + } + // Try to add a transactions in + if err := pool.addRemoteSync(pricedTransaction(2, 100000, big.NewInt(1000), keyA)); !errors.Is(err, ErrInflightTxLimitReached) { + t.Fatalf("unexpected error %v, expecting %v", err, ErrInflightTxLimitReached) + } + // Simulate the chain moving + blockchain.statedb.SetNonce(addrA, 2, tracing.NonceChangeAuthorization) + blockchain.statedb.SetCode(addrA, nil) + <-pool.requestReset(nil, nil) + // Now send two transactions from addrA + if err := pool.addRemoteSync(pricedTransaction(2, 100000, big.NewInt(1000), keyA)); err != nil { + t.Fatalf("failed to added single transaction: %v", err) + } + if err := pool.addRemoteSync(pricedTransaction(3, 100000, big.NewInt(1000), keyA)); err != nil { + t.Fatalf("failed to added single transaction: %v", err) + } +} + // Benchmarks the speed of validating the contents of the pending queue of the // transaction pool. func BenchmarkPendingDemotion100(b *testing.B) { benchmarkPendingDemotion(b, 100) } diff --git a/core/txpool/validation.go b/core/txpool/validation.go index 043270811c..01de860a46 100644 --- a/core/txpool/validation.go +++ b/core/txpool/validation.go @@ -213,7 +213,7 @@ type ValidationOptionsWithState struct { // nonce gaps will be ignored and permitted. FirstNonceGap func(addr common.Address) uint64 - // UsedAndLeftSlots is a mandatory callback to retrieve the number of tx slots + // UsedAndLeftSlots is an optional callback to retrieve the number of tx slots // used and the number still permitted for an account. New transactions will // be rejected once the number of remaining slots reaches zero. UsedAndLeftSlots func(addr common.Address) (int, int) @@ -225,11 +225,6 @@ type ValidationOptionsWithState struct { // ExistingCost is a mandatory callback to retrieve an already pooled // transaction's cost with the given nonce to check for overdrafts. ExistingCost func(addr common.Address, nonce uint64) *big.Int - - // KnownConflicts is an optional callback which iterates over the list of - // addresses and returns all addresses known to the pool with in-flight - // transactions. - KnownConflicts func(sender common.Address, authorizers []common.Address) []common.Address } // ValidateTransactionWithState is a helper method to check whether a transaction @@ -280,15 +275,9 @@ func ValidateTransactionWithState(tx *types.Transaction, signer types.Signer, op // Transaction takes a new nonce value out of the pool. Ensure it doesn't // overflow the number of permitted transactions from a single account // (i.e. max cancellable via out-of-bound transaction). - if used, left := opts.UsedAndLeftSlots(from); left <= 0 { - return fmt.Errorf("%w: pooled %d txs", ErrAccountLimitExceeded, used) - } - - // Verify no authorizations will invalidate existing transactions known to - // the pool. - if opts.KnownConflicts != nil { - if conflicts := opts.KnownConflicts(from, tx.SetCodeAuthorities()); len(conflicts) > 0 { - return fmt.Errorf("%w: authorization conflicts with other known tx", ErrAuthorityReserved) + if opts.UsedAndLeftSlots != nil { + if used, left := opts.UsedAndLeftSlots(from); left <= 0 { + return fmt.Errorf("%w: pooled %d txs", ErrAccountLimitExceeded, used) } } } diff --git a/core/types/transaction.go b/core/types/transaction.go index 00c3e1c8d5..f47d354056 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -493,15 +493,23 @@ func (tx *Transaction) SetCodeAuthorizations() []SetCodeAuthorization { return setcodetx.AuthList } -// SetCodeAuthorities returns a list of each authorization's corresponding authority. +// SetCodeAuthorities returns a list of unique authorities from the +// authorization list. func (tx *Transaction) SetCodeAuthorities() []common.Address { setcodetx, ok := tx.inner.(*SetCodeTx) if !ok { return nil } - auths := make([]common.Address, 0, len(setcodetx.AuthList)) + var ( + marks = make(map[common.Address]bool) + auths = make([]common.Address, 0, len(setcodetx.AuthList)) + ) for _, auth := range setcodetx.AuthList { if addr, err := auth.Authority(); err == nil { + if marks[addr] { + continue + } + marks[addr] = true auths = append(auths, addr) } } diff --git a/core/vm/contracts_test.go b/core/vm/contracts_test.go index d3875b123d..e77ab82468 100644 --- a/core/vm/contracts_test.go +++ b/core/vm/contracts_test.go @@ -325,6 +325,8 @@ func TestPrecompiledBLS12381Pairing(t *testing.T) { testJson("blsPairing", "f func TestPrecompiledBLS12381MapG1(t *testing.T) { testJson("blsMapG1", "f0f", t) } func TestPrecompiledBLS12381MapG2(t *testing.T) { testJson("blsMapG2", "f10", t) } +func TestPrecompiledBlsSignatureVerify(t *testing.T) { testJson("blsSignatureVerify", "66", t) } + func TestPrecompiledPointEvaluation(t *testing.T) { testJson("pointEvaluation", "0a", t) } func BenchmarkPrecompiledPointEvaluation(b *testing.B) { benchJson("pointEvaluation", "0a", b) } @@ -348,6 +350,8 @@ func TestPrecompiledBLS12381PairingFail(t *testing.T) { testJsonFail("blsPair func TestPrecompiledBLS12381MapG1Fail(t *testing.T) { testJsonFail("blsMapG1", "f0f", t) } func TestPrecompiledBLS12381MapG2Fail(t *testing.T) { testJsonFail("blsMapG2", "f10", t) } +func TestPrecompiledBlsSignatureVerifyFail(t *testing.T) { testJson("blsSignatureVerify", "66", t) } + func loadJson(name string) ([]precompiledTest, error) { data, err := os.ReadFile(fmt.Sprintf("testdata/precompiles/%v.json", name)) if err != nil { diff --git a/core/vm/testdata/precompiles/blsSignatureVerify.json b/core/vm/testdata/precompiles/blsSignatureVerify.json new file mode 100644 index 0000000000..758a3ec6be --- /dev/null +++ b/core/vm/testdata/precompiles/blsSignatureVerify.json @@ -0,0 +1,9 @@ +[ + { + "Input": "f2d8e8e5bf354429e3ce8b97c4e88f7a0bf7bc917e856de762ed6d70dd8ec2d289a04d63285e4b45309e7c180ea82565e375dd62c7b80d957aea4c9b7e16bdb28a0f910036bd3220fe3d7614fb137a8f0a68b3c564ddd214b5041d8f7a124e6e7285ac42635e75eeb9051a052fb500b1c2bc23bd4290db59fc02be11f2b80896b6e22d5b8dd31ba2e49b13cd6be19fcd01c1e23af3e5165d88d8b9deaf38baa77770fa6a358e2eebdffd1bd8a1eb7386", + "Expected": "01", + "Name": "vector 0", + "Gas": 4500, + "NoBenchmark": false + } +] \ No newline at end of file diff --git a/core/vm/testdata/precompiles/fail-blsSignatureVerify.json b/core/vm/testdata/precompiles/fail-blsSignatureVerify.json new file mode 100644 index 0000000000..c01108a205 --- /dev/null +++ b/core/vm/testdata/precompiles/fail-blsSignatureVerify.json @@ -0,0 +1,12 @@ +[ + { + "Input": "", + "ExpectedError": "execution reverted", + "Name": "bls_signatureVerify_empty_input" + }, + { + "Input": "f2d8e8e5bf354429e3ce8b97c4e88f7a0bf7bc917e856de762ed6d70dd8ec2d289a04d63285e4b45309e7c180ea82565e375dd62c7b80d957aea4c9b7e16bdb28a0f910036bd3220fe3d7614fb137a8f0a68b3c564ddd214b5041d8f7a124e6e7285ac42635e75eeb9051a052fb500b1c2bc23bd4290db59fc02be11f2b80896b6e22d5b8dd31ba2e49b13cd6be19fcd01c1e23af3e5165d88d8b9deaf38baa77770fa6a358e2eebdffd1bd8a1eb7387", + "ExpectedError": "execution reverted", + "Name": "bls_signatureVerify_wrong_signature" + } + ] \ No newline at end of file diff --git a/eth/api_miner.go b/eth/api_miner.go index 56db9e94b1..64b3130dae 100644 --- a/eth/api_miner.go +++ b/eth/api_miner.go @@ -73,8 +73,8 @@ func (api *MinerAPI) SetGasPrice(gasPrice hexutil.Big) bool { // SetGasLimit sets the gaslimit to target towards during mining. func (api *MinerAPI) SetGasLimit(gasLimit hexutil.Uint64) bool { api.e.Miner().SetGasCeil(uint64(gasLimit)) - if uint64(gasLimit) > params.SystemTxsGas { - api.e.TxPool().SetMaxGas(uint64(gasLimit) - params.SystemTxsGas) + if uint64(gasLimit) > params.SystemTxsGasSoftLimit { + api.e.TxPool().SetMaxGas(uint64(gasLimit) - params.SystemTxsGasSoftLimit) } return true } diff --git a/eth/backend.go b/eth/backend.go index 251e1e1bb2..8214984b59 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -201,6 +201,10 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { chainConfig.PragueTime = config.OverridePrague overrides.OverridePrague = config.OverridePrague } + if config.OverrideLorentz != nil { + chainConfig.LorentzTime = config.OverrideLorentz + overrides.OverrideLorentz = config.OverrideLorentz + } if config.OverrideVerkle != nil { chainConfig.VerkleTime = config.OverrideVerkle overrides.OverrideVerkle = config.OverrideVerkle diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index d41499c6a8..46873adb75 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -191,6 +191,9 @@ type Config struct { // OverridePrague (TODO: remove after the fork) OverridePrague *uint64 `toml:",omitempty"` + // OverrideLorentz (TODO: remove after the fork) + OverrideLorentz *uint64 `toml:",omitempty"` + // OverrideVerkle (TODO: remove after the fork) OverrideVerkle *uint64 `toml:",omitempty"` diff --git a/eth/ethconfig/gen_config.go b/eth/ethconfig/gen_config.go index 5448993c58..3927240350 100644 --- a/eth/ethconfig/gen_config.go +++ b/eth/ethconfig/gen_config.go @@ -66,6 +66,7 @@ func (c Config) MarshalTOML() (interface{}, error) { OverridePassedForkTime *uint64 `toml:",omitempty"` OverridePascal *uint64 `toml:",omitempty"` OverridePrague *uint64 `toml:",omitempty"` + OverrideLorentz *uint64 `toml:",omitempty"` OverrideVerkle *uint64 `toml:",omitempty"` BlobExtraReserve uint64 } @@ -120,6 +121,7 @@ func (c Config) MarshalTOML() (interface{}, error) { enc.OverridePassedForkTime = c.OverridePassedForkTime enc.OverridePascal = c.OverridePascal enc.OverridePrague = c.OverridePrague + enc.OverrideLorentz = c.OverrideLorentz enc.OverrideVerkle = c.OverrideVerkle enc.BlobExtraReserve = c.BlobExtraReserve return &enc, nil @@ -178,6 +180,7 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { OverridePassedForkTime *uint64 `toml:",omitempty"` OverridePascal *uint64 `toml:",omitempty"` OverridePrague *uint64 `toml:",omitempty"` + OverrideLorentz *uint64 `toml:",omitempty"` OverrideVerkle *uint64 `toml:",omitempty"` BlobExtraReserve *uint64 } @@ -335,6 +338,9 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { if dec.OverridePrague != nil { c.OverridePrague = dec.OverridePrague } + if dec.OverrideLorentz != nil { + c.OverrideLorentz = dec.OverrideLorentz + } if dec.OverrideVerkle != nil { c.OverrideVerkle = dec.OverrideVerkle } diff --git a/eth/gasprice/feehistory.go b/eth/gasprice/feehistory.go index fe84950c50..59830e9fe8 100644 --- a/eth/gasprice/feehistory.go +++ b/eth/gasprice/feehistory.go @@ -107,8 +107,8 @@ func (oracle *Oracle) processBlock(bf *blockFees, percentiles []float64) { // Compute gas used ratio for normal and blob gas. bf.results.gasUsedRatio = float64(bf.header.GasUsed) / float64(bf.header.GasLimit) if blobGasUsed := bf.header.BlobGasUsed; blobGasUsed != nil { - maxBlobs := eip4844.MaxBlobsPerBlock(config, bf.header.Time) - bf.results.blobGasUsedRatio = float64(*blobGasUsed) / float64(maxBlobs) + maxBlobGas := eip4844.MaxBlobGasPerBlock(config, bf.header.Time) + bf.results.blobGasUsedRatio = float64(*blobGasUsed) / float64(maxBlobGas) } if len(percentiles) == 0 { diff --git a/go.mod b/go.mod index 2eac0dd7ac..e6e8c8bf1a 100644 --- a/go.mod +++ b/go.mod @@ -69,7 +69,7 @@ require ( github.com/shirou/gopsutil v3.21.11+incompatible github.com/status-im/keycard-go v0.2.0 github.com/stretchr/testify v1.10.0 - github.com/supranational/blst v0.3.13 + github.com/supranational/blst v0.3.14 github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 github.com/tendermint/go-amino v0.14.1 github.com/tendermint/iavl v0.12.0 @@ -134,7 +134,7 @@ require ( github.com/dustin/go-humanize v1.0.0 // indirect github.com/elastic/gosigar v0.14.3 // indirect github.com/etcd-io/bbolt v1.3.3 // indirect - github.com/ferranbt/fastssz v0.1.3 // indirect + github.com/ferranbt/fastssz v0.0.0-20210120143747-11b9eff30ea9 // indirect github.com/flynn/noise v1.1.0 // indirect github.com/francoispqt/gojay v1.2.13 // indirect github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61 // indirect @@ -164,7 +164,7 @@ require ( github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-retryablehttp v0.7.7 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect - github.com/herumi/bls-eth-go-binary v1.31.0 // indirect + github.com/herumi/bls-eth-go-binary v0.0.0-20210917013441-d37c07cfda4e // indirect github.com/influxdata/line-protocol v0.0.0-20210311194329-9aa0e372d097 // indirect github.com/ipfs/go-cid v0.4.1 // indirect github.com/ipfs/go-log/v2 v2.5.1 // indirect @@ -282,7 +282,7 @@ require ( github.com/tyler-smith/go-bip39 v1.1.0 // indirect github.com/uber/jaeger-client-go v2.25.0+incompatible // indirect github.com/wealdtech/go-bytesutil v1.1.1 // indirect - github.com/wealdtech/go-eth2-types/v2 v2.8.2 // indirect + github.com/wealdtech/go-eth2-types/v2 v2.5.2 // indirect github.com/wealdtech/go-eth2-util v1.6.3 // indirect github.com/wlynxg/anet v0.0.5 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect diff --git a/go.sum b/go.sum index a7d3ab67f7..6548905886 100644 --- a/go.sum +++ b/go.sum @@ -325,9 +325,8 @@ github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/ferranbt/fastssz v0.0.0-20210120143747-11b9eff30ea9 h1:9VDpsWq096+oGMDTT/SgBD/VgZYf4pTF+KTPmZ+OaKM= github.com/ferranbt/fastssz v0.0.0-20210120143747-11b9eff30ea9/go.mod h1:DyEu2iuLBnb/T51BlsiO3yLYdJC6UbGMrIkqK1KmQxM= -github.com/ferranbt/fastssz v0.1.3 h1:ZI+z3JH05h4kgmFXdHuR1aWYsgrg7o+Fw7/NCzM16Mo= -github.com/ferranbt/fastssz v0.1.3/go.mod h1:0Y9TEd/9XuFlh7mskMPfXiI2Dkw4Ddg9EyXt1W7MRvE= github.com/fjl/gencodec v0.1.0 h1:B3K0xPfc52cw52BBgUbSPxYo+HlLfAgWMVKRWXUXBcs= github.com/fjl/gencodec v0.1.0/go.mod h1:Um1dFHPONZGTHog1qD1NaWjXJW/SPB38wPv0O8uZ2fI= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= @@ -589,8 +588,8 @@ github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0m github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/herumi/bls-eth-go-binary v0.0.0-20210130185500-57372fb27371/go.mod h1:luAnRm3OsMQeokhGzpYmc0ZKwawY7o87PUEP11Z7r7U= -github.com/herumi/bls-eth-go-binary v1.31.0 h1:9eeW3EA4epCb7FIHt2luENpAW69MvKGL5jieHlBiP+w= -github.com/herumi/bls-eth-go-binary v1.31.0/go.mod h1:luAnRm3OsMQeokhGzpYmc0ZKwawY7o87PUEP11Z7r7U= +github.com/herumi/bls-eth-go-binary v0.0.0-20210917013441-d37c07cfda4e h1:wCMygKUQhmcQAjlk2Gquzq6dLmyMv2kF+llRspoRgrk= +github.com/herumi/bls-eth-go-binary v0.0.0-20210917013441-d37c07cfda4e/go.mod h1:luAnRm3OsMQeokhGzpYmc0ZKwawY7o87PUEP11Z7r7U= github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4 h1:X4egAf/gcS1zATw6wn4Ej8vjuVGxeHdan+bRb2ebyv4= github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= @@ -1146,8 +1145,8 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/supranational/blst v0.3.13 h1:AYeSxdOMacwu7FBmpfloBz5pbFXDmJL33RuwnKtmTjk= -github.com/supranational/blst v0.3.13/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/supranational/blst v0.3.14 h1:xNMoHRJOTwMn63ip6qoWJ2Ymgvj7E2b9jY2FAwY+qRo= +github.com/supranational/blst v0.3.14/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= @@ -1183,8 +1182,6 @@ github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2n github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= github.com/uber/jaeger-client-go v2.25.0+incompatible h1:IxcNZ7WRY1Y3G4poYlx24szfsn/3LvK9QHCq9oQw8+U= github.com/uber/jaeger-client-go v2.25.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= -github.com/umbracle/gohashtree v0.0.2-alpha.0.20230207094856-5b775a815c10 h1:CQh33pStIp/E30b7TxDlXfM0145bn2e8boI30IxAhTg= -github.com/umbracle/gohashtree v0.0.2-alpha.0.20230207094856-5b775a815c10/go.mod h1:x/Pa0FF5Te9kdrlZKJK82YmAkvL8+f989USgz6Jiw7M= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= @@ -1197,9 +1194,8 @@ github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49u github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= github.com/wealdtech/go-bytesutil v1.1.1 h1:ocEg3Ke2GkZ4vQw5lp46rmO+pfqCCTgq35gqOy8JKVc= github.com/wealdtech/go-bytesutil v1.1.1/go.mod h1:jENeMqeTEU8FNZyDFRVc7KqBdRKSnJ9CCh26TcuNb9s= +github.com/wealdtech/go-eth2-types/v2 v2.5.2 h1:tiA6T88M6XQIbrV5Zz53l1G5HtRERcxQfmET225V4Ls= github.com/wealdtech/go-eth2-types/v2 v2.5.2/go.mod h1:8lkNUbgklSQ4LZ2oMSuxSdR7WwJW3L9ge1dcoCVyzws= -github.com/wealdtech/go-eth2-types/v2 v2.8.2 h1:b5aXlNBLKgjAg/Fft9VvGlqAUCQMP5LzYhlHRrr4yPg= -github.com/wealdtech/go-eth2-types/v2 v2.8.2/go.mod h1:IAz9Lz1NVTaHabQa+4zjk2QDKMv8LVYo0n46M9o/TXw= github.com/wealdtech/go-eth2-util v1.6.3 h1:2INPeOR35x5LdFFpSzyw954WzTD+DFyHe3yKlJnG5As= github.com/wealdtech/go-eth2-util v1.6.3/go.mod h1:0hFMj/qtio288oZFHmAbCnPQ9OB3c4WFzs5NVPKTY4k= github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4 v1.1.3 h1:SxrDVSr+oXuT1x8kZt4uWqNCvv5xXEGV9zd7cuSrZS8= diff --git a/miner/bid_simulator.go b/miner/bid_simulator.go index a48407b7e2..baa2c9c1c5 100644 --- a/miner/bid_simulator.go +++ b/miner/bid_simulator.go @@ -17,6 +17,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/bidutil" "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/parlia" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/txpool" "github.com/ethereum/go-ethereum/core/types" @@ -582,7 +583,9 @@ func (b *bidSimulator) simBid(interruptCh chan int32, bidRuntime *BidRuntime) { gasLimit := bidRuntime.env.header.GasLimit if bidRuntime.env.gasPool == nil { bidRuntime.env.gasPool = new(core.GasPool).AddGas(gasLimit) - bidRuntime.env.gasPool.SubGas(params.SystemTxsGas) + if p, ok := b.engine.(*parlia.Parlia); ok { + bidRuntime.env.gasPool.SubGas(p.EstimateGasReservedForSystemTxs(b.chain, bidRuntime.env.header)) + } bidRuntime.env.gasPool.SubGas(params.PayBidTxGasLimit) } diff --git a/miner/worker.go b/miner/worker.go index e23b0eced0..6a32188484 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -450,6 +450,11 @@ func (w *worker) newWorkLoop(recommit time.Duration) { if !w.isRunning() { continue } + if interruptCh != nil { + interruptCh <- commitInterruptNewHead + close(interruptCh) + interruptCh = nil + } clearPending(head.Header.Number.Uint64()) timestamp = time.Now().Unix() if p, ok := w.engine.(*parlia.Parlia); ok { @@ -790,7 +795,11 @@ func (w *worker) commitTransactions(env *environment, plainTxs, blobTxs *transac gasLimit := env.header.GasLimit if env.gasPool == nil { env.gasPool = new(core.GasPool).AddGas(gasLimit) - env.gasPool.SubGas(params.SystemTxsGas) + if p, ok := w.engine.(*parlia.Parlia); ok { + gasReserved := p.EstimateGasReservedForSystemTxs(w.chain, env.header) + env.gasPool.SubGas(gasReserved) + log.Debug("commitTransactions", "number", env.header.Number.Uint64(), "time", env.header.Time, "EstimateGasReservedForSystemTxs", gasReserved) + } } var coalescedLogs []*types.Log @@ -1187,7 +1196,7 @@ func (w *worker) generateWork(params *generateParams, witness bool) *newPayloadR } // Collect consensus-layer requests if Prague is enabled. var requests [][]byte - if w.chainConfig.IsPrague(work.header.Number, work.header.Time) { + if w.chainConfig.IsPrague(work.header.Number, work.header.Time) && w.chainConfig.Parlia == nil { requests = [][]byte{} // EIP-6110 deposits if err := core.ParseDepositLogs(&requests, allLogs, w.chainConfig); err != nil { diff --git a/params/config.go b/params/config.go index 8a8d1f5cae..a5dd62808f 100644 --- a/params/config.go +++ b/params/config.go @@ -187,9 +187,9 @@ var ( HaberTime: newUint64(1718863500), // 2024-06-20 06:05:00 AM UTC HaberFixTime: newUint64(1727316120), // 2024-09-26 02:02:00 AM UTC BohrTime: newUint64(1727317200), // 2024-09-26 02:20:00 AM UTC - // TODO - PascalTime: nil, - PragueTime: nil, + PascalTime: newUint64(1742436600), + PragueTime: newUint64(1742436600), + LorentzTime: nil, Parlia: &ParliaConfig{ Period: 3, @@ -237,6 +237,8 @@ var ( BohrTime: newUint64(1724116996), // 2024-08-20 01:23:16 AM UTC PascalTime: newUint64(1740452880), // 2025-02-25 03:08:00 AM UTC PragueTime: newUint64(1740452880), // 2025-02-25 03:08:00 AM UTC + // TODO + LorentzTime: nil, Parlia: &ParliaConfig{ Period: 3, @@ -284,8 +286,9 @@ var ( HaberFixTime: newUint64(0), BohrTime: newUint64(0), // TODO: set them to `0` when passed on the mainnet - PascalTime: nil, - PragueTime: nil, + PascalTime: nil, + PragueTime: nil, + LorentzTime: nil, Parlia: &ParliaConfig{ Period: 3, @@ -610,6 +613,7 @@ type ChainConfig struct { PascalTime *uint64 `json:"pascalTime,omitempty"` // Pascal switch time (nil = no fork, 0 = already on pascal) PragueTime *uint64 `json:"pragueTime,omitempty"` // Prague switch time (nil = no fork, 0 = already on prague) OsakaTime *uint64 `json:"osakaTime,omitempty"` // Osaka switch time (nil = no fork, 0 = already on osaka) + LorentzTime *uint64 `json:"lorentzTime,omitempty"` // Lorentz switch time (nil = no fork, 0 = already on lorentz) VerkleTime *uint64 `json:"verkleTime,omitempty"` // Verkle switch time (nil = no fork, 0 = already on verkle) // TerminalTotalDifficulty is the amount of total difficulty reached by @@ -773,7 +777,12 @@ func (c *ChainConfig) String() string { PragueTime = big.NewInt(0).SetUint64(*c.PragueTime) } - 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, HaberFixTime: %v, BohrTime: %v, PascalTime: %v, PragueTime: %v, Engine: %v}", + var LorentzTime *big.Int + if c.LorentzTime != nil { + LorentzTime = big.NewInt(0).SetUint64(*c.LorentzTime) + } + + 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, HaberFixTime: %v, BohrTime: %v, PascalTime: %v, PragueTime: %v, LorentzTime: %v, Engine: %v}", c.ChainID, c.HomesteadBlock, c.DAOForkBlock, @@ -815,6 +824,7 @@ func (c *ChainConfig) String() string { BohrTime, PascalTime, PragueTime, + LorentzTime, engine, ) } @@ -1157,6 +1167,20 @@ func (c *ChainConfig) IsOnPrague(currentBlockNumber *big.Int, lastBlockTime uint return !c.IsPrague(lastBlockNumber, lastBlockTime) && c.IsPrague(currentBlockNumber, currentBlockTime) } +// IsLorentz returns whether time is either equal to the Lorentz fork time or greater. +func (c *ChainConfig) IsLorentz(num *big.Int, time uint64) bool { + return c.IsLondon(num) && isTimestampForked(c.LorentzTime, time) +} + +// IsOnLorentz returns whether currentBlockTime is either equal to the Lorentz fork time or greater firstly. +func (c *ChainConfig) IsOnLorentz(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.IsLorentz(lastBlockNumber, lastBlockTime) && c.IsLorentz(currentBlockNumber, currentBlockTime) +} + // IsOsaka returns whether time is either equal to the Osaka fork time or greater. func (c *ChainConfig) IsOsaka(num *big.Int, time uint64) bool { return c.IsLondon(num) && isTimestampForked(c.OsakaTime, time) @@ -1245,6 +1269,7 @@ func (c *ChainConfig) CheckConfigForkOrder() error { {name: "pascalTime", timestamp: c.PascalTime}, {name: "pragueTime", timestamp: c.PragueTime}, {name: "osakaTime", timestamp: c.OsakaTime, optional: true}, + {name: "lorentzTime", timestamp: c.LorentzTime}, {name: "verkleTime", timestamp: c.VerkleTime, optional: true}, } { if lastFork.name != "" { @@ -1297,13 +1322,13 @@ func (c *ChainConfig) CheckConfigForkOrder() error { } { if cur.config != nil { if err := cur.config.validate(); err != nil { - return fmt.Errorf("invalid blob configuration for fork %s: %v", cur.name, err) + return fmt.Errorf("invalid chain configuration in blobSchedule for fork %q: %v", cur.name, err) } } if cur.timestamp != nil { // If the fork is configured, a blob schedule must be defined for it. if cur.config == nil { - return fmt.Errorf("unsupported fork configuration: missing blob configuration entry for %v in schedule", cur.name) + return fmt.Errorf("invalid chain configuration: missing entry for fork %q in blobSchedule", cur.name) } } } @@ -1448,6 +1473,9 @@ func (c *ChainConfig) checkCompatible(newcfg *ChainConfig, headNumber *big.Int, if isForkTimestampIncompatible(c.OsakaTime, newcfg.OsakaTime, headTimestamp) { return newTimestampCompatError("Osaka fork timestamp", c.OsakaTime, newcfg.OsakaTime) } + if isForkTimestampIncompatible(c.LorentzTime, newcfg.LorentzTime, headTimestamp) { + return newTimestampCompatError("Lorentz fork timestamp", c.LorentzTime, newcfg.LorentzTime) + } if isForkTimestampIncompatible(c.VerkleTime, newcfg.VerkleTime, headTimestamp) { return newTimestampCompatError("Verkle fork timestamp", c.VerkleTime, newcfg.VerkleTime) } @@ -1633,7 +1661,8 @@ type Rules struct { IsHertz bool IsHertzfix bool IsShanghai, IsKepler, IsFeynman, IsCancun, IsHaber bool - IsBohr, IsPascal, IsPrague, IsOsaka, IsVerkle bool + IsBohr, IsPascal, IsPrague, IsLorentz, IsOsaka bool + IsVerkle bool } // Rules ensures c's ChainID is not nil. @@ -1675,6 +1704,7 @@ func (c *ChainConfig) Rules(num *big.Int, isMerge bool, timestamp uint64) Rules IsPascal: c.IsPascal(num, timestamp), IsPrague: c.IsPrague(num, timestamp), IsOsaka: c.IsOsaka(num, timestamp), + IsLorentz: c.IsLorentz(num, timestamp), IsVerkle: c.IsVerkle(num, timestamp), IsEIP4762: isVerkle, } diff --git a/params/protocol_params.go b/params/protocol_params.go index c83e330a8c..800c8578cd 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -38,7 +38,8 @@ const ( CallValueTransferGas uint64 = 9000 // Paid for CALL when the value transfer is non-zero. CallNewAccountGas uint64 = 25000 // Paid for CALL when the destination address didn't exist prior. TxGas uint64 = 21000 // Per transaction not creating a contract. NOTE: Not payable on data of calls between transactions. - SystemTxsGas uint64 = 20000000 // The gas reserved for system txs; only for parlia consensus + SystemTxsGasHardLimit uint64 = 20000000 // Maximum gas reserved for system transactions (Parlia consensus only) + SystemTxsGasSoftLimit uint64 = 1000000 // Maximum gas reserved for system transactions, excluding validator update transactions (Parlia consensus only) TxGasContractCreation uint64 = 53000 // Per transaction that creates a contract. NOTE: Not payable on data of calls between transactions. TxDataZeroGas uint64 = 4 // Per byte of data attached to a transaction that equals zero. NOTE: Not payable on data of calls between transactions. QuadCoeffDiv uint64 = 512 // Divisor for the quadratic particle of the memory cost equation. diff --git a/signer/core/apitypes/types.go b/signer/core/apitypes/types.go index 082e5009e2..9687906698 100644 --- a/signer/core/apitypes/types.go +++ b/signer/core/apitypes/types.go @@ -505,7 +505,16 @@ func (typedData *TypedData) encodeArrayValue(encValue interface{}, encType strin for _, item := range arrayValue { if reflect.TypeOf(item).Kind() == reflect.Slice || reflect.TypeOf(item).Kind() == reflect.Array { - encodedData, err := typedData.encodeArrayValue(item, parsedType, depth+1) + var ( + encodedData hexutil.Bytes + err error + ) + if reflect.TypeOf(item).Elem().Kind() == reflect.Uint8 { + // the item type is bytes. encode the bytes array directly instead of recursing. + encodedData, err = typedData.EncodePrimitiveValue(parsedType, item, depth+1) + } else { + encodedData, err = typedData.encodeArrayValue(item, parsedType, depth+1) + } if err != nil { return nil, err } diff --git a/signer/core/signed_data_test.go b/signer/core/signed_data_test.go index d0637010ba..b6c080736c 100644 --- a/signer/core/signed_data_test.go +++ b/signer/core/signed_data_test.go @@ -1014,3 +1014,49 @@ func TestComplexTypedDataWithLowercaseReftype(t *testing.T) { t.Fatalf("Error, got %x, wanted %x", sighash, expSigHash) } } + +var recursiveBytesTypesStandard = apitypes.Types{ + "EIP712Domain": { + { + Name: "name", + Type: "string", + }, + { + Name: "version", + Type: "string", + }, + { + Name: "chainId", + Type: "uint256", + }, + { + Name: "verifyingContract", + Type: "address", + }, + }, + "Val": { + { + Name: "field", + Type: "bytes[][]", + }, + }, +} + +var recursiveBytesMessageStandard = map[string]interface{}{ + "field": [][][]byte{{{1}, {2}}, {{3}, {4}}}, +} + +var recursiveBytesTypedData = apitypes.TypedData{ + Types: recursiveBytesTypesStandard, + PrimaryType: "Val", + Domain: domainStandard, + Message: recursiveBytesMessageStandard, +} + +func TestEncodeDataRecursiveBytes(t *testing.T) { + typedData := recursiveBytesTypedData + _, err := typedData.EncodeData(typedData.PrimaryType, typedData.Message, 0) + if err != nil { + t.Fatalf("got err %v", err) + } +} diff --git a/trie/secure_trie.go b/trie/secure_trie.go index 915afaf8ff..249d11db96 100644 --- a/trie/secure_trie.go +++ b/trie/secure_trie.go @@ -275,6 +275,7 @@ func (t *StateTrie) Copy() *StateTrie { trie: *t.trie.Copy(), db: t.db, secKeyCache: t.secKeyCache, + preimages: t.preimages, } } diff --git a/version/version.go b/version/version.go index 9ced639abb..ef23218d31 100644 --- a/version/version.go +++ b/version/version.go @@ -19,6 +19,6 @@ package version const ( Major = 1 // Major version component of the current release Minor = 5 // Minor version component of the current release - Patch = 6 // Patch version component of the current release + Patch = 7 // Patch version component of the current release Meta = "" // Version metadata to append to the version string )