diff options
-rw-r--r-- | plugin/evm/export_tx.go | 24 | ||||
-rw-r--r-- | plugin/evm/import_tx.go | 23 | ||||
-rw-r--r-- | plugin/evm/tx.go | 2 | ||||
-rw-r--r-- | plugin/evm/vm.go | 9 |
4 files changed, 48 insertions, 10 deletions
diff --git a/plugin/evm/export_tx.go b/plugin/evm/export_tx.go index 249b7cf..9af11e0 100644 --- a/plugin/evm/export_tx.go +++ b/plugin/evm/export_tx.go @@ -5,15 +5,17 @@ package evm import ( "fmt" + "math/big" + + "github.com/ava-labs/coreth/core/state" "github.com/ava-labs/gecko/database" "github.com/ava-labs/gecko/ids" "github.com/ava-labs/gecko/snow" "github.com/ava-labs/gecko/utils/crypto" + safemath "github.com/ava-labs/gecko/utils/math" "github.com/ava-labs/gecko/vms/components/avax" "github.com/ava-labs/gecko/vms/secp256k1fx" - - safemath "github.com/ava-labs/gecko/utils/math" ) // UnsignedExportTx is an unsigned ExportTx @@ -31,6 +33,8 @@ type UnsignedExportTx struct { Ins []EVMInput `serialize:"true" json:"inputs"` // Outputs that are exported to the chain ExportedOutputs []*avax.TransferableOutput `serialize:"true" json:"exportedOutputs"` + // EVM nonce + nonce uint64 } // InputUTXOs returns an empty set @@ -144,3 +148,19 @@ func (vm *VM) newExportTx( } return tx, utx.Verify(vm.ctx.XChainID, vm.ctx, vm.txFee, vm.ctx.AVAXAssetID) } + +func (tx *UnsignedExportTx) EVMStateTransfer(state *state.StateDB) error { + for _, from := range tx.Ins { + amount := new(big.Int).Mul( + new(big.Int).SetUint64(from.Amount), x2cRate) + if state.GetBalance(from.Address).Cmp(amount) < 0 { + return errInsufficientFunds + } + state.SubBalance(from.Address, amount) + if state.GetNonce(from.Address) != tx.nonce { + return errInvalidNonce + } + state.SetNonce(from.Address, tx.nonce+1) + } + return nil +} diff --git a/plugin/evm/import_tx.go b/plugin/evm/import_tx.go index 995e488..68e5fed 100644 --- a/plugin/evm/import_tx.go +++ b/plugin/evm/import_tx.go @@ -34,8 +34,8 @@ type UnsignedImportTx struct { ImportedInputs []*avax.TransferableInput `serialize:"true" json:"importedInputs"` // Outputs Outs []EVMOutput `serialize:"true" json:"outputs"` - // Memo field contains arbitrary bytes, up to maxMemoSize - Memo []byte `serialize:"true" json:"memo"` + // EVM nonce + nonce uint64 } // InputUTXOs returns the UTXOIDs of the imported funds @@ -193,6 +193,10 @@ func (vm *VM) newImportTx( }) } + nonce, err := vm.GetAcceptedNonce(to) + if err != nil { + return nil, err + } // Create the transaction utx := &UnsignedImportTx{ NetworkID: vm.ctx.NetworkID, @@ -200,6 +204,7 @@ func (vm *VM) newImportTx( Outs: outs, ImportedInputs: importedInputs, SourceChain: chainID, + nonce: nonce, } tx := &Tx{UnsignedTx: utx} if err := tx.Sign(vm.codec, signers); err != nil { @@ -208,11 +213,15 @@ func (vm *VM) newImportTx( return tx, utx.Verify(vm.ctx.XChainID, vm.ctx, vm.txFee, vm.ctx.AVAXAssetID) } -func (tx *UnsignedImportTx) EVMStateTransfer(state *state.StateDB) { +func (tx *UnsignedImportTx) EVMStateTransfer(state *state.StateDB) error { for _, to := range tx.Outs { - amount := new(big.Int).SetUint64(to.Amount) - state.AddBalance(to.Address, new(big.Int).Mul(amount, x2cRate)) - nonce := state.GetNonce(to.Address) - state.SetNonce(to.Address, nonce+1) + state.AddBalance(to.Address, + new(big.Int).Mul( + new(big.Int).SetUint64(to.Amount), x2cRate)) + if state.GetNonce(to.Address) != tx.nonce { + return errInvalidNonce + } + state.SetNonce(to.Address, tx.nonce+1) } + return nil } diff --git a/plugin/evm/tx.go b/plugin/evm/tx.go index dfd49f0..7e60c79 100644 --- a/plugin/evm/tx.go +++ b/plugin/evm/tx.go @@ -65,7 +65,7 @@ type UnsignedAtomicTx interface { // Accept this transaction with the additionally provided state transitions. Accept(ctx *snow.Context, batch database.Batch) error - EVMStateTransfer(state *state.StateDB) + EVMStateTransfer(state *state.StateDB) error } // Tx is a signed transaction diff --git a/plugin/evm/vm.go b/plugin/evm/vm.go index 53810ee..6155728 100644 --- a/plugin/evm/vm.go +++ b/plugin/evm/vm.go @@ -95,6 +95,7 @@ var ( errNoExportOutputs = errors.New("no export outputs") errOutputsNotSorted = errors.New("outputs not sorted") errOverflowExport = errors.New("overflow when computing export amount + txFee") + errInvalidNonce = errors.New("invalid nonce") ) func maxDuration(x, y time.Duration) time.Duration { @@ -721,3 +722,11 @@ func (vm *VM) GetSpendableCanonical(keys []*crypto.PrivateKeySECP256K1R, amount } return inputs, signers, nil } + +func (vm *VM) GetAcceptedNonce(address common.Address) (uint64, error) { + state, err := vm.chain.BlockState(vm.lastAccepted.ethBlock) + if err != nil { + return 0, err + } + return state.GetNonce(address), nil +} |