github.com/algorand/go-algorand-sdk@v1.24.0/future/transaction_test.go (about)

     1  package future
     2  
     3  import (
     4  	"encoding/base64"
     5  	"testing"
     6  
     7  	"github.com/algorand/go-algorand-sdk/crypto"
     8  	"github.com/algorand/go-algorand-sdk/encoding/msgpack"
     9  	"github.com/algorand/go-algorand-sdk/mnemonic"
    10  	"github.com/algorand/go-algorand-sdk/types"
    11  	"github.com/stretchr/testify/require"
    12  )
    13  
    14  func byteFromBase64(s string) []byte {
    15  	b, _ := base64.StdEncoding.DecodeString(s)
    16  	return b
    17  }
    18  
    19  func byte32ArrayFromBase64(s string) (out [32]byte) {
    20  	slice := byteFromBase64(s)
    21  	if len(slice) != 32 {
    22  		panic("wrong length: input slice not 32 bytes")
    23  	}
    24  	copy(out[:], slice)
    25  	return
    26  }
    27  
    28  func byte64ArrayFromBase64(s string) (out [64]byte) {
    29  	slice := byteFromBase64(s)
    30  	if len(slice) != 64 {
    31  		panic("wrong length: input slice not 64 bytes")
    32  	}
    33  	copy(out[:], slice)
    34  	return
    35  }
    36  
    37  func TestMakePaymentTxn(t *testing.T) {
    38  	const fromAddress = "47YPQTIGQEO7T4Y4RWDYWEKV6RTR2UNBQXBABEEGM72ESWDQNCQ52OPASU"
    39  	const toAddress = "PNWOET7LLOWMBMLE4KOCELCX6X3D3Q4H2Q4QJASYIEOF7YIPPQBG3YQ5YI"
    40  	const referenceTxID = "5FJDJD5LMZC3EHUYYJNH5I23U4X6H2KXABNDGPIL557ZMJ33GZHQ"
    41  	const mn = "advice pudding treat near rule blouse same whisper inner electric quit surface sunny dismiss leader blood seat clown cost exist hospital century reform able sponsor"
    42  	const golden = "gqNzaWfEQPhUAZ3xkDDcc8FvOVo6UinzmKBCqs0woYSfodlmBMfQvGbeUx3Srxy3dyJDzv7rLm26BRv9FnL2/AuT7NYfiAWjdHhui6NhbXTNA+ilY2xvc2XEIEDpNJKIJWTLzpxZpptnVCaJ6aHDoqnqW2Wm6KRCH/xXo2ZlZc0EmKJmds0wsqNnZW6sZGV2bmV0LXYzMy4womdoxCAmCyAJoJOohot5WHIvpeVG7eftF+TYXEx4r7BFJpDt0qJsds00mqRub3RlxAjqABVHQ2y/lqNyY3bEIHts4k/rW6zAsWTinCIsV/X2PcOH1DkEglhBHF/hD3wCo3NuZMQg5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKGkdHlwZaNwYXk="
    43  	gh := byteFromBase64("JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=")
    44  
    45  	params := types.SuggestedParams{
    46  		Fee:             4,
    47  		FirstRoundValid: 12466,
    48  		LastRoundValid:  13466,
    49  		GenesisID:       "devnet-v33.0",
    50  		GenesisHash:     gh,
    51  	}
    52  	txn, err := MakePaymentTxn(fromAddress, toAddress, 1000, byteFromBase64("6gAVR0Nsv5Y="), "IDUTJEUIEVSMXTU4LGTJWZ2UE2E6TIODUKU6UW3FU3UKIQQ77RLUBBBFLA", params)
    53  	require.NoError(t, err)
    54  
    55  	key, err := mnemonic.ToPrivateKey(mn)
    56  	require.NoError(t, err)
    57  
    58  	id, bytes, err := crypto.SignTransaction(key, txn)
    59  	require.NoError(t, err)
    60  
    61  	stxBytes := byteFromBase64(golden)
    62  	require.Equal(t, stxBytes, bytes)
    63  
    64  	require.Equal(t, referenceTxID, id)
    65  }
    66  
    67  // should fail on a lack of GenesisHash
    68  func TestMakePaymentTxn2(t *testing.T) {
    69  	const fromAddress = "47YPQTIGQEO7T4Y4RWDYWEKV6RTR2UNBQXBABEEGM72ESWDQNCQ52OPASU"
    70  	const toAddress = "PNWOET7LLOWMBMLE4KOCELCX6X3D3Q4H2Q4QJASYIEOF7YIPPQBG3YQ5YI"
    71  	params := types.SuggestedParams{
    72  		Fee:             4,
    73  		FirstRoundValid: 12466,
    74  		LastRoundValid:  13466,
    75  		GenesisID:       "devnet-v33.0",
    76  	}
    77  	_, err := MakePaymentTxn(fromAddress, toAddress, 1000, byteFromBase64("6gAVR0Nsv5Y="), "IDUTJEUIEVSMXTU4LGTJWZ2UE2E6TIODUKU6UW3FU3UKIQQ77RLUBBBFLA", params)
    78  	require.Error(t, err)
    79  }
    80  
    81  func TestMakePaymentTxnWithLease(t *testing.T) {
    82  	const fromAddress = "47YPQTIGQEO7T4Y4RWDYWEKV6RTR2UNBQXBABEEGM72ESWDQNCQ52OPASU"
    83  	const toAddress = "PNWOET7LLOWMBMLE4KOCELCX6X3D3Q4H2Q4QJASYIEOF7YIPPQBG3YQ5YI"
    84  	const referenceTxID = "7BG6COBZKF6I6W5XY72ZE4HXV6LLZ6ENSR6DASEGSTXYXR4XJOOQ"
    85  	const mn = "advice pudding treat near rule blouse same whisper inner electric quit surface sunny dismiss leader blood seat clown cost exist hospital century reform able sponsor"
    86  	const golden = "gqNzaWfEQOMmFSIKsZvpW0txwzhmbgQjxv6IyN7BbV5sZ2aNgFbVcrWUnqPpQQxfPhV/wdu9jzEPUU1jAujYtcNCxJ7ONgejdHhujKNhbXTNA+ilY2xvc2XEIEDpNJKIJWTLzpxZpptnVCaJ6aHDoqnqW2Wm6KRCH/xXo2ZlZc0FLKJmds0wsqNnZW6sZGV2bmV0LXYzMy4womdoxCAmCyAJoJOohot5WHIvpeVG7eftF+TYXEx4r7BFJpDt0qJsds00mqJseMQgAQIDBAECAwQBAgMEAQIDBAECAwQBAgMEAQIDBAECAwSkbm90ZcQI6gAVR0Nsv5ajcmN2xCB7bOJP61uswLFk4pwiLFf19j3Dh9Q5BIJYQRxf4Q98AqNzbmTEIOfw+E0GgR358xyNh4sRVfRnHVGhhcIAkIZn9ElYcGihpHR5cGWjcGF5"
    87  	gh := byteFromBase64("JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=")
    88  	params := types.SuggestedParams{
    89  		Fee:             4,
    90  		FirstRoundValid: 12466,
    91  		LastRoundValid:  13466,
    92  		GenesisID:       "devnet-v33.0",
    93  		GenesisHash:     gh,
    94  	}
    95  	lease := [32]byte{1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4}
    96  	txn, err := MakePaymentTxn(fromAddress, toAddress, 1000, byteFromBase64("6gAVR0Nsv5Y="), "IDUTJEUIEVSMXTU4LGTJWZ2UE2E6TIODUKU6UW3FU3UKIQQ77RLUBBBFLA", params)
    97  	require.NoError(t, err)
    98  	txn.AddLease(lease, 4)
    99  	require.NoError(t, err)
   100  
   101  	key, err := mnemonic.ToPrivateKey(mn)
   102  	require.NoError(t, err)
   103  
   104  	id, stxBytes, err := crypto.SignTransaction(key, txn)
   105  	require.NoError(t, err)
   106  
   107  	goldenBytes := byteFromBase64(golden)
   108  	require.Equal(t, goldenBytes, stxBytes)
   109  	require.Equal(t, referenceTxID, id)
   110  }
   111  
   112  func TestKeyRegTxn(t *testing.T) {
   113  	// preKeyRegTxn is an unsigned signed keyreg txn with zero Sender
   114  	const addr = "BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4"
   115  	a, err := types.DecodeAddress(addr)
   116  	require.NoError(t, err)
   117  	const addrSK = "awful drop leaf tennis indoor begin mandate discover uncle seven only coil atom any hospital uncover make any climb actor armed measure need above hundred"
   118  	expKeyRegTxn := types.Transaction{
   119  		Type: types.KeyRegistrationTx,
   120  		Header: types.Header{
   121  			Sender:      a,
   122  			Fee:         1000,
   123  			FirstValid:  322575,
   124  			LastValid:   323575,
   125  			GenesisHash: byte32ArrayFromBase64("SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI="),
   126  			GenesisID:   "",
   127  		},
   128  		KeyregTxnFields: types.KeyregTxnFields{
   129  			VotePK:          byte32ArrayFromBase64("Kv7QI7chi1y6axoy+t7wzAVpePqRq/rkjzWh/RMYyLo="),
   130  			SelectionPK:     byte32ArrayFromBase64("bPgrv4YogPcdaUAxrt1QysYZTVyRAuUMD4zQmCu9llc="),
   131  			VoteFirst:       10000,
   132  			VoteLast:        10111,
   133  			VoteKeyDilution: 11,
   134  		},
   135  	}
   136  	const signedGolden = "gqNzaWfEQEA8ANbrvTRxU9c8v6WERcEPw7D/HacRgg4vICa61vEof60Wwtx6KJKDyvBuvViFeacLlngPY6vYCVP0DktTwQ2jdHhui6NmZWXNA+iiZnbOAATsD6JnaMQgSGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiKibHbOAATv96ZzZWxrZXnEIGz4K7+GKID3HWlAMa7dUMrGGU1ckQLlDA+M0JgrvZZXo3NuZMQgCfvSdiwI+Gxa5r9t16epAd5mdddQ4H6MXHaYZH224f2kdHlwZaZrZXlyZWendm90ZWZzdM0nEKZ2b3Rla2QLp3ZvdGVrZXnEICr+0CO3IYtcumsaMvre8MwFaXj6kav65I81of0TGMi6p3ZvdGVsc3TNJ38="
   137  	// now, sign
   138  	private, err := mnemonic.ToPrivateKey(addrSK)
   139  	require.NoError(t, err)
   140  	txid, newStxBytes, err := crypto.SignTransaction(private, expKeyRegTxn)
   141  	require.NoError(t, err)
   142  	require.Equal(t, "MDRIUVH5AW4Z3GMOB67WP44LYLEVM2MP3ZEPKFHUB5J47A2J6TUQ", txid)
   143  	require.EqualValues(t, newStxBytes, byteFromBase64(signedGolden))
   144  }
   145  
   146  func TestMakeKeyRegTxn(t *testing.T) {
   147  	const addr = "BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4"
   148  	ghAsArray := byte32ArrayFromBase64("SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=")
   149  	params := types.SuggestedParams{
   150  		Fee:             10,
   151  		FirstRoundValid: 322575,
   152  		LastRoundValid:  323575,
   153  		GenesisHash:     ghAsArray[:],
   154  	}
   155  	tx, err := MakeKeyRegTxn(addr, []byte{45, 67}, params, "Kv7QI7chi1y6axoy+t7wzAVpePqRq/rkjzWh/RMYyLo=", "bPgrv4YogPcdaUAxrt1QysYZTVyRAuUMD4zQmCu9llc=", 10000, 10111, 11)
   156  	require.NoError(t, err)
   157  
   158  	a, err := types.DecodeAddress(addr)
   159  	require.NoError(t, err)
   160  	expKeyRegTxn := types.Transaction{
   161  		Type: types.KeyRegistrationTx,
   162  		Header: types.Header{
   163  			Sender:      a,
   164  			Fee:         3060,
   165  			FirstValid:  322575,
   166  			LastValid:   323575,
   167  			Note:        []byte{45, 67},
   168  			GenesisHash: byte32ArrayFromBase64("SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI="),
   169  			GenesisID:   "",
   170  		},
   171  		KeyregTxnFields: types.KeyregTxnFields{
   172  			VotePK:          byte32ArrayFromBase64("Kv7QI7chi1y6axoy+t7wzAVpePqRq/rkjzWh/RMYyLo="),
   173  			SelectionPK:     byte32ArrayFromBase64("bPgrv4YogPcdaUAxrt1QysYZTVyRAuUMD4zQmCu9llc="),
   174  			VoteFirst:       10000,
   175  			VoteLast:        10111,
   176  			VoteKeyDilution: 11,
   177  		},
   178  	}
   179  	require.Equal(t, expKeyRegTxn, tx)
   180  }
   181  
   182  func TestMakeKeyRegTxnWithStateProofKey(t *testing.T) {
   183  	const addr = "BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4"
   184  	ghAsArray := byte32ArrayFromBase64("SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=")
   185  	params := types.SuggestedParams{
   186  		Fee:             10,
   187  		FirstRoundValid: 322575,
   188  		LastRoundValid:  323575,
   189  		GenesisHash:     ghAsArray[:],
   190  	}
   191  	// nonparticipation
   192  	tx, err := MakeKeyRegTxnWithStateProofKey(addr, []byte{45, 67}, params, "", "", "", 0, 0, 0, true)
   193  	require.NoError(t, err)
   194  	a, err := types.DecodeAddress(addr)
   195  	require.NoError(t, err)
   196  	expKeyRegTxn := types.Transaction{
   197  		Type: types.KeyRegistrationTx,
   198  		Header: types.Header{
   199  			Sender:      a,
   200  			Fee:         2020,
   201  			FirstValid:  322575,
   202  			LastValid:   323575,
   203  			Note:        []byte{45, 67},
   204  			GenesisHash: byte32ArrayFromBase64("SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI="),
   205  			GenesisID:   "",
   206  		},
   207  		KeyregTxnFields: types.KeyregTxnFields{
   208  			Nonparticipation: true,
   209  		},
   210  	}
   211  	require.Equal(t, expKeyRegTxn, tx)
   212  
   213  	// online
   214  	stateProof := "mYR0GVEObMTSNdsKM6RwYywHYPqVDqg3E4JFzxZOreH9NU8B+tKzUanyY8AQ144hETgSMX7fXWwjBdHz6AWk9w=="
   215  	tx, err = MakeKeyRegTxnWithStateProofKey(addr, []byte{45, 67}, params, "Kv7QI7chi1y6axoy+t7wzAVpePqRq/rkjzWh/RMYyLo=", "bPgrv4YogPcdaUAxrt1QysYZTVyRAuUMD4zQmCu9llc=", stateProof, 10000, 10111, 11, false)
   216  	require.NoError(t, err)
   217  
   218  	a, err = types.DecodeAddress(addr)
   219  	require.NoError(t, err)
   220  	expKeyRegTxn = types.Transaction{
   221  		Type: types.KeyRegistrationTx,
   222  		Header: types.Header{
   223  			Sender:      a,
   224  			Fee:         3800,
   225  			FirstValid:  322575,
   226  			LastValid:   323575,
   227  			Note:        []byte{45, 67},
   228  			GenesisHash: byte32ArrayFromBase64("SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI="),
   229  			GenesisID:   "",
   230  		},
   231  		KeyregTxnFields: types.KeyregTxnFields{
   232  			VotePK:           byte32ArrayFromBase64("Kv7QI7chi1y6axoy+t7wzAVpePqRq/rkjzWh/RMYyLo="),
   233  			SelectionPK:      byte32ArrayFromBase64("bPgrv4YogPcdaUAxrt1QysYZTVyRAuUMD4zQmCu9llc="),
   234  			VoteFirst:        10000,
   235  			VoteLast:         10111,
   236  			VoteKeyDilution:  11,
   237  			Nonparticipation: false,
   238  			StateProofPK:     byte64ArrayFromBase64(stateProof),
   239  		},
   240  	}
   241  	require.Equal(t, expKeyRegTxn, tx)
   242  }
   243  
   244  func TestMakeAssetCreateTxn(t *testing.T) {
   245  	const addr = "BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4"
   246  	const defaultFrozen = false
   247  	const genesisHash = "SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI="
   248  	ghAsArray := byte32ArrayFromBase64(genesisHash)
   249  	const total = 100
   250  	const reserve = addr
   251  	const freeze = addr
   252  	const clawback = addr
   253  	const unitName = "tst"
   254  	const assetName = "testcoin"
   255  	const testURL = "website"
   256  	const metadataHash = "fACPO4nRgO55j1ndAK3W6Sgc4APkcyFh"
   257  	params := types.SuggestedParams{
   258  		Fee:             10,
   259  		FirstRoundValid: 322575,
   260  		LastRoundValid:  323575,
   261  		GenesisHash:     ghAsArray[:],
   262  	}
   263  	tx, err := MakeAssetCreateTxn(addr, nil, params, total, 0, defaultFrozen, addr, reserve, freeze, clawback, unitName, assetName, testURL, metadataHash)
   264  	require.NoError(t, err)
   265  
   266  	a, err := types.DecodeAddress(addr)
   267  	require.NoError(t, err)
   268  	expectedAssetCreationTxn := types.Transaction{
   269  		Type: types.AssetConfigTx,
   270  		Header: types.Header{
   271  			Sender:      a,
   272  			Fee:         4020,
   273  			FirstValid:  322575,
   274  			LastValid:   323575,
   275  			GenesisHash: byte32ArrayFromBase64(genesisHash),
   276  			GenesisID:   "",
   277  		},
   278  	}
   279  	expectedAssetCreationTxn.AssetParams = types.AssetParams{
   280  		Total:         total,
   281  		DefaultFrozen: defaultFrozen,
   282  		Manager:       a,
   283  		Reserve:       a,
   284  		Freeze:        a,
   285  		Clawback:      a,
   286  		UnitName:      unitName,
   287  		AssetName:     assetName,
   288  		URL:           testURL,
   289  	}
   290  	copy(expectedAssetCreationTxn.AssetParams.MetadataHash[:], []byte(metadataHash))
   291  	require.Equal(t, expectedAssetCreationTxn, tx)
   292  
   293  	const addrSK = "awful drop leaf tennis indoor begin mandate discover uncle seven only coil atom any hospital uncover make any climb actor armed measure need above hundred"
   294  	private, err := mnemonic.ToPrivateKey(addrSK)
   295  	require.NoError(t, err)
   296  	_, newStxBytes, err := crypto.SignTransaction(private, tx)
   297  	require.NoError(t, err)
   298  	signedGolden := "gqNzaWfEQEDd1OMRoQI/rzNlU4iiF50XQXmup3k5czI9hEsNqHT7K4KsfmA/0DUVkbzOwtJdRsHS8trm3Arjpy9r7AXlbAujdHhuh6RhcGFyiaJhbcQgZkFDUE80blJnTzU1ajFuZEFLM1c2U2djNEFQa2N5RmiiYW6odGVzdGNvaW6iYXWnd2Vic2l0ZaFjxCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aFmxCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aFtxCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aFyxCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aF0ZKJ1bqN0c3SjZmVlzQ+0omZ2zgAE7A+iZ2jEIEhjtRiks8hOyBDyLU8QgcsPcfBZp6wg3sYvf3DlCToiomx2zgAE7/ejc25kxCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aR0eXBlpGFjZmc="
   299  	require.EqualValues(t, newStxBytes, byteFromBase64(signedGolden))
   300  }
   301  
   302  func TestMakeAssetCreateTxnWithDecimals(t *testing.T) {
   303  	const addr = "BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4"
   304  	const defaultFrozen = false
   305  	const genesisHash = "SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI="
   306  	ghAsArray := byte32ArrayFromBase64(genesisHash)
   307  	const total = 100
   308  	const decimals = 1
   309  	const reserve = addr
   310  	const freeze = addr
   311  	const clawback = addr
   312  	const unitName = "tst"
   313  	const assetName = "testcoin"
   314  	const testURL = "website"
   315  	const metadataHash = "fACPO4nRgO55j1ndAK3W6Sgc4APkcyFh"
   316  	params := types.SuggestedParams{
   317  		Fee:             10,
   318  		FirstRoundValid: 322575,
   319  		LastRoundValid:  323575,
   320  		GenesisHash:     ghAsArray[:],
   321  	}
   322  	tx, err := MakeAssetCreateTxn(addr, nil, params, total, decimals, defaultFrozen, addr, reserve, freeze, clawback, unitName, assetName, testURL, metadataHash)
   323  	require.NoError(t, err)
   324  
   325  	a, err := types.DecodeAddress(addr)
   326  	require.NoError(t, err)
   327  	expectedAssetCreationTxn := types.Transaction{
   328  		Type: types.AssetConfigTx,
   329  		Header: types.Header{
   330  			Sender:      a,
   331  			Fee:         4060,
   332  			FirstValid:  322575,
   333  			LastValid:   323575,
   334  			GenesisHash: byte32ArrayFromBase64(genesisHash),
   335  			GenesisID:   "",
   336  		},
   337  	}
   338  	expectedAssetCreationTxn.AssetParams = types.AssetParams{
   339  		Total:         total,
   340  		Decimals:      decimals,
   341  		DefaultFrozen: defaultFrozen,
   342  		Manager:       a,
   343  		Reserve:       a,
   344  		Freeze:        a,
   345  		Clawback:      a,
   346  		UnitName:      unitName,
   347  		AssetName:     assetName,
   348  		URL:           testURL,
   349  	}
   350  	copy(expectedAssetCreationTxn.AssetParams.MetadataHash[:], []byte(metadataHash))
   351  	require.Equal(t, expectedAssetCreationTxn, tx)
   352  
   353  	const addrSK = "awful drop leaf tennis indoor begin mandate discover uncle seven only coil atom any hospital uncover make any climb actor armed measure need above hundred"
   354  	private, err := mnemonic.ToPrivateKey(addrSK)
   355  	require.NoError(t, err)
   356  	_, newStxBytes, err := crypto.SignTransaction(private, tx)
   357  	require.NoError(t, err)
   358  	signedGolden := "gqNzaWfEQCj5xLqNozR5ahB+LNBlTG+d0gl0vWBrGdAXj1ibsCkvAwOsXs5KHZK1YdLgkdJecQiWm4oiZ+pm5Yg0m3KFqgqjdHhuh6RhcGFyiqJhbcQgZkFDUE80blJnTzU1ajFuZEFLM1c2U2djNEFQa2N5RmiiYW6odGVzdGNvaW6iYXWnd2Vic2l0ZaFjxCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aJkYwGhZsQgCfvSdiwI+Gxa5r9t16epAd5mdddQ4H6MXHaYZH224f2hbcQgCfvSdiwI+Gxa5r9t16epAd5mdddQ4H6MXHaYZH224f2hcsQgCfvSdiwI+Gxa5r9t16epAd5mdddQ4H6MXHaYZH224f2hdGSidW6jdHN0o2ZlZc0P3KJmds4ABOwPomdoxCBIY7UYpLPITsgQ8i1PEIHLD3HwWaesIN7GL39w5Qk6IqJsds4ABO/3o3NuZMQgCfvSdiwI+Gxa5r9t16epAd5mdddQ4H6MXHaYZH224f2kdHlwZaRhY2Zn"
   359  	require.EqualValues(t, newStxBytes, byteFromBase64(signedGolden))
   360  }
   361  
   362  func TestMakeAssetConfigTxn(t *testing.T) {
   363  	const addr = "BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4"
   364  	const genesisHash = "SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI="
   365  	ghAsArray := byte32ArrayFromBase64(genesisHash)
   366  	const manager = addr
   367  	const reserve = addr
   368  	const freeze = addr
   369  	const clawback = addr
   370  	const assetIndex = 1234
   371  	params := types.SuggestedParams{
   372  		Fee:             10,
   373  		FirstRoundValid: 322575,
   374  		LastRoundValid:  323575,
   375  		GenesisHash:     ghAsArray[:],
   376  	}
   377  	tx, err := MakeAssetConfigTxn(addr, nil, params, assetIndex, manager, reserve, freeze, clawback, false)
   378  	require.NoError(t, err)
   379  
   380  	a, err := types.DecodeAddress(addr)
   381  	require.NoError(t, err)
   382  	expectedAssetConfigTxn := types.Transaction{
   383  		Type: types.AssetConfigTx,
   384  		Header: types.Header{
   385  			Sender:      a,
   386  			Fee:         3400,
   387  			FirstValid:  322575,
   388  			LastValid:   323575,
   389  			GenesisHash: byte32ArrayFromBase64(genesisHash),
   390  			GenesisID:   "",
   391  		},
   392  	}
   393  
   394  	expectedAssetConfigTxn.AssetParams = types.AssetParams{
   395  		Manager:  a,
   396  		Reserve:  a,
   397  		Freeze:   a,
   398  		Clawback: a,
   399  	}
   400  	expectedAssetConfigTxn.ConfigAsset = types.AssetIndex(assetIndex)
   401  	require.Equal(t, expectedAssetConfigTxn, tx)
   402  
   403  	const addrSK = "awful drop leaf tennis indoor begin mandate discover uncle seven only coil atom any hospital uncover make any climb actor armed measure need above hundred"
   404  	private, err := mnemonic.ToPrivateKey(addrSK)
   405  	require.NoError(t, err)
   406  	_, newStxBytes, err := crypto.SignTransaction(private, tx)
   407  	require.NoError(t, err)
   408  	signedGolden := "gqNzaWfEQBBkfw5n6UevuIMDo2lHyU4dS80JCCQ/vTRUcTx5m0ivX68zTKyuVRrHaTbxbRRc3YpJ4zeVEnC9Fiw3Wf4REwejdHhuiKRhcGFyhKFjxCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aFmxCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aFtxCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aFyxCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aRjYWlkzQTSo2ZlZc0NSKJmds4ABOwPomdoxCBIY7UYpLPITsgQ8i1PEIHLD3HwWaesIN7GL39w5Qk6IqJsds4ABO/3o3NuZMQgCfvSdiwI+Gxa5r9t16epAd5mdddQ4H6MXHaYZH224f2kdHlwZaRhY2Zn"
   409  	require.EqualValues(t, newStxBytes, byteFromBase64(signedGolden))
   410  }
   411  
   412  func TestMakeAssetConfigTxnStrictChecking(t *testing.T) {
   413  	const addr = "BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4"
   414  	const genesisHash = "SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI="
   415  	ghAsArray := byte32ArrayFromBase64(genesisHash)
   416  	const manager = addr
   417  	const reserve = addr
   418  	const freeze = ""
   419  	const clawback = addr
   420  	const assetIndex = 1234
   421  	params := types.SuggestedParams{
   422  		Fee:             10,
   423  		FirstRoundValid: 322575,
   424  		LastRoundValid:  323575,
   425  		GenesisHash:     ghAsArray[:],
   426  	}
   427  	_, err := MakeAssetConfigTxn(addr, nil, params, assetIndex, manager, reserve, freeze, clawback, true)
   428  	require.Error(t, err)
   429  }
   430  
   431  func TestMakeAssetDestroyTxn(t *testing.T) {
   432  	const addr = "BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4"
   433  	const genesisHash = "SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI="
   434  	ghAsArray := byte32ArrayFromBase64(genesisHash)
   435  	const creator = addr
   436  	const assetIndex = 1
   437  	const firstValidRound = 322575
   438  	const lastValidRound = 323575
   439  	params := types.SuggestedParams{
   440  		Fee:             10,
   441  		FirstRoundValid: firstValidRound,
   442  		LastRoundValid:  lastValidRound,
   443  		GenesisHash:     ghAsArray[:],
   444  	}
   445  	tx, err := MakeAssetDestroyTxn(creator, nil, params, assetIndex)
   446  	require.NoError(t, err)
   447  
   448  	a, err := types.DecodeAddress(creator)
   449  	require.NoError(t, err)
   450  
   451  	expectedAssetDestroyTxn := types.Transaction{
   452  		Type: types.AssetConfigTx,
   453  		Header: types.Header{
   454  			Sender:      a,
   455  			Fee:         1880,
   456  			FirstValid:  firstValidRound,
   457  			LastValid:   lastValidRound,
   458  			GenesisHash: byte32ArrayFromBase64(genesisHash),
   459  			GenesisID:   "",
   460  		},
   461  	}
   462  	expectedAssetDestroyTxn.AssetParams = types.AssetParams{}
   463  	expectedAssetDestroyTxn.ConfigAsset = types.AssetIndex(assetIndex)
   464  	require.Equal(t, expectedAssetDestroyTxn, tx)
   465  
   466  	const addrSK = "awful drop leaf tennis indoor begin mandate discover uncle seven only coil atom any hospital uncover make any climb actor armed measure need above hundred"
   467  	private, err := mnemonic.ToPrivateKey(addrSK)
   468  	require.NoError(t, err)
   469  	_, newStxBytes, err := crypto.SignTransaction(private, tx)
   470  	require.NoError(t, err)
   471  	signedGolden := "gqNzaWfEQBSP7HtzD/Lvn4aVvaNpeR4T93dQgo4LvywEwcZgDEoc/WVl3aKsZGcZkcRFoiWk8AidhfOZzZYutckkccB8RgGjdHhuh6RjYWlkAaNmZWXNB1iiZnbOAATsD6JnaMQgSGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiKibHbOAATv96NzbmTEIAn70nYsCPhsWua/bdenqQHeZnXXUOB+jFx2mGR9tuH9pHR5cGWkYWNmZw=="
   472  	require.EqualValues(t, newStxBytes, byteFromBase64(signedGolden))
   473  }
   474  
   475  func TestMakeAssetFreezeTxn(t *testing.T) {
   476  	const addr = "BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4"
   477  	const genesisHash = "SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI="
   478  	ghAsArray := byte32ArrayFromBase64(genesisHash)
   479  	const assetIndex = 1
   480  	const firstValidRound = 322575
   481  	const lastValidRound = 323576
   482  	const freezeSetting = true
   483  	const target = addr
   484  	params := types.SuggestedParams{
   485  		Fee:             10,
   486  		FirstRoundValid: firstValidRound,
   487  		LastRoundValid:  lastValidRound,
   488  		GenesisHash:     ghAsArray[:],
   489  	}
   490  	tx, err := MakeAssetFreezeTxn(addr, nil, params, assetIndex, target, freezeSetting)
   491  	require.NoError(t, err)
   492  
   493  	a, err := types.DecodeAddress(addr)
   494  	require.NoError(t, err)
   495  
   496  	expectedAssetFreezeTxn := types.Transaction{
   497  		Type: types.AssetFreezeTx,
   498  		Header: types.Header{
   499  			Sender:      a,
   500  			Fee:         2330,
   501  			FirstValid:  firstValidRound,
   502  			LastValid:   lastValidRound,
   503  			GenesisHash: byte32ArrayFromBase64(genesisHash),
   504  			GenesisID:   "",
   505  		},
   506  	}
   507  	expectedAssetFreezeTxn.FreezeAsset = types.AssetIndex(assetIndex)
   508  	expectedAssetFreezeTxn.AssetFrozen = freezeSetting
   509  	expectedAssetFreezeTxn.FreezeAccount = a
   510  	require.Equal(t, expectedAssetFreezeTxn, tx)
   511  
   512  	const addrSK = "awful drop leaf tennis indoor begin mandate discover uncle seven only coil atom any hospital uncover make any climb actor armed measure need above hundred"
   513  	private, err := mnemonic.ToPrivateKey(addrSK)
   514  	require.NoError(t, err)
   515  	_, newStxBytes, err := crypto.SignTransaction(private, tx)
   516  	require.NoError(t, err)
   517  	signedGolden := "gqNzaWfEQAhru5V2Xvr19s4pGnI0aslqwY4lA2skzpYtDTAN9DKSH5+qsfQQhm4oq+9VHVj7e1rQC49S28vQZmzDTVnYDQGjdHhuiaRhZnJ6w6RmYWRkxCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aRmYWlkAaNmZWXNCRqiZnbOAATsD6JnaMQgSGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiKibHbOAATv+KNzbmTEIAn70nYsCPhsWua/bdenqQHeZnXXUOB+jFx2mGR9tuH9pHR5cGWkYWZyeg=="
   518  	require.EqualValues(t, newStxBytes, byteFromBase64(signedGolden))
   519  }
   520  
   521  func TestMakeAssetTransferTxn(t *testing.T) {
   522  	const addrSK = "awful drop leaf tennis indoor begin mandate discover uncle seven only coil atom any hospital uncover make any climb actor armed measure need above hundred"
   523  	private, err := mnemonic.ToPrivateKey(addrSK)
   524  	require.NoError(t, err)
   525  
   526  	const addr = "BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4"
   527  	const genesisHash = "SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI="
   528  	ghAsArray := byte32ArrayFromBase64(genesisHash)
   529  	const sender, recipient, closeAssetsTo = addr, addr, addr
   530  	const assetIndex = 1
   531  	const firstValidRound = 322575
   532  	const lastValidRound = 323576
   533  	const amountToSend = 1
   534  	params := types.SuggestedParams{
   535  		Fee:             10,
   536  		FirstRoundValid: firstValidRound,
   537  		LastRoundValid:  lastValidRound,
   538  		GenesisHash:     ghAsArray[:],
   539  	}
   540  	tx, err := MakeAssetTransferTxn(sender, recipient, amountToSend, nil, params, closeAssetsTo, assetIndex)
   541  	require.NoError(t, err)
   542  
   543  	sendAddr, err := types.DecodeAddress(sender)
   544  	require.NoError(t, err)
   545  
   546  	expectedAssetTransferTxn := types.Transaction{
   547  		Type: types.AssetTransferTx,
   548  		Header: types.Header{
   549  			Sender:      sendAddr,
   550  			Fee:         2750,
   551  			FirstValid:  firstValidRound,
   552  			LastValid:   lastValidRound,
   553  			GenesisHash: byte32ArrayFromBase64(genesisHash),
   554  			GenesisID:   "",
   555  		},
   556  	}
   557  
   558  	expectedAssetID := types.AssetIndex(assetIndex)
   559  	expectedAssetTransferTxn.XferAsset = expectedAssetID
   560  
   561  	receiveAddr, err := types.DecodeAddress(recipient)
   562  	require.NoError(t, err)
   563  	expectedAssetTransferTxn.AssetReceiver = receiveAddr
   564  
   565  	closeAddr, err := types.DecodeAddress(closeAssetsTo)
   566  	require.NoError(t, err)
   567  	expectedAssetTransferTxn.AssetCloseTo = closeAddr
   568  
   569  	expectedAssetTransferTxn.AssetAmount = amountToSend
   570  
   571  	require.Equal(t, expectedAssetTransferTxn, tx)
   572  
   573  	// now compare tx against a golden
   574  	const signedGolden = "gqNzaWfEQNkEs3WdfFq6IQKJdF1n0/hbV9waLsvojy9pM1T4fvwfMNdjGQDy+LeesuQUfQVTneJD4VfMP7zKx4OUlItbrwSjdHhuiqRhYW10AaZhY2xvc2XEIAn70nYsCPhsWua/bdenqQHeZnXXUOB+jFx2mGR9tuH9pGFyY3bEIAn70nYsCPhsWua/bdenqQHeZnXXUOB+jFx2mGR9tuH9o2ZlZc0KvqJmds4ABOwPomdoxCBIY7UYpLPITsgQ8i1PEIHLD3HwWaesIN7GL39w5Qk6IqJsds4ABO/4o3NuZMQgCfvSdiwI+Gxa5r9t16epAd5mdddQ4H6MXHaYZH224f2kdHlwZaVheGZlcqR4YWlkAQ=="
   575  	_, newStxBytes, err := crypto.SignTransaction(private, tx)
   576  	require.NoError(t, err)
   577  	require.EqualValues(t, newStxBytes, byteFromBase64(signedGolden))
   578  }
   579  
   580  func TestMakeAssetAcceptanceTxn(t *testing.T) {
   581  	const sender = "BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4"
   582  	const genesisHash = "SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI="
   583  	ghAsArray := byte32ArrayFromBase64(genesisHash)
   584  	const assetIndex = 1
   585  	const firstValidRound = 322575
   586  	const lastValidRound = 323575
   587  	params := types.SuggestedParams{
   588  		Fee:             10,
   589  		FirstRoundValid: firstValidRound,
   590  		LastRoundValid:  lastValidRound,
   591  		GenesisHash:     ghAsArray[:],
   592  	}
   593  	tx, err := MakeAssetAcceptanceTxn(sender, nil, params, assetIndex)
   594  	require.NoError(t, err)
   595  
   596  	sendAddr, err := types.DecodeAddress(sender)
   597  	require.NoError(t, err)
   598  
   599  	expectedAssetAcceptanceTxn := types.Transaction{
   600  		Type: types.AssetTransferTx,
   601  		Header: types.Header{
   602  			Sender:      sendAddr,
   603  			Fee:         2280,
   604  			FirstValid:  firstValidRound,
   605  			LastValid:   lastValidRound,
   606  			GenesisHash: byte32ArrayFromBase64(genesisHash),
   607  			GenesisID:   "",
   608  		},
   609  	}
   610  
   611  	expectedAssetID := types.AssetIndex(assetIndex)
   612  	expectedAssetAcceptanceTxn.XferAsset = expectedAssetID
   613  	expectedAssetAcceptanceTxn.AssetReceiver = sendAddr
   614  	expectedAssetAcceptanceTxn.AssetAmount = 0
   615  
   616  	require.Equal(t, expectedAssetAcceptanceTxn, tx)
   617  
   618  	const addrSK = "awful drop leaf tennis indoor begin mandate discover uncle seven only coil atom any hospital uncover make any climb actor armed measure need above hundred"
   619  	private, err := mnemonic.ToPrivateKey(addrSK)
   620  	require.NoError(t, err)
   621  	_, newStxBytes, err := crypto.SignTransaction(private, tx)
   622  	require.NoError(t, err)
   623  	signedGolden := "gqNzaWfEQJ7q2rOT8Sb/wB0F87ld+1zMprxVlYqbUbe+oz0WM63FctIi+K9eYFSqT26XBZ4Rr3+VTJpBE+JLKs8nctl9hgijdHhuiKRhcmN2xCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aNmZWXNCOiiZnbOAATsD6JnaMQgSGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiKibHbOAATv96NzbmTEIAn70nYsCPhsWua/bdenqQHeZnXXUOB+jFx2mGR9tuH9pHR5cGWlYXhmZXKkeGFpZAE="
   624  	require.EqualValues(t, newStxBytes, byteFromBase64(signedGolden))
   625  }
   626  
   627  func TestMakeAssetRevocationTransaction(t *testing.T) {
   628  	const addr = "BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4"
   629  	const genesisHash = "SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI="
   630  	ghAsArray := byte32ArrayFromBase64(genesisHash)
   631  	const revoker, recipient, revoked = addr, addr, addr
   632  	const assetIndex = 1
   633  	const firstValidRound = 322575
   634  	const lastValidRound = 323575
   635  	const amountToSend = 1
   636  
   637  	params := types.SuggestedParams{
   638  		Fee:             10,
   639  		FirstRoundValid: firstValidRound,
   640  		LastRoundValid:  lastValidRound,
   641  		GenesisHash:     ghAsArray[:],
   642  	}
   643  	tx, err := MakeAssetRevocationTxn(revoker, revoked, amountToSend, recipient, nil, params, assetIndex)
   644  	require.NoError(t, err)
   645  
   646  	sendAddr, err := types.DecodeAddress(revoker)
   647  	require.NoError(t, err)
   648  
   649  	expectedAssetRevocationTxn := types.Transaction{
   650  		Type: types.AssetTransferTx,
   651  		Header: types.Header{
   652  			Sender:      sendAddr,
   653  			Fee:         2730,
   654  			FirstValid:  firstValidRound,
   655  			LastValid:   lastValidRound,
   656  			GenesisHash: byte32ArrayFromBase64(genesisHash),
   657  			GenesisID:   "",
   658  		},
   659  	}
   660  
   661  	expectedAssetID := types.AssetIndex(assetIndex)
   662  	expectedAssetRevocationTxn.XferAsset = expectedAssetID
   663  
   664  	receiveAddr, err := types.DecodeAddress(recipient)
   665  	require.NoError(t, err)
   666  	expectedAssetRevocationTxn.AssetReceiver = receiveAddr
   667  
   668  	expectedAssetRevocationTxn.AssetAmount = amountToSend
   669  
   670  	targetAddr, err := types.DecodeAddress(revoked)
   671  	require.NoError(t, err)
   672  	expectedAssetRevocationTxn.AssetSender = targetAddr
   673  
   674  	require.Equal(t, expectedAssetRevocationTxn, tx)
   675  
   676  	const addrSK = "awful drop leaf tennis indoor begin mandate discover uncle seven only coil atom any hospital uncover make any climb actor armed measure need above hundred"
   677  	private, err := mnemonic.ToPrivateKey(addrSK)
   678  	require.NoError(t, err)
   679  	_, newStxBytes, err := crypto.SignTransaction(private, tx)
   680  	require.NoError(t, err)
   681  	signedGolden := "gqNzaWfEQHsgfEAmEHUxLLLR9s+Y/yq5WeoGo/jAArCbany+7ZYwExMySzAhmV7M7S8+LBtJalB4EhzEUMKmt3kNKk6+vAWjdHhuiqRhYW10AaRhcmN2xCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aRhc25kxCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aNmZWXNCqqiZnbOAATsD6JnaMQgSGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiKibHbOAATv96NzbmTEIAn70nYsCPhsWua/bdenqQHeZnXXUOB+jFx2mGR9tuH9pHR5cGWlYXhmZXKkeGFpZAE="
   682  	require.EqualValues(t, newStxBytes, byteFromBase64(signedGolden))
   683  }
   684  
   685  func TestMakeApplicationCallTx(t *testing.T) {
   686  	const fee = 1000
   687  	const firstRound = 2063137
   688  	const genesisID = "devnet-v1.0"
   689  	genesisHash := byteFromBase64("sC3P7e2SdbqKJK0tbiCdK9tdSpbe6XeCGKdoNzmlj0E=")
   690  
   691  	params := types.SuggestedParams{
   692  		Fee:             fee,
   693  		FirstRoundValid: firstRound,
   694  		LastRoundValid:  firstRound + 1000,
   695  		GenesisHash:     genesisHash,
   696  		GenesisID:       genesisID,
   697  		FlatFee:         true,
   698  	}
   699  	note := byteFromBase64("8xMCTuLQ810=")
   700  	program := []byte{1, 32, 1, 1, 34}
   701  	args := make([][]byte, 2)
   702  	args[0] = []byte("123")
   703  	args[1] = []byte("456")
   704  	foreignApps := make([]uint64, 1)
   705  	foreignApps[0] = 10
   706  	foreignAssets := foreignApps
   707  	gSchema := types.StateSchema{NumUint: uint64(1), NumByteSlice: uint64(1)}
   708  	lSchema := types.StateSchema{NumUint: uint64(1), NumByteSlice: uint64(1)}
   709  	extraPages := uint32(2)
   710  	addr := make([]string, 1)
   711  	addr[0] = "47YPQTIGQEO7T4Y4RWDYWEKV6RTR2UNBQXBABEEGM72ESWDQNCQ52OPASU"
   712  	boxReferences := make([]types.AppBoxReference, 3)
   713  	boxReferences[0] = types.AppBoxReference{AppID: 2, Name: []byte("box_name")}
   714  	boxReferences[1] = types.AppBoxReference{AppID: 10, Name: []byte("box_name")}
   715  	boxReferences[2] = types.AppBoxReference{AppID: 10, Name: []byte("box_name2")}
   716  
   717  	tx, err := MakeApplicationCallTxWithBoxes(2, args, addr, foreignApps, foreignAssets, boxReferences, types.NoOpOC, program, program, gSchema, lSchema, extraPages, params, types.Address{}, note, types.Digest{}, [32]byte{}, types.Address{})
   718  	require.NoError(t, err)
   719  	require.EqualValues(t, 2, tx.ExtraProgramPages)
   720  
   721  	// verify that the correct app index was calculated
   722  	require.EqualValues(t, 0, tx.BoxReferences[0].ForeignAppIdx)
   723  	require.EqualValues(t, 1, tx.BoxReferences[1].ForeignAppIdx)
   724  	require.EqualValues(t, 1, tx.BoxReferences[2].ForeignAppIdx)
   725  
   726  	// the current app can also be referenced with AppID = 0
   727  	boxReferences[0].AppID = 0
   728  	tx, err = MakeApplicationCallTxWithBoxes(2, args, addr, foreignApps, foreignAssets, boxReferences, types.NoOpOC, program, program, gSchema, lSchema, extraPages, params, types.Address{}, note, types.Digest{}, [32]byte{}, types.Address{})
   729  	require.NoError(t, err)
   730  	require.EqualValues(t, 0, tx.BoxReferences[0].ForeignAppIdx)
   731  	require.EqualValues(t, 1, tx.BoxReferences[1].ForeignAppIdx)
   732  	require.EqualValues(t, 1, tx.BoxReferences[2].ForeignAppIdx)
   733  
   734  	// if the current app's ID is provided explicitly AND is present in the foreignApps array
   735  	// then the index in the array should be returned rather than the usual value of 0
   736  	boxReferences[0].AppID = 2
   737  	foreignApps = append(foreignApps, 2)
   738  	tx, err = MakeApplicationCallTxWithBoxes(2, args, addr, foreignApps, foreignAssets, boxReferences, types.NoOpOC, program, program, gSchema, lSchema, extraPages, params, types.Address{}, note, types.Digest{}, [32]byte{}, types.Address{})
   739  	require.NoError(t, err)
   740  	require.EqualValues(t, 2, tx.BoxReferences[0].ForeignAppIdx)
   741  	require.EqualValues(t, 1, tx.BoxReferences[1].ForeignAppIdx)
   742  	require.EqualValues(t, 1, tx.BoxReferences[2].ForeignAppIdx)
   743  }
   744  
   745  func TestMakeApplicationCallTxInvalidBoxes(t *testing.T) {
   746  	const fee = 1000
   747  	const firstRound = 2063137
   748  	const genesisID = "devnet-v1.0"
   749  	genesisHash := byteFromBase64("sC3P7e2SdbqKJK0tbiCdK9tdSpbe6XeCGKdoNzmlj0E=")
   750  
   751  	params := types.SuggestedParams{
   752  		Fee:             fee,
   753  		FirstRoundValid: firstRound,
   754  		LastRoundValid:  firstRound + 1000,
   755  		GenesisHash:     genesisHash,
   756  		GenesisID:       genesisID,
   757  		FlatFee:         true,
   758  	}
   759  	note := byteFromBase64("8xMCTuLQ810=")
   760  	program := []byte{1, 32, 1, 1, 34}
   761  	args := make([][]byte, 2)
   762  	args[0] = []byte("123")
   763  	args[1] = []byte("456")
   764  	foreignApps := make([]uint64, 1)
   765  	foreignApps[0] = 10
   766  	foreignAssets := foreignApps
   767  	gSchema := types.StateSchema{NumUint: uint64(1), NumByteSlice: uint64(1)}
   768  	lSchema := types.StateSchema{NumUint: uint64(1), NumByteSlice: uint64(1)}
   769  	extraPages := uint32(2)
   770  	addr := make([]string, 1)
   771  	addr[0] = "47YPQTIGQEO7T4Y4RWDYWEKV6RTR2UNBQXBABEEGM72ESWDQNCQ52OPASU"
   772  	boxReferences := make([]types.AppBoxReference, 3)
   773  	boxReferences[0] = types.AppBoxReference{AppID: 2, Name: []byte("box_name")}
   774  	boxReferences[1] = types.AppBoxReference{AppID: 10, Name: []byte("box_name")}
   775  	boxReferences[2] = types.AppBoxReference{AppID: 11, Name: []byte("box_name")}
   776  
   777  	_, err := MakeApplicationCallTxWithBoxes(2, args, addr, foreignApps, foreignAssets, boxReferences, types.NoOpOC, program, program, gSchema, lSchema, extraPages, params, types.Address{}, note, types.Digest{}, [32]byte{}, types.Address{})
   778  	require.Error(t, err, "the app id 10 provided for this box is not in the foreignApps array")
   779  }
   780  
   781  func TestComputeGroupID(t *testing.T) {
   782  	// compare regular transactions created in SDK with 'goal clerk send' result
   783  	// compare transaction group created in SDK with 'goal clerk group' result
   784  	const address = "UPYAFLHSIPMJOHVXU2MPLQ46GXJKSDCEMZ6RLCQ7GWB5PRDKJUWKKXECXI"
   785  	const fromAddress, toAddress = address, address
   786  	const fee = 1000
   787  	const amount = 2000
   788  	const genesisID = "devnet-v1.0"
   789  	genesisHash := byteFromBase64("sC3P7e2SdbqKJK0tbiCdK9tdSpbe6XeCGKdoNzmlj0E=")
   790  
   791  	const firstRound1 = 710399
   792  	params1 := types.SuggestedParams{
   793  		Fee:             fee,
   794  		FirstRoundValid: firstRound1,
   795  		LastRoundValid:  firstRound1 + 1000,
   796  		GenesisHash:     genesisHash,
   797  		GenesisID:       genesisID,
   798  		FlatFee:         true,
   799  	}
   800  	note1 := byteFromBase64("wRKw5cJ0CMo=")
   801  	tx1, err := MakePaymentTxn(fromAddress, toAddress, amount, note1, "", params1)
   802  	require.NoError(t, err)
   803  
   804  	const firstRound2 = 710515
   805  	params2 := types.SuggestedParams{
   806  		Fee:             fee,
   807  		FirstRoundValid: firstRound2,
   808  		LastRoundValid:  firstRound2 + 1000,
   809  		GenesisHash:     genesisHash,
   810  		GenesisID:       genesisID,
   811  		FlatFee:         true,
   812  	}
   813  	note2 := byteFromBase64("dBlHI6BdrIg=")
   814  	tx2, err := MakePaymentTxn(fromAddress, toAddress, amount, note2, "", params2)
   815  	require.NoError(t, err)
   816  
   817  	const goldenTx1 = "gaN0eG6Ko2FtdM0H0KNmZWXNA+iiZnbOAArW/6NnZW6rZGV2bmV0LXYxLjCiZ2jEILAtz+3tknW6iiStLW4gnSvbXUqW3ul3ghinaDc5pY9Bomx2zgAK2uekbm90ZcQIwRKw5cJ0CMqjcmN2xCCj8AKs8kPYlx63ppj1w5410qkMRGZ9FYofNYPXxGpNLKNzbmTEIKPwAqzyQ9iXHremmPXDnjXSqQxEZn0Vih81g9fEak0spHR5cGWjcGF5"
   818  	const goldenTx2 = "gaN0eG6Ko2FtdM0H0KNmZWXNA+iiZnbOAArXc6NnZW6rZGV2bmV0LXYxLjCiZ2jEILAtz+3tknW6iiStLW4gnSvbXUqW3ul3ghinaDc5pY9Bomx2zgAK21ukbm90ZcQIdBlHI6BdrIijcmN2xCCj8AKs8kPYlx63ppj1w5410qkMRGZ9FYofNYPXxGpNLKNzbmTEIKPwAqzyQ9iXHremmPXDnjXSqQxEZn0Vih81g9fEak0spHR5cGWjcGF5"
   819  
   820  	// goal clerk send dumps unsigned transaction as signed with empty signature in order to save tx type
   821  	stx1 := types.SignedTxn{Sig: types.Signature{}, Msig: types.MultisigSig{}, Txn: tx1}
   822  	stx2 := types.SignedTxn{Sig: types.Signature{}, Msig: types.MultisigSig{}, Txn: tx2}
   823  	require.Equal(t, byteFromBase64(goldenTx1), msgpack.Encode(stx1))
   824  	require.Equal(t, byteFromBase64(goldenTx2), msgpack.Encode(stx2))
   825  
   826  	gid, err := crypto.ComputeGroupID([]types.Transaction{tx1, tx2})
   827  	require.NoError(t, err)
   828  
   829  	// goal clerk group sets Group to every transaction and concatenate them in output file
   830  	// simulating that behavior here
   831  	stx1.Txn.Group = gid
   832  	stx2.Txn.Group = gid
   833  
   834  	var txg []byte
   835  	txg = append(txg, msgpack.Encode(stx1)...)
   836  	txg = append(txg, msgpack.Encode(stx2)...)
   837  
   838  	const goldenTxg = "gaN0eG6Lo2FtdM0H0KNmZWXNA+iiZnbOAArW/6NnZW6rZGV2bmV0LXYxLjCiZ2jEILAtz+3tknW6iiStLW4gnSvbXUqW3ul3ghinaDc5pY9Bo2dycMQgLiQ9OBup9H/bZLSfQUH2S6iHUM6FQ3PLuv9FNKyt09SibHbOAAra56Rub3RlxAjBErDlwnQIyqNyY3bEIKPwAqzyQ9iXHremmPXDnjXSqQxEZn0Vih81g9fEak0so3NuZMQgo/ACrPJD2Jcet6aY9cOeNdKpDERmfRWKHzWD18RqTSykdHlwZaNwYXmBo3R4boujYW10zQfQo2ZlZc0D6KJmds4ACtdzo2dlbqtkZXZuZXQtdjEuMKJnaMQgsC3P7e2SdbqKJK0tbiCdK9tdSpbe6XeCGKdoNzmlj0GjZ3JwxCAuJD04G6n0f9tktJ9BQfZLqIdQzoVDc8u6/0U0rK3T1KJsds4ACttbpG5vdGXECHQZRyOgXayIo3JjdsQgo/ACrPJD2Jcet6aY9cOeNdKpDERmfRWKHzWD18RqTSyjc25kxCCj8AKs8kPYlx63ppj1w5410qkMRGZ9FYofNYPXxGpNLKR0eXBlo3BheQ=="
   839  
   840  	require.Equal(t, byteFromBase64(goldenTxg), txg)
   841  
   842  	// check transaction.AssignGroupID, do not validate correctness of Group field calculation
   843  	result, err := AssignGroupID([]types.Transaction{tx1, tx2}, "BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4")
   844  	require.NoError(t, err)
   845  	require.Equal(t, 0, len(result))
   846  
   847  	result, err = AssignGroupID([]types.Transaction{tx1, tx2}, address)
   848  	require.NoError(t, err)
   849  	require.Equal(t, 2, len(result))
   850  
   851  	result, err = AssignGroupID([]types.Transaction{tx1, tx2}, "")
   852  	require.NoError(t, err)
   853  	require.Equal(t, 2, len(result))
   854  }
   855  
   856  func TestLogicSig(t *testing.T) {
   857  	// validate LogicSig signed transaction against goal
   858  	const fromAddress = "47YPQTIGQEO7T4Y4RWDYWEKV6RTR2UNBQXBABEEGM72ESWDQNCQ52OPASU"
   859  	const toAddress = "PNWOET7LLOWMBMLE4KOCELCX6X3D3Q4H2Q4QJASYIEOF7YIPPQBG3YQ5YI"
   860  	const referenceTxID = "5FJDJD5LMZC3EHUYYJNH5I23U4X6H2KXABNDGPIL557ZMJ33GZHQ"
   861  	const mn = "advice pudding treat near rule blouse same whisper inner electric quit surface sunny dismiss leader blood seat clown cost exist hospital century reform able sponsor"
   862  	const fee = 1000
   863  	const amount = 2000
   864  	const firstRound = 2063137
   865  	const genesisID = "devnet-v1.0"
   866  	genesisHash := byteFromBase64("sC3P7e2SdbqKJK0tbiCdK9tdSpbe6XeCGKdoNzmlj0E=")
   867  	note := byteFromBase64("8xMCTuLQ810=")
   868  
   869  	params := types.SuggestedParams{
   870  		Fee:             fee,
   871  		FirstRoundValid: firstRound,
   872  		LastRoundValid:  firstRound + 1000,
   873  		GenesisHash:     genesisHash,
   874  		GenesisID:       genesisID,
   875  		FlatFee:         true,
   876  	}
   877  	tx, err := MakePaymentTxn(fromAddress, toAddress, amount, note, "", params)
   878  	require.NoError(t, err)
   879  
   880  	// goal clerk send -o tx3 -a 2000 --fee 1000 -d ~/.algorand -w test -L sig.lsig --argb64 MTIz --argb64 NDU2 \
   881  	// -f 47YPQTIGQEO7T4Y4RWDYWEKV6RTR2UNBQXBABEEGM72ESWDQNCQ52OPASU \
   882  	// -t PNWOET7LLOWMBMLE4KOCELCX6X3D3Q4H2Q4QJASYIEOF7YIPPQBG3YQ5YI
   883  	const golden = "gqRsc2lng6NhcmeSxAMxMjPEAzQ1NqFsxAUBIAEBIqNzaWfEQE6HXaI5K0lcq50o/y3bWOYsyw9TLi/oorZB4xaNdn1Z14351u2f6JTON478fl+JhIP4HNRRAIh/I8EWXBPpJQ2jdHhuiqNhbXTNB9CjZmVlzQPoomZ2zgAfeyGjZ2Vuq2Rldm5ldC12MS4womdoxCCwLc/t7ZJ1uookrS1uIJ0r211Klt7pd4IYp2g3OaWPQaJsds4AH38JpG5vdGXECPMTAk7i0PNdo3JjdsQge2ziT+tbrMCxZOKcIixX9fY9w4fUOQSCWEEcX+EPfAKjc25kxCDn8PhNBoEd+fMcjYeLEVX0Zx1RoYXCAJCGZ/RJWHBooaR0eXBlo3BheQ=="
   884  
   885  	program := []byte{1, 32, 1, 1, 34}
   886  	args := make([][]byte, 2)
   887  	args[0] = []byte("123")
   888  	args[1] = []byte("456")
   889  	key, err := mnemonic.ToPrivateKey(mn)
   890  	var pk crypto.MultisigAccount
   891  	require.NoError(t, err)
   892  	lsig, err := crypto.MakeLogicSig(program, args, key, pk)
   893  	require.NoError(t, err)
   894  
   895  	_, stxBytes, err := crypto.SignLogicsigTransaction(lsig, tx)
   896  	require.NoError(t, err)
   897  
   898  	require.Equal(t, byteFromBase64(golden), stxBytes)
   899  
   900  	sender, err := types.DecodeAddress(fromAddress)
   901  	require.NoError(t, err)
   902  
   903  	verified := crypto.VerifyLogicSig(lsig, sender)
   904  	require.True(t, verified)
   905  }
   906  
   907  func TestFee(t *testing.T) {
   908  	testcases := []struct {
   909  		name     string
   910  		flatFee  bool
   911  		fee      types.MicroAlgos
   912  		expected types.MicroAlgos
   913  	}{
   914  		{
   915  			name:     "Use flat fee",
   916  			flatFee:  true,
   917  			fee:      1001,
   918  			expected: 1001,
   919  		},
   920  		{
   921  			name:     "Flat fee does not get overridden with min fee",
   922  			flatFee:  true,
   923  			fee:      999,
   924  			expected: 999,
   925  		},
   926  		{
   927  			name:     "Estimated fee overridden with min fee",
   928  			flatFee:  false,
   929  			fee:      1,
   930  			expected: 1000,
   931  		},
   932  		{
   933  			name:     "Flat fee set to 0",
   934  			flatFee:  true,
   935  			fee:      0,
   936  			expected: 0,
   937  		},
   938  	}
   939  	addr := types.Address{}.String()
   940  	for _, testcase := range testcases {
   941  		t.Run(testcase.name, func(t *testing.T) {
   942  			var tx types.Transaction
   943  			var err error
   944  
   945  			params := types.SuggestedParams{
   946  				FlatFee:         testcase.flatFee,
   947  				Fee:             testcase.fee,
   948  				FirstRoundValid: 1,
   949  				LastRoundValid:  1001,
   950  				GenesisHash:     byteFromBase64("JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI="),
   951  			}
   952  
   953  			tx, err = MakeAssetTransferTxn(addr, addr, 1, nil, params, "", 1)
   954  			require.NoError(t, err)
   955  			require.Equal(t, testcase.expected, tx.Fee)
   956  
   957  			tx, err = MakeAssetAcceptanceTxn(addr, nil, params, 1)
   958  			require.NoError(t, err)
   959  			require.Equal(t, testcase.expected, tx.Fee)
   960  
   961  			tx, err = MakeAssetRevocationTxn(addr, addr, 1, addr, nil, params, 1)
   962  			require.NoError(t, err)
   963  			require.Equal(t, testcase.expected, tx.Fee)
   964  
   965  			tx, err = MakeAssetDestroyTxn(addr, nil, params, 1)
   966  			require.NoError(t, err)
   967  			require.Equal(t, testcase.expected, tx.Fee)
   968  
   969  			tx, err = MakeAssetCreateTxn(addr, nil, params, 1, 1, false, addr, addr, addr, addr, "", "", "", "")
   970  			require.NoError(t, err)
   971  			require.Equal(t, testcase.expected, tx.Fee)
   972  
   973  			tx, err = MakeAssetConfigTxn(addr, nil, params, 1, addr, addr, addr, addr, false)
   974  			require.NoError(t, err)
   975  			require.Equal(t, testcase.expected, tx.Fee)
   976  
   977  			tx, err = MakeAssetFreezeTxn(addr, nil, params, 1, addr, true)
   978  			require.NoError(t, err)
   979  			require.Equal(t, testcase.expected, tx.Fee)
   980  		})
   981  	}
   982  }
   983  
   984  func TestParseBoxReferences(t *testing.T) {
   985  
   986  	genWithAppId := func(appId uint64) types.AppBoxReference {
   987  		return types.AppBoxReference{appId, []byte("example")}
   988  	}
   989  
   990  	genWithNewAppId := func() types.AppBoxReference {
   991  		return types.AppBoxReference{0, []byte("example")}
   992  	}
   993  
   994  	t.Run("appIndexExists", func(t *testing.T) {
   995  		appId := uint64(7)
   996  		abr := genWithAppId(appId)
   997  
   998  		brs, err := parseBoxReferences(
   999  			[]types.AppBoxReference{abr},
  1000  			[]uint64{1, 3, 4, appId},
  1001  			appId-1)
  1002  		require.NoError(t, err)
  1003  		require.Equal(t,
  1004  			[]types.BoxReference{{
  1005  				ForeignAppIdx: uint64(4),
  1006  				Name:          abr.Name}},
  1007  			brs)
  1008  	})
  1009  
  1010  	t.Run("appIndexDoesNotExist", func(t *testing.T) {
  1011  		appId := uint64(7)
  1012  		abr := genWithAppId(appId)
  1013  
  1014  		_, err := parseBoxReferences(
  1015  			[]types.AppBoxReference{abr},
  1016  			[]uint64{1, 3, 4},
  1017  			appId-1)
  1018  		require.Error(t, err)
  1019  	})
  1020  
  1021  	t.Run("newAppId", func(t *testing.T) {
  1022  		abr := genWithNewAppId()
  1023  
  1024  		brs, err := parseBoxReferences(
  1025  			[]types.AppBoxReference{abr},
  1026  			[]uint64{},
  1027  			uint64(1))
  1028  		require.NoError(t, err)
  1029  		require.Equal(t,
  1030  			[]types.BoxReference{{
  1031  				ForeignAppIdx: uint64(0),
  1032  				Name:          abr.Name}},
  1033  			brs)
  1034  	})
  1035  
  1036  	t.Run("fallbackToCurrentApp", func(t *testing.T) {
  1037  		// Mirrors priority search in goal from `cmd/goal/application.go::translateBoxRefs`.
  1038  		appId := uint64(7)
  1039  		abr := genWithAppId(appId)
  1040  
  1041  		// Prefer foreign apps index when present.
  1042  		brs, err := parseBoxReferences(
  1043  			[]types.AppBoxReference{abr},
  1044  			[]uint64{1, 3, 4, appId},
  1045  			appId)
  1046  		require.NoError(t, err)
  1047  		require.Equal(t,
  1048  			[]types.BoxReference{{
  1049  				ForeignAppIdx: uint64(4),
  1050  				Name:          abr.Name}},
  1051  			brs)
  1052  
  1053  		// Fallback to current app when absent from foreign apps.
  1054  		brs, err = parseBoxReferences(
  1055  			[]types.AppBoxReference{abr},
  1056  			[]uint64{1, 3, 4},
  1057  			appId)
  1058  		require.NoError(t, err)
  1059  		require.Equal(t,
  1060  			[]types.BoxReference{{
  1061  				ForeignAppIdx: uint64(0),
  1062  				Name:          abr.Name}},
  1063  			brs)
  1064  	})
  1065  }