flare-foundation / go-songbird

BSD 3-Clause "New" or "Revised" License
66 stars 32 forks source link

Apricot Phase 3 hard fork is (partially) disabled on Songbird network #11

Closed awfm9 closed 2 years ago

awfm9 commented 2 years ago

Through my investigation into why the new code base wasn't properly syncing with the Songbird network, I discovered that the Apricot Phase 3 hard fork has not been activated on the Songbird network, and will indeed never activate because it is disabled.

For past hard forks, their timing was defined in the configuration of the C-Chain genesis file, see: https://gitlab.com/flarenetwork/flare/-/blob/master/src/genesis/genesis_songbird.go#L23

"config": {
    "chainId": 19,
    "homesteadBlock": 0,
    "daoForkBlock": 0,
    "daoForkSupport": true,
    "eip150Block": 0,
    "eip150Hash": "0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0",
    "eip155Block": 0,
    "eip158Block": 0,
    "byzantiumBlock": 0,
    "constantinopleBlock": 0,
    "petersburgBlock": 0,
    "istanbulBlock": 0,
    "muirGlacierBlock": 0,
    "apricotPhase1BlockTimestamp": 0,
    "apricotPhase2BlockTimestamp": 0
}

A timestamp of zero means that the fork is enabled from the start of the network.

However, the genesis file can not be modified after the initial network start, so newer forks were not included in the configuration of the C-Chain genesis file. In fact, Avalanche itself doesn't include any Apricot forks in the genesis file.

Instead, they keep a separate instance of hard-coded fork configurations for each network in the Coreth repository, see: https://github.com/ava-labs/coreth/blob/master/params/config.go#L90

// AvalancheFujiChainConfig is the configuration for the Fuji Test Network
AvalancheFujiChainConfig = &ChainConfig{
    ChainID:                     AvalancheFujiChainID,
    HomesteadBlock:              big.NewInt(0),
    DAOForkBlock:                big.NewInt(0),
    DAOForkSupport:              true,
    EIP150Block:                 big.NewInt(0),
    EIP150Hash:                  common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"),
    EIP155Block:                 big.NewInt(0),
    EIP158Block:                 big.NewInt(0),
    ByzantiumBlock:              big.NewInt(0),
    ConstantinopleBlock:         big.NewInt(0),
    PetersburgBlock:             big.NewInt(0),
    IstanbulBlock:               big.NewInt(0),
    MuirGlacierBlock:            big.NewInt(0),
    ApricotPhase1BlockTimestamp: big.NewInt(time.Date(2021, time.March, 26, 14, 0, 0, 0, time.UTC).Unix()),
    ApricotPhase2BlockTimestamp: big.NewInt(time.Date(2021, time.May, 5, 14, 0, 0, 0, time.UTC).Unix()),
    ApricotPhase3BlockTimestamp: big.NewInt(time.Date(2021, time.August, 16, 19, 0, 0, 0, time.UTC).Unix()),
    ApricotPhase4BlockTimestamp: big.NewInt(time.Date(2021, time.September, 16, 21, 0, 0, 0, time.UTC).Unix()),
    ApricotPhase5BlockTimestamp: big.NewInt(time.Date(2021, time.November, 24, 15, 0, 0, 0, time.UTC).Unix()),
}

This chain configuration overwrites the configuration in the C-Chain genesis block if the network has one of the hard-coded chain IDs, see: https://github.com/ava-labs/coreth/blob/master/plugin/evm/vm.go#L300

// Set the chain config for mainnet/fuji chain IDs
switch {
case g.Config.ChainID.Cmp(params.AvalancheMainnetChainID) == 0:
    g.Config = params.AvalancheMainnetChainConfig
    phase0BlockValidator.extDataHashes = mainnetExtDataHashes
case g.Config.ChainID.Cmp(params.AvalancheFujiChainID) == 0:
    g.Config = params.AvalancheFujiChainConfig
    phase0BlockValidator.extDataHashes = fujiExtDataHashes
case g.Config.ChainID.Cmp(params.AvalancheLocalChainID) == 0:
    g.Config = params.AvalancheLocalChainConfig
}

However, the chain IDs are also redundantly defined in the Coreth repository, see: https://github.com/ava-labs/coreth/blob/master/params/config.go#L43

// Avalanche ChainIDs
var (
    // AvalancheMainnetChainID ...
    AvalancheMainnetChainID = big.NewInt(43114)
    // AvalancheFujiChainID ...
    AvalancheFujiChainID = big.NewInt(43113)
    // AvalancheLocalChainID ...
    AvalancheLocalChainID = big.NewInt(43112)
)

Unfortunately, in our previous repository, we did not update the chain ID in the Coreth repository. You would think that this would decode the value of the fork timestamp as zero, and thus enable the hard fork by default, but not so, see: https://github.com/ava-labs/coreth/blob/master/params/config.go#L158

// Apricot Phase 3 introduces dynamic fees and a modified version of the London Hard Fork from Ethereum (nil = no fork, 0 = already activated)
ApricotPhase3BlockTimestamp *big.Int `json:"apricotPhase3BlockTimestamp,omitempty"`

Pointers default to decoding as nil with the default Go JSON decoder, which disabled the hard fork. Hence, we are running on Apricot Phase 2 on the EVM of the Songbird network currently.

However, Apricot Phase 3 is activated from the point of view of the Avalanche repository, which is defined here: https://github.com/ava-labs/avalanchego/blob/master/version/constants.go#L48

ApricotPhase3Times = map[uint32]time.Time{
    constants.MainnetID: time.Date(2021, time.August, 24, 14, 0, 0, 0, time.UTC),
    constants.FujiID:    time.Date(2021, time.August, 16, 19, 0, 0, 0, time.UTC),
}
ApricotPhase3DefaultTime = time.Date(2020, time.December, 5, 5, 0, 0, 0, time.UTC)

Because on the Avalanche repository, the hard-coded fork configurations use the network IDs and are actually correctly defined, see: https://github.com/ava-labs/avalanchego/blob/master/utils/constants/network_ids.go#L20

const (
    MainnetID uint32 = 1
    CascadeID uint32 = 2
    DenaliID  uint32 = 3
    EverestID uint32 = 4
    FujiID    uint32 = 5
)

My recommendation would be to plan for a full Apricot Phase 3 rollout as soon as possible.

awfm9 commented 2 years ago

This is all fixed with the v0.5.1 release.