Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions core/state_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,11 @@ func (p *StateProcessor) Process(
return nil, nil, nil, nil, 0, nil, statedb, err
}

if p.bc.Config().IsPrague(block.Epoch()) {
// This should not underflow as genesis block is not processed.
ProcessBlockHashHistory(statedb, block.Header(), p.bc.Config(), p.bc)
}

processTxsAndStxs := true
cxReceipt, err := MayBalanceMigration(gp, header, statedb, p.bc)
if err != nil {
Expand Down Expand Up @@ -697,3 +702,54 @@ func generateOneMigrationMessage(
}
return nil, nil
}

// ProcessBlockHashHistory is called at every block to insert the parent block hash
// in the history storage contract as per EIP-2935. At the EIP-2935 fork block, it
// populates the whole buffer with block hashes.
func ProcessBlockHashHistory(statedb *state.DB, header *block.Header, chainConfig *params.ChainConfig, chain BlockChain) {
var (
prevHash = header.ParentHash()
parent = chain.GetHeaderByHash(prevHash)
number = header.Number().Uint64()
prevNumber = parent.Number().Uint64()
)

// Store the immediate parent hash
ProcessParentBlockHash(statedb, prevHash, prevNumber)

// If this is the EIP-2935 fork block or genesis, populate the entire history buffer
if chainConfig.IsPrague(parent.Epoch()) || prevNumber == 0 {
return
}

// Populate the history buffer with all available block hashes
var low uint64
if number > params.HistoryServeWindow {
low = number - params.HistoryServeWindow
}

// Walk backwards through the chain to populate the history buffer
for i := prevNumber; i > low; i-- {
ProcessParentBlockHash(statedb, parent.ParentHash(), i-1)
parent = chain.GetHeader(parent.ParentHash(), i-1)
if parent == nil {
break // Stop if we can't find the parent
}
}
}

// ProcessParentBlockHash stores the parent block hash in the history storage contract
// as per EIP-2935.
func ProcessParentBlockHash(statedb *state.DB, prevHash common.Hash, blockNumber uint64) {
// For now, implement a simple version that directly stores the hash in state
// This avoids the complex EVM dependencies that are missing from this codebase
// TODO: Implement full EVM-based version when all dependencies are available

// Calculate the ring index for the history storage based on block number
ringIndex := blockNumber % params.HistoryServeWindow
var key common.Hash
binary.BigEndian.PutUint64(key[24:], ringIndex)

// Store the hash directly in the state
statedb.SetState(params.HistoryStorageAddress, key, prevHash)
}
43 changes: 43 additions & 0 deletions core/state_transition_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@ package core

import (
"crypto/ecdsa"
"encoding/binary"
"fmt"
"math"
"math/big"
"testing"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/harmony-one/harmony/core/rawdb"
"github.com/harmony-one/harmony/core/state"
hmyState "github.com/harmony-one/harmony/core/state"
"github.com/harmony-one/harmony/core/types"
"github.com/harmony-one/harmony/core/vm"
shardingconfig "github.com/harmony-one/harmony/internal/configs/sharding"
Expand Down Expand Up @@ -267,3 +271,42 @@ func TestPrepare(t *testing.T) {
t.Fatal("value should be empty")
}
}

func TestProcessBlockHashHistory(t *testing.T) {
// Create a simple state database for testing
database := rawdb.NewMemoryDatabase()
statedb, err := state.New(common.Hash{}, state.NewDatabase(database), nil)
if err != nil {
t.Fatal("Failed to create state database:", err)
}

hashA := common.Hash{0x01}
hashB := common.Hash{0x02}

// Store the hashes directly in the state to test the basic functionality
storeParentBlockHash(statedb, 1, hashA)
storeParentBlockHash(statedb, 0, hashB)

// make sure that the state is correct
if have := getParentBlockHash(statedb, 1); have != hashA {
t.Fail()
}
if have := getParentBlockHash(statedb, 0); have != hashB {
t.Fail()
}
}

// storeParentBlockHash stores the parent block hash in the state for testing
func storeParentBlockHash(statedb *hmyState.DB, number uint64, hash common.Hash) {
ringIndex := number % params.HistoryServeWindow
var key common.Hash
binary.BigEndian.PutUint64(key[24:], ringIndex)
statedb.SetState(params.HistoryStorageAddress, key, hash)
}

func getParentBlockHash(statedb *hmyState.DB, number uint64) common.Hash {
ringIndex := number % params.HistoryServeWindow
var key common.Hash
binary.BigEndian.PutUint64(key[24:], ringIndex)
return statedb.GetState(params.HistoryStorageAddress, key)
}
2 changes: 2 additions & 0 deletions core/vm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -605,3 +605,5 @@ func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *

// ChainConfig returns the environment's chain configuration
func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig }

func (evm *EVM) Config() Config { return evm.vmConfig }
16 changes: 16 additions & 0 deletions core/vm/instructions.go
Original file line number Diff line number Diff line change
Expand Up @@ -595,13 +595,29 @@ func opGasprice(pc *uint64, interpreter *EVMInterpreter, contract *Contract, mem

func opBlockhash(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
num := stack.pop()
num64 := num.Uint64()

n := interpreter.intPool.get().Sub(interpreter.evm.BlockNumber, common.Big257)
if num.Cmp(n) > 0 && num.Cmp(interpreter.evm.BlockNumber) < 0 {
stack.push(interpreter.evm.GetHash(num.Uint64()).Big())
} else {
stack.push(interpreter.intPool.getZero())
}

var upper, lower uint64
upper = interpreter.evm.Context.BlockNumber.Uint64()
if upper < 257 {
lower = 0
} else {
lower = upper - 256
}

if num64 >= lower && num64 < upper {
num.SetBytes(interpreter.evm.Context.GetHash(num64).Bytes())
} else {
num.SetUint64(0)
}

interpreter.intPool.put(num, n)
return nil, nil
}
Expand Down
14 changes: 14 additions & 0 deletions internal/params/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ var (
HIP32Epoch: big.NewInt(2152), // 2024-10-31 13:02 UTC
IsOneSecondEpoch: EpochTBD,
EIP2537PrecompileEpoch: EpochTBD,
PragueEpoch: EpochTBD,
}

// TestnetChainConfig contains the chain parameters to run a node on the harmony test network.
Expand Down Expand Up @@ -133,6 +134,7 @@ var (
TestnetExternalEpoch: big.NewInt(3044),
IsOneSecondEpoch: EpochTBD,
EIP2537PrecompileEpoch: EpochTBD,
PragueEpoch: EpochTBD,
}
// PangaeaChainConfig contains the chain parameters for the Pangaea network.
// All features except for CrossLink are enabled at launch.
Expand Down Expand Up @@ -183,6 +185,7 @@ var (
TestnetExternalEpoch: EpochTBD,
IsOneSecondEpoch: EpochTBD,
EIP2537PrecompileEpoch: EpochTBD,
PragueEpoch: EpochTBD,
}

// PartnerChainConfig contains the chain parameters for the Partner network.
Expand Down Expand Up @@ -234,6 +237,7 @@ var (
DevnetExternalEpoch: big.NewInt(144),
IsOneSecondEpoch: big.NewInt(17436),
EIP2537PrecompileEpoch: EpochTBD,
PragueEpoch: EpochTBD,
}

// StressnetChainConfig contains the chain parameters for the Stress test network.
Expand Down Expand Up @@ -285,6 +289,7 @@ var (
TestnetExternalEpoch: EpochTBD,
IsOneSecondEpoch: EpochTBD,
EIP2537PrecompileEpoch: EpochTBD,
PragueEpoch: EpochTBD,
}

// LocalnetChainConfig contains the chain parameters to run for local development.
Expand Down Expand Up @@ -335,6 +340,7 @@ var (
TestnetExternalEpoch: EpochTBD,
IsOneSecondEpoch: big.NewInt(4),
EIP2537PrecompileEpoch: EpochTBD,
PragueEpoch: EpochTBD,
}

// AllProtocolChanges ...
Expand Down Expand Up @@ -388,6 +394,7 @@ var (
big.NewInt(0),
big.NewInt(0),
big.NewInt(0), // EIP2537PrecompileEpoch
big.NewInt(0), // PragueEpoch
}

// TestChainConfig ...
Expand Down Expand Up @@ -441,6 +448,7 @@ var (
big.NewInt(0),
big.NewInt(0),
big.NewInt(0), // EIP2537PrecompileEpoch
big.NewInt(0), // PragueEpoch
}

// TestRules ...
Expand Down Expand Up @@ -627,6 +635,8 @@ type ChainConfig struct {
HIP32Epoch *big.Int `json:"hip32-epoch,omitempty"`

IsOneSecondEpoch *big.Int `json:"is-one-second-epoch,omitempty"`

PragueEpoch *big.Int `json:"prague-epoch,omitempty"`
}

// String implements the fmt.Stringer interface.
Expand Down Expand Up @@ -924,6 +934,10 @@ func (c *ChainConfig) IsTopMaxRate(epoch *big.Int) bool {
return isForked(c.TopMaxRateEpoch, epoch)
}

func (c *ChainConfig) IsPrague(epoch *big.Int) bool {
return isForked(c.PragueEpoch, epoch)
}

// During this epoch, shards 2 and 3 will start sending
// their balances over to shard 0 or 1.
func (c *ChainConfig) IsOneEpochBeforeHIP30(epoch *big.Int) bool {
Expand Down
15 changes: 13 additions & 2 deletions internal/params/protocol_params.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package params

import "math/big"
import (
"math/big"

"github.com/ethereum/go-ethereum/common"
)

// nolint
const (
Expand Down Expand Up @@ -158,8 +162,9 @@ const (
// MaxCodeSize ...
MaxCodeSize = 24576 // Maximum bytecode to permit for a contract

// Precompiled contract gas prices
HistoryServeWindow = 8192 // Number of blocks to serve historical block hashes for, EIP-2935

// Precompiled contract gas prices
EcrecoverGas uint64 = 3000 // Elliptic curve sender recovery gas price
Sha256BaseGas uint64 = 60 // Base price for a SHA256 operation
Sha256PerWordGas uint64 = 12 // Per-word price for a SHA256 operation
Expand Down Expand Up @@ -205,3 +210,9 @@ var Bls12381G1MultiExpDiscountTable = [128]uint64{1000, 949, 848, 797, 764, 750,

// Gas discount table for BLS12-381 G2 multi exponentiation operation
var Bls12381G2MultiExpDiscountTable = [128]uint64{1000, 1000, 923, 884, 855, 832, 812, 796, 782, 770, 759, 749, 740, 732, 724, 717, 711, 704, 699, 693, 688, 683, 679, 674, 670, 666, 663, 659, 655, 652, 649, 646, 643, 640, 637, 634, 632, 629, 627, 624, 622, 620, 618, 615, 613, 611, 609, 607, 606, 604, 602, 600, 598, 597, 595, 593, 592, 590, 589, 587, 586, 584, 583, 582, 580, 579, 578, 576, 575, 574, 573, 571, 570, 569, 568, 567, 566, 565, 563, 562, 561, 560, 559, 558, 557, 556, 555, 554, 553, 552, 552, 551, 550, 549, 548, 547, 546, 545, 545, 544, 543, 542, 541, 541, 540, 539, 538, 537, 537, 536, 535, 535, 534, 533, 532, 532, 531, 530, 530, 529, 528, 528, 527, 526, 526, 525, 524, 524}

// HistoryStorageAddress is where the historical block hashes are stored.
var HistoryStorageAddress = common.HexToAddress("0x0aae40965e6800cd9b1f4b05ff21581047e3f91e")

// HistoryStorageCode is the code with getters for historical block hashes.
var HistoryStorageCode = common.FromHex("3373fffffffffffffffffffffffffffffffffffffffe1460575767ffffffffffffffff5f3511605357600143035f3511604b575f35612000014311604b57611fff5f3516545f5260205ff35b5f5f5260205ff35b5f5ffd5b5f35611fff60014303165500")