erigon-pulse/core/system_contract_lookup.go
ledgerwatch 0a31f5ac2a
Workaround for the code history of BSC system contracts (#6274)
Works around a flaw in the upgrade logic of the system contracts. Since
they are updated directly, without first being self-destructed and then
re-created, the usual incarnation logic does not get activated, and all
historical records of the code of these contracts are retrieved as the
most recent version. This problem will not exist in erigon3, but until
then, a workaround will be used to access code of such contracts through
a special structure, `SystemContractCodeLookup`

Fixes https://github.com/ledgerwatch/erigon/issues/5865

Co-authored-by: Alexey Sharp <alexeysharp@Alexeys-iMac.local>
2022-12-10 22:41:04 +00:00

94 lines
3.1 KiB
Go

package core
import (
"encoding/hex"
"fmt"
"github.com/ledgerwatch/erigon/common"
"github.com/ledgerwatch/erigon/core/state"
"github.com/ledgerwatch/erigon/core/systemcontracts"
"github.com/ledgerwatch/erigon/params"
"github.com/ledgerwatch/erigon/params/networkname"
)
func init() {
// Initialise systemContractCodeLookup
for _, chainName := range []string{networkname.BSCChainName, networkname.ChapelChainName, networkname.RialtoChainName} {
byChain := map[common.Address][]state.CodeRecord{}
systemcontracts.SystemContractCodeLookup[chainName] = byChain
// Apply genesis with the block number 0
genesisBlock := DefaultGenesisBlockByChainName(chainName)
for addr, alloc := range genesisBlock.Alloc {
if len(alloc.Code) > 0 {
list := byChain[addr]
codeHash, err := common.HashData(alloc.Code)
if err != nil {
panic(fmt.Errorf("failed to hash system contract code: %s", err.Error()))
}
list = append(list, state.CodeRecord{BlockNumber: 0, CodeHash: codeHash})
byChain[addr] = list
}
}
// Process upgrades
chainConfig := params.ChainConfigByChainName(chainName)
if chainConfig.RamanujanBlock != nil {
blockNum := chainConfig.RamanujanBlock.Uint64()
if blockNum != 0 {
addCodeRecords(systemcontracts.RamanujanUpgrade[chainName], blockNum, byChain)
}
}
if chainConfig.NielsBlock != nil {
blockNum := chainConfig.NielsBlock.Uint64()
if blockNum != 0 {
addCodeRecords(systemcontracts.NielsUpgrade[chainName], blockNum, byChain)
}
}
if chainConfig.MirrorSyncBlock != nil {
blockNum := chainConfig.MirrorSyncBlock.Uint64()
if blockNum != 0 {
addCodeRecords(systemcontracts.MirrorUpgrade[chainName], blockNum, byChain)
}
}
if chainConfig.BrunoBlock != nil {
blockNum := chainConfig.BrunoBlock.Uint64()
if blockNum != 0 {
addCodeRecords(systemcontracts.BrunoUpgrade[chainName], blockNum, byChain)
}
}
if chainConfig.EulerBlock != nil {
blockNum := chainConfig.EulerBlock.Uint64()
if blockNum != 0 {
addCodeRecords(systemcontracts.EulerUpgrade[chainName], blockNum, byChain)
}
}
if chainConfig.MoranBlock != nil {
blockNum := chainConfig.MoranBlock.Uint64()
if blockNum != 0 {
addCodeRecords(systemcontracts.MoranUpgrade[chainName], blockNum, byChain)
}
}
if chainConfig.GibbsBlock != nil {
blockNum := chainConfig.GibbsBlock.Uint64()
if blockNum != 0 {
addCodeRecords(systemcontracts.GibbsUpgrade[chainName], blockNum, byChain)
}
}
}
}
func addCodeRecords(upgrade *systemcontracts.Upgrade, blockNum uint64, byChain map[common.Address][]state.CodeRecord) {
for _, config := range upgrade.Configs {
list := byChain[config.ContractAddr]
code, err := hex.DecodeString(config.Code)
if err != nil {
panic(fmt.Errorf("failed to decode system contract code: %s", err.Error()))
}
codeHash, err := common.HashData(code)
if err != nil {
panic(fmt.Errorf("failed to hash system contract code: %s", err.Error()))
}
list = append(list, state.CodeRecord{BlockNumber: blockNum, CodeHash: codeHash})
byChain[config.ContractAddr] = list
}
}