From 11d11ec733f334bcddaa8a2bc078152e4dbf0963 Mon Sep 17 00:00:00 2001 From: Jorge Silva Date: Wed, 25 Oct 2023 12:07:13 +0100 Subject: [PATCH 1/5] fix: evm deposits from L1, receipt successful, add agentID to the txData --- packages/vm/core/accounts/impl.go | 7 +++--- packages/vm/core/evm/evmimpl/impl.go | 10 ++++++-- packages/vm/core/evm/evmnames/evmnames.go | 2 ++ packages/vm/core/evm/evmtest/evm_test.go | 29 ++++++++++++++++++++++- packages/vm/core/evm/interface.go | 2 ++ 5 files changed, 44 insertions(+), 6 deletions(-) diff --git a/packages/vm/core/accounts/impl.go b/packages/vm/core/accounts/impl.go index c51423b47d..52fbde2ebe 100644 --- a/packages/vm/core/accounts/impl.go +++ b/packages/vm/core/accounts/impl.go @@ -69,10 +69,10 @@ func deposit(ctx isc.Sandbox) dict.Dict { // Params: // - ParamAgentID. AgentID. Required func transferAllowanceTo(ctx isc.Sandbox) dict.Dict { - ctx.Log().Debugf("accounts.transferAllowanceTo.begin -- %s", ctx.AllowanceAvailable()) targetAccount := ctx.Params().MustGetAgentID(ParamAgentID) allowance := ctx.AllowanceAvailable().Clone() ctx.TransferAllowedFunds(targetAccount) + if targetAccount.Kind() == isc.AgentIDKindEthereumAddress { evmAcc := targetAccount.(*isc.EthereumAddressAgentID).EthAddress().Bytes() // issue an "custom" etherum tx so the funds appear on the explorer @@ -80,8 +80,9 @@ func transferAllowanceTo(ctx isc.Sandbox) dict.Dict { evm.Contract.Hname(), evm.FuncNewL1Deposit.Hname(), dict.Dict{ - evm.FieldAddress: evmAcc, - evm.FieldAssets: allowance.Bytes(), + evm.FieldAddress: evmAcc, + evm.FieldAssets: allowance.Bytes(), + evm.FieldAgentIDDepositOriginator: ctx.Caller().Bytes(), }, nil, ) diff --git a/packages/vm/core/evm/evmimpl/impl.go b/packages/vm/core/evm/evmimpl/impl.go index b45e0333ff..6af6a32f47 100644 --- a/packages/vm/core/evm/evmimpl/impl.go +++ b/packages/vm/core/evm/evmimpl/impl.go @@ -442,6 +442,7 @@ func newL1Deposit(ctx isc.Sandbox) dict.Dict { // can only be called from the accounts contract ctx.RequireCaller(isc.NewContractAgentID(ctx.ChainID(), accounts.Contract.Hname())) params := ctx.Params() + agentIDBytes := params.MustGetBytes(evm.FieldAgentIDDepositOriginator) addr := common.BytesToAddress(params.MustGetBytes(evm.FieldAddress)) assets, err := isc.AssetsFromBytes(params.MustGetBytes(evm.FieldAssets)) ctx.RequireNoError(err, "unable to parse assets from params") @@ -449,14 +450,19 @@ func newL1Deposit(ctx isc.Sandbox) dict.Dict { // create a fake tx so that the deposit is visible by the EVM value := util.BaseTokensDecimalsToEthereumDecimals(assets.BaseTokens, newEmulatorContext(ctx).BaseTokensDecimals()) nonce := uint64(0) - tx := types.NewTransaction(nonce, addr, value, 0, util.Big0, assets.Bytes()) + // encode the txdata as + + txData := []byte{} + txData = append(txData, agentIDBytes...) + txData = append(txData, assets.Bytes()...) + tx := types.NewTransaction(nonce, addr, value, 0, util.Big0, txData) // create a fake receipt receipt := &types.Receipt{ Type: types.LegacyTxType, CumulativeGasUsed: createBlockchainDB(ctx.State(), ctx.ChainInfo()).GetPendingCumulativeGasUsed(), - GasUsed: 0, + GasUsed: 0, // TODO write gas used? Logs: make([]*types.Log, 0), + Status: types.ReceiptStatusSuccessful, } receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) diff --git a/packages/vm/core/evm/evmnames/evmnames.go b/packages/vm/core/evm/evmnames/evmnames.go index 4ccf601c1d..bcbd606ffd 100644 --- a/packages/vm/core/evm/evmnames/evmnames.go +++ b/packages/vm/core/evm/evmnames/evmnames.go @@ -42,4 +42,6 @@ const ( FieldNFTCollectionID = "C" FieldFoundryTokenScheme = "T" FieldTargetAddress = "A" + + FieldAgentIDDepositOriginator = "l" ) diff --git a/packages/vm/core/evm/evmtest/evm_test.go b/packages/vm/core/evm/evmtest/evm_test.go index dd51437fd1..580364e79c 100644 --- a/packages/vm/core/evm/evmtest/evm_test.go +++ b/packages/vm/core/evm/evmtest/evm_test.go @@ -7,6 +7,7 @@ import ( "bytes" "encoding/json" "fmt" + "io" "math" "math/big" "strings" @@ -40,6 +41,7 @@ import ( "github.com/iotaledger/wasp/packages/testutil/testdbhash" "github.com/iotaledger/wasp/packages/testutil/testmisc" "github.com/iotaledger/wasp/packages/util" + "github.com/iotaledger/wasp/packages/util/rwutil" "github.com/iotaledger/wasp/packages/vm" "github.com/iotaledger/wasp/packages/vm/core/accounts" "github.com/iotaledger/wasp/packages/vm/core/evm/iscmagic" @@ -2045,7 +2047,16 @@ func TestEmitEventAndRevert(t *testing.T) { func TestL1DepositEVM(t *testing.T) { env := InitEVM(t) // ensure that after a deposit to an EVM account, there is a tx/receipt for it to be auditable on the EVM side - _, ethAddr := env.Chain.NewEthereumAccountWithL2Funds() + wallet, l1Addr := env.solo.NewKeyPairWithFunds() + _, ethAddr := solo.NewEthereumAccount() + amount := 1 * isc.Million + err := env.Chain.TransferAllowanceTo( + isc.NewAssetsBaseTokens(amount), + isc.NewEthereumAddressAgentID(env.Chain.ID(), ethAddr), + wallet, + ) + require.NoError(t, err) + bal, err := env.Chain.EVM().Balance(ethAddr, nil) require.NoError(t, err) @@ -2058,6 +2069,22 @@ func TestL1DepositEVM(t *testing.T) { require.True(t, ethAddr == *tx.To()) require.Zero(t, tx.Value().Cmp(bal)) + // assert txData has the expected information ( + assets) + buf := (bytes.NewReader(tx.Data())) + rr := rwutil.NewReader(buf) + a := isc.AgentIDFromReader(rr) + require.True(t, a.Equals(isc.NewAddressAgentID(l1Addr))) + var assets isc.Assets + assets.Read(buf) + n, err := buf.Read([]byte{}) + require.Zero(t, n) + require.ErrorIs(t, err, io.EOF) + + require.EqualValues(t, + util.EthereumDecimalsToBaseTokenDecimals(bal, parameters.L1().BaseToken.Decimals), + assets.BaseTokens) + rec := env.Chain.EVM().TransactionReceipt(tx.Hash()) require.NotNil(t, rec) + require.Equal(t, types.ReceiptStatusSuccessful, rec.Status) } diff --git a/packages/vm/core/evm/interface.go b/packages/vm/core/evm/interface.go index 69f9cee331..cb3a48da6c 100644 --- a/packages/vm/core/evm/interface.go +++ b/packages/vm/core/evm/interface.go @@ -55,6 +55,8 @@ const ( FieldNFTCollectionID = evmnames.FieldNFTCollectionID // NFTID FieldFoundryTokenScheme = evmnames.FieldFoundryTokenScheme FieldTargetAddress = evmnames.FieldTargetAddress + + FieldAgentIDDepositOriginator = evmnames.FieldAgentIDDepositOriginator ) const ( From 78be03a1168dbac6548a73bb715f2f241aedc0d0 Mon Sep 17 00:00:00 2001 From: Jorge Silva Date: Wed, 25 Oct 2023 12:40:20 +0100 Subject: [PATCH 2/5] fix: evm deposits from L1, set gas usage in receipt --- packages/vm/core/evm/evmimpl/impl.go | 11 ++++++----- packages/vm/core/evm/evmtest/evm_test.go | 10 +++++++--- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/packages/vm/core/evm/evmimpl/impl.go b/packages/vm/core/evm/evmimpl/impl.go index 6af6a32f47..b52c39c0c4 100644 --- a/packages/vm/core/evm/evmimpl/impl.go +++ b/packages/vm/core/evm/evmimpl/impl.go @@ -457,16 +457,17 @@ func newL1Deposit(ctx isc.Sandbox) dict.Dict { tx := types.NewTransaction(nonce, addr, value, 0, util.Big0, txData) // create a fake receipt + chainInfo := ctx.ChainInfo() receipt := &types.Receipt{ - Type: types.LegacyTxType, - CumulativeGasUsed: createBlockchainDB(ctx.State(), ctx.ChainInfo()).GetPendingCumulativeGasUsed(), - GasUsed: 0, // TODO write gas used? - Logs: make([]*types.Log, 0), - Status: types.ReceiptStatusSuccessful, + Type: types.LegacyTxType, + Logs: make([]*types.Log, 0), + Status: types.ReceiptStatusSuccessful, } receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) ctx.Privileged().OnWriteReceipt(func(evmPartition kv.KVStore) { + receipt.GasUsed = gas.ISCGasBurnedToEVM(ctx.Gas().Burned(), &chainInfo.GasFeePolicy.EVMGasRatio) + receipt.CumulativeGasUsed = createBlockchainDB(ctx.State(), chainInfo).GetPendingCumulativeGasUsed() + receipt.GasUsed createBlockchainDB(evmPartition, ctx.ChainInfo()).AddTransaction(tx, receipt) }) diff --git a/packages/vm/core/evm/evmtest/evm_test.go b/packages/vm/core/evm/evmtest/evm_test.go index 580364e79c..6ac1c15f5d 100644 --- a/packages/vm/core/evm/evmtest/evm_test.go +++ b/packages/vm/core/evm/evmtest/evm_test.go @@ -2084,7 +2084,11 @@ func TestL1DepositEVM(t *testing.T) { util.EthereumDecimalsToBaseTokenDecimals(bal, parameters.L1().BaseToken.Decimals), assets.BaseTokens) - rec := env.Chain.EVM().TransactionReceipt(tx.Hash()) - require.NotNil(t, rec) - require.Equal(t, types.ReceiptStatusSuccessful, rec.Status) + evmRec := env.Chain.EVM().TransactionReceipt(tx.Hash()) + require.NotNil(t, evmRec) + require.Equal(t, types.ReceiptStatusSuccessful, evmRec.Status) + iscRec := env.Chain.LastReceipt() + feePolicy := env.Chain.GetGasFeePolicy() + expectedGas := gas.ISCGasBudgetToEVM(iscRec.GasBurned, &feePolicy.EVMGasRatio) + require.EqualValues(t, expectedGas, evmRec.GasUsed) } From 11aa6d067333e9a7bc717930a57ed2fcd7e94d77 Mon Sep 17 00:00:00 2001 From: Jorge Silva Date: Wed, 25 Oct 2023 12:56:20 +0100 Subject: [PATCH 3/5] update test dbhash --- packages/testutil/testdbhash/TestStorageContract.hex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/testutil/testdbhash/TestStorageContract.hex b/packages/testutil/testdbhash/TestStorageContract.hex index b72ef8ed3b..bdb1f9f770 100644 --- a/packages/testutil/testdbhash/TestStorageContract.hex +++ b/packages/testutil/testdbhash/TestStorageContract.hex @@ -1 +1 @@ -0xf7c89a451c83d8d59b4ec7d6904a8421166463bd4b28feeffe3362313b3ee6d4 +0xa487f3801a48076cae3d99370a55398a401d2716893841e543165ccce74c8b2b From acda2a75da06382c574fe0acb2c03a54b4f3a577 Mon Sep 17 00:00:00 2001 From: Jorge Silva Date: Wed, 25 Oct 2023 15:55:36 +0100 Subject: [PATCH 4/5] added a guard so contracts callers cannot create custom EVM receipts for deposits --- packages/vm/core/accounts/impl.go | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/packages/vm/core/accounts/impl.go b/packages/vm/core/accounts/impl.go index 52fbde2ebe..85ae6b7f41 100644 --- a/packages/vm/core/accounts/impl.go +++ b/packages/vm/core/accounts/impl.go @@ -73,20 +73,23 @@ func transferAllowanceTo(ctx isc.Sandbox) dict.Dict { allowance := ctx.AllowanceAvailable().Clone() ctx.TransferAllowedFunds(targetAccount) - if targetAccount.Kind() == isc.AgentIDKindEthereumAddress { - evmAcc := targetAccount.(*isc.EthereumAddressAgentID).EthAddress().Bytes() - // issue an "custom" etherum tx so the funds appear on the explorer - ctx.Call( - evm.Contract.Hname(), - evm.FuncNewL1Deposit.Hname(), - dict.Dict{ - evm.FieldAddress: evmAcc, - evm.FieldAssets: allowance.Bytes(), - evm.FieldAgentIDDepositOriginator: ctx.Caller().Bytes(), - }, - nil, - ) + if targetAccount.Kind() != isc.AgentIDKindEthereumAddress { + return nil // done + } + if !ctx.Caller().Equals(ctx.Request().SenderAccount()) { + return nil // only issue "custom EVM tx" when this function is called directly by the request sender } + // issue a "custom EVM tx" so the funds appear on the explorer + ctx.Call( + evm.Contract.Hname(), + evm.FuncNewL1Deposit.Hname(), + dict.Dict{ + evm.FieldAddress: targetAccount.(*isc.EthereumAddressAgentID).EthAddress().Bytes(), + evm.FieldAssets: allowance.Bytes(), + evm.FieldAgentIDDepositOriginator: ctx.Caller().Bytes(), + }, + nil, + ) ctx.Log().Debugf("accounts.transferAllowanceTo.success: target: %s\n%s", targetAccount, ctx.AllowanceAvailable()) return nil } From 06663267f4b95a6036891688cae7332fe28c21d5 Mon Sep 17 00:00:00 2001 From: Jorge Silva Date: Wed, 25 Oct 2023 16:01:25 +0100 Subject: [PATCH 5/5] refactor: modify CoreCallbackFunc to pass gasBurned as a parameter --- packages/isc/sandbox_interface.go | 2 +- packages/vm/core/evm/evmimpl/impl.go | 8 ++++---- packages/vm/vmimpl/internal.go | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/isc/sandbox_interface.go b/packages/isc/sandbox_interface.go index b939155e00..2334737811 100644 --- a/packages/isc/sandbox_interface.go +++ b/packages/isc/sandbox_interface.go @@ -148,7 +148,7 @@ type Privileged interface { SendOnBehalfOf(caller ContractIdentity, metadata RequestParameters) } -type CoreCallbackFunc func(contractPartition kv.KVStore) +type CoreCallbackFunc func(contractPartition kv.KVStore, gasBurned uint64) // RequestParameters represents parameters of the on-ledger request. The output is build from these parameters type RequestParameters struct { diff --git a/packages/vm/core/evm/evmimpl/impl.go b/packages/vm/core/evm/evmimpl/impl.go index b52c39c0c4..76d18cb095 100644 --- a/packages/vm/core/evm/evmimpl/impl.go +++ b/packages/vm/core/evm/evmimpl/impl.go @@ -154,7 +154,7 @@ func applyTransaction(ctx isc.Sandbox) dict.Dict { // make sure we always store the EVM tx/receipt in the BlockchainDB, even // if the ISC request is reverted - ctx.Privileged().OnWriteReceipt(func(evmPartition kv.KVStore) { + ctx.Privileged().OnWriteReceipt(func(evmPartition kv.KVStore, _ uint64) { saveExecutedTx(evmPartition, chainInfo, tx, receipt) }) // revert the changes in the state / txbuilder in case of error @@ -465,9 +465,9 @@ func newL1Deposit(ctx isc.Sandbox) dict.Dict { } receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) - ctx.Privileged().OnWriteReceipt(func(evmPartition kv.KVStore) { - receipt.GasUsed = gas.ISCGasBurnedToEVM(ctx.Gas().Burned(), &chainInfo.GasFeePolicy.EVMGasRatio) - receipt.CumulativeGasUsed = createBlockchainDB(ctx.State(), chainInfo).GetPendingCumulativeGasUsed() + receipt.GasUsed + ctx.Privileged().OnWriteReceipt(func(evmPartition kv.KVStore, gasBurned uint64) { + receipt.GasUsed = gas.ISCGasBurnedToEVM(gasBurned, &chainInfo.GasFeePolicy.EVMGasRatio) + receipt.CumulativeGasUsed = createBlockchainDB(evmPartition, chainInfo).GetPendingCumulativeGasUsed() + receipt.GasUsed createBlockchainDB(evmPartition, ctx.ChainInfo()).AddTransaction(tx, receipt) }) diff --git a/packages/vm/vmimpl/internal.go b/packages/vm/vmimpl/internal.go index 5648f6ddfd..9c30b3520d 100644 --- a/packages/vm/vmimpl/internal.go +++ b/packages/vm/vmimpl/internal.go @@ -170,7 +170,7 @@ func (reqctx *requestContext) writeReceiptToBlockLog(vmError *isc.VMError) *bloc } for _, f := range reqctx.onWriteReceipt { reqctx.callCore(corecontracts.All[f.contract], func(s kv.KVStore) { - f.callback(s) + f.callback(s, receipt.GasBurned) }) } return receipt