Skip to content

Commit

Permalink
R4R: mint eth to msg.from and transfer revertable (#31)
Browse files Browse the repository at this point in the history
  • Loading branch information
abelliumnt authored Feb 5, 2024
1 parent 77b7f84 commit d120ff2
Show file tree
Hide file tree
Showing 8 changed files with 148 additions and 39 deletions.
1 change: 1 addition & 0 deletions core/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,7 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *trie.Database, gen
mantleUpgradeChainConfig := GetUpgradeConfigForMantle(config.ChainID)
if mantleUpgradeChainConfig != nil {
config.BaseFeeTime = mantleUpgradeChainConfig.BaseFeeTime
config.BVMETHMintUpgradeTime = mantleUpgradeChainConfig.BVMETHMintUpgradeTime
}

if overrides != nil && overrides.OverrideShanghai != nil {
Expand Down
24 changes: 15 additions & 9 deletions core/mantle_upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,32 @@ import (

var (
MantleMainnetUpgradeConfig = MantleUpgradeChainConfig{
ChainID: params.MantleMainnetChainId,
BaseFeeTime: u64Ptr(0),
ChainID: params.MantleMainnetChainId,
BaseFeeTime: u64Ptr(0),
BVMETHMintUpgradeTime: u64Ptr(0),
}

MantleSepoliaUpgradeConfig = MantleUpgradeChainConfig{
ChainID: params.MantleSepoliaChainId,
BaseFeeTime: u64Ptr(1_704_891_600),
ChainID: params.MantleSepoliaChainId,
BaseFeeTime: u64Ptr(1_704_891_600),
BVMETHMintUpgradeTime: nil, //TODO set upgrade timestamp
}
MantleLocalUpgradeConfig = MantleUpgradeChainConfig{
ChainID: params.MantleLocalChainId,
BaseFeeTime: u64Ptr(0),
ChainID: params.MantleLocalChainId,
BaseFeeTime: u64Ptr(0),
BVMETHMintUpgradeTime: u64Ptr(0),
}
MantleDefaultUpgradeConfig = MantleUpgradeChainConfig{
BaseFeeTime: u64Ptr(0),
BaseFeeTime: u64Ptr(0),
BVMETHMintUpgradeTime: u64Ptr(0),
}
)

type MantleUpgradeChainConfig struct {
ChainID *big.Int `json:"chainId"` // chainId identifies the current chain and is used for replay protection
BaseFeeTime *uint64 `json:"BaseFeeTime"` // Mantle BaseFee switch time (nil = no fork, 0 = already on mantle baseFee)
ChainID *big.Int `json:"chainId"` // chainId identifies the current chain and is used for replay protection

BaseFeeTime *uint64 `json:"BaseFeeTime"` // Mantle BaseFee switch time (nil = no fork, 0 = already on mantle baseFee)
BVMETHMintUpgradeTime *uint64 `json:"BVMETHMintUpgradeTime"` // BVM_ETH mint upgrade switch time (nil = no fork, 0 = already on)
}

func GetUpgradeConfigForMantle(chainID *big.Int) *MantleUpgradeChainConfig {
Expand Down
95 changes: 84 additions & 11 deletions core/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ type Message struct {
IsDepositTx bool // IsDepositTx indicates the message is force-included and can persist a mint.
Mint *big.Int // Mint is the amount to mint before EVM processing, or nil if there is no minting.
ETHValue *big.Int // ETHValue is the amount to mint BVM_ETH before EVM processing, or nil if there is no minting.
ETHTxValue *big.Int // ETHTxValue is the amount to be transferred to msg.To before EVM processing, and the transfer will be reverted if EVM failed
MetaTxParams *types.MetaTxParams // MetaTxParams contains necessary parameter to sponsor gas fee for msg.From.
RollupDataGas types.RollupGasData // RollupDataGas indicates the rollup cost of the message, 0 if not a rollup or no cost.

Expand Down Expand Up @@ -189,6 +190,7 @@ func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.In
Mint: tx.Mint(),
RollupDataGas: tx.RollupDataGas(),
ETHValue: tx.ETHValue(),
ETHTxValue: tx.ETHTxValue(),
MetaTxParams: metaTxParams,
SkipAccountChecks: false,
RunMode: CommitMode,
Expand Down Expand Up @@ -438,19 +440,18 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
if mint := st.msg.Mint; mint != nil {
st.state.AddBalance(st.msg.From, mint)
}

//Mint BVM_ETH
rules := st.evm.ChainConfig().Rules(st.evm.Context.BlockNumber, st.evm.Context.Random != nil, st.evm.Context.Time)
//add eth value
if ethValue := st.msg.ETHValue; ethValue != nil && ethValue.Cmp(big.NewInt(0)) != 0 {
var ethRecipient common.Address
if st.msg.To != nil {
ethRecipient = *st.msg.To
} else {
ethRecipient = crypto.CreateAddress(st.msg.From, st.evm.StateDB.GetNonce(st.msg.From))
}
st.addBVMETHBalance(ethRecipient, ethValue)
st.addBVMETHTotalSupply(ethValue)
st.generateBVMETHMintEvent(ethRecipient, ethValue)
st.mintBVMETH(ethValue, rules)
}
snap := st.state.Snapshot()
// Will be reverted if failed
if ethTxValue := st.msg.ETHTxValue; ethTxValue != nil && ethTxValue.Cmp(big.NewInt(0)) != 0 {
st.transferBVMETH(ethTxValue, rules)
}

result, err := st.innerTransitionDb()
// Failed deposits must still be included. Unless we cannot produce the block at all due to the gas limit.
Expand Down Expand Up @@ -671,12 +672,33 @@ func (st *StateTransition) gasUsed() uint64 {
return st.initialGas - st.gasRemaining
}

func (st *StateTransition) addBVMETHBalance(ethRecipient common.Address, ethValue *big.Int) {
key := getBVMETHBalanceKey(ethRecipient)
func (st *StateTransition) mintBVMETH(ethValue *big.Int, rules params.Rules) {
if !rules.IsMantleBVMETHMintUpgrade {
var key common.Hash
var ethRecipient common.Address
if st.msg.To != nil {
ethRecipient = *st.msg.To
} else {
ethRecipient = crypto.CreateAddress(st.msg.From, st.evm.StateDB.GetNonce(st.msg.From))
}
key = getBVMETHBalanceKey(ethRecipient)
value := st.state.GetState(BVM_ETH_ADDR, key)
bal := value.Big()
bal = bal.Add(bal, ethValue)
st.state.SetState(BVM_ETH_ADDR, key, common.BigToHash(bal))

st.addBVMETHTotalSupply(ethValue)
st.generateBVMETHMintEvent(ethRecipient, ethValue)
return
}
key := getBVMETHBalanceKey(st.msg.From)
value := st.state.GetState(BVM_ETH_ADDR, key)
bal := value.Big()
bal = bal.Add(bal, ethValue)
st.state.SetState(BVM_ETH_ADDR, key, common.BigToHash(bal))

st.addBVMETHTotalSupply(ethValue)
st.generateBVMETHMintEvent(st.msg.From, ethValue)
}

func (st *StateTransition) addBVMETHTotalSupply(ethValue *big.Int) {
Expand All @@ -687,6 +709,38 @@ func (st *StateTransition) addBVMETHTotalSupply(ethValue *big.Int) {
st.state.SetState(BVM_ETH_ADDR, key, common.BigToHash(bal))
}

func (st *StateTransition) transferBVMETH(ethValue *big.Int, rules params.Rules) {
if !rules.IsMantleBVMETHMintUpgrade {
return
}
var ethRecipient common.Address
if st.msg.To != nil {
ethRecipient = *st.msg.To
} else {
ethRecipient = crypto.CreateAddress(st.msg.From, st.evm.StateDB.GetNonce(st.msg.From))
}
if ethRecipient == st.msg.From {
return
}

fromKey := getBVMETHBalanceKey(st.msg.From)
toKey := getBVMETHBalanceKey(ethRecipient)

fromBalanceValue := st.state.GetState(BVM_ETH_ADDR, fromKey)
toBalanceValue := st.state.GetState(BVM_ETH_ADDR, toKey)

fromBalance := fromBalanceValue.Big()
toBalance := toBalanceValue.Big()

fromBalance = new(big.Int).Sub(fromBalance, ethValue)
toBalance = new(big.Int).Add(toBalance, ethValue)

st.state.SetState(BVM_ETH_ADDR, fromKey, common.BigToHash(fromBalance))
st.state.SetState(BVM_ETH_ADDR, toKey, common.BigToHash(toBalance))

st.generateBVMETHTransferEvent(st.msg.From, ethRecipient, ethValue)
}

func getBVMETHBalanceKey(addr common.Address) common.Hash {
position := common.Big0
hasher := sha3.NewLegacyKeccak256()
Expand Down Expand Up @@ -717,3 +771,22 @@ func (st *StateTransition) generateBVMETHMintEvent(mintAddress common.Address, m
BlockNumber: st.evm.Context.BlockNumber.Uint64(),
})
}

func (st *StateTransition) generateBVMETHTransferEvent(from, to common.Address, amount *big.Int) {
// keccak("Transfer(address,address,uint256)") = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
methodHash := common.HexToHash("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")
topics := make([]common.Hash, 3)
topics[0] = methodHash
topics[1] = from.Hash()
topics[2] = to.Hash()
//data means the transfer amount in Transfer EVENT.
data := common.HexToHash(common.Bytes2Hex(amount.Bytes())).Bytes()
st.evm.StateDB.AddLog(&types.Log{
Address: BVM_ETH_ADDR,
Topics: topics,
Data: data,
// This is a non-consensus field, but assigned here because
// core/state doesn't know the current block number.
BlockNumber: st.evm.Context.BlockNumber.Uint64(),
})
}
6 changes: 6 additions & 0 deletions core/types/deposit_tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ type DepositTx struct {
EthValue *big.Int `rlp:"nil"`
// Normal Tx data
Data []byte
// EthTxValue means L2 BVM_ETH tx tag, nil means that there is no need to transfer BVM_ETH to msg.To.
EthTxValue *big.Int `rlp:"optional"`
}

// copy creates a deep copy of the transaction data and initializes all fields.
Expand All @@ -57,6 +59,7 @@ func (tx *DepositTx) copy() TxData {
IsSystemTransaction: tx.IsSystemTransaction,
Data: common.CopyBytes(tx.Data),
EthValue: nil,
EthTxValue: nil,
}
if tx.Mint != nil {
cpy.Mint = new(big.Int).Set(tx.Mint)
Expand All @@ -67,6 +70,9 @@ func (tx *DepositTx) copy() TxData {
if tx.EthValue != nil {
cpy.EthValue = new(big.Int).Set(tx.EthValue)
}
if tx.EthTxValue != nil {
cpy.EthTxValue = new(big.Int).Set(tx.EthTxValue)
}
return cpy
}

Expand Down
9 changes: 9 additions & 0 deletions core/types/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,15 @@ func (tx *Transaction) ETHValue() *big.Int {
return nil
}

// ETHTxValue returns the BVM_ETH to be transferred in the deposit tx.
// This returns nil if there is nothing to transfer, or if this is not a deposit tx.
func (tx *Transaction) ETHTxValue() *big.Int {
if dep, ok := tx.inner.(*DepositTx); ok {
return dep.EthTxValue
}
return nil
}

// IsDepositTx returns true if the transaction is a deposit tx type.
func (tx *Transaction) IsDepositTx() bool {
return tx.Type() == DepositTxType
Expand Down
6 changes: 6 additions & 0 deletions core/types/transaction_marshalling.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ type txJSON struct {
From *common.Address `json:"from,omitempty"`
Mint *hexutil.Big `json:"mint,omitempty"`
EthValue *hexutil.Big `json:"ethValue,omitempty"`
EthTxValue *hexutil.Big `json:"ethTxValue,omitempty"`
IsSystemTx *bool `json:"isSystemTx,omitempty"`

// Access list transaction fields:
Expand Down Expand Up @@ -141,6 +142,9 @@ func (tx *Transaction) MarshalJSON() ([]byte, error) {
if itx.EthValue != nil {
enc.EthValue = (*hexutil.Big)(itx.EthValue)
}
if itx.EthTxValue != nil {
enc.EthTxValue = (*hexutil.Big)(itx.EthTxValue)
}
enc.IsSystemTx = &itx.IsSystemTransaction
// other fields will show up as null.
}
Expand Down Expand Up @@ -419,6 +423,8 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error {
itx.Mint = (*big.Int)(dec.Mint)
// ethValue may be omitted or nil if there is nothing to mint.
itx.EthValue = (*big.Int)(dec.EthValue)
// ethValue may be omitted or nil if there is nothing to transfer to msg.To.
itx.EthTxValue = (*big.Int)(dec.EthTxValue)
if dec.Data == nil {
return errors.New("missing required field 'input' in transaction")
}
Expand Down
2 changes: 2 additions & 0 deletions internal/ethapi/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -1503,6 +1503,7 @@ type RPCTransaction struct {
SourceHash *common.Hash `json:"sourceHash,omitempty"`
Mint *hexutil.Big `json:"mint,omitempty"`
EthValue *hexutil.Big `json:"ethValue,omitempty"`
EthTxValue *hexutil.Big `json:"ethTxValue,omitempty"`
IsSystemTx *bool `json:"isSystemTx,omitempty"`
}

Expand Down Expand Up @@ -1543,6 +1544,7 @@ func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber
}
result.Mint = (*hexutil.Big)(tx.Mint())
result.EthValue = (*hexutil.Big)(tx.ETHValue())
result.EthTxValue = (*hexutil.Big)(tx.ETHTxValue())
if receipt != nil && receipt.DepositNonce != nil {
result.Nonce = hexutil.Uint64(*receipt.DepositNonce)
}
Expand Down
44 changes: 25 additions & 19 deletions params/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -458,8 +458,8 @@ type ChainConfig struct {
MergeNetsplitBlock *big.Int `json:"mergeNetsplitBlock,omitempty"` // Virtual fork after The Merge to use as a network splitter

// Mantle upgrade configs
BaseFeeTime *uint64 `json:"baseFeeTime,omitempty"` // Mantle BaseFee switch time (nil = no fork, 0 = already on mantle baseFee)

BaseFeeTime *uint64 `json:"baseFeeTime,omitempty"` // Mantle BaseFee switch time (nil = no fork, 0 = already on mantle baseFee)
BVMETHMintUpgradeTime *uint64 `json:"bvmETHMintUpgradeTime,omitempty"` // BVM_ETH mint upgrade switch time (nil = no fork, 0 = already on)
// Fork scheduling was switched from blocks to timestamps here

ShanghaiTime *uint64 `json:"shanghaiTime,omitempty"` // Shanghai switch time (nil = no fork, 0 = already on shanghai)
Expand Down Expand Up @@ -677,6 +677,11 @@ func (c *ChainConfig) IsMantleBaseFee(time uint64) bool {
return isTimestampForked(c.BaseFeeTime, time)
}

// IsMantleBVMETHMintUpgrade returns whether time is either equal to the BVM_ETH mint upgrade fork time or greater.
func (c *ChainConfig) IsMantleBVMETHMintUpgrade(time uint64) bool {
return isTimestampForked(c.BVMETHMintUpgradeTime, time)
}

// IsArrowGlacier returns whether num is either equal to the Arrow Glacier (EIP-4345) fork block or greater.
func (c *ChainConfig) IsArrowGlacier(num *big.Int) bool {
return isBlockForked(c.ArrowGlacierBlock, num)
Expand Down Expand Up @@ -1046,7 +1051,7 @@ type Rules struct {
IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool
IsBerlin, IsLondon bool
IsMerge, IsShanghai, isCancun, isPrague bool
IsMantleBaseFee bool
IsMantleBaseFee, IsMantleBVMETHMintUpgrade bool
IsOptimismBedrock, IsOptimismRegolith bool
}

Expand All @@ -1057,22 +1062,23 @@ func (c *ChainConfig) Rules(num *big.Int, isMerge bool, timestamp uint64) Rules
chainID = new(big.Int)
}
return Rules{
ChainID: new(big.Int).Set(chainID),
IsHomestead: c.IsHomestead(num),
IsEIP150: c.IsEIP150(num),
IsEIP155: c.IsEIP155(num),
IsEIP158: c.IsEIP158(num),
IsByzantium: c.IsByzantium(num),
IsConstantinople: c.IsConstantinople(num),
IsPetersburg: c.IsPetersburg(num),
IsIstanbul: c.IsIstanbul(num),
IsBerlin: c.IsBerlin(num),
IsLondon: c.IsLondon(num),
IsMerge: isMerge,
IsMantleBaseFee: c.IsMantleBaseFee(timestamp),
IsShanghai: c.IsShanghai(timestamp),
isCancun: c.IsCancun(timestamp),
isPrague: c.IsPrague(timestamp),
ChainID: new(big.Int).Set(chainID),
IsHomestead: c.IsHomestead(num),
IsEIP150: c.IsEIP150(num),
IsEIP155: c.IsEIP155(num),
IsEIP158: c.IsEIP158(num),
IsByzantium: c.IsByzantium(num),
IsConstantinople: c.IsConstantinople(num),
IsPetersburg: c.IsPetersburg(num),
IsIstanbul: c.IsIstanbul(num),
IsBerlin: c.IsBerlin(num),
IsLondon: c.IsLondon(num),
IsMerge: isMerge,
IsMantleBaseFee: c.IsMantleBaseFee(timestamp),
IsMantleBVMETHMintUpgrade: c.IsMantleBVMETHMintUpgrade(timestamp),
IsShanghai: c.IsShanghai(timestamp),
isCancun: c.IsCancun(timestamp),
isPrague: c.IsPrague(timestamp),
// Optimism
IsOptimismBedrock: c.IsOptimismBedrock(num),
IsOptimismRegolith: c.IsOptimismRegolith(timestamp),
Expand Down

0 comments on commit d120ff2

Please sign in to comment.