Add support for PulseChain

Thanks @bretep for the original integration.
This commit is contained in:
Shane Bammel 2023-02-24 20:54:12 -06:00
parent aa3a804757
commit 75db06fa1d
43 changed files with 542 additions and 129 deletions

View File

@ -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"]

View File

@ -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} \
.

View File

@ -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

View File

@ -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}
}
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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)

View File

@ -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),

View File

@ -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,
}

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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()))

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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))}

View File

@ -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()

View File

@ -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 {

View File

@ -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)

View File

@ -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

View File

@ -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)

1
go.mod
View File

@ -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

2
go.sum
View File

@ -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=

View File

@ -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}
}
}

View File

@ -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

View File

@ -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{}
}

View File

@ -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"
}
}
}

View File

@ -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": {}
}

View File

@ -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
}

78
pulse/deposit_contract.go Normal file

File diff suppressed because one or more lines are too long

13
pulse/pulse.go Normal file
View File

@ -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)
}

BIN
pulse/sacrifice_credits.bin Normal file

Binary file not shown.

View File

@ -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 🤑")
}

View File

@ -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...")

View File

@ -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)

View File

@ -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

View File

@ -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)