diff --git a/Dockerfile b/Dockerfile index e9850144a..7a75ca491 100644 --- a/Dockerfile +++ b/Dockerfile @@ -42,17 +42,6 @@ FROM docker.io/library/alpine:3.17 RUN apk add --no-cache ca-certificates libstdc++ tzdata RUN apk add --no-cache curl jq bind-tools -# Setup user and group -# -# from the perspective of the container, uid=1000, gid=1000 is a sensible choice -# (mimicking Ubuntu Server), but if caller creates a .env (example in repo root), -# these defaults will get overridden when make calls docker-compose -ARG UID=1000 -ARG GID=1000 -RUN adduser -D -u $UID -g $GID erigon -USER erigon -RUN mkdir -p ~/.local/share/erigon - # copy compiled artifacts from builder ## first do the mdbx ones - since these wont change as often COPY --from=tools-builder /app/build/bin/mdbx_chk /usr/local/bin/mdbx_chk @@ -98,13 +87,13 @@ ARG BUILD_DATE ARG VCS_REF ARG VERSION LABEL org.label-schema.build-date=$BUILD_DATE \ - org.label-schema.description="Erigon Ethereum Client" \ - org.label-schema.name="Erigon" \ + org.label-schema.description="Erigon Ethereum Client with PulseChain" \ + org.label-schema.name="Erigon PulseChain" \ org.label-schema.schema-version="1.0" \ - org.label-schema.url="https://torquem.ch" \ + org.label-schema.url="https://gitlab.com/pulsechaincom/erigon-pulse" \ org.label-schema.vcs-ref=$VCS_REF \ - org.label-schema.vcs-url="https://github.com/ledgerwatch/erigon.git" \ - org.label-schema.vendor="Torquem" \ + org.label-schema.vcs-url="https://gitlab.com/pulsechaincom/erigon-pulse.git" \ + org.label-schema.vendor="PulseChain" \ org.label-schema.version=$VERSION ENTRYPOINT ["erigon"] diff --git a/Makefile b/Makefile index 7ab3fb424..85bf54f25 100644 --- a/Makefile +++ b/Makefile @@ -79,6 +79,7 @@ docker: validate_docker_build_args git-submodules --build-arg VERSION=${GIT_TAG} \ --build-arg UID=${DOCKER_UID} \ --build-arg GID=${DOCKER_GID} \ + --build-arg DEPLOY_TOKEN=${DEPLOY_TOKEN} \ ${DOCKER_FLAGS} \ . diff --git a/cmd/sentry/main.go b/cmd/sentry/main.go index abd86dca1..4ad46035f 100644 --- a/cmd/sentry/main.go +++ b/cmd/sentry/main.go @@ -35,6 +35,7 @@ var ( maxPendPeers int healthCheck bool metrics bool + chainName string ) func init() { @@ -55,6 +56,7 @@ func init() { rootCmd.Flags().IntVar(&maxPendPeers, utils.MaxPendingPeersFlag.Name, utils.MaxPendingPeersFlag.Value, utils.MaxPendingPeersFlag.Usage) rootCmd.Flags().BoolVar(&healthCheck, utils.HealthCheckFlag.Name, false, utils.HealthCheckFlag.Usage) rootCmd.Flags().BoolVar(&metrics, utils.MetricsEnabledFlag.Name, false, utils.MetricsEnabledFlag.Usage) + rootCmd.Flags().StringVar(&chainName, utils.ChainFlag.Name, utils.ChainFlag.Value, utils.ChainFlag.Usage) if err := rootCmd.MarkFlagDirname(utils.DataDirFlag.Name); err != nil { panic(err) @@ -88,6 +90,7 @@ var rootCmd = &cobra.Command{ protocol, allowedPorts, metrics, + chainName, ) if err != nil { return err diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 23bb4fa55..26f60b93a 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -97,7 +97,7 @@ var ( } ChainFlag = cli.StringFlag{ Name: "chain", - Usage: "name of the network to join", + Usage: "Name of the network to join", Value: networkname.MainnetChainName, } IdentityFlag = cli.StringFlag{ @@ -1062,6 +1062,7 @@ func NewP2PConfig( protocol uint, allowedPorts []uint, metricsEnabled bool, + chainName string, ) (*p2p.Config, error) { var enodeDBPath string switch protocol { @@ -1092,6 +1093,7 @@ func NewP2PConfig( AllowedPorts: allowedPorts, TmpDir: dirs.Tmp, MetricsEnabled: metricsEnabled, + ChainName: chainName, } if netRestrict != "" { cfg.NetRestrict = new(netutil.Netlist) @@ -1771,7 +1773,7 @@ func SetDNSDiscoveryDefaults(cfg *ethconfig.Config, genesis libcommon.Hash) { return // already set through flags/config } protocol := "all" - if url := params.KnownDNSNetwork(genesis, protocol); url != "" { + if url := params.KnownDNSNetwork(genesis, cfg.NetworkID, protocol); url != "" { cfg.EthDiscoveryURLs = []string{url} } } diff --git a/common/paths/paths.go b/common/paths/paths.go index 5b2bd1f69..93f56d7be 100644 --- a/common/paths/paths.go +++ b/common/paths/paths.go @@ -100,6 +100,10 @@ func DataDirForNetwork(datadir string, network string) string { return networkDataDirCheckingLegacy(datadir, "gnosis") case networkname.ChiadoChainName: return networkDataDirCheckingLegacy(datadir, "chiado") + case networkname.PulsechainChainName: + return networkDataDirCheckingLegacy(datadir, "pulsechain") + case networkname.PulsechainTestnetChainName: + return networkDataDirCheckingLegacy(datadir, "pulsechain-testnet") default: return datadir diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go index 5c92c2061..fadb086ad 100644 --- a/consensus/ethash/consensus.go +++ b/consensus/ethash/consensus.go @@ -39,6 +39,7 @@ import ( "github.com/ledgerwatch/erigon/core/state" "github.com/ledgerwatch/erigon/core/types" "github.com/ledgerwatch/erigon/params" + "github.com/ledgerwatch/erigon/pulse" "github.com/ledgerwatch/erigon/rlp" ) @@ -294,6 +295,8 @@ func (ethash *Ethash) CalcDifficulty(chain consensus.ChainHeaderReader, time, pa func CalcDifficulty(config *chain.Config, time, parentTime uint64, parentDifficulty *big.Int, parentNumber uint64, parentUncleHash libcommon.Hash) *big.Int { next := parentNumber + 1 switch { + case config.IsPrimordialPulseBlock(next): + return chain.PulseChainTTDOffset case config.IsGrayGlacier(next): return calcDifficultyEip5133(time, parentTime, parentDifficulty, parentNumber, parentUncleHash) case config.IsArrowGlacier(next): @@ -562,6 +565,11 @@ func (ethash *Ethash) Finalize(config *chain.Config, header *types.Header, state txs types.Transactions, uncles []*types.Header, r types.Receipts, withdrawals []*types.Withdrawal, chain consensus.ChainReader, syscall consensus.SystemCall, logger log.Logger, ) (types.Transactions, types.Receipts, error) { + // Apply fork changes on PrimordialPulse block + if cfg := chain.Config(); cfg.IsPrimordialPulseBlock(header.Number.Uint64()) { + pulse.PrimordialPulseFork(state, cfg.PulseChain) + } + // Accumulate any block and uncle rewards and commit the final state root accumulateRewards(config, state, header, uncles) return txs, r, nil diff --git a/consensus/merge/merge.go b/consensus/merge/merge.go index c5544ef33..6d5cf111b 100644 --- a/consensus/merge/merge.go +++ b/consensus/merge/merge.go @@ -82,8 +82,8 @@ func (s *Merge) VerifyHeader(chain consensus.ChainHeaderReader, header *types.He if err != nil { return err } - if !reached { - // Not verifying seals if the TTD is passed + if !reached && (chain.Config().PulseChain == nil || !misc.IsPoSHeader(header)) { + // Delegate non-PoS block to eth1 verification return s.eth1Engine.VerifyHeader(chain, header, !chain.Config().TerminalTotalDifficultyPassed) } // Short circuit if the parent is not known diff --git a/core/genesis_write.go b/core/genesis_write.go index f30ca60bc..85f8f81a2 100644 --- a/core/genesis_write.go +++ b/core/genesis_write.go @@ -121,12 +121,18 @@ func WriteGenesisBlock(tx kv.RwTx, genesis *types.Genesis, overrideCancunTime *b return genesis.Config, block, nil } + // Assume mainnet chainId first + chainId := params.MainnetChainConfig.ChainID.Uint64() + // Check whether the genesis block is already written. if genesis != nil { block, _, err1 := GenesisToBlock(genesis, tmpDir, logger) if err1 != nil { return genesis.Config, nil, err1 } + // Use the incoming chainID for building chain config. + // Allows for mainnet genesis w/ pulsechain config. + chainId = genesis.Config.ChainID.Uint64() hash := block.Hash() if hash != storedHash { return genesis.Config, block, &types.GenesisMismatchError{Stored: storedHash, New: hash} @@ -140,13 +146,18 @@ func WriteGenesisBlock(tx kv.RwTx, genesis *types.Genesis, overrideCancunTime *b return genesis.Config, nil, err } } + storedCfg, storedErr := rawdb.ReadChainConfig(tx, storedHash) + if storedErr == nil && storedCfg != nil { + // Use the existing chainID for building chain config. + // Allows for mainnet genesis w/ pulsechain config. + chainId = storedCfg.ChainID.Uint64() + } // Get the existing chain configuration. - newCfg := genesis.ConfigOrDefault(storedHash) + newCfg := genesis.ConfigOrDefault(storedHash, chainId) applyOverrides(newCfg) if err := newCfg.CheckConfigForkOrder(); err != nil { return newCfg, nil, err } - storedCfg, storedErr := rawdb.ReadChainConfig(tx, storedHash) if storedErr != nil && newCfg.Bor == nil { return newCfg, nil, storedErr } @@ -457,6 +468,28 @@ func ChiadoGenesisBlock() *types.Genesis { } } +func PulsechainGenesisBlock() *types.Genesis { + return &types.Genesis{ + Config: params.PulsechainChainConfig, + Nonce: 66, + ExtraData: hexutil.MustDecode("0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa"), + GasLimit: 5000, + Difficulty: big.NewInt(17179869184), + Alloc: readPrealloc("allocs/mainnet.json"), + } +} + +func PulsechainTestnetGenesisBlock() *types.Genesis { + return &types.Genesis{ + Config: params.PulsechainTestnetChainConfig, + Nonce: 66, + ExtraData: hexutil.MustDecode("0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa"), + GasLimit: 5000, + Difficulty: big.NewInt(17179869184), + Alloc: readPrealloc("allocs/mainnet.json"), + } +} + // Pre-calculated version of: // // DevnetSignPrivateKey = crypto.HexToECDSA(sha256.Sum256([]byte("erigon devnet key"))) @@ -675,6 +708,10 @@ func GenesisBlockByChainName(chain string) *types.Genesis { return GnosisGenesisBlock() case networkname.ChiadoChainName: return ChiadoGenesisBlock() + case networkname.PulsechainChainName: + return PulsechainGenesisBlock() + case networkname.PulsechainTestnetChainName: + return PulsechainTestnetGenesisBlock() default: return nil } diff --git a/core/skip_analysis.go b/core/skip_analysis.go index 55d38fc15..b767e0de8 100644 --- a/core/skip_analysis.go +++ b/core/skip_analysis.go @@ -36,8 +36,10 @@ import ( // 0x21ab7bf7245a87eae265124aaf180d91133377e47db2b1a4866493ec4b371150 (block 13119520) var analysisBlocks = map[string][]uint64{ - networkname.MainnetChainName: {5_800_596, 6_426_298, 6_426_432, 11_079_912, 13_119_520, 15_081_051}, - networkname.BorMainnetChainName: {29_447_463}, + networkname.MainnetChainName: {5_800_596, 6_426_298, 6_426_432, 11_079_912, 13_119_520, 15_081_051}, + networkname.BorMainnetChainName: {29_447_463}, + networkname.PulsechainChainName: {5_800_596, 6_426_298, 6_426_432, 11_079_912, 13_119_520, 15_081_051}, + networkname.PulsechainTestnetChainName: {5_800_596, 6_426_298, 6_426_432, 11_079_912, 13_119_520, 15_081_051}, } func SkipAnalysis(config *chain.Config, blockNumber uint64) bool { diff --git a/core/types/genesis.go b/core/types/genesis.go index d6f0e8cbc..301e3375d 100644 --- a/core/types/genesis.go +++ b/core/types/genesis.go @@ -25,6 +25,7 @@ import ( "math/big" "github.com/ledgerwatch/erigon-lib/chain" + "github.com/ledgerwatch/erigon-lib/chain/networkname" "github.com/ledgerwatch/erigon-lib/common" "github.com/ledgerwatch/erigon-lib/common/hexutility" @@ -165,12 +166,23 @@ func (e *GenesisMismatchError) Error() string { } return fmt.Sprintf("database contains incompatible genesis (try with --chain=%s)", config.ChainName) } -func (g *Genesis) ConfigOrDefault(genesisHash common.Hash) *chain.Config { +func (g *Genesis) ConfigOrDefault(genesisHash common.Hash, chainId uint64) *chain.Config { if g != nil { return g.Config } - config := params.ChainConfigByGenesisHash(genesisHash) + var config *chain.Config + pulseChainConfig := params.ChainConfigByChainName(networkname.PulsechainChainName) + pulseChainTestnetConfig := params.ChainConfigByChainName(networkname.PulsechainTestnetChainName) + switch chainId { + case pulseChainConfig.ChainID.Uint64(): + config = pulseChainConfig + case pulseChainTestnetConfig.ChainID.Uint64(): + config = pulseChainTestnetConfig + default: + config = params.ChainConfigByGenesisHash(genesisHash) + } + if config != nil { return config } else { diff --git a/core/types/transaction_signing.go b/core/types/transaction_signing.go index 19695c6c9..0e181fd25 100644 --- a/core/types/transaction_signing.go +++ b/core/types/transaction_signing.go @@ -42,6 +42,12 @@ func MakeSigner(config *chain.Config, blockNumber uint64, blockTime uint64) *Sig if overflow { panic(fmt.Errorf("chainID higher than 2^256-1")) } + + if config.PrimordialPulseAhead(blockNumber) { + // ethereum mainnet chainID is 1 + // required to validate transactions on mainnet + chainId.SetOne() + } } signer.unprotected = true switch { diff --git a/core/vm/evm.go b/core/vm/evm.go index 56ffc3bc2..92dff0e8d 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -17,6 +17,7 @@ package vm import ( + "math/big" "sync/atomic" "github.com/holiman/uint256" @@ -97,13 +98,24 @@ type EVM struct { // NewEVM returns a new EVM. The returned EVM is not thread safe and should // only ever be used *once*. func NewEVM(blockCtx evmtypes.BlockContext, txCtx evmtypes.TxContext, state evmtypes.IntraBlockState, chainConfig *chain.Config, vmConfig Config) *EVM { + flexChainConfig := chainConfig + + if flexChainConfig.PrimordialPulseAhead(blockCtx.BlockNumber) { + // create a shallow of chainConfig struct and set to ethereum mainnet + chainCfgCpy := *chainConfig + chainCfgCpy.ChainID = big.NewInt(1) + + // use the new chainCfgCpy + flexChainConfig = &chainCfgCpy + } + evm := &EVM{ Context: blockCtx, TxContext: txCtx, intraBlockState: state, config: vmConfig, - chainConfig: chainConfig, - chainRules: chainConfig.Rules(blockCtx.BlockNumber, blockCtx.Time), + chainConfig: flexChainConfig, + chainRules: flexChainConfig.Rules(blockCtx.BlockNumber, blockCtx.Time), } evm.interpreter = NewEVMInterpreter(evm, vmConfig) diff --git a/erigon-lib/chain/chain_config.go b/erigon-lib/chain/chain_config.go index 4d2069ee3..0fe1dadd3 100644 --- a/erigon-lib/chain/chain_config.go +++ b/erigon-lib/chain/chain_config.go @@ -68,6 +68,9 @@ type Config struct { CancunTime *big.Int `json:"cancunTime,omitempty"` PragueTime *big.Int `json:"pragueTime,omitempty"` + // PulseChain fork blocks + PrimordialPulseBlock *big.Int `json:"primordialPulseBlock,omitempty"` // PrimordialPulseBlock switch block (nil = no fork, 0 = already activated) + // Optional EIP-4844 parameters MinBlobGasPrice *uint64 `json:"minBlobGasPrice,omitempty"` MaxBlobGasPerBlock *uint64 `json:"maxBlobGasPerBlock,omitempty"` @@ -84,6 +87,8 @@ type Config struct { Bor BorConfig `json:"-"` BorJSON json.RawMessage `json:"bor,omitempty"` + + PulseChain *PulseChainConfig `json:"pulseChain,omitempty"` } type BorConfig interface { @@ -97,7 +102,7 @@ type BorConfig interface { func (c *Config) String() string { engine := c.getEngine() - return fmt.Sprintf("{ChainID: %v, Homestead: %v, DAO: %v, Tangerine Whistle: %v, Spurious Dragon: %v, Byzantium: %v, Constantinople: %v, Petersburg: %v, Istanbul: %v, Muir Glacier: %v, Berlin: %v, London: %v, Arrow Glacier: %v, Gray Glacier: %v, Terminal Total Difficulty: %v, Merge Netsplit: %v, Shanghai: %v, Cancun: %v, Prague: %v, Engine: %v}", + return fmt.Sprintf("{ChainID: %v, Homestead: %v, DAO: %v, Tangerine Whistle: %v, Spurious Dragon: %v, Byzantium: %v, Constantinople: %v, Petersburg: %v, Istanbul: %v, Muir Glacier: %v, Berlin: %v, London: %v, Arrow Glacier: %v, Gray Glacier: %v, Primordial Pulse: %v, Terminal Total Difficulty: %v, Merge Netsplit: %v, Shanghai: %v, Cancun: %v, Prague: %v, Engine: %v}", c.ChainID, c.HomesteadBlock, c.DAOForkBlock, @@ -112,6 +117,7 @@ func (c *Config) String() string { c.LondonBlock, c.ArrowGlacierBlock, c.GrayGlacierBlock, + c.PrimordialPulseBlock, c.TerminalTotalDifficulty, c.MergeNetsplitBlock, c.ShanghaiTime, @@ -231,6 +237,17 @@ func (c *Config) IsPrague(time uint64) bool { return isForked(c.PragueTime, time) } +// IsPrimordialPulseBlock returns whether or not the given block is the primordial pulse block. +func (c *Config) IsPrimordialPulseBlock(number uint64) bool { + return c.PrimordialPulseBlock != nil && c.PrimordialPulseBlock.Uint64() == number +} + +// PrimordialPulseAhead Returns true if there is a PrimordialPulse block in the future, indicating this chain +// should still be evaluated using the ethash consensus engine and with mainnet ChainID. +func (c *Config) PrimordialPulseAhead(number uint64) bool { + return c.PrimordialPulseBlock != nil && c.PrimordialPulseBlock.Uint64() > number +} + func (c *Config) GetBurntContract(num uint64) *common.Address { if len(c.BurntContract) == 0 { return nil @@ -363,7 +380,8 @@ func (c *Config) checkCompatible(newcfg *Config, head uint64) *ConfigCompatError if incompatible(c.SpuriousDragonBlock, newcfg.SpuriousDragonBlock, head) { return newCompatError("Spurious Dragon fork block", c.SpuriousDragonBlock, newcfg.SpuriousDragonBlock) } - if c.IsSpuriousDragon(head) && !numEqual(c.ChainID, newcfg.ChainID) { + // allow mismatching ChainID if there is a PrimordialPulse block ahead + if c.IsSpuriousDragon(head) && !numEqual(c.ChainID, newcfg.ChainID) && !newcfg.PrimordialPulseAhead(head) { return newCompatError("EIP155 chain ID", c.SpuriousDragonBlock, newcfg.SpuriousDragonBlock) } if incompatible(c.ByzantiumBlock, newcfg.ByzantiumBlock, head) { @@ -400,6 +418,9 @@ func (c *Config) checkCompatible(newcfg *Config, head uint64) *ConfigCompatError if incompatible(c.MergeNetsplitBlock, newcfg.MergeNetsplitBlock, head) { return newCompatError("Merge netsplit block", c.MergeNetsplitBlock, newcfg.MergeNetsplitBlock) } + if incompatible(c.PrimordialPulseBlock, newcfg.PrimordialPulseBlock, head) { + return newCompatError("PrimordialPulse fork block", c.PrimordialPulseBlock, newcfg.PrimordialPulseBlock) + } return nil } @@ -506,6 +527,12 @@ func (c *Config) Rules(num uint64, time uint64) *Rules { if chainID == nil { chainID = new(big.Int) } + isShanghai := c.IsShanghai(time) + if c.PrimordialPulseAhead(num) { + // If the PrimordialPulse fork is ahead, derive the `isShanghai` rule + // from the Ethereum Mainnet Shanghai timestamp. + isShanghai = time >= 1681338455 + } return &Rules{ ChainID: new(big.Int).Set(chainID), @@ -518,7 +545,7 @@ func (c *Config) Rules(num uint64, time uint64) *Rules { IsIstanbul: c.IsIstanbul(num), IsBerlin: c.IsBerlin(num), IsLondon: c.IsLondon(num), - IsShanghai: c.IsShanghai(time) || c.IsAgra(num), + IsShanghai: isShanghai || c.IsAgra(num), IsCancun: c.IsCancun(time), IsNapoli: c.IsNapoli(num), IsPrague: c.IsPrague(time), diff --git a/erigon-lib/chain/networkname/network_name.go b/erigon-lib/chain/networkname/network_name.go index 877b37389..28c4df909 100644 --- a/erigon-lib/chain/networkname/network_name.go +++ b/erigon-lib/chain/networkname/network_name.go @@ -1,18 +1,20 @@ package networkname const ( - MainnetChainName = "mainnet" - HoleskyChainName = "holesky" - SepoliaChainName = "sepolia" - GoerliChainName = "goerli" - DevChainName = "dev" - MumbaiChainName = "mumbai" - AmoyChainName = "amoy" - BorMainnetChainName = "bor-mainnet" - BorDevnetChainName = "bor-devnet" - GnosisChainName = "gnosis" - BorE2ETestChain2ValName = "bor-e2e-test-2Val" - ChiadoChainName = "chiado" + MainnetChainName = "mainnet" + HoleskyChainName = "holesky" + SepoliaChainName = "sepolia" + GoerliChainName = "goerli" + DevChainName = "dev" + MumbaiChainName = "mumbai" + AmoyChainName = "amoy" + BorMainnetChainName = "bor-mainnet" + BorDevnetChainName = "bor-devnet" + GnosisChainName = "gnosis" + BorE2ETestChain2ValName = "bor-e2e-test-2Val" + ChiadoChainName = "chiado" + PulsechainChainName = "pulsechain" + PulsechainTestnetChainName = "pulsechain-testnet" ) var All = []string{ @@ -26,4 +28,6 @@ var All = []string{ BorDevnetChainName, GnosisChainName, ChiadoChainName, + PulsechainChainName, + PulsechainTestnetChainName, } diff --git a/erigon-lib/chain/pulsechain_config.go b/erigon-lib/chain/pulsechain_config.go new file mode 100644 index 000000000..0a89290eb --- /dev/null +++ b/erigon-lib/chain/pulsechain_config.go @@ -0,0 +1,25 @@ +package chain + +import ( + "math/big" +) + +type PulseChainConfig struct { + // An optional treasury which will receive allocations during the PrimordialPulseBlock. + Treasury *PulseChainTreasury `json:"treasury,omitempty"` +} + +// String implements the stringer interface, returning the consensus engine details. +func (b *PulseChainConfig) String() string { + return "PulseChain" +} + +// PulseChainTreasury represents an optional treasury for launching PulseChain testnets. +type PulseChainTreasury struct { + Addr string `json:"addr"` + Balance string `json:"balance"` +} + +// PulseChainTTDOffset is a trivially small amount of work added to the Ethereum Mainnet TTD +// to allow for un-merging and merging with the PulseChain beacon chain. +var PulseChainTTDOffset = big.NewInt(131_072) diff --git a/erigon-lib/chain/snapcfg/util.go b/erigon-lib/chain/snapcfg/util.go index db1e42d32..8e41caa8d 100644 --- a/erigon-lib/chain/snapcfg/util.go +++ b/erigon-lib/chain/snapcfg/util.go @@ -10,19 +10,22 @@ import ( snapshothashes "github.com/ledgerwatch/erigon-snapshot" "github.com/ledgerwatch/erigon-snapshot/webseed" "github.com/pelletier/go-toml/v2" + pulseSnapshotHashes "gitlab.com/pulsechaincom/erigon-pulse-snapshot" "golang.org/x/exp/slices" ) var ( Mainnet = fromToml(snapshothashes.Mainnet) // Holesky = fromToml(snapshothashes.Holesky) - Sepolia = fromToml(snapshothashes.Sepolia) - Goerli = fromToml(snapshothashes.Goerli) - Mumbai = fromToml(snapshothashes.Mumbai) - Amoy = fromToml(snapshothashes.Amoy) - BorMainnet = fromToml(snapshothashes.BorMainnet) - Gnosis = fromToml(snapshothashes.Gnosis) - Chiado = fromToml(snapshothashes.Chiado) + Sepolia = fromToml(snapshothashes.Sepolia) + Goerli = fromToml(snapshothashes.Goerli) + Mumbai = fromToml(snapshothashes.Mumbai) + Amoy = fromToml(snapshothashes.Amoy) + BorMainnet = fromToml(snapshothashes.BorMainnet) + Gnosis = fromToml(snapshothashes.Gnosis) + Chiado = fromToml(snapshothashes.Chiado) + PulseChainMainnet = fromToml(pulseSnapshotHashes.PulseChainMainnet) + PulseChainTestnet = fromToml(snapshothashes.Mainnet) ) type PreverifiedItem struct { @@ -137,13 +140,15 @@ type Cfg struct { var knownPreverified = map[string]Preverified{ networkname.MainnetChainName: Mainnet, // networkname.HoleskyChainName: HoleskyChainSnapshotCfg, - networkname.SepoliaChainName: Sepolia, - networkname.GoerliChainName: Goerli, - networkname.MumbaiChainName: Mumbai, - networkname.AmoyChainName: Amoy, - networkname.BorMainnetChainName: BorMainnet, - networkname.GnosisChainName: Gnosis, - networkname.ChiadoChainName: Chiado, + networkname.SepoliaChainName: Sepolia, + networkname.GoerliChainName: Goerli, + networkname.MumbaiChainName: Mumbai, + networkname.AmoyChainName: Amoy, + networkname.BorMainnetChainName: BorMainnet, + networkname.GnosisChainName: Gnosis, + networkname.ChiadoChainName: Chiado, + networkname.PulsechainChainName: PulseChainMainnet, + networkname.PulsechainTestnetChainName: PulseChainTestnet, } // KnownCfg return list of preverified hashes for given network, but apply whiteList filter if it's not empty diff --git a/erigon-lib/txpool/fetch.go b/erigon-lib/txpool/fetch.go index a4ca77bff..057187986 100644 --- a/erigon-lib/txpool/fetch.go +++ b/erigon-lib/txpool/fetch.go @@ -74,8 +74,8 @@ func NewFetch(ctx context.Context, sentryClients []direct.SentryClient, pool Poo coreDB: coreDB, db: db, stateChangesClient: stateChangesClient, - stateChangesParseCtx: types2.NewTxParseContext(chainID).ChainIDRequired(), //TODO: change ctx if rules changed - pooledTxsParseCtx: types2.NewTxParseContext(chainID).ChainIDRequired(), + stateChangesParseCtx: types2.NewTxParseContext(chainID, false).ChainIDRequired(), //TODO: change ctx if rules changed + pooledTxsParseCtx: types2.NewTxParseContext(chainID, false).ChainIDRequired(), logger: logger, } f.pooledTxsParseCtx.ValidateRLP(f.pool.ValidateSerializedTxn) diff --git a/erigon-lib/txpool/fetch_test.go b/erigon-lib/txpool/fetch_test.go index b000f7d38..a9c765c6d 100644 --- a/erigon-lib/txpool/fetch_test.go +++ b/erigon-lib/txpool/fetch_test.go @@ -46,7 +46,7 @@ func TestFetch(t *testing.T) { sentryClient := direct.NewSentryClientDirect(direct.ETH66, m) pool := &PoolMock{} - fetch := NewFetch(ctx, []direct.SentryClient{sentryClient}, pool, &remote.KVClientMock{}, nil, nil, *u256.N1, log.New()) + fetch := NewFetch(ctx, []direct.SentryClient{sentryClient}, pool, &remote.KVClientMock{}, nil, nil, *u256.N1, log.New(), false) var wg sync.WaitGroup fetch.SetWaitGroup(&wg) m.StreamWg.Add(2) @@ -184,7 +184,7 @@ func TestOnNewBlock(t *testing.T) { }, } pool := &PoolMock{} - fetch := NewFetch(ctx, nil, pool, stateChanges, coreDB, db, *u256.N1, log.New()) + fetch := NewFetch(ctx, nil, pool, stateChanges, coreDB, db, *u256.N1, log.New(), false) err := fetch.handleStateChanges(ctx, stateChanges) assert.ErrorIs(t, io.EOF, err) assert.Equal(t, 1, len(pool.OnNewBlockCalls())) diff --git a/erigon-lib/txpool/pool.go b/erigon-lib/txpool/pool.go index fa5a0b1b5..f0475cac8 100644 --- a/erigon-lib/txpool/pool.go +++ b/erigon-lib/txpool/pool.go @@ -654,7 +654,7 @@ func (p *TxPool) getCachedBlobTxnLocked(tx kv.Tx, hash []byte) (*metaTx, error) if err != nil { return nil, err } - parseCtx := types.NewTxParseContext(p.chainID) + parseCtx := types.NewTxParseContext(p.chainID, false) parseCtx.WithSender(false) txSlot := &types.TxSlot{} parseCtx.ParseTransaction(txn, 0, txSlot, nil, false, true, nil) @@ -2048,7 +2048,7 @@ func (p *TxPool) fromDB(ctx context.Context, tx kv.Tx, coreTx kv.Tx) error { } txs := types.TxSlots{} - parseCtx := types.NewTxParseContext(p.chainID) + parseCtx := types.NewTxParseContext(p.chainID, false) parseCtx.WithSender(false) i := 0 diff --git a/erigon-lib/txpool/pool_fuzz_test.go b/erigon-lib/txpool/pool_fuzz_test.go index 54b1beb02..07f36a089 100644 --- a/erigon-lib/txpool/pool_fuzz_test.go +++ b/erigon-lib/txpool/pool_fuzz_test.go @@ -199,7 +199,7 @@ func poolsFromFuzzBytes(rawTxNonce, rawValues, rawTips, rawFeeCap, rawSender []b senderIDs[senders.AddressAt(i%senders.Len())] = senderID } txs.Txs = make([]*types.TxSlot, len(txNonce)) - parseCtx := types.NewTxParseContext(*u256.N1) + parseCtx := types.NewTxParseContext(*u256.N1, false) parseCtx.WithSender(false) for i := range txNonce { txs.Txs[i] = &types.TxSlot{ @@ -314,7 +314,7 @@ func FuzzOnNewBlocks(f *testing.F) { cfg := txpoolcfg.DefaultConfig sendersCache := kvcache.New(kvcache.DefaultCoherentConfig) - pool, err := New(ch, coreDB, cfg, sendersCache, *u256.N1, nil, nil, nil, fixedgas.DefaultMaxBlobsPerBlock, nil, log.New()) + pool, err := New(ch, coreDB, cfg, sendersCache, *u256.N1, nil, nil, nil, fixedgas.DefaultMaxBlobsPerBlock, nil, log.New(), false) assert.NoError(err) err = pool.Start(ctx, db) @@ -540,7 +540,7 @@ func FuzzOnNewBlocks(f *testing.F) { check(p2pReceived, types.TxSlots{}, "after_flush") checkNotify(p2pReceived, types.TxSlots{}, "after_flush") - p2, err := New(ch, coreDB, txpoolcfg.DefaultConfig, sendersCache, *u256.N1, nil, nil, nil, fixedgas.DefaultMaxBlobsPerBlock, nil, log.New()) + p2, err := New(ch, coreDB, txpoolcfg.DefaultConfig, sendersCache, *u256.N1, nil, nil, nil, fixedgas.DefaultMaxBlobsPerBlock, nil, log.New(), false) assert.NoError(err) p2.senders = pool.senders // senders are not persisted diff --git a/erigon-lib/txpool/pool_test.go b/erigon-lib/txpool/pool_test.go index 9dcbf1cc9..d50154aac 100644 --- a/erigon-lib/txpool/pool_test.go +++ b/erigon-lib/txpool/pool_test.go @@ -53,7 +53,7 @@ func TestNonceFromAddress(t *testing.T) { cfg := txpoolcfg.DefaultConfig sendersCache := kvcache.New(kvcache.DefaultCoherentConfig) - pool, err := New(ch, coreDB, cfg, sendersCache, *u256.N1, nil, nil, nil, fixedgas.DefaultMaxBlobsPerBlock, nil, log.New()) + pool, err := New(ch, coreDB, cfg, sendersCache, *u256.N1, nil, nil, nil, fixedgas.DefaultMaxBlobsPerBlock, nil, log.New(), false) assert.NoError(err) require.True(pool != nil) ctx := context.Background() @@ -173,7 +173,7 @@ func TestReplaceWithHigherFee(t *testing.T) { cfg := txpoolcfg.DefaultConfig sendersCache := kvcache.New(kvcache.DefaultCoherentConfig) - pool, err := New(ch, coreDB, cfg, sendersCache, *u256.N1, nil, nil, nil, fixedgas.DefaultMaxBlobsPerBlock, nil, log.New()) + pool, err := New(ch, coreDB, cfg, sendersCache, *u256.N1, nil, nil, nil, fixedgas.DefaultMaxBlobsPerBlock, nil, log.New(), false) assert.NoError(err) require.NotEqual(nil, pool) ctx := context.Background() @@ -290,7 +290,7 @@ func TestReverseNonces(t *testing.T) { cfg := txpoolcfg.DefaultConfig sendersCache := kvcache.New(kvcache.DefaultCoherentConfig) - pool, err := New(ch, coreDB, cfg, sendersCache, *u256.N1, nil, nil, nil, fixedgas.DefaultMaxBlobsPerBlock, nil, log.New()) + pool, err := New(ch, coreDB, cfg, sendersCache, *u256.N1, nil, nil, nil, fixedgas.DefaultMaxBlobsPerBlock, nil, log.New(), false) assert.NoError(err) require.True(pool != nil) ctx := context.Background() @@ -417,7 +417,7 @@ func TestTxPoke(t *testing.T) { cfg := txpoolcfg.DefaultConfig sendersCache := kvcache.New(kvcache.DefaultCoherentConfig) - pool, err := New(ch, coreDB, cfg, sendersCache, *u256.N1, nil, nil, nil, fixedgas.DefaultMaxBlobsPerBlock, nil, log.New()) + pool, err := New(ch, coreDB, cfg, sendersCache, *u256.N1, nil, nil, nil, fixedgas.DefaultMaxBlobsPerBlock, nil, log.New(), false) assert.NoError(err) require.True(pool != nil) ctx := context.Background() @@ -682,7 +682,7 @@ func TestShanghaiValidateTx(t *testing.T) { } cache := &kvcache.DummyCache{} - pool, err := New(ch, coreDB, cfg, cache, *u256.N1, shanghaiTime, nil /* agraBlock */, nil /* cancunTime */, fixedgas.DefaultMaxBlobsPerBlock, nil, logger) + pool, err := New(ch, coreDB, cfg, cache, *u256.N1, shanghaiTime, nil /* agraBlock */, nil /* cancunTime */, fixedgas.DefaultMaxBlobsPerBlock, nil, logger, false) asrt.NoError(err) ctx := context.Background() tx, err := coreDB.BeginRw(ctx) @@ -728,7 +728,7 @@ func TestBlobTxReplacement(t *testing.T) { db, coreDB := memdb.NewTestPoolDB(t), memdb.NewTestDB(t) cfg := txpoolcfg.DefaultConfig sendersCache := kvcache.New(kvcache.DefaultCoherentConfig) - pool, err := New(ch, coreDB, cfg, sendersCache, *u256.N1, common.Big0, nil, common.Big0, fixedgas.DefaultMaxBlobsPerBlock, nil, log.New()) + pool, err := New(ch, coreDB, cfg, sendersCache, *u256.N1, common.Big0, nil, common.Big0, fixedgas.DefaultMaxBlobsPerBlock, nil, log.New(), false) assert.NoError(err) require.True(pool != nil) ctx := context.Background() @@ -929,7 +929,7 @@ func makeBlobTx() types.TxSlot { tip, feeCap, blobFeeCap := uint256.NewInt(100_000), uint256.NewInt(200_000), uint256.NewInt(200_000) blobTx := types.TxSlot{} - tctx := types.NewTxParseContext(*uint256.NewInt(5)) + tctx := types.NewTxParseContext(*uint256.NewInt(5), false) tctx.WithSender(false) tctx.ParseTransaction(wrapperRlp, 0, &blobTx, nil, false, true, nil) blobTx.BlobHashes = make([]common.Hash, 2) diff --git a/erigon-lib/txpool/txpool_grpc_server.go b/erigon-lib/txpool/txpool_grpc_server.go index de4c26db3..69719b364 100644 --- a/erigon-lib/txpool/txpool_grpc_server.go +++ b/erigon-lib/txpool/txpool_grpc_server.go @@ -182,7 +182,7 @@ func (s *GrpcServer) Add(ctx context.Context, in *txpool_proto.AddRequest) (*txp defer tx.Rollback() var slots types.TxSlots - parseCtx := types.NewTxParseContext(s.chainID).ChainIDRequired() + parseCtx := types.NewTxParseContext(s.chainID, false).ChainIDRequired() parseCtx.ValidateRLP(s.txPool.ValidateSerializedTxn) reply := &txpool_proto.AddReply{Imported: make([]txpool_proto.ImportResult, len(in.RlpTxs)), Errors: make([]string, len(in.RlpTxs))} diff --git a/erigon-lib/types/txn.go b/erigon-lib/types/txn.go index d7f96d781..b9cefc2e5 100644 --- a/erigon-lib/types/txn.go +++ b/erigon-lib/types/txn.go @@ -47,33 +47,35 @@ type TxParseConfig struct { // TxParseContext is object that is required to parse transactions and turn transaction payload into TxSlot objects // usage of TxContext helps avoid extra memory allocations type TxParseContext struct { - Keccak2 hash.Hash - Keccak1 hash.Hash - validateRlp func([]byte) error - ChainID uint256.Int // Signature values - R uint256.Int // Signature values - S uint256.Int // Signature values - V uint256.Int // Signature values - ChainIDMul uint256.Int - DeriveChainID uint256.Int // pre-allocated variable to calculate Sub(&ctx.v, &ctx.chainIDMul) - cfg TxParseConfig - buf [65]byte // buffer needs to be enough for hashes (32 bytes) and for public key (65 bytes) - Sig [65]byte - Sighash [32]byte - withSender bool - allowPreEip2s bool // Allow s > secp256k1n/2; see EIP-2 - chainIDRequired bool - IsProtected bool + Keccak2 hash.Hash + Keccak1 hash.Hash + validateRlp func([]byte) error + ChainID uint256.Int // Signature values + R uint256.Int // Signature values + S uint256.Int // Signature values + V uint256.Int // Signature values + ChainIDMul uint256.Int + DeriveChainID uint256.Int // pre-allocated variable to calculate Sub(&ctx.v, &ctx.chainIDMul) + cfg TxParseConfig + buf [65]byte // buffer needs to be enough for hashes (32 bytes) and for public key (65 bytes) + Sig [65]byte + Sighash [32]byte + withSender bool + allowPreEip2s bool // Allow s > secp256k1n/2; see EIP-2 + chainIDRequired bool + IsProtected bool + allowPulseChainLegacy bool // If allowPulseChainLegacy is true, allow prefork transactions from the Ethereum Mainnet chain. } -func NewTxParseContext(chainID uint256.Int) *TxParseContext { +func NewTxParseContext(chainID uint256.Int, allowPulseChainLegacy bool) *TxParseContext { if chainID.IsZero() { panic("wrong chainID") } ctx := &TxParseContext{ - withSender: true, - Keccak1: sha3.NewLegacyKeccak256(), - Keccak2: sha3.NewLegacyKeccak256(), + withSender: true, + Keccak1: sha3.NewLegacyKeccak256(), + Keccak2: sha3.NewLegacyKeccak256(), + allowPulseChainLegacy: allowPulseChainLegacy, } // behave as of London enabled @@ -337,7 +339,13 @@ func (ctx *TxParseContext) parseTransactionBody(payload []byte, pos, p0 int, slo ctx.ChainID.Set(&ctx.cfg.ChainID) } if !ctx.ChainID.Eq(&ctx.cfg.ChainID) { - return 0, fmt.Errorf("%w: %s, %d (expected %d)", ErrParseTxn, "invalid chainID", ctx.ChainID.Uint64(), ctx.cfg.ChainID.Uint64()) + if !ctx.allowPulseChainLegacy { + return 0, fmt.Errorf("%w: %s, %d (expected %d)", ErrParseTxn, "invalid chainID", ctx.ChainID.Uint64(), ctx.cfg.ChainID.Uint64()) + } + // If allowPulseChainLegacy, ChainID must be 1 (Ethereum Mainnet). + if !ctx.ChainID.Eq(uint256.NewInt(1)) { + return 0, fmt.Errorf("%w: %s, %d (expected %d)", ErrParseTxn, "invalid PulseChain chainID", ctx.ChainID.Uint64(), ctx.cfg.ChainID.Uint64()) + } } } // Next follows the nonce, which we need to parse @@ -486,7 +494,13 @@ func (ctx *TxParseContext) parseTransactionBody(payload []byte, pos, p0 int, slo ctx.ChainID.Sub(&ctx.V, u256.N35) ctx.ChainID.Rsh(&ctx.ChainID, 1) if !ctx.ChainID.Eq(&ctx.cfg.ChainID) { - return 0, fmt.Errorf("%w: %s, %d (expected %d)", ErrParseTxn, "invalid chainID", ctx.ChainID.Uint64(), ctx.cfg.ChainID.Uint64()) + if !ctx.allowPulseChainLegacy { + return 0, fmt.Errorf("%w: %s, %d (expected %d)", ErrParseTxn, "invalid chainID", ctx.ChainID.Uint64(), ctx.cfg.ChainID.Uint64()) + } + // If allowPulseChainLegacy, ChainID must be 1 (Ethereum Mainnet). + if !ctx.ChainID.Eq(uint256.NewInt(1)) { + return 0, fmt.Errorf("%w: %s, %d (expected %d)", ErrParseTxn, "invalid PulseChain chainID", ctx.ChainID.Uint64(), ctx.cfg.ChainID.Uint64()) + } } chainIDBits = ctx.ChainID.BitLen() diff --git a/erigon-lib/types/txn_packets_fuzz_test.go b/erigon-lib/types/txn_packets_fuzz_test.go index dfb593228..c2e379a46 100644 --- a/erigon-lib/types/txn_packets_fuzz_test.go +++ b/erigon-lib/types/txn_packets_fuzz_test.go @@ -16,7 +16,7 @@ func FuzzPooledTransactions66(f *testing.F) { f.Add(hexutility.MustDecodeHex("e8bfffffffffffffffffffffffff71e866666666955ef90c91f9fa08f96ebfbfbf007d765059effe33")) f.Fuzz(func(t *testing.T, in []byte) { t.Parallel() - ctx := NewTxParseContext(*u256.N1) + ctx := NewTxParseContext(*u256.N1, false) slots := TxSlots{} reqId, _, err := ParsePooledTransactions66(in, 0, ctx, &slots, nil) if err != nil { diff --git a/erigon-lib/types/txn_packets_test.go b/erigon-lib/types/txn_packets_test.go index a21c523fd..da5e1285f 100644 --- a/erigon-lib/types/txn_packets_test.go +++ b/erigon-lib/types/txn_packets_test.go @@ -143,7 +143,7 @@ func TestPooledTransactionsPacket66(t *testing.T) { encodeBuf = EncodePooledTransactions66(tt.txs, tt.requestID, encodeBuf) require.Equal(tt.encoded, fmt.Sprintf("%x", encodeBuf)) - ctx := NewTxParseContext(*uint256.NewInt(tt.chainID)) + ctx := NewTxParseContext(*uint256.NewInt(tt.chainID), false) slots := &TxSlots{} requestID, _, err := ParsePooledTransactions66(encodeBuf, 0, ctx, slots, nil) require.NoError(err) @@ -162,7 +162,7 @@ func TestPooledTransactionsPacket66(t *testing.T) { require.Equal(tt.encoded, fmt.Sprintf("%x", encodeBuf)) chainID := uint256.NewInt(tt.chainID) - ctx := NewTxParseContext(*chainID) + ctx := NewTxParseContext(*chainID, false) slots := &TxSlots{} requestID, _, err := ParsePooledTransactions66(encodeBuf, 0, ctx, slots, func(bytes []byte) error { return ErrRejected }) require.NoError(err) @@ -203,7 +203,7 @@ func TestTransactionsPacket(t *testing.T) { encodeBuf = EncodeTransactions(tt.txs, encodeBuf) require.Equal(tt.encoded, fmt.Sprintf("%x", encodeBuf)) - ctx := NewTxParseContext(*uint256.NewInt(tt.chainID)) + ctx := NewTxParseContext(*uint256.NewInt(tt.chainID), false) slots := &TxSlots{} _, err := ParseTransactions(encodeBuf, 0, ctx, slots, nil) require.NoError(err) @@ -221,7 +221,7 @@ func TestTransactionsPacket(t *testing.T) { require.Equal(tt.encoded, fmt.Sprintf("%x", encodeBuf)) chainID := uint256.NewInt(tt.chainID) - ctx := NewTxParseContext(*chainID) + ctx := NewTxParseContext(*chainID, false) slots := &TxSlots{} _, err := ParseTransactions(encodeBuf, 0, ctx, slots, func(bytes []byte) error { return ErrRejected }) require.NoError(err) diff --git a/erigon-lib/types/txn_test.go b/erigon-lib/types/txn_test.go index 1a0eb1200..cf0cbf680 100644 --- a/erigon-lib/types/txn_test.go +++ b/erigon-lib/types/txn_test.go @@ -36,7 +36,7 @@ func TestParseTransactionRLP(t *testing.T) { testSet := testSet t.Run(strconv.Itoa(int(testSet.chainID.Uint64())), func(t *testing.T) { require := require.New(t) - ctx := NewTxParseContext(testSet.chainID) + ctx := NewTxParseContext(testSet.chainID, false) tx, txSender := &TxSlot{}, [20]byte{} for i, tt := range testSet.tests { tt := tt @@ -72,7 +72,7 @@ func TestParseTransactionRLP(t *testing.T) { func TestTransactionSignatureValidity1(t *testing.T) { chainId := new(uint256.Int).SetUint64(1) - ctx := NewTxParseContext(*chainId) + ctx := NewTxParseContext(*chainId, false) ctx.WithAllowPreEip2s(true) tx, txSender := &TxSlot{}, [20]byte{} @@ -96,7 +96,7 @@ func TestTransactionSignatureValidity1(t *testing.T) { // Problematic txn included in a bad block on Görli func TestTransactionSignatureValidity2(t *testing.T) { chainId := new(uint256.Int).SetUint64(5) - ctx := NewTxParseContext(*chainId) + ctx := NewTxParseContext(*chainId, false) slot, sender := &TxSlot{}, [20]byte{} rlp := hexutility.MustDecodeHex("02f8720513844190ab00848321560082520894cab441d2f45a3fee83d15c6b6b6c36a139f55b6288054607fc96a6000080c001a0dffe4cb5651e663d0eac8c4d002de734dd24db0f1109b062d17da290a133cc02a0913fb9f53f7a792bcd9e4d7cced1b8545d1ab82c77432b0bc2e9384ba6c250c5") _, err := ctx.ParseTransaction(rlp, 0, slot, sender[:], false /* hasEnvelope */, true /* wrappedWithBlobs */, nil) @@ -200,7 +200,7 @@ func TestBlobTxParsing(t *testing.T) { bodyEnvelope = append(bodyEnvelope, BlobTxType) bodyEnvelope = append(bodyEnvelope, bodyRlp...) - ctx := NewTxParseContext(*uint256.NewInt(5)) + ctx := NewTxParseContext(*uint256.NewInt(5), false) ctx.withSender = false var thinTx TxSlot // only tx body, no blobs diff --git a/erigon-lib/types/txn_types_fuzz_test.go b/erigon-lib/types/txn_types_fuzz_test.go index 1f43a695d..df14c364c 100644 --- a/erigon-lib/types/txn_types_fuzz_test.go +++ b/erigon-lib/types/txn_types_fuzz_test.go @@ -24,7 +24,7 @@ func FuzzParseTx(f *testing.F) { f.Add([]byte{1}, 0) f.Fuzz(func(t *testing.T, in []byte, pos int) { t.Parallel() - ctx := NewTxParseContext(*u256.N1) + ctx := NewTxParseContext(*u256.N1, false) txn := &TxSlot{} sender := make([]byte, 20) _, _ = ctx.ParseTransaction(in, pos, txn, sender, false /* hasEnvelope */, true /* wrappedWithBlobs */, nil) diff --git a/go.mod b/go.mod index 4aaa18484..1db9c4248 100644 --- a/go.mod +++ b/go.mod @@ -88,6 +88,7 @@ require ( github.com/valyala/fastjson v1.6.4 github.com/vektah/gqlparser/v2 v2.5.10 github.com/xsleonard/go-merkle v1.1.0 + gitlab.com/pulsechaincom/erigon-pulse-snapshot v0.0.2 go.uber.org/zap v1.26.0 golang.org/x/crypto v0.17.0 golang.org/x/exp v0.0.0-20230905200255-921286631fa9 diff --git a/go.sum b/go.sum index 5484d069c..9be3e07d3 100644 --- a/go.sum +++ b/go.sum @@ -928,6 +928,8 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +gitlab.com/pulsechaincom/erigon-pulse-snapshot v0.0.2 h1:M/ReqIKALiW5a56bdT8w9j9GZA7yqSEhO9joesRsWBQ= +gitlab.com/pulsechaincom/erigon-pulse-snapshot v0.0.2/go.mod h1:U/W4jVFkJQ1XeXul9ng2MFGYRV2bBqTxVjSEojtm18c= go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= diff --git a/p2p/sentry/sentry_grpc_server.go b/p2p/sentry/sentry_grpc_server.go index c745aadef..3e947375a 100644 --- a/p2p/sentry/sentry_grpc_server.go +++ b/p2p/sentry/sentry_grpc_server.go @@ -262,7 +262,10 @@ func makeP2PServer( protocols []p2p.Protocol, ) (*p2p.Server, error) { var urls []string - chainConfig := params.ChainConfigByGenesisHash(genesisHash) + chainConfig := params.ChainConfigByChainName(p2pConfig.ChainName) + if chainConfig == nil { + chainConfig = params.ChainConfigByGenesisHash(genesisHash) + } if chainConfig != nil { urls = params.BootnodeURLsOfChain(chainConfig.ChainName) } @@ -990,7 +993,7 @@ func (ss *GrpcServer) SetStatus(ctx context.Context, statusData *proto_sentry.St var err error if !ss.p2p.NoDiscovery { if len(ss.discoveryDNS) == 0 { - if url := params.KnownDNSNetwork(genesisHash, "all"); url != "" { + if url := params.KnownDNSNetwork(genesisHash, statusData.NetworkId, "all"); url != "" { ss.discoveryDNS = []string{url} } } diff --git a/p2p/server.go b/p2p/server.go index c42f88d03..85cef9277 100644 --- a/p2p/server.go +++ b/p2p/server.go @@ -173,6 +173,9 @@ type Config struct { // whenever a message is sent to or received from a peer EnableMsgEvents bool + // ChainName is the name of the chain config + ChainName string `toml:",omitempty"` + // it is actually used but a linter got confused clock mclock.Clock //nolint:structcheck diff --git a/params/bootnodes.go b/params/bootnodes.go index 5e7734c74..2faac28a5 100644 --- a/params/bootnodes.go +++ b/params/bootnodes.go @@ -146,16 +146,42 @@ var ChiadoBootnodes = []string{ "enode://595160631241ea41b187b85716f9f9572a266daa940d74edbe3b83477264ce284d69208e61cf50e91641b1b4f9a03fa8e60eb73d435a84cf4616b1c969bc2512@3.69.35.13:30303", } -const dnsPrefix = "enrtree://AKA3AM6LPBYEUDMVNU3BSVQJ5AD45Y7YPOHJLEF6W26QOE4VTUDPE@" +// PulsechainBootnodes are the enode URLs of the P2P bootstrap nodes running on +// the main Pulsechain network. +var PulsechainBootnodes = []string{ + // Pulsechain Go Bootnodes +} + +// PulsechainTestnetBootnodes are the enode URLs of the P2P bootstrap nodes running on +// the Pulsechain testnet network. +var PulsechainTestnetBootnodes = []string{ + // Pulsechain Go Bootnodes +} // KnownDNSNetwork returns the address of a public DNS-based node list for the given // genesis hash and protocol. See https://github.com/ethereum/discv4-dns-lists for more // information. -func KnownDNSNetwork(genesis libcommon.Hash, protocol string) string { +func KnownDNSNetwork(genesis libcommon.Hash, networkID uint64, protocol string) string { var net string + var dnsPrefix = "enrtree://AKA3AM6LPBYEUDMVNU3BSVQJ5AD45Y7YPOHJLEF6W26QOE4VTUDPE@" + var tld = ".ethdisco.net" + + if networkID == NetworkIDByChainName(networkname.PulsechainChainName) || networkID == NetworkIDByChainName(networkname.PulsechainTestnetChainName) { + tld = ".pulsedisco.net" + dnsPrefix = "enrtree://APFXO36RU3TWV7XFGWI2TYF5IDA3WM2GPTRL3TCZINWHZX4R6TAOK@" + } + switch genesis { case MainnetGenesisHash: - net = "mainnet" + switch networkID { + case NetworkIDByChainName(networkname.PulsechainChainName): + net = "PulseChain" + case NetworkIDByChainName(networkname.PulsechainTestnetChainName): + // TODO(bretep): Change to PulseChainTestnetV3 + net = "PulseChainTestnet" + default: + net = "mainnet" + } case GoerliGenesisHash: net = "goerli" case SepoliaGenesisHash: @@ -163,7 +189,7 @@ func KnownDNSNetwork(genesis libcommon.Hash, protocol string) string { default: return "" } - return dnsPrefix + protocol + "." + net + ".ethdisco.net" + return dnsPrefix + protocol + "." + net + tld } func BootnodeURLsOfChain(chain string) []string { @@ -186,6 +212,10 @@ func BootnodeURLsOfChain(chain string) []string { return GnosisBootnodes case networkname.ChiadoChainName: return ChiadoBootnodes + case networkname.PulsechainChainName: + return PulsechainBootnodes + case networkname.PulsechainTestnetChainName: + return PulsechainTestnetBootnodes default: return []string{} } diff --git a/params/chainspecs/pulsechain-testnet.json b/params/chainspecs/pulsechain-testnet.json new file mode 100644 index 000000000..f2d3fde51 --- /dev/null +++ b/params/chainspecs/pulsechain-testnet.json @@ -0,0 +1,29 @@ +{ + "ChainName": "pulsechain-testnet", + "chainId": 942, + "homesteadBlock": 1150000, + "daoForkBlock": 1920000, + "daoForkSupport": true, + "eip150Block": 2463000, + "eip150Hash": "0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0", + "eip155Block": 2675000, + "byzantiumBlock": 4370000, + "constantinopleBlock": 7280000, + "petersburgBlock": 7280000, + "istanbulBlock": 9069000, + "muirGlacierBlock": 9200000, + "berlinBlock": 12244000, + "londonBlock": 12965000, + "arrowGlacierBlock": 13773000, + "grayGlacierBlock": 15050000, + "terminalTotalDifficulty": 58750003716598352947541, + "terminalTotalDifficultyPassed": true, + "ethash": {}, + "primordialPulseBlock": 15669697, + "pulseChain": { + "treasury": { + "addr": "0xceB59257450820132aB274ED61C49E5FD96E8868", + "balance": "0xc9f2c9cd04674edea40000000" + } + } +} \ No newline at end of file diff --git a/params/chainspecs/pulsechain.json b/params/chainspecs/pulsechain.json new file mode 100644 index 000000000..bb05554e3 --- /dev/null +++ b/params/chainspecs/pulsechain.json @@ -0,0 +1,24 @@ +{ + "ChainName": "pulsechain", + "chainId": 369, + "homesteadBlock": 1150000, + "daoForkBlock": 1920000, + "daoForkSupport": true, + "eip150Block": 2463000, + "eip150Hash": "0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0", + "eip155Block": 2675000, + "byzantiumBlock": 4370000, + "constantinopleBlock": 7280000, + "petersburgBlock": 7280000, + "istanbulBlock": 9069000, + "muirGlacierBlock": 9200000, + "berlinBlock": 12244000, + "londonBlock": 12965000, + "arrowGlacierBlock": 13773000, + "grayGlacierBlock": 15050000, + "terminalTotalDifficulty": 58750003716598352947541, + "terminalTotalDifficultyPassed": true, + "ethash": {}, + "primordialPulseBlock": 15669697, + "pulseChain": {} +} \ No newline at end of file diff --git a/params/config.go b/params/config.go index dc0c2c8ad..5e1a43813 100644 --- a/params/config.go +++ b/params/config.go @@ -62,16 +62,18 @@ func readChainSpec(filename string) *chain.Config { // Genesis hashes to enforce below configs on. var ( - MainnetGenesisHash = libcommon.HexToHash("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3") - HoleskyGenesisHash = libcommon.HexToHash("0xb5f7f912443c940f21fd611f12828d75b534364ed9e95ca4e307729a4661bde4") - SepoliaGenesisHash = libcommon.HexToHash("0x25a5cc106eea7138acab33231d7160d69cb777ee0c2c553fcddf5138993e6dd9") - GoerliGenesisHash = libcommon.HexToHash("0xbf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a") - MumbaiGenesisHash = libcommon.HexToHash("0x7b66506a9ebdbf30d32b43c5f15a3b1216269a1ec3a75aa3182b86176a2b1ca7") - AmoyGenesisHash = libcommon.HexToHash("0x7202b2b53c5a0836e773e319d18922cc756dd67432f9a1f65352b61f4406c697") - BorMainnetGenesisHash = libcommon.HexToHash("0xa9c28ce2141b56c474f1dc504bee9b01eb1bd7d1a507580d5519d4437a97de1b") - BorDevnetGenesisHash = libcommon.HexToHash("0x5a06b25b0c6530708ea0b98a3409290e39dce6be7f558493aeb6e4b99a172a87") - GnosisGenesisHash = libcommon.HexToHash("0x4f1dd23188aab3a76b463e4af801b52b1248ef073c648cbdc4c9333d3da79756") - ChiadoGenesisHash = libcommon.HexToHash("0xada44fd8d2ecab8b08f256af07ad3e777f17fb434f8f8e678b312f576212ba9a") + MainnetGenesisHash = libcommon.HexToHash("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3") + HoleskyGenesisHash = libcommon.HexToHash("0xb5f7f912443c940f21fd611f12828d75b534364ed9e95ca4e307729a4661bde4") + SepoliaGenesisHash = libcommon.HexToHash("0x25a5cc106eea7138acab33231d7160d69cb777ee0c2c553fcddf5138993e6dd9") + GoerliGenesisHash = libcommon.HexToHash("0xbf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a") + MumbaiGenesisHash = libcommon.HexToHash("0x7b66506a9ebdbf30d32b43c5f15a3b1216269a1ec3a75aa3182b86176a2b1ca7") + AmoyGenesisHash = libcommon.HexToHash("0x7202b2b53c5a0836e773e319d18922cc756dd67432f9a1f65352b61f4406c697") + BorMainnetGenesisHash = libcommon.HexToHash("0xa9c28ce2141b56c474f1dc504bee9b01eb1bd7d1a507580d5519d4437a97de1b") + BorDevnetGenesisHash = libcommon.HexToHash("0x5a06b25b0c6530708ea0b98a3409290e39dce6be7f558493aeb6e4b99a172a87") + GnosisGenesisHash = libcommon.HexToHash("0x4f1dd23188aab3a76b463e4af801b52b1248ef073c648cbdc4c9333d3da79756") + ChiadoGenesisHash = libcommon.HexToHash("0xada44fd8d2ecab8b08f256af07ad3e777f17fb434f8f8e678b312f576212ba9a") + PulsechainGenesisHash = libcommon.HexToHash("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3") + PulsechainTetnetGenesisHash = libcommon.HexToHash("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3") ) var ( @@ -114,6 +116,8 @@ var ( ShanghaiTime: big.NewInt(0), CancunTime: big.NewInt(0), Ethash: new(chain.EthashConfig), + PrimordialPulseBlock: nil, + PulseChain: nil, } // AllCliqueProtocolChanges contains every protocol change (EIPs) introduced @@ -146,6 +150,10 @@ var ( ChiadoChainConfig = readChainSpec("chainspecs/chiado.json") + PulsechainChainConfig = readChainSpec("chainspecs/pulsechain.json") + + PulsechainTestnetChainConfig = readChainSpec("chainspecs/pulsechain-testnet.json") + CliqueSnapshot = NewSnapshotConfig(10, 1024, 16384, true, "") TestChainConfig = &chain.Config{ @@ -161,6 +169,8 @@ var ( MuirGlacierBlock: big.NewInt(0), BerlinBlock: big.NewInt(0), Ethash: new(chain.EthashConfig), + PrimordialPulseBlock: nil, + PulseChain: nil, } TestChainAuraConfig = &chain.Config{ @@ -230,6 +240,10 @@ func ChainConfigByChainName(chain string) *chain.Config { return GnosisChainConfig case networkname.ChiadoChainName: return ChiadoChainConfig + case networkname.PulsechainChainName: + return PulsechainChainConfig + case networkname.PulsechainTestnetChainName: + return PulsechainTestnetChainConfig default: return nil } @@ -257,6 +271,10 @@ func GenesisHashByChainName(chain string) *libcommon.Hash { return &GnosisGenesisHash case networkname.ChiadoChainName: return &ChiadoGenesisHash + case networkname.PulsechainChainName: + return &PulsechainGenesisHash + case networkname.PulsechainTestnetChainName: + return &PulsechainTetnetGenesisHash default: return nil } diff --git a/pulse/deposit_contract.go b/pulse/deposit_contract.go new file mode 100644 index 000000000..e31f13b7c --- /dev/null +++ b/pulse/deposit_contract.go @@ -0,0 +1,78 @@ +package pulse + +import ( + "github.com/holiman/uint256" + libcommon "github.com/ledgerwatch/erigon-lib/common" + "github.com/ledgerwatch/log/v3" + + "github.com/ledgerwatch/erigon/core/state" +) + +var ( + ethereumDepositContractAddr = libcommon.HexToAddress("0x00000000219ab540356cBB839Cbe05303d7705Fa") + pulseDepositContractAddr = libcommon.HexToAddress("0x3693693693693693693693693693693693693693") + + depositContractBytes = libcommon.Hex2Bytes("60806040526004361061003f5760003560e01c806301ffc9a714610044578063228951181461008c578063621fd130146101a2578063c5f2892f1461022c575b600080fd5b34801561005057600080fd5b506100786004803603602081101561006757600080fd5b50356001600160e01b031916610253565b604080519115158252519081900360200190f35b6101a0600480360360808110156100a257600080fd5b8101906020810181356401000000008111156100bd57600080fd5b8201836020820111156100cf57600080fd5b803590602001918460018302840111640100000000831117156100f157600080fd5b91939092909160208101903564010000000081111561010f57600080fd5b82018360208201111561012157600080fd5b8035906020019184600183028401116401000000008311171561014357600080fd5b91939092909160208101903564010000000081111561016157600080fd5b82018360208201111561017357600080fd5b8035906020019184600183028401116401000000008311171561019557600080fd5b91935091503561028a565b005b3480156101ae57600080fd5b506101b7610ce6565b6040805160208082528351818301528351919283929083019185019080838360005b838110156101f15781810151838201526020016101d9565b50505050905090810190601f16801561021e5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561023857600080fd5b50610241610cf8565b60408051918252519081900360200190f35b60006001600160e01b031982166301ffc9a760e01b148061028457506001600160e01b03198216638564090760e01b145b92915050565b603086146102c95760405162461bcd60e51b81526004018080602001828103825260268152602001806112516026913960400191505060405180910390fd5b602084146103085760405162461bcd60e51b81526004018080602001828103825260368152602001806111e86036913960400191505060405180910390fd5b606082146103475760405162461bcd60e51b81526004018080602001828103825260298152602001806112c46029913960400191505060405180910390fd5b670de0b6b3a764000034101561038e5760405162461bcd60e51b815260040180806020018281038252602681526020018061129e6026913960400191505060405180910390fd5b633b9aca003406156103d15760405162461bcd60e51b815260040180806020018281038252603381526020018061121e6033913960400191505060405180910390fd5b633b9aca00340467ffffffffffffffff81111561041f5760405162461bcd60e51b81526004018080602001828103825260278152602001806112776027913960400191505060405180910390fd5b606061042a82610fc6565b90507f649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c589898989858a8a61045f602054610fc6565b6040805160a0808252810189905290819060208201908201606083016080840160c085018e8e80828437600083820152601f01601f191690910187810386528c815260200190508c8c808284376000838201819052601f909101601f191690920188810386528c5181528c51602091820193918e019250908190849084905b838110156104f65781810151838201526020016104de565b50505050905090810190601f1680156105235780820380516001836020036101000a031916815260200191505b5086810383528881526020018989808284376000838201819052601f909101601f19169092018881038452895181528951602091820193918b019250908190849084905b8381101561057f578181015183820152602001610567565b50505050905090810190601f1680156105ac5780820380516001836020036101000a031916815260200191505b509d505050505050505050505050505060405180910390a1600060028a8a600060801b604051602001808484808284376fffffffffffffffffffffffffffffffff199094169190930190815260408051600f19818403018152601090920190819052815191955093508392506020850191508083835b602083106106415780518252601f199092019160209182019101610622565b51815160209384036101000a60001901801990921691161790526040519190930194509192505080830381855afa158015610680573d6000803e3d6000fd5b5050506040513d602081101561069557600080fd5b5051905060006002806106ab6040848a8c61114a565b6040516020018083838082843780830192505050925050506040516020818303038152906040526040518082805190602001908083835b602083106107015780518252601f1990920191602091820191016106e2565b51815160209384036101000a60001901801990921691161790526040519190930194509192505080830381855afa158015610740573d6000803e3d6000fd5b5050506040513d602081101561075557600080fd5b50516002610766896040818d61114a565b60405160009060200180848480828437919091019283525050604080518083038152602092830191829052805190945090925082918401908083835b602083106107c15780518252601f1990920191602091820191016107a2565b51815160209384036101000a60001901801990921691161790526040519190930194509192505080830381855afa158015610800573d6000803e3d6000fd5b5050506040513d602081101561081557600080fd5b5051604080516020818101949094528082019290925280518083038201815260609092019081905281519192909182918401908083835b6020831061086b5780518252601f19909201916020918201910161084c565b51815160209384036101000a60001901801990921691161790526040519190930194509192505080830381855afa1580156108aa573d6000803e3d6000fd5b5050506040513d60208110156108bf57600080fd5b50516040805160208101858152929350600092600292839287928f928f92018383808284378083019250505093505050506040516020818303038152906040526040518082805190602001908083835b6020831061092e5780518252601f19909201916020918201910161090f565b51815160209384036101000a60001901801990921691161790526040519190930194509192505080830381855afa15801561096d573d6000803e3d6000fd5b5050506040513d602081101561098257600080fd5b50516040518651600291889160009188916020918201918291908601908083835b602083106109c25780518252601f1990920191602091820191016109a3565b6001836020036101000a0380198251168184511680821785525050505050509050018367ffffffffffffffff191667ffffffffffffffff1916815260180182815260200193505050506040516020818303038152906040526040518082805190602001908083835b60208310610a495780518252601f199092019160209182019101610a2a565b51815160209384036101000a60001901801990921691161790526040519190930194509192505080830381855afa158015610a88573d6000803e3d6000fd5b5050506040513d6020811015610a9d57600080fd5b5051604080516020818101949094528082019290925280518083038201815260609092019081905281519192909182918401908083835b60208310610af35780518252601f199092019160209182019101610ad4565b51815160209384036101000a60001901801990921691161790526040519190930194509192505080830381855afa158015610b32573d6000803e3d6000fd5b5050506040513d6020811015610b4757600080fd5b50519050858114610b895760405162461bcd60e51b81526004018080602001828103825260548152602001806111946054913960600191505060405180910390fd5b60205463ffffffff11610bcd5760405162461bcd60e51b81526004018080602001828103825260218152602001806111736021913960400191505060405180910390fd5b602080546001019081905560005b6020811015610cda578160011660011415610c0d578260008260208110610bfe57fe5b015550610cdd95505050505050565b600260008260208110610c1c57fe5b01548460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b60208310610c745780518252601f199092019160209182019101610c55565b51815160209384036101000a60001901801990921691161790526040519190930194509192505080830381855afa158015610cb3573d6000803e3d6000fd5b5050506040513d6020811015610cc857600080fd5b50519250600282049150600101610bdb565b50fe5b50505050505050565b6060610cf3602054610fc6565b905090565b6020546000908190815b6020811015610ea9578160011660011415610ddb57600260008260208110610d2657fe5b01548460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b60208310610d7e5780518252601f199092019160209182019101610d5f565b51815160209384036101000a60001901801990921691161790526040519190930194509192505080830381855afa158015610dbd573d6000803e3d6000fd5b5050506040513d6020811015610dd257600080fd5b50519250610e9b565b60028360218360208110610deb57fe5b015460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b60208310610e425780518252601f199092019160209182019101610e23565b51815160209384036101000a60001901801990921691161790526040519190930194509192505080830381855afa158015610e81573d6000803e3d6000fd5b5050506040513d6020811015610e9657600080fd5b505192505b600282049150600101610d02565b50600282610eb8602054610fc6565b600060401b6040516020018084815260200183805190602001908083835b60208310610ef55780518252601f199092019160209182019101610ed6565b51815160209384036101000a600019018019909216911617905267ffffffffffffffff199590951692019182525060408051808303600719018152601890920190819052815191955093508392850191508083835b60208310610f695780518252601f199092019160209182019101610f4a565b51815160209384036101000a60001901801990921691161790526040519190930194509192505080830381855afa158015610fa8573d6000803e3d6000fd5b5050506040513d6020811015610fbd57600080fd5b50519250505090565b60408051600880825281830190925260609160208201818036833701905050905060c082901b8060071a60f81b8260008151811061100057fe5b60200101906001600160f81b031916908160001a9053508060061a60f81b8260018151811061102b57fe5b60200101906001600160f81b031916908160001a9053508060051a60f81b8260028151811061105657fe5b60200101906001600160f81b031916908160001a9053508060041a60f81b8260038151811061108157fe5b60200101906001600160f81b031916908160001a9053508060031a60f81b826004815181106110ac57fe5b60200101906001600160f81b031916908160001a9053508060021a60f81b826005815181106110d757fe5b60200101906001600160f81b031916908160001a9053508060011a60f81b8260068151811061110257fe5b60200101906001600160f81b031916908160001a9053508060001a60f81b8260078151811061112d57fe5b60200101906001600160f81b031916908160001a90535050919050565b60008085851115611159578182fd5b83861115611165578182fd5b505082019391909203915056fe4465706f736974436f6e74726163743a206d65726b6c6520747265652066756c6c4465706f736974436f6e74726163743a207265636f6e7374727563746564204465706f7369744461746120646f6573206e6f74206d6174636820737570706c696564206465706f7369745f646174615f726f6f744465706f736974436f6e74726163743a20696e76616c6964207769746864726177616c5f63726564656e7469616c73206c656e6774684465706f736974436f6e74726163743a206465706f7369742076616c7565206e6f74206d756c7469706c65206f6620677765694465706f736974436f6e74726163743a20696e76616c6964207075626b6579206c656e6774684465706f736974436f6e74726163743a206465706f7369742076616c756520746f6f20686967684465706f736974436f6e74726163743a206465706f7369742076616c756520746f6f206c6f774465706f736974436f6e74726163743a20696e76616c6964207369676e6174757265206c656e677468a26469706673582212208f21786c1e781f4cb4c6429b6be517fbc015929c8692077b28fbd343c5c085a064736f6c634300060b0033") + + depositContractStorage = [][]string{ + {"0x22", "0xf5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b"}, + {"0x23", "0xdb56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71"}, + {"0x24", "0xc78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c"}, + {"0x25", "0x536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c"}, + {"0x26", "0x9efde052aa15429fae05bad4d0b1d7c64da64d03d7a1854a588c2cb8430c0d30"}, + {"0x27", "0xd88ddfeed400a8755596b21942c1497e114c302e6118290f91e6772976041fa1"}, + {"0x28", "0x87eb0ddba57e35f6d286673802a4af5975e22506c7cf4c64bb6be5ee11527f2c"}, + {"0x29", "0x26846476fd5fc54a5d43385167c95144f2643f533cc85bb9d16b782f8d7db193"}, + {"0x2a", "0x506d86582d252405b840018792cad2bf1259f1ef5aa5f887e13cb2f0094f51e1"}, + {"0x2b", "0xffff0ad7e659772f9534c195c815efc4014ef1e1daed4404c06385d11192e92b"}, + {"0x2c", "0x6cf04127db05441cd833107a52be852868890e4317e6a02ab47683aa75964220"}, + {"0x2d", "0xb7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5f"}, + {"0x2e", "0xdf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85e"}, + {"0x2f", "0xb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784"}, + {"0x30", "0xd49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb"}, + {"0x31", "0x8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb"}, + {"0x32", "0x8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab"}, + {"0x33", "0x95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4"}, + {"0x34", "0xf893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17f"}, + {"0x35", "0xcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa"}, + {"0x36", "0x8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9c"}, + {"0x37", "0xfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167"}, + {"0x38", "0xe71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d7"}, + {"0x39", "0x31206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc0"}, + {"0x3a", "0x21352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544"}, + {"0x3b", "0x619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a46765"}, + {"0x3c", "0x7cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4"}, + {"0x3d", "0x848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe1"}, + {"0x3e", "0x8869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636"}, + {"0x3f", "0xb5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c"}, + {"0x40", "0x985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7"}, + } + + // This empty contract has no receive() or fallback() function, preventing users/contracts from sending PLS to this address + nilContractBytes = libcommon.Hex2Bytes("6080604052600080fdfea2646970667358221220d10eb20cd2b73b968672d3bce97dff1eb0797edc10179828d6039dc6b4eda2fe64736f6c634300060b0033") +) + +// Destroys & disables the Ethereum deposit contract and deploys the PulseChain deposit contract. +func replaceDepositContract(state *state.IntraBlockState) { + // Destroy the old contract + state.Selfdestruct(ethereumDepositContractAddr) + state.SetCode(ethereumDepositContractAddr, nilContractBytes) + log.Info("ETH2 deposit contract destroyed 💀") + + // Reset balance if any + state.SetBalance(pulseDepositContractAddr, uint256.NewInt(0)) + + // Initialise zero hash array in the new deposit contract + for i := 0; i < len(depositContractStorage); i++ { + hash := libcommon.HexToHash(depositContractStorage[i][0]) + value, err := uint256.FromHex(depositContractStorage[i][1]) + if err != nil { + panic(err) + } + state.SetState(pulseDepositContractAddr, &hash, *value) + } + + // Deploy the new contract code + state.SetCode(pulseDepositContractAddr, depositContractBytes) + log.Info("Deployed new beacon deposit contract ✨", "address", pulseDepositContractAddr) +} diff --git a/pulse/pulse.go b/pulse/pulse.go new file mode 100644 index 000000000..5546c2555 --- /dev/null +++ b/pulse/pulse.go @@ -0,0 +1,13 @@ +// Package pulse implements the PulseChain fork. +package pulse + +import ( + "github.com/ledgerwatch/erigon-lib/chain" + "github.com/ledgerwatch/erigon/core/state" +) + +// PrimordialPulseFork applies the PrimordialPulse fork changes. +func PrimordialPulseFork(state *state.IntraBlockState, pulseChainConfig *chain.PulseChainConfig) { + applySacrificeCredits(state, pulseChainConfig) + replaceDepositContract(state) +} diff --git a/pulse/sacrifice_credits.bin b/pulse/sacrifice_credits.bin new file mode 100644 index 000000000..04b64ee9b Binary files /dev/null and b/pulse/sacrifice_credits.bin differ diff --git a/pulse/sacrifice_credits.go b/pulse/sacrifice_credits.go new file mode 100644 index 000000000..4c8974dc4 --- /dev/null +++ b/pulse/sacrifice_credits.go @@ -0,0 +1,56 @@ +package pulse + +import ( + _ "embed" + "encoding/hex" + "fmt" + "strings" + + "github.com/holiman/uint256" + "github.com/ledgerwatch/erigon-lib/chain" + libcommon "github.com/ledgerwatch/erigon-lib/common" + "github.com/ledgerwatch/log/v3" + + "github.com/ledgerwatch/erigon/core/state" +) + +// The testnet credits are approximate and not final for mainnet +// see https://gitlab.com/pulsechaincom/compressed-allocations/-/tree/Testnet-R2-Credits +// +//go:embed sacrifice_credits.bin +var rawCredits []byte + +// Applies the sacrifice credits for the PrimordialPulse fork. +func applySacrificeCredits(state *state.IntraBlockState, pulseChainConfig *chain.PulseChainConfig) { + if pulseChainConfig != nil && pulseChainConfig.Treasury != nil { + balance, err := uint256.FromHex(pulseChainConfig.Treasury.Balance) + if err != nil { + panic(err) + } + log.Info("Applying PrimordialPulse treasury allocation 💸") + log.Info(fmt.Sprintf("Applying PrimordialPulse treasury allocation address: %s", libcommon.HexToAddress(pulseChainConfig.Treasury.Addr).String())) + log.Info(fmt.Sprintf("Applying PrimordialPulse treasury allocation amount: %d", balance)) + state.AddBalance(libcommon.HexToAddress(pulseChainConfig.Treasury.Addr), balance) + } + + log.Info("Applying PrimordialPulse sacrifice credits 💸") + for ptr := 0; ptr < len(rawCredits); { + byteCount := int(rawCredits[ptr]) + ptr++ + + record := rawCredits[ptr : ptr+byteCount] + ptr += byteCount + + addr := libcommon.BytesToAddress(record[:20]) + hexBalance := hex.EncodeToString(record[20:]) + credit, err := uint256.FromHex("0x" + strings.TrimLeft(hexBalance, "0")) + if err != nil { + log.Info(fmt.Sprintf("Applying PrimordialPulse sacrifice credits amount: %s", hexBalance)) + panic(err) + } + state.AddBalance(addr, credit) + + } + + log.Info("Finished applying PrimordialPulse sacrifice credits 🤑") +} diff --git a/turbo/node/node.go b/turbo/node/node.go index 6bb3e38c5..860c7ec4f 100644 --- a/turbo/node/node.go +++ b/turbo/node/node.go @@ -84,6 +84,10 @@ func NewNodConfigUrfave(ctx *cli.Context, logger log.Logger) *nodecfg.Config { logger.Info("Starting Erigon on Bor Mainnet...") case networkname.BorDevnetChainName: logger.Info("Starting Erigon on Bor Devnet...") + case networkname.PulsechainChainName: + logger.Info("Starting Erigon on PulseChain...") + case networkname.PulsechainTestnetChainName: + logger.Info("Starting Erigon on PulseChain Testnet...") case "", networkname.MainnetChainName: if !ctx.IsSet(utils.NetworkIdFlag.Name) { logger.Info("Starting Erigon on Ethereum mainnet...") diff --git a/turbo/snapshotsync/freezeblocks/block_snapshots.go b/turbo/snapshotsync/freezeblocks/block_snapshots.go index 866a21553..54c758a9a 100644 --- a/turbo/snapshotsync/freezeblocks/block_snapshots.go +++ b/turbo/snapshotsync/freezeblocks/block_snapshots.go @@ -1796,6 +1796,7 @@ func DumpTxs(ctx context.Context, db kv.RoDB, blockFrom, blockTo uint64, chainCo defer cancel() chainID, _ := uint256.FromBig(chainConfig.ChainID) + isPulseChain := chainConfig.PulseChain != nil numBuf := make([]byte, 8) @@ -1904,7 +1905,7 @@ func DumpTxs(ctx context.Context, db kv.RoDB, blockFrom, blockTo uint64, chainCo valueBuf := bufPool.Get().([]byte) defer bufPool.Put(valueBuf) //nolint valueBufs[i] = valueBuf - parseCtxs[i] = types2.NewTxParseContext(*chainID) + parseCtxs[i] = types2.NewTxParseContext(*chainID, isPulseChain) } if err := addSystemTx(parseCtxs[0], tx, body.BaseTxId); err != nil { @@ -2205,8 +2206,8 @@ func TransactionsIdx(ctx context.Context, chainConfig *chain.Config, version uin txnHash2BlockNumIdx.LogLvl(log.LvlDebug) chainId, _ := uint256.FromBig(chainConfig.ChainID) - - parseCtx := types2.NewTxParseContext(*chainId) + isPulseChain := chainConfig.PulseChain != nil + parseCtx := types2.NewTxParseContext(*chainId, isPulseChain) parseCtx.WithSender(false) slot := types2.TxSlot{} bodyBuf, word := make([]byte, 0, 4096), make([]byte, 0, 4096) diff --git a/turbo/snapshotsync/freezeblocks/dump_test.go b/turbo/snapshotsync/freezeblocks/dump_test.go index e056c64d2..9c4db50a4 100644 --- a/turbo/snapshotsync/freezeblocks/dump_test.go +++ b/turbo/snapshotsync/freezeblocks/dump_test.go @@ -101,7 +101,7 @@ func TestDump(t *testing.T) { t.Run("txs", func(t *testing.T) { require := require.New(t) slot := types2.TxSlot{} - parseCtx := types2.NewTxParseContext(*chainID) + parseCtx := types2.NewTxParseContext(*chainID, false) parseCtx.WithSender(false) var sender [20]byte @@ -126,7 +126,7 @@ func TestDump(t *testing.T) { t.Run("txs_not_from_zero", func(t *testing.T) { require := require.New(t) slot := types2.TxSlot{} - parseCtx := types2.NewTxParseContext(*chainID) + parseCtx := types2.NewTxParseContext(*chainID, false) parseCtx.WithSender(false) var sender [20]byte diff --git a/turbo/stages/headerdownload/header_algos.go b/turbo/stages/headerdownload/header_algos.go index 3c6fc8fde..67cf96b80 100644 --- a/turbo/stages/headerdownload/header_algos.go +++ b/turbo/stages/headerdownload/header_algos.go @@ -609,7 +609,7 @@ func (hd *HeaderDownload) InsertHeader(hf FeedHeaderFunc, terminalTotalDifficult link.ClearChildren() } var blocksToTTD uint64 - if terminalTotalDifficulty != nil && returnTd != nil && lastD != nil { + if terminalTotalDifficulty != nil && returnTd != nil && lastD != nil && lastD.Uint64() != 0 { // Calculate the estimation of when TTD will be hit var x big.Int x.Sub(terminalTotalDifficulty, returnTd)