diff --git a/data/transactions/asset_test.go b/data/transactions/asset_test.go new file mode 100644 index 0000000000..b4e1cbf53c --- /dev/null +++ b/data/transactions/asset_test.go @@ -0,0 +1,198 @@ +// Copyright (C) 2019-2025 Algorand, Inc. +// This file is part of go-algorand +// +// go-algorand is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// go-algorand is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with go-algorand. If not, see . + +package transactions + +import ( + "fmt" + "strings" + "testing" + + "github.com/algorand/go-algorand/config" + "github.com/algorand/go-algorand/data/basics" + "github.com/algorand/go-algorand/protocol" + "github.com/algorand/go-algorand/test/partitiontest" + "github.com/stretchr/testify/require" +) + +func TestAxferWellFormedErrors(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + cases := []struct { + axfer AssetTransferTxnFields + expectedError string + }{ + { + axfer: AssetTransferTxnFields{ + XferAsset: basics.AssetIndex(0), + AssetAmount: 0, + AssetReceiver: basics.Address{}, + }, + }, + { + axfer: AssetTransferTxnFields{ + XferAsset: basics.AssetIndex(0), + AssetAmount: 1, + AssetReceiver: basics.Address{0x01}, + }, + expectedError: "asset ID cannot be zero", + }, + { + axfer: AssetTransferTxnFields{ + XferAsset: basics.AssetIndex(1), + AssetAmount: 0, + AssetSender: basics.Address{0x01}, + AssetCloseTo: basics.Address{0x02}, + }, + expectedError: "cannot close asset by clawback", + }, + } + + for i, ax := range cases { + name := fmt.Sprintf("axfer_i=%d", i) + if ax.expectedError != "" { + name = ax.expectedError + } + t.Run(name, func(t *testing.T) { + err := ax.axfer.wellFormed() + if ax.expectedError != "" { + require.ErrorContains(t, err, ax.expectedError) + } else { + require.NoError(t, err) + } + }) + } +} + +func TestAcfgWellFormedErrors(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + cv18 := protocol.ConsensusV18 + cv20 := protocol.ConsensusV20 + cv28 := protocol.ConsensusV28 + + cases := []struct { + acfg AssetConfigTxnFields + cv protocol.ConsensusVersion + expectedError string + }{ + { + acfg: AssetConfigTxnFields{ + AssetParams: basics.AssetParams{ + AssetName: strings.Repeat("A", 33), + }, + }, + cv: cv18, + expectedError: "transaction asset name too big: 33 > 32", + }, + { + acfg: AssetConfigTxnFields{ + AssetParams: basics.AssetParams{ + UnitName: strings.Repeat("B", 9), + }, + }, + expectedError: "transaction asset unit name too big: 9 > 8", + }, + { + acfg: AssetConfigTxnFields{ + AssetParams: basics.AssetParams{ + URL: strings.Repeat("C", 33), + }, + }, + cv: cv18, + expectedError: "transaction asset url too big: 33 > 32", + }, + { + acfg: AssetConfigTxnFields{ + AssetParams: basics.AssetParams{ + Decimals: 20, + }, + }, + cv: cv20, + expectedError: "transaction asset decimals is too high (max is 19)", + }, + { + acfg: AssetConfigTxnFields{ + AssetParams: basics.AssetParams{ + URL: strings.Repeat("D", 97), + }, + }, + cv: cv28, + expectedError: "transaction asset url too big: 97 > 96", + }, + } + + for i, ac := range cases { + name := fmt.Sprintf("acfg_i=%d", i) + if ac.expectedError != "" { + name = ac.expectedError + } + t.Run(name, func(t *testing.T) { + cv := ac.cv + if cv == "" { + cv = protocol.ConsensusFuture + } + err := ac.acfg.wellFormed(config.Consensus[cv]) + if ac.expectedError != "" { + require.ErrorContains(t, err, ac.expectedError) + } else { + require.NoError(t, err) + } + }) + } +} + +func TestAfrzWellFormedErrors(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + cases := []struct { + afrz AssetFreezeTxnFields + expectedError string + }{ + { + afrz: AssetFreezeTxnFields{ + FreezeAccount: basics.Address{0x01}, + FreezeAsset: 0, + }, + expectedError: "asset ID cannot be zero", + }, + { + afrz: AssetFreezeTxnFields{ + FreezeAccount: basics.Address{}, + FreezeAsset: 1, + }, + expectedError: "freeze account cannot be empty", + }, + } + + for i, ac := range cases { + name := fmt.Sprintf("afrz_i=%d", i) + if ac.expectedError != "" { + name = ac.expectedError + } + t.Run(name, func(t *testing.T) { + err := ac.afrz.wellFormed() + if ac.expectedError != "" { + require.ErrorContains(t, err, ac.expectedError) + } else { + require.NoError(t, err) + } + }) + } +} diff --git a/test/scripts/e2e_subs/asset-misc.sh b/test/scripts/e2e_subs/asset-misc.sh index 35198091e2..58d27fbb0d 100755 --- a/test/scripts/e2e_subs/asset-misc.sh +++ b/test/scripts/e2e_subs/asset-misc.sh @@ -106,7 +106,7 @@ ${gcmd} asset create --creator "${ACCOUNT}" --manager "${ACCOUNTB}" --reserve "$ EXPERROR='account asset info not found' RES=$(${gcmd} asset info --creator $ACCOUNT --unitname dma 2>&1 || true) if [[ $RES != *"${EXPERROR}"* ]]; then - date '+asset-misc FAIL asset info should fail unless reserve account was opted in %Y%m%d_%H%M%S' + date "+${scriptname} FAIL asset info should fail unless reserve account was opted in %Y%m%d_%H%M%S" exit 1 else echo ok @@ -187,4 +187,28 @@ else exit 1 fi +# Test Scenario - check transferring of the 0 asset +# case 1: send 0 units of 0 asset to self should fail +EXPERROR='asset 0 does not exist or has been deleted' +RES=$(${gcmd} asset send --from "${ACCOUNT}" --to "${ACCOUNT}" --assetid 0 --amount 0 2>&1 || true) +if [[ $RES != *"${EXPERROR}"* ]]; then + date "+${scriptname} FAIL asset transfer of 0 units of 0 asset should not be allowed to self in %Y%m%d_%H%M%S" + exit 1 +else + echo ok +fi + +# case 2: send 0 units of 0 asset to someone else should succeed +${gcmd} asset send --from "${ACCOUNT}" --to "${ACCOUNTB}" --assetid 0 --amount 0 + +# case 3: send 0 units of 0 asset to someone else including a close-to should fail +EXPERROR='asset 0 not present in account' +RES=$(${gcmd} asset send --from "${ACCOUNT}" --to "${ACCOUNTB}" --assetid 0 --amount 0 --close-to "${ACCOUNTB}" 2>&1 || true) +if [[ $RES != *"${EXPERROR}"* ]]; then + date "+${scriptname} FAIL asset transfer of 0 units of 0 asset including a close-to should not be allowed in %Y%m%d_%H%M%S" + exit 1 +else + echo ok +fi + date "+$scriptname OK %Y%m%d_%H%M%S"