Add EthChainID and update signers accordingly for processing eth txns. (#3497)

* allow eth hmy txn conversion

* Add EthChainID and update signers for it

* resolve comments about hash()
pull/3314/head
Rongjian Lan 4 years ago committed by GitHub
parent 9381b95cdc
commit d6e98436f4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      cmd/harmony/main.go
  2. 8
      core/blockchain.go
  3. 9
      core/state_processor.go
  4. 39
      core/types/eth_transaction.go
  5. 48
      core/types/transaction.go
  6. 13
      core/types/transaction_signing.go
  7. 2
      core/types/transaction_test.go
  8. 1
      core/vm/eips.go
  9. 1
      core/vm/runtime/runtime.go
  10. 3
      hmy/hmy.go
  11. 35
      hmy/tracer.go
  12. 50
      internal/params/config.go
  13. 12
      node/worker/worker.go
  14. 5
      staking/types/transaction.go

@ -16,6 +16,8 @@ import (
"syscall" "syscall"
"time" "time"
"github.com/harmony-one/harmony/internal/params"
ethCommon "github.com/ethereum/go-ethereum/common" ethCommon "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/harmony-one/bls/ffi/go/bls" "github.com/harmony-one/bls/ffi/go/bls"
@ -279,6 +281,9 @@ func setupNodeAndRun(hc harmonyConfig) {
nodeconfig.GetDefaultConfig().ShardID = nodeConfig.ShardID nodeconfig.GetDefaultConfig().ShardID = nodeConfig.ShardID
nodeconfig.GetDefaultConfig().IsOffline = nodeConfig.IsOffline nodeconfig.GetDefaultConfig().IsOffline = nodeConfig.IsOffline
// Update ethereum compatible chain ids
params.UpdateEthChainIDByShard(nodeConfig.ShardID)
// Check NTP configuration // Check NTP configuration
accurate, err := ntp.CheckLocalTimeAccurate(nodeConfig.NtpServer) accurate, err := ntp.CheckLocalTimeAccurate(nodeConfig.NtpServer)
if !accurate { if !accurate {

@ -956,6 +956,7 @@ func (bc *BlockChain) Rollback(chain []common.Hash) error {
// SetReceiptsData computes all the non-consensus fields of the receipts // SetReceiptsData computes all the non-consensus fields of the receipts
func SetReceiptsData(config *params.ChainConfig, block *types.Block, receipts types.Receipts) error { func SetReceiptsData(config *params.ChainConfig, block *types.Block, receipts types.Receipts) error {
signer := types.MakeSigner(config, block.Epoch()) signer := types.MakeSigner(config, block.Epoch())
ethSigner := types.NewEIP155Signer(config.EthCompatibleChainID)
transactions, stakingTransactions, logIndex := block.Transactions(), block.StakingTransactions(), uint(0) transactions, stakingTransactions, logIndex := block.Transactions(), block.StakingTransactions(), uint(0)
if len(transactions)+len(stakingTransactions) != len(receipts) { if len(transactions)+len(stakingTransactions) != len(receipts) {
@ -973,7 +974,12 @@ func SetReceiptsData(config *params.ChainConfig, block *types.Block, receipts ty
// The contract address can be derived from the transaction itself // The contract address can be derived from the transaction itself
if transactions[j].To() == nil { if transactions[j].To() == nil {
// Deriving the signer is expensive, only do if it's actually needed // Deriving the signer is expensive, only do if it's actually needed
from, _ := types.Sender(signer, transactions[j]) var from common.Address
if transactions[j].IsEthCompatible() {
from, _ = types.Sender(ethSigner, transactions[j])
} else {
from, _ = types.Sender(signer, transactions[j])
}
receipts[j].ContractAddress = crypto.CreateAddress(from, transactions[j].Nonce()) receipts[j].ContractAddress = crypto.CreateAddress(from, transactions[j].Nonce())
} }
// The derived log fields can simply be set from the block and transaction // The derived log fields can simply be set from the block and transaction

@ -187,7 +187,14 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
) )
} }
msg, err := tx.AsMessage(types.MakeSigner(config, header.Epoch())) var signer types.Signer
if tx.IsEthCompatible() {
signer = types.NewEIP155Signer(config.EthCompatibleChainID)
} else {
signer = types.MakeSigner(config, header.Epoch())
}
msg, err := tx.AsMessage(signer)
// skip signer err for additiononly tx // skip signer err for additiononly tx
if err != nil { if err != nil {
return nil, nil, 0, err return nil, nil, 0, err

@ -21,6 +21,8 @@ import (
"math/big" "math/big"
"sync/atomic" "sync/atomic"
"github.com/harmony-one/harmony/internal/params"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/harmony-one/harmony/crypto/hash" "github.com/harmony-one/harmony/crypto/hash"
@ -32,9 +34,6 @@ import (
//go:generate gencodec -type ethTxdata -field-override ethTxdataMarshaling -out gen_eth_tx_json.go //go:generate gencodec -type ethTxdata -field-override ethTxdataMarshaling -out gen_eth_tx_json.go
// Shard0ChainID to be reserved unique chain ID for eth compatible chains.
const Shard0ChainID = 0
// EthTransaction ethereum-compatible transaction // EthTransaction ethereum-compatible transaction
type EthTransaction struct { type EthTransaction struct {
data ethTxdata data ethTxdata
@ -152,12 +151,12 @@ func (tx *EthTransaction) Data() []byte {
// ShardID returns which shard id this transaction was signed for (if at all) // ShardID returns which shard id this transaction was signed for (if at all)
func (tx *EthTransaction) ShardID() uint32 { func (tx *EthTransaction) ShardID() uint32 {
return uint32(tx.ChainID().Uint64()) - Shard0ChainID return uint32(tx.ChainID().Uint64() - params.EthMainnetChainID.Uint64())
} }
// ToShardID returns the destination shard id this transaction is going to // ToShardID returns the destination shard id this transaction is going to
func (tx *EthTransaction) ToShardID() uint32 { func (tx *EthTransaction) ToShardID() uint32 {
return uint32(tx.ChainID().Uint64()) - Shard0ChainID return uint32(tx.ChainID().Uint64() - params.EthMainnetChainID.Uint64())
} }
// ChainID returns which chain id this transaction was signed for (if at all) // ChainID returns which chain id this transaction was signed for (if at all)
@ -177,6 +176,31 @@ func (tx *EthTransaction) Copy() *EthTransaction {
return &tx2 return &tx2
} }
// ConvertToHmy converts eth txn to hmy txn by filling in ShardID and ToShardID fields.
func (tx *EthTransaction) ConvertToHmy() *Transaction {
var tx2 Transaction
d := &tx.data
d2 := &tx2.data
d2.AccountNonce = d.AccountNonce
d2.Price = new(big.Int).Set(d.Price)
d2.GasLimit = d.GasLimit
d2.Recipient = copyAddr(d.Recipient)
d2.Amount = new(big.Int).Set(d.Amount)
d2.Payload = append(d.Payload[:0:0], d.Payload...)
d2.V = new(big.Int).Set(d.V)
d2.R = new(big.Int).Set(d.R)
d2.S = new(big.Int).Set(d.S)
d2.ShardID = tx.ShardID()
d2.ToShardID = tx.ToShardID()
copy := tx2.Hash()
d2.Hash = &copy
return &tx2
}
// EncodeRLP implements rlp.Encoder // EncodeRLP implements rlp.Encoder
func (tx *EthTransaction) EncodeRLP(w io.Writer) error { func (tx *EthTransaction) EncodeRLP(w io.Writer) error {
return rlp.Encode(w, &tx.data) return rlp.Encode(w, &tx.data)
@ -302,6 +326,11 @@ func (tx *EthTransaction) SenderAddress() (common.Address, error) {
return addr, nil return addr, nil
} }
// IsEthCompatible returns whether the txn is ethereum compatible
func (tx *EthTransaction) IsEthCompatible() bool {
return true
}
// AsMessage returns the transaction as a core.Message. // AsMessage returns the transaction as a core.Message.
// //
// AsMessage requires a signer to derive the sender. // AsMessage requires a signer to derive the sender.

@ -24,6 +24,8 @@ import (
"math/big" "math/big"
"sync/atomic" "sync/atomic"
"github.com/harmony-one/harmony/internal/params"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
@ -70,6 +72,7 @@ type InternalTransaction interface {
R() *big.Int R() *big.Int
S() *big.Int S() *big.Int
IsEthCompatible() bool
AsMessage(s Signer) (Message, error) AsMessage(s Signer) (Message, error)
} }
@ -412,6 +415,33 @@ func (tx *Transaction) Size() common.StorageSize {
return common.StorageSize(c) return common.StorageSize(c)
} }
// IsEthCompatible returns whether the txn is ethereum compatible
func (tx *Transaction) IsEthCompatible() bool {
return params.IsEthCompatible(tx.ChainID())
}
// ConvertToEth converts hmy txn to eth txn by removing the ShardID and ToShardID fields.
func (tx *Transaction) ConvertToEth() *EthTransaction {
var tx2 EthTransaction
d := &tx.data
d2 := &tx2.data
d2.AccountNonce = d.AccountNonce
d2.Price = new(big.Int).Set(d.Price)
d2.GasLimit = d.GasLimit
d2.Recipient = copyAddr(d.Recipient)
d2.Amount = new(big.Int).Set(d.Amount)
d2.Payload = append(d.Payload[:0:0], d.Payload...)
d2.V = new(big.Int).Set(d.V)
d2.R = new(big.Int).Set(d.R)
d2.S = new(big.Int).Set(d.S)
copy := tx2.Hash()
d2.Hash = &copy
return &tx2
}
// AsMessage returns the transaction as a core.Message. // AsMessage returns the transaction as a core.Message.
// //
// AsMessage requires a signer to derive the sender. // AsMessage requires a signer to derive the sender.
@ -518,6 +548,7 @@ type TransactionsByPriceAndNonce struct {
txs map[common.Address]Transactions // Per account nonce-sorted list of transactions txs map[common.Address]Transactions // Per account nonce-sorted list of transactions
heads TxByPrice // Next transaction for each unique account (price heap) heads TxByPrice // Next transaction for each unique account (price heap)
signer Signer // Signer for the set of transactions signer Signer // Signer for the set of transactions
ethSigner Signer // Signer for the set of transactions
} }
// NewTransactionsByPriceAndNonce creates a transaction set that can retrieve // NewTransactionsByPriceAndNonce creates a transaction set that can retrieve
@ -525,12 +556,16 @@ type TransactionsByPriceAndNonce struct {
// //
// Note, the input map is reowned so the caller should not interact any more with // Note, the input map is reowned so the caller should not interact any more with
// if after providing it to the constructor. // if after providing it to the constructor.
func NewTransactionsByPriceAndNonce(signer Signer, txs map[common.Address]Transactions) *TransactionsByPriceAndNonce { func NewTransactionsByPriceAndNonce(hmySigner Signer, ethSigner Signer, txs map[common.Address]Transactions) *TransactionsByPriceAndNonce {
// Initialize a price based heap with the head transactions // Initialize a price based heap with the head transactions
heads := make(TxByPrice, 0, len(txs)) heads := make(TxByPrice, 0, len(txs))
for from, accTxs := range txs { for from, accTxs := range txs {
heads = append(heads, accTxs[0]) heads = append(heads, accTxs[0])
// Ensure the sender address is from the signer // Ensure the sender address is from the signer
signer := hmySigner
if accTxs[0].IsEthCompatible() {
signer = ethSigner
}
acc, _ := Sender(signer, accTxs[0]) acc, _ := Sender(signer, accTxs[0])
txs[acc] = accTxs[1:] txs[acc] = accTxs[1:]
if from != acc { if from != acc {
@ -543,12 +578,13 @@ func NewTransactionsByPriceAndNonce(signer Signer, txs map[common.Address]Transa
return &TransactionsByPriceAndNonce{ return &TransactionsByPriceAndNonce{
txs: txs, txs: txs,
heads: heads, heads: heads,
signer: signer, signer: hmySigner,
ethSigner: ethSigner,
} }
} }
// Peek returns the next transaction by price. // Peek returns the next transaction by price.
func (t *TransactionsByPriceAndNonce) Peek() InternalTransaction { func (t *TransactionsByPriceAndNonce) Peek() *Transaction {
if len(t.heads) == 0 { if len(t.heads) == 0 {
return nil return nil
} }
@ -557,7 +593,11 @@ func (t *TransactionsByPriceAndNonce) Peek() InternalTransaction {
// Shift replaces the current best head with the next one from the same account. // Shift replaces the current best head with the next one from the same account.
func (t *TransactionsByPriceAndNonce) Shift() { func (t *TransactionsByPriceAndNonce) Shift() {
acc, _ := Sender(t.signer, t.heads[0]) signer := t.signer
if t.heads[0].IsEthCompatible() {
signer = t.ethSigner
}
acc, _ := Sender(signer, t.heads[0])
if txs, ok := t.txs[acc]; ok && len(txs) > 0 { if txs, ok := t.txs[acc]; ok && len(txs) > 0 {
t.heads[0], t.txs[acc] = txs[0], txs[1:] t.heads[0], t.txs[acc] = txs[0], txs[1:]
heap.Fix(&t.heads, 0) heap.Fix(&t.heads, 0)

@ -132,6 +132,7 @@ func (s EIP155Signer) Sender(tx InternalTransaction) (common.Address, error) {
if !tx.Protected() { if !tx.Protected() {
return HomesteadSigner{}.Sender(tx) return HomesteadSigner{}.Sender(tx)
} }
if tx.ChainID().Cmp(s.chainID) != 0 { if tx.ChainID().Cmp(s.chainID) != 0 {
return common.Address{}, ErrInvalidChainID return common.Address{}, ErrInvalidChainID
} }
@ -157,6 +158,18 @@ func (s EIP155Signer) SignatureValues(tx InternalTransaction, sig []byte) (R, S,
// Hash returns the hash to be signed by the sender. // Hash returns the hash to be signed by the sender.
// It does not uniquely identify the transaction. // It does not uniquely identify the transaction.
func (s EIP155Signer) Hash(tx InternalTransaction) common.Hash { func (s EIP155Signer) Hash(tx InternalTransaction) common.Hash {
if params.IsEthCompatible(s.chainID) {
// following the same logic as in go-eth implementation
return hash.FromRLP([]interface{}{
tx.Nonce(),
tx.GasPrice(),
tx.GasLimit(),
tx.To(),
tx.Value(),
tx.Data(),
s.chainID, uint(0), uint(0),
})
}
return hash.FromRLP([]interface{}{ return hash.FromRLP([]interface{}{
tx.Nonce(), tx.Nonce(),
tx.GasPrice(), tx.GasPrice(),

@ -53,7 +53,7 @@ func TestTransactionPriceNonceSort(t *testing.T) {
} }
} }
// Sort the transactions and cross check the nonce ordering // Sort the transactions and cross check the nonce ordering
txset := NewTransactionsByPriceAndNonce(signer, groups) txset := NewTransactionsByPriceAndNonce(signer, signer, groups)
txs := InternalTransactions{} txs := InternalTransactions{}
for tx := txset.Peek(); tx != nil; tx = txset.Peek() { for tx := txset.Peek(); tx != nil; tx = txset.Peek() {

@ -81,6 +81,7 @@ func enable1344(jt *JumpTable) {
// opChainID implements CHAINID opcode // opChainID implements CHAINID opcode
func opChainID(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { func opChainID(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
// TODO: CHAINID - add fork (or a new opcode) to use ethereum compatible chainID
chainID := interpreter.intPool.get().Set(interpreter.evm.chainConfig.ChainID) chainID := interpreter.intPool.get().Set(interpreter.evm.chainConfig.ChainID)
stack.push(chainID) stack.push(chainID)
return nil, nil return nil, nil

@ -54,6 +54,7 @@ func setDefaults(cfg *Config) {
if cfg.ChainConfig == nil { if cfg.ChainConfig == nil {
cfg.ChainConfig = &params.ChainConfig{ cfg.ChainConfig = &params.ChainConfig{
ChainID: big.NewInt(1), ChainID: big.NewInt(1),
EthCompatibleChainID: params.EthMainnetChainID,
CrossTxEpoch: new(big.Int), CrossTxEpoch: new(big.Int),
CrossLinkEpoch: new(big.Int), CrossLinkEpoch: new(big.Int),
EIP155Epoch: new(big.Int), EIP155Epoch: new(big.Int),

@ -55,6 +55,8 @@ type Harmony struct {
NodeAPI NodeAPI NodeAPI NodeAPI
// ChainID is used to identify which network we are using // ChainID is used to identify which network we are using
ChainID uint64 ChainID uint64
// EthCompatibleChainID is used to identify the Ethereum compatible chain ID
EthChainID uint64
// RPCGasCap is the global gas cap for eth-call variants. // RPCGasCap is the global gas cap for eth-call variants.
RPCGasCap *big.Int `toml:",omitempty"` RPCGasCap *big.Int `toml:",omitempty"`
ShardID uint32 ShardID uint32
@ -130,6 +132,7 @@ func New(
chainDb: chainDb, chainDb: chainDb,
NodeAPI: nodeAPI, NodeAPI: nodeAPI,
ChainID: nodeAPI.Blockchain().Config().ChainID.Uint64(), ChainID: nodeAPI.Blockchain().Config().ChainID.Uint64(),
EthChainID: nodeAPI.Blockchain().Config().EthCompatibleChainID.Uint64(),
ShardID: shardID, ShardID: shardID,
leaderCache: leaderCache, leaderCache: leaderCache,
totalStakeCache: totalStakeCache, totalStakeCache: totalStakeCache,

@ -162,10 +162,15 @@ func (hmy *Harmony) TraceChain(ctx context.Context, start, end *types.Block, con
// Fetch and execute the next block trace tasks // Fetch and execute the next block trace tasks
for task := range tasks { for task := range tasks {
signer := types.MakeSigner(hmy.BlockChain.Config(), task.block.Number()) hmySigner := types.MakeSigner(hmy.BlockChain.Config(), task.block.Number())
ethSigner := types.NewEIP155Signer(hmy.BlockChain.Config().EthCompatibleChainID)
// Trace all the transactions contained within // Trace all the transactions contained within
for i, tx := range task.block.Transactions() { for i, tx := range task.block.Transactions() {
signer := hmySigner
if tx.IsEthCompatible() {
signer = ethSigner
}
msg, _ := tx.AsMessage(signer) msg, _ := tx.AsMessage(signer)
vmCtx := core.NewEVMContext(msg, task.block.Header(), hmy.BlockChain, nil) vmCtx := core.NewEVMContext(msg, task.block.Header(), hmy.BlockChain, nil)
@ -359,8 +364,8 @@ func (hmy *Harmony) TraceBlock(ctx context.Context, block *types.Block, config *
} }
// Execute all the transaction contained within the block concurrently // Execute all the transaction contained within the block concurrently
var ( var (
signer = types.MakeSigner(hmy.BlockChain.Config(), block.Number()) hmySigner = types.MakeSigner(hmy.BlockChain.Config(), block.Number())
ethSigner = types.NewEIP155Signer(hmy.BlockChain.Config().EthCompatibleChainID)
txs = block.Transactions() txs = block.Transactions()
results = make([]*TxTraceResult, len(txs)) results = make([]*TxTraceResult, len(txs))
@ -378,6 +383,11 @@ func (hmy *Harmony) TraceBlock(ctx context.Context, block *types.Block, config *
// Fetch and execute the next transaction trace tasks // Fetch and execute the next transaction trace tasks
for task := range jobs { for task := range jobs {
signer := hmySigner
if txs[task.index].IsEthCompatible() {
signer = ethSigner
}
msg, _ := txs[task.index].AsMessage(signer) msg, _ := txs[task.index].AsMessage(signer)
vmctx := core.NewEVMContext(msg, block.Header(), hmy.BlockChain, nil) vmctx := core.NewEVMContext(msg, block.Header(), hmy.BlockChain, nil)
@ -396,6 +406,10 @@ func (hmy *Harmony) TraceBlock(ctx context.Context, block *types.Block, config *
// Send the trace task over for execution // Send the trace task over for execution
jobs <- &txTraceTask{statedb: statedb.Copy(), index: i} jobs <- &txTraceTask{statedb: statedb.Copy(), index: i}
signer := hmySigner
if tx.IsEthCompatible() {
signer = ethSigner
}
// Generate the next state snapshot fast without tracing // Generate the next state snapshot fast without tracing
msg, _ := tx.AsMessage(signer) msg, _ := tx.AsMessage(signer)
vmctx := core.NewEVMContext(msg, block.Header(), hmy.BlockChain, nil) vmctx := core.NewEVMContext(msg, block.Header(), hmy.BlockChain, nil)
@ -459,10 +473,15 @@ func (hmy *Harmony) standardTraceBlockToFile(ctx context.Context, block *types.B
// Execute transaction, either tracing all or just the requested one // Execute transaction, either tracing all or just the requested one
var ( var (
signer = types.MakeSigner(hmy.BlockChain.Config(), block.Number()) hmySigner = types.MakeSigner(hmy.BlockChain.Config(), block.Number())
ethSigner = types.NewEIP155Signer(hmy.BlockChain.Config().EthCompatibleChainID)
dumps []string dumps []string
) )
for i, tx := range block.Transactions() { for i, tx := range block.Transactions() {
signer := hmySigner
if tx.IsEthCompatible() {
signer = ethSigner
}
// Prepare the transaction for un-traced execution // Prepare the transaction for un-traced execution
var ( var (
msg, _ = tx.AsMessage(signer) msg, _ = tx.AsMessage(signer)
@ -685,9 +704,15 @@ func (hmy *Harmony) ComputeTxEnv(block *types.Block, txIndex int, reexec uint64)
} }
// Recompute transactions up to the target index. // Recompute transactions up to the target index.
signer := types.MakeSigner(hmy.BlockChain.Config(), block.Number()) hmySigner := types.MakeSigner(hmy.BlockChain.Config(), block.Number())
ethSigner := types.NewEIP155Signer(hmy.BlockChain.Config().EthCompatibleChainID)
for idx, tx := range block.Transactions() { for idx, tx := range block.Transactions() {
signer := hmySigner
if tx.IsEthCompatible() {
signer = ethSigner
}
// Assemble the transaction call message and return if the requested offset // Assemble the transaction call message and return if the requested offset
msg, _ := tx.AsMessage(signer) msg, _ := tx.AsMessage(signer)
context := core.NewEVMContext(msg, block.Header(), hmy.BlockChain, nil) context := core.NewEVMContext(msg, block.Header(), hmy.BlockChain, nil)

@ -3,6 +3,7 @@ package params
import ( import (
"fmt" "fmt"
"math/big" "math/big"
"sync"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
) )
@ -16,16 +17,27 @@ var (
StressnetChainID = big.NewInt(5) StressnetChainID = big.NewInt(5)
TestChainID = big.NewInt(99) // not a real network TestChainID = big.NewInt(99) // not a real network
AllProtocolChangesChainID = big.NewInt(100) // not a real network AllProtocolChangesChainID = big.NewInt(100) // not a real network
// EthMainnetChainID to be reserved unique chain ID for eth compatible chains.
EthMainnetChainID = big.NewInt(1666600000)
EthTestnetChainID = big.NewInt(1666700000)
EthPangaeaChainID = big.NewInt(1666800000)
EthPartnerChainID = big.NewInt(1666900000)
EthStressnetChainID = big.NewInt(1661000000)
EthTestChainID = big.NewInt(1661100000) // not a real network
EthAllProtocolChangesChainID = big.NewInt(1661200000) // not a real network
) )
// EpochTBD is a large, “not anytime soon” epoch. It used as a placeholder // EpochTBD is a large, “not anytime soon” epoch. It used as a placeholder
// until the exact epoch is decided. // until the exact epoch is decided.
var EpochTBD = big.NewInt(10000000) var EpochTBD = big.NewInt(10000000)
var once sync.Once
var ( var (
// MainnetChainConfig is the chain parameters to run a node on the main network. // MainnetChainConfig is the chain parameters to run a node on the main network.
MainnetChainConfig = &ChainConfig{ MainnetChainConfig = &ChainConfig{
ChainID: MainnetChainID, ChainID: MainnetChainID,
EthCompatibleChainID: EthMainnetChainID,
CrossTxEpoch: big.NewInt(28), CrossTxEpoch: big.NewInt(28),
CrossLinkEpoch: big.NewInt(186), CrossLinkEpoch: big.NewInt(186),
StakingEpoch: big.NewInt(186), StakingEpoch: big.NewInt(186),
@ -43,6 +55,7 @@ var (
// TestnetChainConfig contains the chain parameters to run a node on the harmony test network. // TestnetChainConfig contains the chain parameters to run a node on the harmony test network.
TestnetChainConfig = &ChainConfig{ TestnetChainConfig = &ChainConfig{
ChainID: TestnetChainID, ChainID: TestnetChainID,
EthCompatibleChainID: EthTestnetChainID,
CrossTxEpoch: big.NewInt(0), CrossTxEpoch: big.NewInt(0),
CrossLinkEpoch: big.NewInt(2), CrossLinkEpoch: big.NewInt(2),
StakingEpoch: big.NewInt(2), StakingEpoch: big.NewInt(2),
@ -61,6 +74,7 @@ var (
// All features except for CrossLink are enabled at launch. // All features except for CrossLink are enabled at launch.
PangaeaChainConfig = &ChainConfig{ PangaeaChainConfig = &ChainConfig{
ChainID: PangaeaChainID, ChainID: PangaeaChainID,
EthCompatibleChainID: EthPangaeaChainID,
CrossTxEpoch: big.NewInt(0), CrossTxEpoch: big.NewInt(0),
CrossLinkEpoch: big.NewInt(2), CrossLinkEpoch: big.NewInt(2),
StakingEpoch: big.NewInt(2), StakingEpoch: big.NewInt(2),
@ -79,6 +93,7 @@ var (
// All features except for CrossLink are enabled at launch. // All features except for CrossLink are enabled at launch.
PartnerChainConfig = &ChainConfig{ PartnerChainConfig = &ChainConfig{
ChainID: PartnerChainID, ChainID: PartnerChainID,
EthCompatibleChainID: EthPartnerChainID,
CrossTxEpoch: big.NewInt(0), CrossTxEpoch: big.NewInt(0),
CrossLinkEpoch: big.NewInt(2), CrossLinkEpoch: big.NewInt(2),
StakingEpoch: big.NewInt(2), StakingEpoch: big.NewInt(2),
@ -97,6 +112,7 @@ var (
// All features except for CrossLink are enabled at launch. // All features except for CrossLink are enabled at launch.
StressnetChainConfig = &ChainConfig{ StressnetChainConfig = &ChainConfig{
ChainID: StressnetChainID, ChainID: StressnetChainID,
EthCompatibleChainID: EthStressnetChainID,
CrossTxEpoch: big.NewInt(0), CrossTxEpoch: big.NewInt(0),
CrossLinkEpoch: big.NewInt(2), CrossLinkEpoch: big.NewInt(2),
StakingEpoch: big.NewInt(2), StakingEpoch: big.NewInt(2),
@ -114,6 +130,7 @@ var (
// LocalnetChainConfig contains the chain parameters to run for local development. // LocalnetChainConfig contains the chain parameters to run for local development.
LocalnetChainConfig = &ChainConfig{ LocalnetChainConfig = &ChainConfig{
ChainID: TestnetChainID, ChainID: TestnetChainID,
EthCompatibleChainID: EthTestnetChainID,
CrossTxEpoch: big.NewInt(0), CrossTxEpoch: big.NewInt(0),
CrossLinkEpoch: big.NewInt(2), CrossLinkEpoch: big.NewInt(2),
StakingEpoch: big.NewInt(2), StakingEpoch: big.NewInt(2),
@ -133,6 +150,7 @@ var (
// adding flags to the config to also have to set these fields. // adding flags to the config to also have to set these fields.
AllProtocolChanges = &ChainConfig{ AllProtocolChanges = &ChainConfig{
AllProtocolChangesChainID, // ChainID AllProtocolChangesChainID, // ChainID
EthAllProtocolChangesChainID, // EthCompatibleChainID
big.NewInt(0), // CrossTxEpoch big.NewInt(0), // CrossTxEpoch
big.NewInt(0), // CrossLinkEpoch big.NewInt(0), // CrossLinkEpoch
big.NewInt(0), // StakingEpoch big.NewInt(0), // StakingEpoch
@ -152,6 +170,7 @@ var (
// adding flags to the config to also have to set these fields. // adding flags to the config to also have to set these fields.
TestChainConfig = &ChainConfig{ TestChainConfig = &ChainConfig{
TestChainID, // ChainID TestChainID, // ChainID
EthTestChainID, // EthCompatibleChainID
big.NewInt(0), // CrossTxEpoch big.NewInt(0), // CrossTxEpoch
big.NewInt(0), // CrossLinkEpoch big.NewInt(0), // CrossLinkEpoch
big.NewInt(0), // StakingEpoch big.NewInt(0), // StakingEpoch
@ -191,6 +210,9 @@ type ChainConfig struct {
// ChainId identifies the current chain and is used for replay protection // ChainId identifies the current chain and is used for replay protection
ChainID *big.Int `json:"chain-id"` ChainID *big.Int `json:"chain-id"`
// EthCompatibleChainID identifies the chain id used for ethereum compatible transactions
EthCompatibleChainID *big.Int `json:"eth-compatible-chain-id"`
// CrossTxEpoch is the epoch where cross-shard transaction starts being // CrossTxEpoch is the epoch where cross-shard transaction starts being
// processed. // processed.
CrossTxEpoch *big.Int `json:"cross-tx-epoch,omitempty"` CrossTxEpoch *big.Int `json:"cross-tx-epoch,omitempty"`
@ -235,8 +257,9 @@ type ChainConfig struct {
// String implements the fmt.Stringer interface. // String implements the fmt.Stringer interface.
func (c *ChainConfig) String() string { func (c *ChainConfig) String() string {
return fmt.Sprintf("{ChainID: %v EIP155: %v CrossTx: %v Staking: %v CrossLink: %v ReceiptLog: %v}", return fmt.Sprintf("{ChainID: %v EthCompatibleChainID: %v EIP155: %v CrossTx: %v Staking: %v CrossLink: %v ReceiptLog: %v}",
c.ChainID, c.ChainID,
c.EthCompatibleChainID,
c.EIP155Epoch, c.EIP155Epoch,
c.CrossTxEpoch, c.CrossTxEpoch,
c.StakingEpoch, c.StakingEpoch,
@ -320,6 +343,25 @@ func (c *ChainConfig) IsReceiptLog(epoch *big.Int) bool {
return isForked(c.ReceiptLogEpoch, epoch) return isForked(c.ReceiptLogEpoch, epoch)
} }
// UpdateEthChainIDByShard update the ethChainID based on shard ID.
func UpdateEthChainIDByShard(shardID uint32) {
once.Do(func() {
MainnetChainConfig.EthCompatibleChainID.Add(MainnetChainConfig.EthCompatibleChainID, big.NewInt(int64(shardID)))
TestnetChainConfig.EthCompatibleChainID.Add(TestnetChainConfig.EthCompatibleChainID, big.NewInt(int64(shardID)))
PangaeaChainConfig.EthCompatibleChainID.Add(PangaeaChainConfig.EthCompatibleChainID, big.NewInt(int64(shardID)))
PartnerChainConfig.EthCompatibleChainID.Add(PartnerChainConfig.EthCompatibleChainID, big.NewInt(int64(shardID)))
StressnetChainConfig.EthCompatibleChainID.Add(StressnetChainConfig.EthCompatibleChainID, big.NewInt(int64(shardID)))
LocalnetChainConfig.EthCompatibleChainID.Add(LocalnetChainConfig.EthCompatibleChainID, big.NewInt(int64(shardID)))
AllProtocolChanges.EthCompatibleChainID.Add(AllProtocolChanges.EthCompatibleChainID, big.NewInt(int64(shardID)))
TestChainConfig.EthCompatibleChainID.Add(TestChainConfig.EthCompatibleChainID, big.NewInt(int64(shardID)))
})
}
// IsEthCompatible returns whether the chainID is for ethereum compatible txn or not
func IsEthCompatible(chainID *big.Int) bool {
return chainID.Cmp(EthMainnetChainID) >= 0
}
// GasTable returns the gas table corresponding to the current phase (homestead or homestead reprice). // GasTable returns the gas table corresponding to the current phase (homestead or homestead reprice).
// //
// The returned GasTable's fields shouldn't, under any circumstances, be changed. // The returned GasTable's fields shouldn't, under any circumstances, be changed.
@ -350,6 +392,7 @@ func isForked(s, epoch *big.Int) bool {
// phases. // phases.
type Rules struct { type Rules struct {
ChainID *big.Int ChainID *big.Int
EthChainID *big.Int
IsCrossLink, IsEIP155, IsS3, IsReceiptLog, IsIstanbul bool IsCrossLink, IsEIP155, IsS3, IsReceiptLog, IsIstanbul bool
} }
@ -359,8 +402,13 @@ func (c *ChainConfig) Rules(epoch *big.Int) Rules {
if chainID == nil { if chainID == nil {
chainID = new(big.Int) chainID = new(big.Int)
} }
ethChainID := c.EthCompatibleChainID
if ethChainID == nil {
ethChainID = new(big.Int)
}
return Rules{ return Rules{
ChainID: new(big.Int).Set(chainID), ChainID: new(big.Int).Set(chainID),
EthChainID: new(big.Int).Set(ethChainID),
IsCrossLink: c.IsCrossLink(epoch), IsCrossLink: c.IsCrossLink(epoch),
IsEIP155: c.IsEIP155(epoch), IsEIP155: c.IsEIP155(epoch),
IsS3: c.IsS3(epoch), IsS3: c.IsS3(epoch),

@ -34,6 +34,7 @@ import (
// environment is the worker's current environment and holds all of the current state information. // environment is the worker's current environment and holds all of the current state information.
type environment struct { type environment struct {
signer types.Signer signer types.Signer
ethSigner types.Signer
state *state.DB // apply state changes here state *state.DB // apply state changes here
gasPool *core.GasPool // available gas used to pack transactions gasPool *core.GasPool // available gas used to pack transactions
header *block.Header header *block.Header
@ -76,7 +77,11 @@ func (w *Worker) CommitSortedTransactions(
// Error may be ignored here. The error has already been checked // Error may be ignored here. The error has already been checked
// during transaction acceptance is the transaction pool. // during transaction acceptance is the transaction pool.
// We use the eip155 signer regardless of the current hf. // We use the eip155 signer regardless of the current hf.
from, _ := types.Sender(w.current.signer, tx) signer := w.current.signer
if tx.IsEthCompatible() {
signer = w.current.ethSigner
}
from, _ := types.Sender(signer, tx)
// Check whether the tx is replay protected. If we're not in the EIP155 hf // Check whether the tx is replay protected. If we're not in the EIP155 hf
// phase, start ignoring the sender until we do. // phase, start ignoring the sender until we do.
if tx.Protected() && !w.config.IsEIP155(w.current.header.Epoch()) { if tx.Protected() && !w.config.IsEIP155(w.current.header.Epoch()) {
@ -92,7 +97,7 @@ func (w *Worker) CommitSortedTransactions(
// Start executing the transaction // Start executing the transaction
w.current.state.Prepare(tx.Hash(), common.Hash{}, len(w.current.txs)) w.current.state.Prepare(tx.Hash(), common.Hash{}, len(w.current.txs))
_, err := w.commitTransaction(tx.(*types.Transaction), coinbase) _, err := w.commitTransaction(tx, coinbase)
sender, _ := common2.AddressToBech32(from) sender, _ := common2.AddressToBech32(from)
switch err { switch err {
@ -134,7 +139,7 @@ func (w *Worker) CommitTransactions(
} }
// HARMONY TXNS // HARMONY TXNS
normalTxns := types.NewTransactionsByPriceAndNonce(w.current.signer, pendingNormal) normalTxns := types.NewTransactionsByPriceAndNonce(w.current.signer, w.current.ethSigner, pendingNormal)
w.CommitSortedTransactions(normalTxns, coinbase) w.CommitSortedTransactions(normalTxns, coinbase)
// STAKING - only beaconchain process staking transaction // STAKING - only beaconchain process staking transaction
@ -301,6 +306,7 @@ func (w *Worker) makeCurrent(parent *types.Block, header *block.Header) error {
} }
env := &environment{ env := &environment{
signer: types.NewEIP155Signer(w.config.ChainID), signer: types.NewEIP155Signer(w.config.ChainID),
ethSigner: types.NewEIP155Signer(w.config.EthCompatibleChainID),
state: state, state: state,
header: header, header: header,
} }

@ -259,6 +259,11 @@ func (tx *StakingTransaction) Size() common.StorageSize {
return common.StorageSize(c) return common.StorageSize(c)
} }
// IsEthCompatible returns whether the txn is ethereum compatible
func (tx *StakingTransaction) IsEthCompatible() bool {
return false
}
type writeCounter common.StorageSize type writeCounter common.StorageSize
func (c *writeCounter) Write(b []byte) (int, error) { func (c *writeCounter) Write(b []byte) (int, error) {

Loading…
Cancel
Save