From 4fbda505d0d0ec8976226d4a527c971cdffb4191 Mon Sep 17 00:00:00 2001 From: Diego Essaya Date: Thu, 2 Mar 2023 15:44:10 -0300 Subject: [PATCH] fix(vm): prevent the sender from leaving not enough for gas in account --- packages/isc/sandbox_interface.go | 2 - packages/vm/core/evm/evmimpl/internal.go | 9 ----- .../vm/core/evm/evmimpl/iscmagic_sandbox.go | 8 ---- packages/vm/core/evm/evmtest/evm_test.go | 39 +++++++++++++++++++ packages/vm/vmcontext/sandbox.go | 14 ++++++- 5 files changed, 52 insertions(+), 20 deletions(-) diff --git a/packages/isc/sandbox_interface.go b/packages/isc/sandbox_interface.go index 89ccc6dff2..7ff95ec95c 100644 --- a/packages/isc/sandbox_interface.go +++ b/packages/isc/sandbox_interface.go @@ -133,8 +133,6 @@ type Privileged interface { SubscribeBlockContext(openFunc Hname, closeFunc Hname) SetBlockContext(bctx interface{}) BlockContext() interface{} - // the amount of tokens available to pay for the gas of the current request - TotalGasTokens() *Assets } // RequestParameters represents parameters of the on-ledger request. The output is build from these parameters diff --git a/packages/vm/core/evm/evmimpl/internal.go b/packages/vm/core/evm/evmimpl/internal.go index 1e8029fb59..177abd72ad 100644 --- a/packages/vm/core/evm/evmimpl/internal.go +++ b/packages/vm/core/evm/evmimpl/internal.go @@ -19,7 +19,6 @@ import ( "github.com/iotaledger/wasp/packages/kv/dict" "github.com/iotaledger/wasp/packages/parameters" "github.com/iotaledger/wasp/packages/util" - "github.com/iotaledger/wasp/packages/vm" "github.com/iotaledger/wasp/packages/vm/core/accounts" "github.com/iotaledger/wasp/packages/vm/core/evm" "github.com/iotaledger/wasp/packages/vm/core/evm/emulator" @@ -324,12 +323,4 @@ func (b *l2Balance) Sub(addr common.Address, amount *big.Int) { feePolicy := b.getFeePolicy() tokens := assetsForFeeFromEthereumDecimals(feePolicy, amount) b.ctx.Privileged().DebitFromAccount(isc.NewEthereumAddressAgentID(addr), tokens) - - // assert that remaining tokens in the sender's account are enough to pay for the gas budget - if !b.ctx.HasInAccount( - b.ctx.Request().SenderAccount(), - b.ctx.Privileged().TotalGasTokens(), - ) { - panic(vm.ErrNotEnoughTokensLeftForGas) - } } diff --git a/packages/vm/core/evm/evmimpl/iscmagic_sandbox.go b/packages/vm/core/evm/evmimpl/iscmagic_sandbox.go index 2b8f4dfa6b..c0f084bf54 100644 --- a/packages/vm/core/evm/evmimpl/iscmagic_sandbox.go +++ b/packages/vm/core/evm/evmimpl/iscmagic_sandbox.go @@ -10,7 +10,6 @@ import ( "github.com/iotaledger/wasp/packages/hashing" "github.com/iotaledger/wasp/packages/isc" - iscvm "github.com/iotaledger/wasp/packages/vm" "github.com/iotaledger/wasp/packages/vm/core/evm/iscmagic" ) @@ -76,13 +75,6 @@ func (h *magicContractHandler) Send( h.moveAssetsToCommonAccount(req.Assets) - // assert that remaining tokens in the sender's account are enough to pay for the gas budget - if !h.ctx.HasInAccount( - h.ctx.Request().SenderAccount(), - h.ctx.Privileged().TotalGasTokens(), - ) { - panic(iscvm.ErrNotEnoughTokensLeftForGas) - } h.ctx.Send(req) } diff --git a/packages/vm/core/evm/evmtest/evm_test.go b/packages/vm/core/evm/evmtest/evm_test.go index 8d11ee25e0..928c3a2d65 100644 --- a/packages/vm/core/evm/evmtest/evm_test.go +++ b/packages/vm/core/evm/evmtest/evm_test.go @@ -519,6 +519,45 @@ func TestSendBaseTokens(t *testing.T) { require.True(t, getAllowanceTo(iscTest.address).IsEmpty()) } +func TestCannotDepleteAccount(t *testing.T) { + env := initEVM(t) + + ethKey, ethAddress := env.soloChain.NewEthereumAccountWithL2Funds() + _, receiver := env.solo.NewKeyPair() + + iscTest := env.deployISCTestContract(ethKey) + + require.Zero(t, env.solo.L1BaseTokens(receiver)) + senderInitialBalance := env.soloChain.L2BaseTokens(isc.NewEthereumAddressAgentID(ethAddress)) + + // we eill attempt to transfer so much that we are left with no funds for gas + transfer := senderInitialBalance - 300 + + // allow ISCTest to take the tokens + _, err := env.ISCMagicSandbox(ethKey).callFn( + []ethCallOptions{{sender: ethKey}}, + "allow", + iscTest.address, + iscmagic.WrapISCAssets(isc.NewAssetsBaseTokens(transfer)), + ) + require.NoError(t, err) + + getAllowanceTo := func(target common.Address) *isc.Assets { + var ret struct{ Allowance iscmagic.ISCAssets } + env.ISCMagicSandbox(ethKey).callView("getAllowanceTo", []interface{}{target}, &ret) + return ret.Allowance.Unwrap() + } + + // stored allowance should be == transfer + require.Equal(t, transfer, getAllowanceTo(iscTest.address).BaseTokens) + + const allAllowed = uint64(0) + _, err = iscTest.callFn([]ethCallOptions{{ + gasLimit: 100_000, // skip estimate gas (which will fail) + }}, "sendBaseTokens", iscmagic.WrapL1Address(receiver), allAllowed) + require.ErrorContains(t, err, vm.ErrNotEnoughTokensLeftForGas.Error()) +} + func TestSendNFT(t *testing.T) { env := initEVM(t) ethKey, ethAddr := env.soloChain.NewEthereumAccountWithL2Funds() diff --git a/packages/vm/vmcontext/sandbox.go b/packages/vm/vmcontext/sandbox.go index 43e8534977..282b259441 100644 --- a/packages/vm/vmcontext/sandbox.go +++ b/packages/vm/vmcontext/sandbox.go @@ -157,17 +157,29 @@ func (s *contractSandbox) GasBurnEnable(enable bool) { func (s *contractSandbox) MustMoveBetweenAccounts(fromAgentID, toAgentID isc.AgentID, assets *isc.Assets) { s.Ctx.(*VMContext).mustMoveBetweenAccounts(fromAgentID, toAgentID, assets) + s.checkRemainingTokens(fromAgentID) } func (s *contractSandbox) DebitFromAccount(agentID isc.AgentID, tokens *isc.Assets) { s.Ctx.(*VMContext).debitFromAccount(agentID, tokens) + s.checkRemainingTokens(agentID) +} + +func (s *contractSandbox) checkRemainingTokens(debitedAccount isc.AgentID) { + // assert that remaining tokens in the sender's account are enough to pay for the gas budget + if debitedAccount.Equals(s.Request().SenderAccount()) && !s.HasInAccount( + debitedAccount, + s.totalGasTokens(), + ) { + panic(vm.ErrNotEnoughTokensLeftForGas) + } } func (s *contractSandbox) CreditToAccount(agentID isc.AgentID, tokens *isc.Assets) { s.Ctx.(*VMContext).creditToAccount(agentID, tokens) } -func (s *contractSandbox) TotalGasTokens() *isc.Assets { +func (s *contractSandbox) totalGasTokens() *isc.Assets { if s.Ctx.(*VMContext).task.EstimateGasMode { return isc.NewEmptyAssets() }