From 13da8508530b7dc2e9ae62766cccd0baaff0f4bd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 12 Oct 2022 20:58:23 -0400 Subject: [PATCH 1/3] Regenerate code with generator#43 --- client/v2/algod/accountInformation.go | 1 + client/v2/algod/getPendingTransactions.go | 1 + .../algod/getPendingTransactionsByAddress.go | 1 + client/v2/algod/getTransactionProof.go | 1 + client/v2/algod/tealCompile.go | 1 + .../v2/indexer/lookupAccountAppLocalStates.go | 4 ++++ client/v2/indexer/lookupAccountAssets.go | 4 ++++ client/v2/indexer/lookupAccountByID.go | 3 +++ .../lookupAccountCreatedApplications.go | 4 ++++ .../v2/indexer/lookupAccountCreatedAssets.go | 4 ++++ client/v2/indexer/lookupAccountTransactions.go | 14 ++++++++++++++ client/v2/indexer/lookupApplicationByID.go | 1 + client/v2/indexer/lookupApplicationLogsByID.go | 6 ++++++ client/v2/indexer/lookupAssetBalances.go | 5 +++++ client/v2/indexer/lookupAssetByID.go | 1 + client/v2/indexer/lookupAssetTransactions.go | 16 ++++++++++++++++ client/v2/indexer/lookupBlock.go | 1 + client/v2/indexer/searchForAccounts.go | 10 ++++++++++ client/v2/indexer/searchForApplications.go | 5 +++++ client/v2/indexer/searchForAssets.go | 7 +++++++ client/v2/indexer/searchForTransactions.go | 18 ++++++++++++++++++ 21 files changed, 108 insertions(+) diff --git a/client/v2/algod/accountInformation.go b/client/v2/algod/accountInformation.go index 12b35faa..44ef753a 100644 --- a/client/v2/algod/accountInformation.go +++ b/client/v2/algod/accountInformation.go @@ -35,6 +35,7 @@ type AccountInformation struct { // `none`. func (s *AccountInformation) Exclude(Exclude string) *AccountInformation { s.p.Exclude = Exclude + return s } diff --git a/client/v2/algod/getPendingTransactions.go b/client/v2/algod/getPendingTransactions.go index 2ef1b55d..509ecb31 100644 --- a/client/v2/algod/getPendingTransactions.go +++ b/client/v2/algod/getPendingTransactions.go @@ -32,6 +32,7 @@ type PendingTransactions struct { // txns. func (s *PendingTransactions) Max(Max uint64) *PendingTransactions { s.p.Max = Max + return s } diff --git a/client/v2/algod/getPendingTransactionsByAddress.go b/client/v2/algod/getPendingTransactionsByAddress.go index 60ddfb73..d4765b3c 100644 --- a/client/v2/algod/getPendingTransactionsByAddress.go +++ b/client/v2/algod/getPendingTransactionsByAddress.go @@ -35,6 +35,7 @@ type PendingTransactionsByAddress struct { // txns. func (s *PendingTransactionsByAddress) Max(Max uint64) *PendingTransactionsByAddress { s.p.Max = Max + return s } diff --git a/client/v2/algod/getTransactionProof.go b/client/v2/algod/getTransactionProof.go index 59271184..2f62b5b1 100644 --- a/client/v2/algod/getTransactionProof.go +++ b/client/v2/algod/getTransactionProof.go @@ -35,6 +35,7 @@ type GetTransactionProof struct { // * sha256 func (s *GetTransactionProof) Hashtype(Hashtype string) *GetTransactionProof { s.p.Hashtype = Hashtype + return s } diff --git a/client/v2/algod/tealCompile.go b/client/v2/algod/tealCompile.go index 18313f24..c503ad7e 100644 --- a/client/v2/algod/tealCompile.go +++ b/client/v2/algod/tealCompile.go @@ -31,6 +31,7 @@ type TealCompile struct { // Defaults to `false`. func (s *TealCompile) Sourcemap(Sourcemap bool) *TealCompile { s.p.Sourcemap = Sourcemap + return s } diff --git a/client/v2/indexer/lookupAccountAppLocalStates.go b/client/v2/indexer/lookupAccountAppLocalStates.go index a6472c33..8dbfb403 100644 --- a/client/v2/indexer/lookupAccountAppLocalStates.go +++ b/client/v2/indexer/lookupAccountAppLocalStates.go @@ -41,6 +41,7 @@ type LookupAccountAppLocalStates struct { // ApplicationID application ID func (s *LookupAccountAppLocalStates) ApplicationID(ApplicationID uint64) *LookupAccountAppLocalStates { s.p.ApplicationID = ApplicationID + return s } @@ -49,6 +50,7 @@ func (s *LookupAccountAppLocalStates) ApplicationID(ApplicationID uint64) *Looku // localstates. func (s *LookupAccountAppLocalStates) IncludeAll(IncludeAll bool) *LookupAccountAppLocalStates { s.p.IncludeAll = IncludeAll + return s } @@ -56,6 +58,7 @@ func (s *LookupAccountAppLocalStates) IncludeAll(IncludeAll bool) *LookupAccount // if the limit is not reached. func (s *LookupAccountAppLocalStates) Limit(Limit uint64) *LookupAccountAppLocalStates { s.p.Limit = Limit + return s } @@ -63,6 +66,7 @@ func (s *LookupAccountAppLocalStates) Limit(Limit uint64) *LookupAccountAppLocal // results. func (s *LookupAccountAppLocalStates) Next(Next string) *LookupAccountAppLocalStates { s.p.Next = Next + return s } diff --git a/client/v2/indexer/lookupAccountAssets.go b/client/v2/indexer/lookupAccountAssets.go index c1330429..596181db 100644 --- a/client/v2/indexer/lookupAccountAssets.go +++ b/client/v2/indexer/lookupAccountAssets.go @@ -41,6 +41,7 @@ type LookupAccountAssets struct { // AssetID asset ID func (s *LookupAccountAssets) AssetID(AssetID uint64) *LookupAccountAssets { s.p.AssetID = AssetID + return s } @@ -49,6 +50,7 @@ func (s *LookupAccountAssets) AssetID(AssetID uint64) *LookupAccountAssets { // localstates. func (s *LookupAccountAssets) IncludeAll(IncludeAll bool) *LookupAccountAssets { s.p.IncludeAll = IncludeAll + return s } @@ -56,6 +58,7 @@ func (s *LookupAccountAssets) IncludeAll(IncludeAll bool) *LookupAccountAssets { // if the limit is not reached. func (s *LookupAccountAssets) Limit(Limit uint64) *LookupAccountAssets { s.p.Limit = Limit + return s } @@ -63,6 +66,7 @@ func (s *LookupAccountAssets) Limit(Limit uint64) *LookupAccountAssets { // results. func (s *LookupAccountAssets) Next(Next string) *LookupAccountAssets { s.p.Next = Next + return s } diff --git a/client/v2/indexer/lookupAccountByID.go b/client/v2/indexer/lookupAccountByID.go index 98beed73..930c5116 100644 --- a/client/v2/indexer/lookupAccountByID.go +++ b/client/v2/indexer/lookupAccountByID.go @@ -39,6 +39,7 @@ type LookupAccountByID struct { // application parameters created by this account. func (s *LookupAccountByID) Exclude(Exclude []string) *LookupAccountByID { s.p.Exclude = Exclude + return s } @@ -47,12 +48,14 @@ func (s *LookupAccountByID) Exclude(Exclude []string) *LookupAccountByID { // localstates. func (s *LookupAccountByID) IncludeAll(IncludeAll bool) *LookupAccountByID { s.p.IncludeAll = IncludeAll + return s } // Round include results for the specified round. func (s *LookupAccountByID) Round(Round uint64) *LookupAccountByID { s.p.Round = Round + return s } diff --git a/client/v2/indexer/lookupAccountCreatedApplications.go b/client/v2/indexer/lookupAccountCreatedApplications.go index cc22ace5..4852d251 100644 --- a/client/v2/indexer/lookupAccountCreatedApplications.go +++ b/client/v2/indexer/lookupAccountCreatedApplications.go @@ -41,6 +41,7 @@ type LookupAccountCreatedApplications struct { // ApplicationID application ID func (s *LookupAccountCreatedApplications) ApplicationID(ApplicationID uint64) *LookupAccountCreatedApplications { s.p.ApplicationID = ApplicationID + return s } @@ -49,6 +50,7 @@ func (s *LookupAccountCreatedApplications) ApplicationID(ApplicationID uint64) * // localstates. func (s *LookupAccountCreatedApplications) IncludeAll(IncludeAll bool) *LookupAccountCreatedApplications { s.p.IncludeAll = IncludeAll + return s } @@ -56,6 +58,7 @@ func (s *LookupAccountCreatedApplications) IncludeAll(IncludeAll bool) *LookupAc // if the limit is not reached. func (s *LookupAccountCreatedApplications) Limit(Limit uint64) *LookupAccountCreatedApplications { s.p.Limit = Limit + return s } @@ -63,6 +66,7 @@ func (s *LookupAccountCreatedApplications) Limit(Limit uint64) *LookupAccountCre // results. func (s *LookupAccountCreatedApplications) Next(Next string) *LookupAccountCreatedApplications { s.p.Next = Next + return s } diff --git a/client/v2/indexer/lookupAccountCreatedAssets.go b/client/v2/indexer/lookupAccountCreatedAssets.go index 7c74fc6d..d28f384c 100644 --- a/client/v2/indexer/lookupAccountCreatedAssets.go +++ b/client/v2/indexer/lookupAccountCreatedAssets.go @@ -41,6 +41,7 @@ type LookupAccountCreatedAssets struct { // AssetID asset ID func (s *LookupAccountCreatedAssets) AssetID(AssetID uint64) *LookupAccountCreatedAssets { s.p.AssetID = AssetID + return s } @@ -49,6 +50,7 @@ func (s *LookupAccountCreatedAssets) AssetID(AssetID uint64) *LookupAccountCreat // localstates. func (s *LookupAccountCreatedAssets) IncludeAll(IncludeAll bool) *LookupAccountCreatedAssets { s.p.IncludeAll = IncludeAll + return s } @@ -56,6 +58,7 @@ func (s *LookupAccountCreatedAssets) IncludeAll(IncludeAll bool) *LookupAccountC // if the limit is not reached. func (s *LookupAccountCreatedAssets) Limit(Limit uint64) *LookupAccountCreatedAssets { s.p.Limit = Limit + return s } @@ -63,6 +66,7 @@ func (s *LookupAccountCreatedAssets) Limit(Limit uint64) *LookupAccountCreatedAs // results. func (s *LookupAccountCreatedAssets) Next(Next string) *LookupAccountCreatedAssets { s.p.Next = Next + return s } diff --git a/client/v2/indexer/lookupAccountTransactions.go b/client/v2/indexer/lookupAccountTransactions.go index ba2296a2..c7b6aa09 100644 --- a/client/v2/indexer/lookupAccountTransactions.go +++ b/client/v2/indexer/lookupAccountTransactions.go @@ -84,6 +84,7 @@ type LookupAccountTransactions struct { // formatted string. func (s *LookupAccountTransactions) AfterTimeString(AfterTime string) *LookupAccountTransactions { s.p.AfterTime = AfterTime + return s } @@ -98,6 +99,7 @@ func (s *LookupAccountTransactions) AfterTime(AfterTime time.Time) *LookupAccoun // AssetID asset ID func (s *LookupAccountTransactions) AssetID(AssetID uint64) *LookupAccountTransactions { s.p.AssetID = AssetID + return s } @@ -105,6 +107,7 @@ func (s *LookupAccountTransactions) AssetID(AssetID uint64) *LookupAccountTransa // formatted string. func (s *LookupAccountTransactions) BeforeTimeString(BeforeTime string) *LookupAccountTransactions { s.p.BeforeTime = BeforeTime + return s } @@ -121,6 +124,7 @@ func (s *LookupAccountTransactions) BeforeTime(BeforeTime time.Time) *LookupAcco // case the asset will be used. func (s *LookupAccountTransactions) CurrencyGreaterThan(CurrencyGreaterThan uint64) *LookupAccountTransactions { s.p.CurrencyGreaterThan = CurrencyGreaterThan + return s } @@ -129,6 +133,7 @@ func (s *LookupAccountTransactions) CurrencyGreaterThan(CurrencyGreaterThan uint // will be used. func (s *LookupAccountTransactions) CurrencyLessThan(CurrencyLessThan uint64) *LookupAccountTransactions { s.p.CurrencyLessThan = CurrencyLessThan + return s } @@ -136,18 +141,21 @@ func (s *LookupAccountTransactions) CurrencyLessThan(CurrencyLessThan uint64) *L // if the limit is not reached. func (s *LookupAccountTransactions) Limit(Limit uint64) *LookupAccountTransactions { s.p.Limit = Limit + return s } // MaxRound include results at or before the specified max-round. func (s *LookupAccountTransactions) MaxRound(MaxRound uint64) *LookupAccountTransactions { s.p.MaxRound = MaxRound + return s } // MinRound include results at or after the specified min-round. func (s *LookupAccountTransactions) MinRound(MinRound uint64) *LookupAccountTransactions { s.p.MinRound = MinRound + return s } @@ -155,6 +163,7 @@ func (s *LookupAccountTransactions) MinRound(MinRound uint64) *LookupAccountTran // results. func (s *LookupAccountTransactions) NextToken(NextToken string) *LookupAccountTransactions { s.p.NextToken = NextToken + return s } @@ -168,12 +177,14 @@ func (s *LookupAccountTransactions) NotePrefix(NotePrefix []byte) *LookupAccount // RekeyTo include results which include the rekey-to field. func (s *LookupAccountTransactions) RekeyTo(RekeyTo bool) *LookupAccountTransactions { s.p.RekeyTo = RekeyTo + return s } // Round include results for the specified round. func (s *LookupAccountTransactions) Round(Round uint64) *LookupAccountTransactions { s.p.Round = Round + return s } @@ -183,18 +194,21 @@ func (s *LookupAccountTransactions) Round(Round uint64) *LookupAccountTransactio // * lsig - LogicSig func (s *LookupAccountTransactions) SigType(SigType string) *LookupAccountTransactions { s.p.SigType = SigType + return s } // TxType func (s *LookupAccountTransactions) TxType(TxType string) *LookupAccountTransactions { s.p.TxType = TxType + return s } // TXID lookup the specific transaction by ID. func (s *LookupAccountTransactions) TXID(TXID string) *LookupAccountTransactions { s.p.TXID = TXID + return s } diff --git a/client/v2/indexer/lookupApplicationByID.go b/client/v2/indexer/lookupApplicationByID.go index 8afda701..7e03f3f2 100644 --- a/client/v2/indexer/lookupApplicationByID.go +++ b/client/v2/indexer/lookupApplicationByID.go @@ -31,6 +31,7 @@ type LookupApplicationByID struct { // localstates. func (s *LookupApplicationByID) IncludeAll(IncludeAll bool) *LookupApplicationByID { s.p.IncludeAll = IncludeAll + return s } diff --git a/client/v2/indexer/lookupApplicationLogsByID.go b/client/v2/indexer/lookupApplicationLogsByID.go index b1d4d44b..ea416f46 100644 --- a/client/v2/indexer/lookupApplicationLogsByID.go +++ b/client/v2/indexer/lookupApplicationLogsByID.go @@ -45,18 +45,21 @@ type LookupApplicationLogsByID struct { // if the limit is not reached. func (s *LookupApplicationLogsByID) Limit(Limit uint64) *LookupApplicationLogsByID { s.p.Limit = Limit + return s } // MaxRound include results at or before the specified max-round. func (s *LookupApplicationLogsByID) MaxRound(MaxRound uint64) *LookupApplicationLogsByID { s.p.MaxRound = MaxRound + return s } // MinRound include results at or after the specified min-round. func (s *LookupApplicationLogsByID) MinRound(MinRound uint64) *LookupApplicationLogsByID { s.p.MinRound = MinRound + return s } @@ -64,18 +67,21 @@ func (s *LookupApplicationLogsByID) MinRound(MinRound uint64) *LookupApplication // results. func (s *LookupApplicationLogsByID) Next(Next string) *LookupApplicationLogsByID { s.p.Next = Next + return s } // SenderAddress only include transactions with this sender address. func (s *LookupApplicationLogsByID) SenderAddress(SenderAddress string) *LookupApplicationLogsByID { s.p.SenderAddress = SenderAddress + return s } // Txid lookup the specific transaction by ID. func (s *LookupApplicationLogsByID) Txid(Txid string) *LookupApplicationLogsByID { s.p.Txid = Txid + return s } diff --git a/client/v2/indexer/lookupAssetBalances.go b/client/v2/indexer/lookupAssetBalances.go index 738f10a1..7e2e845a 100644 --- a/client/v2/indexer/lookupAssetBalances.go +++ b/client/v2/indexer/lookupAssetBalances.go @@ -49,6 +49,7 @@ type LookupAssetBalances struct { // case the asset will be used. func (s *LookupAssetBalances) CurrencyGreaterThan(CurrencyGreaterThan uint64) *LookupAssetBalances { s.p.CurrencyGreaterThan = CurrencyGreaterThan + return s } @@ -57,6 +58,7 @@ func (s *LookupAssetBalances) CurrencyGreaterThan(CurrencyGreaterThan uint64) *L // will be used. func (s *LookupAssetBalances) CurrencyLessThan(CurrencyLessThan uint64) *LookupAssetBalances { s.p.CurrencyLessThan = CurrencyLessThan + return s } @@ -65,6 +67,7 @@ func (s *LookupAssetBalances) CurrencyLessThan(CurrencyLessThan uint64) *LookupA // localstates. func (s *LookupAssetBalances) IncludeAll(IncludeAll bool) *LookupAssetBalances { s.p.IncludeAll = IncludeAll + return s } @@ -72,6 +75,7 @@ func (s *LookupAssetBalances) IncludeAll(IncludeAll bool) *LookupAssetBalances { // if the limit is not reached. func (s *LookupAssetBalances) Limit(Limit uint64) *LookupAssetBalances { s.p.Limit = Limit + return s } @@ -79,6 +83,7 @@ func (s *LookupAssetBalances) Limit(Limit uint64) *LookupAssetBalances { // results. func (s *LookupAssetBalances) NextToken(NextToken string) *LookupAssetBalances { s.p.NextToken = NextToken + return s } diff --git a/client/v2/indexer/lookupAssetByID.go b/client/v2/indexer/lookupAssetByID.go index dbf2d55a..d020277c 100644 --- a/client/v2/indexer/lookupAssetByID.go +++ b/client/v2/indexer/lookupAssetByID.go @@ -31,6 +31,7 @@ type LookupAssetByID struct { // localstates. func (s *LookupAssetByID) IncludeAll(IncludeAll bool) *LookupAssetByID { s.p.IncludeAll = IncludeAll + return s } diff --git a/client/v2/indexer/lookupAssetTransactions.go b/client/v2/indexer/lookupAssetTransactions.go index 81ad96c2..6ded451c 100644 --- a/client/v2/indexer/lookupAssetTransactions.go +++ b/client/v2/indexer/lookupAssetTransactions.go @@ -94,6 +94,7 @@ type LookupAssetTransactions struct { // transaction fields. func (s *LookupAssetTransactions) AddressString(AddressString string) *LookupAssetTransactions { s.p.AddressString = AddressString + return s } @@ -101,6 +102,7 @@ func (s *LookupAssetTransactions) AddressString(AddressString string) *LookupAss // search for. func (s *LookupAssetTransactions) AddressRole(AddressRole string) *LookupAssetTransactions { s.p.AddressRole = AddressRole + return s } @@ -108,6 +110,7 @@ func (s *LookupAssetTransactions) AddressRole(AddressRole string) *LookupAssetTr // formatted string. func (s *LookupAssetTransactions) AfterTimeString(AfterTime string) *LookupAssetTransactions { s.p.AfterTime = AfterTime + return s } @@ -123,6 +126,7 @@ func (s *LookupAssetTransactions) AfterTime(AfterTime time.Time) *LookupAssetTra // formatted string. func (s *LookupAssetTransactions) BeforeTimeString(BeforeTime string) *LookupAssetTransactions { s.p.BeforeTime = BeforeTime + return s } @@ -139,6 +143,7 @@ func (s *LookupAssetTransactions) BeforeTime(BeforeTime time.Time) *LookupAssetT // case the asset will be used. func (s *LookupAssetTransactions) CurrencyGreaterThan(CurrencyGreaterThan uint64) *LookupAssetTransactions { s.p.CurrencyGreaterThan = CurrencyGreaterThan + return s } @@ -147,6 +152,7 @@ func (s *LookupAssetTransactions) CurrencyGreaterThan(CurrencyGreaterThan uint64 // will be used. func (s *LookupAssetTransactions) CurrencyLessThan(CurrencyLessThan uint64) *LookupAssetTransactions { s.p.CurrencyLessThan = CurrencyLessThan + return s } @@ -155,6 +161,7 @@ func (s *LookupAssetTransactions) CurrencyLessThan(CurrencyLessThan uint64) *Loo // receiver, if you would like to exclude them set this parameter to true. func (s *LookupAssetTransactions) ExcludeCloseTo(ExcludeCloseTo bool) *LookupAssetTransactions { s.p.ExcludeCloseTo = ExcludeCloseTo + return s } @@ -162,18 +169,21 @@ func (s *LookupAssetTransactions) ExcludeCloseTo(ExcludeCloseTo bool) *LookupAss // if the limit is not reached. func (s *LookupAssetTransactions) Limit(Limit uint64) *LookupAssetTransactions { s.p.Limit = Limit + return s } // MaxRound include results at or before the specified max-round. func (s *LookupAssetTransactions) MaxRound(MaxRound uint64) *LookupAssetTransactions { s.p.MaxRound = MaxRound + return s } // MinRound include results at or after the specified min-round. func (s *LookupAssetTransactions) MinRound(MinRound uint64) *LookupAssetTransactions { s.p.MinRound = MinRound + return s } @@ -181,6 +191,7 @@ func (s *LookupAssetTransactions) MinRound(MinRound uint64) *LookupAssetTransact // results. func (s *LookupAssetTransactions) NextToken(NextToken string) *LookupAssetTransactions { s.p.NextToken = NextToken + return s } @@ -194,12 +205,14 @@ func (s *LookupAssetTransactions) NotePrefix(NotePrefix []byte) *LookupAssetTran // RekeyTo include results which include the rekey-to field. func (s *LookupAssetTransactions) RekeyTo(RekeyTo bool) *LookupAssetTransactions { s.p.RekeyTo = RekeyTo + return s } // Round include results for the specified round. func (s *LookupAssetTransactions) Round(Round uint64) *LookupAssetTransactions { s.p.Round = Round + return s } @@ -209,18 +222,21 @@ func (s *LookupAssetTransactions) Round(Round uint64) *LookupAssetTransactions { // * lsig - LogicSig func (s *LookupAssetTransactions) SigType(SigType string) *LookupAssetTransactions { s.p.SigType = SigType + return s } // TxType func (s *LookupAssetTransactions) TxType(TxType string) *LookupAssetTransactions { s.p.TxType = TxType + return s } // TXID lookup the specific transaction by ID. func (s *LookupAssetTransactions) TXID(TXID string) *LookupAssetTransactions { s.p.TXID = TXID + return s } diff --git a/client/v2/indexer/lookupBlock.go b/client/v2/indexer/lookupBlock.go index b1f5a024..6f2f931b 100644 --- a/client/v2/indexer/lookupBlock.go +++ b/client/v2/indexer/lookupBlock.go @@ -29,6 +29,7 @@ type LookupBlock struct { // contain the transactions func (s *LookupBlock) HeaderOnly(HeaderOnly bool) *LookupBlock { s.p.HeaderOnly = HeaderOnly + return s } diff --git a/client/v2/indexer/searchForAccounts.go b/client/v2/indexer/searchForAccounts.go index bd8abadb..8085430d 100644 --- a/client/v2/indexer/searchForAccounts.go +++ b/client/v2/indexer/searchForAccounts.go @@ -66,18 +66,21 @@ type SearchAccounts struct { // ApplicationId application ID func (s *SearchAccounts) ApplicationId(ApplicationId uint64) *SearchAccounts { s.p.ApplicationId = ApplicationId + return s } // AssetID asset ID func (s *SearchAccounts) AssetID(AssetID uint64) *SearchAccounts { s.p.AssetID = AssetID + return s } // AuthAddress include accounts configured to use this spending key. func (s *SearchAccounts) AuthAddress(AuthAddress string) *SearchAccounts { s.p.AuthAddress = AuthAddress + return s } @@ -86,6 +89,7 @@ func (s *SearchAccounts) AuthAddress(AuthAddress string) *SearchAccounts { // case the asset will be used. func (s *SearchAccounts) CurrencyGreaterThan(CurrencyGreaterThan uint64) *SearchAccounts { s.p.CurrencyGreaterThan = CurrencyGreaterThan + return s } @@ -94,6 +98,7 @@ func (s *SearchAccounts) CurrencyGreaterThan(CurrencyGreaterThan uint64) *Search // will be used. func (s *SearchAccounts) CurrencyLessThan(CurrencyLessThan uint64) *SearchAccounts { s.p.CurrencyLessThan = CurrencyLessThan + return s } @@ -102,6 +107,7 @@ func (s *SearchAccounts) CurrencyLessThan(CurrencyLessThan uint64) *SearchAccoun // application parameters created by this account. func (s *SearchAccounts) Exclude(Exclude []string) *SearchAccounts { s.p.Exclude = Exclude + return s } @@ -110,6 +116,7 @@ func (s *SearchAccounts) Exclude(Exclude []string) *SearchAccounts { // localstates. func (s *SearchAccounts) IncludeAll(IncludeAll bool) *SearchAccounts { s.p.IncludeAll = IncludeAll + return s } @@ -117,6 +124,7 @@ func (s *SearchAccounts) IncludeAll(IncludeAll bool) *SearchAccounts { // if the limit is not reached. func (s *SearchAccounts) Limit(Limit uint64) *SearchAccounts { s.p.Limit = Limit + return s } @@ -124,6 +132,7 @@ func (s *SearchAccounts) Limit(Limit uint64) *SearchAccounts { // results. func (s *SearchAccounts) NextToken(NextToken string) *SearchAccounts { s.p.NextToken = NextToken + return s } @@ -135,6 +144,7 @@ func (s *SearchAccounts) NextToken(NextToken string) *SearchAccounts { // used. func (s *SearchAccounts) Round(Round uint64) *SearchAccounts { s.p.Round = Round + return s } diff --git a/client/v2/indexer/searchForApplications.go b/client/v2/indexer/searchForApplications.go index b16e3f54..dc29e96f 100644 --- a/client/v2/indexer/searchForApplications.go +++ b/client/v2/indexer/searchForApplications.go @@ -40,12 +40,14 @@ type SearchForApplications struct { // ApplicationId application ID func (s *SearchForApplications) ApplicationId(ApplicationId uint64) *SearchForApplications { s.p.ApplicationId = ApplicationId + return s } // Creator filter just applications with the given creator address. func (s *SearchForApplications) Creator(Creator string) *SearchForApplications { s.p.Creator = Creator + return s } @@ -54,6 +56,7 @@ func (s *SearchForApplications) Creator(Creator string) *SearchForApplications { // localstates. func (s *SearchForApplications) IncludeAll(IncludeAll bool) *SearchForApplications { s.p.IncludeAll = IncludeAll + return s } @@ -61,6 +64,7 @@ func (s *SearchForApplications) IncludeAll(IncludeAll bool) *SearchForApplicatio // if the limit is not reached. func (s *SearchForApplications) Limit(Limit uint64) *SearchForApplications { s.p.Limit = Limit + return s } @@ -68,6 +72,7 @@ func (s *SearchForApplications) Limit(Limit uint64) *SearchForApplications { // results. func (s *SearchForApplications) Next(Next string) *SearchForApplications { s.p.Next = Next + return s } diff --git a/client/v2/indexer/searchForAssets.go b/client/v2/indexer/searchForAssets.go index 06825668..ea014aa5 100644 --- a/client/v2/indexer/searchForAssets.go +++ b/client/v2/indexer/searchForAssets.go @@ -46,12 +46,14 @@ type SearchForAssets struct { // AssetID asset ID func (s *SearchForAssets) AssetID(AssetID uint64) *SearchForAssets { s.p.AssetID = AssetID + return s } // Creator filter just assets with the given creator address. func (s *SearchForAssets) Creator(Creator string) *SearchForAssets { s.p.Creator = Creator + return s } @@ -60,6 +62,7 @@ func (s *SearchForAssets) Creator(Creator string) *SearchForAssets { // localstates. func (s *SearchForAssets) IncludeAll(IncludeAll bool) *SearchForAssets { s.p.IncludeAll = IncludeAll + return s } @@ -67,12 +70,14 @@ func (s *SearchForAssets) IncludeAll(IncludeAll bool) *SearchForAssets { // if the limit is not reached. func (s *SearchForAssets) Limit(Limit uint64) *SearchForAssets { s.p.Limit = Limit + return s } // Name filter just assets with the given name. func (s *SearchForAssets) Name(Name string) *SearchForAssets { s.p.Name = Name + return s } @@ -80,12 +85,14 @@ func (s *SearchForAssets) Name(Name string) *SearchForAssets { // results. func (s *SearchForAssets) NextToken(NextToken string) *SearchForAssets { s.p.NextToken = NextToken + return s } // Unit filter just assets with the given unit. func (s *SearchForAssets) Unit(Unit string) *SearchForAssets { s.p.Unit = Unit + return s } diff --git a/client/v2/indexer/searchForTransactions.go b/client/v2/indexer/searchForTransactions.go index 2a04ada4..b57c0fa4 100644 --- a/client/v2/indexer/searchForTransactions.go +++ b/client/v2/indexer/searchForTransactions.go @@ -98,6 +98,7 @@ type SearchForTransactions struct { // transaction fields. func (s *SearchForTransactions) AddressString(AddressString string) *SearchForTransactions { s.p.AddressString = AddressString + return s } @@ -105,6 +106,7 @@ func (s *SearchForTransactions) AddressString(AddressString string) *SearchForTr // search for. func (s *SearchForTransactions) AddressRole(AddressRole string) *SearchForTransactions { s.p.AddressRole = AddressRole + return s } @@ -112,6 +114,7 @@ func (s *SearchForTransactions) AddressRole(AddressRole string) *SearchForTransa // formatted string. func (s *SearchForTransactions) AfterTimeString(AfterTime string) *SearchForTransactions { s.p.AfterTime = AfterTime + return s } @@ -126,12 +129,14 @@ func (s *SearchForTransactions) AfterTime(AfterTime time.Time) *SearchForTransac // ApplicationId application ID func (s *SearchForTransactions) ApplicationId(ApplicationId uint64) *SearchForTransactions { s.p.ApplicationId = ApplicationId + return s } // AssetID asset ID func (s *SearchForTransactions) AssetID(AssetID uint64) *SearchForTransactions { s.p.AssetID = AssetID + return s } @@ -139,6 +144,7 @@ func (s *SearchForTransactions) AssetID(AssetID uint64) *SearchForTransactions { // formatted string. func (s *SearchForTransactions) BeforeTimeString(BeforeTime string) *SearchForTransactions { s.p.BeforeTime = BeforeTime + return s } @@ -155,6 +161,7 @@ func (s *SearchForTransactions) BeforeTime(BeforeTime time.Time) *SearchForTrans // case the asset will be used. func (s *SearchForTransactions) CurrencyGreaterThan(CurrencyGreaterThan uint64) *SearchForTransactions { s.p.CurrencyGreaterThan = CurrencyGreaterThan + return s } @@ -163,6 +170,7 @@ func (s *SearchForTransactions) CurrencyGreaterThan(CurrencyGreaterThan uint64) // will be used. func (s *SearchForTransactions) CurrencyLessThan(CurrencyLessThan uint64) *SearchForTransactions { s.p.CurrencyLessThan = CurrencyLessThan + return s } @@ -171,6 +179,7 @@ func (s *SearchForTransactions) CurrencyLessThan(CurrencyLessThan uint64) *Searc // receiver, if you would like to exclude them set this parameter to true. func (s *SearchForTransactions) ExcludeCloseTo(ExcludeCloseTo bool) *SearchForTransactions { s.p.ExcludeCloseTo = ExcludeCloseTo + return s } @@ -178,18 +187,21 @@ func (s *SearchForTransactions) ExcludeCloseTo(ExcludeCloseTo bool) *SearchForTr // if the limit is not reached. func (s *SearchForTransactions) Limit(Limit uint64) *SearchForTransactions { s.p.Limit = Limit + return s } // MaxRound include results at or before the specified max-round. func (s *SearchForTransactions) MaxRound(MaxRound uint64) *SearchForTransactions { s.p.MaxRound = MaxRound + return s } // MinRound include results at or after the specified min-round. func (s *SearchForTransactions) MinRound(MinRound uint64) *SearchForTransactions { s.p.MinRound = MinRound + return s } @@ -197,6 +209,7 @@ func (s *SearchForTransactions) MinRound(MinRound uint64) *SearchForTransactions // results. func (s *SearchForTransactions) NextToken(NextToken string) *SearchForTransactions { s.p.NextToken = NextToken + return s } @@ -210,12 +223,14 @@ func (s *SearchForTransactions) NotePrefix(NotePrefix []byte) *SearchForTransact // RekeyTo include results which include the rekey-to field. func (s *SearchForTransactions) RekeyTo(RekeyTo bool) *SearchForTransactions { s.p.RekeyTo = RekeyTo + return s } // Round include results for the specified round. func (s *SearchForTransactions) Round(Round uint64) *SearchForTransactions { s.p.Round = Round + return s } @@ -225,18 +240,21 @@ func (s *SearchForTransactions) Round(Round uint64) *SearchForTransactions { // * lsig - LogicSig func (s *SearchForTransactions) SigType(SigType string) *SearchForTransactions { s.p.SigType = SigType + return s } // TxType func (s *SearchForTransactions) TxType(TxType string) *SearchForTransactions { s.p.TxType = TxType + return s } // TXID lookup the specific transaction by ID. func (s *SearchForTransactions) TXID(TXID string) *SearchForTransactions { s.p.TXID = TXID + return s } From e9c1b3d7b235130d287f3c3144666a2646089c7c Mon Sep 17 00:00:00 2001 From: Michael Diamant Date: Wed, 2 Nov 2022 08:52:49 -0400 Subject: [PATCH 2/3] Boxes: Add support for Boxes (#341) --- .circleci/config.yml | 4 +- .gitignore | 3 + client/v2/algod/algod.go | 8 + client/v2/algod/getApplicationBoxByName.go | 47 ++ client/v2/algod/getApplicationBoxes.go | 42 ++ client/v2/common/models/account.go | 8 + client/v2/common/models/box.go | 10 + client/v2/common/models/box_descriptor.go | 7 + client/v2/common/models/boxes_response.go | 14 + client/v2/indexer/indexer.go | 8 + .../lookupApplicationBoxByIDandName.go | 47 ++ .../v2/indexer/searchForApplicationBoxes.go | 53 +++ future/atomicTransactionComposer.go | 18 +- future/transaction.go | 416 ++++++++++++++++-- future/transaction_test.go | 162 ++++++- test/algodclientv2_test.go | 24 + test/applications_integration_test.go | 269 ++++++++--- test/indexer_unit_test.go | 25 +- test/integration.tags | 3 +- test/steps_test.go | 11 + test/transactions_test.go | 31 +- test/unit.tags | 1 + test/utilities.go | 17 + types/applications.go | 40 +- 24 files changed, 1142 insertions(+), 126 deletions(-) create mode 100644 client/v2/algod/getApplicationBoxByName.go create mode 100644 client/v2/algod/getApplicationBoxes.go create mode 100644 client/v2/common/models/box.go create mode 100644 client/v2/common/models/box_descriptor.go create mode 100644 client/v2/common/models/boxes_response.go create mode 100644 client/v2/indexer/lookupApplicationBoxByIDandName.go create mode 100644 client/v2/indexer/searchForApplicationBoxes.go diff --git a/.circleci/config.yml b/.circleci/config.yml index 8eecf262..2fd81038 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,7 +1,7 @@ version: 2.1 orbs: - go: circleci/go@1.7.0 + go: circleci/go@1.7.1 workflows: circleci_build_and_test: @@ -10,7 +10,7 @@ workflows: name: 'test_go_<< matrix.go_version >>' matrix: parameters: - go_version: ['1.16', '1.17'] + go_version: ['1.17'] jobs: test: diff --git a/.gitignore b/.gitignore index 1120780e..1dc900c4 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,6 @@ coverage.html # Testing files *.feature temp + +# asdf +.tool-versions diff --git a/client/v2/algod/algod.go b/client/v2/algod/algod.go index 96105f0a..a5b8ce87 100644 --- a/client/v2/algod/algod.go +++ b/client/v2/algod/algod.go @@ -128,6 +128,14 @@ func (c *Client) GetApplicationByID(applicationId uint64) *GetApplicationByID { return &GetApplicationByID{c: c, applicationId: applicationId} } +func (c *Client) GetApplicationBoxes(applicationId uint64) *GetApplicationBoxes { + return &GetApplicationBoxes{c: c, applicationId: applicationId} +} + +func (c *Client) GetApplicationBoxByName(applicationId uint64, name []byte) *GetApplicationBoxByName { + return (&GetApplicationBoxByName{c: c, applicationId: applicationId}).name(name) +} + func (c *Client) GetAssetByID(assetId uint64) *GetAssetByID { return &GetAssetByID{c: c, assetId: assetId} } diff --git a/client/v2/algod/getApplicationBoxByName.go b/client/v2/algod/getApplicationBoxByName.go new file mode 100644 index 00000000..0fc8f2af --- /dev/null +++ b/client/v2/algod/getApplicationBoxByName.go @@ -0,0 +1,47 @@ +package algod + +import ( + "context" + "encoding/base64" + "fmt" + + "github.com/algorand/go-algorand-sdk/client/v2/common" + "github.com/algorand/go-algorand-sdk/client/v2/common/models" +) + +// GetApplicationBoxByNameParams contains all of the query parameters for url serialization. +type GetApplicationBoxByNameParams struct { + + // Name a box name, in the goal app call arg form 'encoding:value'. For ints, use + // the form 'int:1234'. For raw bytes, use the form 'b64:A=='. For printable + // strings, use the form 'str:hello'. For addresses, use the form 'addr:XYZ...'. + Name string `url:"name,omitempty"` +} + +// GetApplicationBoxByName given an application ID and box name, it returns the box +// name and value (each base64 encoded). Box names must be in the goal app call arg +// encoding form 'encoding:value'. For ints, use the form 'int:1234'. For raw +// bytes, use the form 'b64:A=='. For printable strings, use the form 'str:hello'. +// For addresses, use the form 'addr:XYZ...'. +type GetApplicationBoxByName struct { + c *Client + + applicationId uint64 + + p GetApplicationBoxByNameParams +} + +// name a box name, in the goal app call arg form 'encoding:value'. For ints, use +// the form 'int:1234'. For raw bytes, use the form 'b64:A=='. For printable +// strings, use the form 'str:hello'. For addresses, use the form 'addr:XYZ...'. +func (s *GetApplicationBoxByName) name(name []byte) *GetApplicationBoxByName { + s.p.Name = "b64:" + base64.StdEncoding.EncodeToString(name) + + return s +} + +// Do performs the HTTP request +func (s *GetApplicationBoxByName) Do(ctx context.Context, headers ...*common.Header) (response models.Box, err error) { + err = s.c.get(ctx, &response, fmt.Sprintf("/v2/applications/%s/box", common.EscapeParams(s.applicationId)...), s.p, headers) + return +} diff --git a/client/v2/algod/getApplicationBoxes.go b/client/v2/algod/getApplicationBoxes.go new file mode 100644 index 00000000..c5a49d8d --- /dev/null +++ b/client/v2/algod/getApplicationBoxes.go @@ -0,0 +1,42 @@ +package algod + +import ( + "context" + "fmt" + + "github.com/algorand/go-algorand-sdk/client/v2/common" + "github.com/algorand/go-algorand-sdk/client/v2/common/models" +) + +// GetApplicationBoxesParams contains all of the query parameters for url serialization. +type GetApplicationBoxesParams struct { + + // Max max number of box names to return. If max is not set, or max == 0, returns + // all box-names. + Max uint64 `url:"max,omitempty"` +} + +// GetApplicationBoxes given an application ID, return all Box names. No particular +// ordering is guaranteed. Request fails when client or server-side configured +// limits prevent returning all Box names. +type GetApplicationBoxes struct { + c *Client + + applicationId uint64 + + p GetApplicationBoxesParams +} + +// Max max number of box names to return. If max is not set, or max == 0, returns +// all box-names. +func (s *GetApplicationBoxes) Max(Max uint64) *GetApplicationBoxes { + s.p.Max = Max + + return s +} + +// Do performs the HTTP request +func (s *GetApplicationBoxes) Do(ctx context.Context, headers ...*common.Header) (response models.BoxesResponse, err error) { + err = s.c.get(ctx, &response, fmt.Sprintf("/v2/applications/%s/boxes", common.EscapeParams(s.applicationId)...), s.p, headers) + return +} diff --git a/client/v2/common/models/account.go b/client/v2/common/models/account.go index 88a054de..c394ca61 100644 --- a/client/v2/common/models/account.go +++ b/client/v2/common/models/account.go @@ -97,6 +97,14 @@ type Account struct { // to the count of AssetHolding objects held by this account. TotalAssetsOptedIn uint64 `json:"total-assets-opted-in"` + // TotalBoxBytes for app-accounts only. The total number of bytes allocated for the + // keys and values of boxes which belong to the associated application. + TotalBoxBytes uint64 `json:"total-box-bytes"` + + // TotalBoxes for app-accounts only. The total number of boxes which belong to the + // associated application. + TotalBoxes uint64 `json:"total-boxes"` + // TotalCreatedApps the count of all apps (AppParams objects) created by this // account. TotalCreatedApps uint64 `json:"total-created-apps"` diff --git a/client/v2/common/models/box.go b/client/v2/common/models/box.go new file mode 100644 index 00000000..5bc552cb --- /dev/null +++ b/client/v2/common/models/box.go @@ -0,0 +1,10 @@ +package models + +// Box box name and its content. +type Box struct { + // Name (name) box name, base64 encoded + Name []byte `json:"name"` + + // Value (value) box value, base64 encoded. + Value []byte `json:"value"` +} diff --git a/client/v2/common/models/box_descriptor.go b/client/v2/common/models/box_descriptor.go new file mode 100644 index 00000000..54d02974 --- /dev/null +++ b/client/v2/common/models/box_descriptor.go @@ -0,0 +1,7 @@ +package models + +// BoxDescriptor box descriptor describes an app box without a value. +type BoxDescriptor struct { + // Name base64 encoded box name + Name []byte `json:"name"` +} diff --git a/client/v2/common/models/boxes_response.go b/client/v2/common/models/boxes_response.go new file mode 100644 index 00000000..bf869efe --- /dev/null +++ b/client/v2/common/models/boxes_response.go @@ -0,0 +1,14 @@ +package models + +// BoxesResponse box names of an application +type BoxesResponse struct { + // ApplicationId (appidx) application index. + ApplicationId uint64 `json:"application-id"` + + // Boxes + Boxes []BoxDescriptor `json:"boxes"` + + // NextToken used for pagination, when making another request provide this token + // with the next parameter. + NextToken string `json:"next-token,omitempty"` +} diff --git a/client/v2/indexer/indexer.go b/client/v2/indexer/indexer.go index 188db972..6bfb86c8 100644 --- a/client/v2/indexer/indexer.go +++ b/client/v2/indexer/indexer.go @@ -87,6 +87,14 @@ func (c *Client) LookupApplicationByID(applicationId uint64) *LookupApplicationB return &LookupApplicationByID{c: c, applicationId: applicationId} } +func (c *Client) SearchForApplicationBoxes(applicationId uint64) *SearchForApplicationBoxes { + return &SearchForApplicationBoxes{c: c, applicationId: applicationId} +} + +func (c *Client) LookupApplicationBoxByIDAndName(applicationId uint64, name []byte) *LookupApplicationBoxByIDAndName { + return (&LookupApplicationBoxByIDAndName{c: c, applicationId: applicationId}).name(name) +} + func (c *Client) LookupApplicationLogsByID(applicationId uint64) *LookupApplicationLogsByID { return &LookupApplicationLogsByID{c: c, applicationId: applicationId} } diff --git a/client/v2/indexer/lookupApplicationBoxByIDandName.go b/client/v2/indexer/lookupApplicationBoxByIDandName.go new file mode 100644 index 00000000..344b3070 --- /dev/null +++ b/client/v2/indexer/lookupApplicationBoxByIDandName.go @@ -0,0 +1,47 @@ +package indexer + +import ( + "context" + "encoding/base64" + "fmt" + + "github.com/algorand/go-algorand-sdk/client/v2/common" + "github.com/algorand/go-algorand-sdk/client/v2/common/models" +) + +// LookupApplicationBoxByIDAndNameParams contains all of the query parameters for url serialization. +type LookupApplicationBoxByIDAndNameParams struct { + + // Name a box name in goal-arg form 'encoding:value'. For ints, use the form + // 'int:1234'. For raw bytes, use the form 'b64:A=='. For printable strings, use + // the form 'str:hello'. For addresses, use the form 'addr:XYZ...'. + Name string `url:"name,omitempty"` +} + +// LookupApplicationBoxByIDAndName given an application ID and box name, returns +// base64 encoded box name and value. Box names must be in the goal app call arg +// form 'encoding:value'. For ints, use the form 'int:1234'. For raw bytes, encode +// base 64 and use 'b64' prefix as in 'b64:A=='. For printable strings, use the +// form 'str:hello'. For addresses, use the form 'addr:XYZ...'. +type LookupApplicationBoxByIDAndName struct { + c *Client + + applicationId uint64 + + p LookupApplicationBoxByIDAndNameParams +} + +// name a box name in goal-arg form 'encoding:value'. For ints, use the form +// 'int:1234'. For raw bytes, use the form 'b64:A=='. For printable strings, use +// the form 'str:hello'. For addresses, use the form 'addr:XYZ...'. +func (s *LookupApplicationBoxByIDAndName) name(name []byte) *LookupApplicationBoxByIDAndName { + s.p.Name = "b64:" + base64.StdEncoding.EncodeToString(name) + + return s +} + +// Do performs the HTTP request +func (s *LookupApplicationBoxByIDAndName) Do(ctx context.Context, headers ...*common.Header) (response models.Box, err error) { + err = s.c.get(ctx, &response, fmt.Sprintf("/v2/applications/%s/box", common.EscapeParams(s.applicationId)...), s.p, headers) + return +} diff --git a/client/v2/indexer/searchForApplicationBoxes.go b/client/v2/indexer/searchForApplicationBoxes.go new file mode 100644 index 00000000..4f05e68e --- /dev/null +++ b/client/v2/indexer/searchForApplicationBoxes.go @@ -0,0 +1,53 @@ +package indexer + +import ( + "context" + "fmt" + + "github.com/algorand/go-algorand-sdk/client/v2/common" + "github.com/algorand/go-algorand-sdk/client/v2/common/models" +) + +// SearchForApplicationBoxesParams contains all of the query parameters for url serialization. +type SearchForApplicationBoxesParams struct { + + // Limit maximum number of results to return. There could be additional pages even + // if the limit is not reached. + Limit uint64 `url:"limit,omitempty"` + + // Next the next page of results. Use the next token provided by the previous + // results. + Next string `url:"next,omitempty"` +} + +// SearchForApplicationBoxes given an application ID, returns the box names of that +// application sorted lexicographically. +type SearchForApplicationBoxes struct { + c *Client + + applicationId uint64 + + p SearchForApplicationBoxesParams +} + +// Limit maximum number of results to return. There could be additional pages even +// if the limit is not reached. +func (s *SearchForApplicationBoxes) Limit(Limit uint64) *SearchForApplicationBoxes { + s.p.Limit = Limit + + return s +} + +// Next the next page of results. Use the next token provided by the previous +// results. +func (s *SearchForApplicationBoxes) Next(Next string) *SearchForApplicationBoxes { + s.p.Next = Next + + return s +} + +// Do performs the HTTP request +func (s *SearchForApplicationBoxes) Do(ctx context.Context, headers ...*common.Header) (response models.BoxesResponse, err error) { + err = s.c.get(ctx, &response, fmt.Sprintf("/v2/applications/%s/boxes", common.EscapeParams(s.applicationId)...), s.p, headers) + return +} diff --git a/future/atomicTransactionComposer.go b/future/atomicTransactionComposer.go index f1e65abc..dd996dc9 100644 --- a/future/atomicTransactionComposer.go +++ b/future/atomicTransactionComposer.go @@ -97,6 +97,9 @@ type AddMethodCallParams struct { // Any foreign accounts to be passed that aren't part of the method signature // If accounts are provided here, the accounts specified in the method args will appear after these ForeignAccounts []string + + // References of the boxes to be accessed by this method call. + BoxReferences []types.AppBoxReference } // ExecuteResult contains the results of successfully calling the Execute method on an @@ -167,8 +170,8 @@ type AtomicTransactionComposer struct { // The current status of the composer. The status increases monotonically. status AtomicTransactionComposerStatus - // The transaction contexts in the group with their respective signers. If status is greater then - // BUILDING then this slice cannot change. + // The transaction contexts in the group with their respective signers. + // If status is greater than BUILDING, then this slice cannot change. txContexts []transactionContext } @@ -373,17 +376,19 @@ func (atc *AtomicTransactionComposer) AddMethodCall(params AddMethodCallParams) encodedAbiArgs = append(encodedAbiArgs, encodedArg) } - tx, err := MakeApplicationCallTx( + tx, err := MakeApplicationCallTxWithBoxes( params.AppID, encodedAbiArgs, foreignAccounts, foreignApps, foreignAssets, + params.BoxReferences, params.OnComplete, params.ApprovalProgram, params.ClearProgram, params.GlobalSchema, params.LocalSchema, + params.ExtraPages, params.SuggestedParams, params.Sender, params.Note, @@ -394,13 +399,6 @@ func (atc *AtomicTransactionComposer) AddMethodCall(params AddMethodCallParams) return err } - if params.ExtraPages != 0 { - tx, err = MakeApplicationCallTxWithExtraPages(tx, params.ExtraPages) - if err != nil { - return err - } - } - txAndSigner := TransactionWithSigner{ Txn: tx, Signer: params.Signer, diff --git a/future/transaction.go b/future/transaction.go index f42addc1..11697c6c 100644 --- a/future/transaction.go +++ b/future/transaction.go @@ -597,17 +597,22 @@ func byte64FromBase64(in string) (out [64]byte, err error) { // maintained inside the account of any users who opt into this // application. The LocalStateSchema is immutable. // +// - extraPages ExtraProgramPages specifies the additional app program size requested in pages. +// A page is 1024 bytes. This field enables execution of app programs +// larger than the default maximum program size. +// // - onComplete This is the faux application type used to distinguish different // application actions. Specifically, OnCompletion specifies what // side effects this transaction will have if it successfully makes // it into a block. // -// - extraPages ExtraProgramPages specifies the additional app program size requested in pages. -// A page is 1024 bytes. This field enables execution of app programs -// larger than the default maximum program size. +// - boxes lists the boxes to be accessed during evaluation of the application +// call. This also must include the boxes accessed by inner app calls. // MakeApplicationCreateTx makes a transaction for creating an application (see above for args desc.) // - optIn: true for opting in on complete, false for no-op. +// +// NOTE: if you need to use extra pages or boxes, use MakeApplicationCreateTxWithBoxes instead. func MakeApplicationCreateTx( optIn bool, approvalProg []byte, @@ -624,23 +629,60 @@ func MakeApplicationCreateTx( group types.Digest, lease [32]byte, rekeyTo types.Address) (tx types.Transaction, err error) { - - oncomp := types.NoOpOC - if optIn { - oncomp = types.OptInOC - } - - return MakeApplicationCallTx( + return MakeApplicationCreateTxWithBoxes( + optIn, + approvalProg, + clearProg, + globalSchema, + localSchema, 0, appArgs, accounts, foreignApps, foreignAssets, - oncomp, + nil, + sp, + sender, + note, + group, + lease, + rekeyTo, + ) +} + +// MakeApplicationCreateTxWithExtraPages makes a transaction for creating an application (see above for args desc.) +// - optIn: true for opting in on complete, false for no-op. +// +// NOTE: if you need to use boxes, use MakeApplicationCreateTxWithBoxes instead. +func MakeApplicationCreateTxWithExtraPages( + optIn bool, + approvalProg []byte, + clearProg []byte, + globalSchema types.StateSchema, + localSchema types.StateSchema, + appArgs [][]byte, + accounts []string, + foreignApps []uint64, + foreignAssets []uint64, + sp types.SuggestedParams, + sender types.Address, + note []byte, + group types.Digest, + lease [32]byte, + rekeyTo types.Address, + extraPages uint32) (tx types.Transaction, err error) { + return MakeApplicationCreateTxWithBoxes( + optIn, approvalProg, clearProg, globalSchema, localSchema, + extraPages, + appArgs, + accounts, + foreignApps, + foreignAssets, + nil, sp, sender, note, @@ -650,53 +692,57 @@ func MakeApplicationCreateTx( ) } -func MakeApplicationCreateTxWithExtraPages( +// MakeApplicationCreateTxWithBoxes makes a transaction for creating an application (see above for args desc.) +// - optIn: true for opting in on complete, false for no-op. +func MakeApplicationCreateTxWithBoxes( optIn bool, approvalProg []byte, clearProg []byte, globalSchema types.StateSchema, localSchema types.StateSchema, + extraPages uint32, appArgs [][]byte, accounts []string, foreignApps []uint64, foreignAssets []uint64, + appBoxReferences []types.AppBoxReference, sp types.SuggestedParams, sender types.Address, note []byte, group types.Digest, lease [32]byte, - rekeyTo types.Address, extraPages uint32) (tx types.Transaction, err error) { + rekeyTo types.Address) (tx types.Transaction, err error) { oncomp := types.NoOpOC if optIn { oncomp = types.OptInOC } - appCallTx, err := MakeApplicationCallTx( + return MakeApplicationCallTxWithBoxes( 0, appArgs, accounts, foreignApps, foreignAssets, + appBoxReferences, oncomp, approvalProg, clearProg, globalSchema, localSchema, + extraPages, sp, sender, note, group, lease, - rekeyTo) - - if err != nil { - return - } - return MakeApplicationCallTxWithExtraPages(appCallTx, extraPages) + rekeyTo, + ) } // MakeApplicationUpdateTx makes a transaction for updating an application's programs (see above for args desc.) +// +// NOTE: if you need to use boxes, use MakeApplicationUpdateTxWithBoxes instead. func MakeApplicationUpdateTx( appIdx uint64, appArgs [][]byte, @@ -711,16 +757,51 @@ func MakeApplicationUpdateTx( group types.Digest, lease [32]byte, rekeyTo types.Address) (tx types.Transaction, err error) { - return MakeApplicationCallTx(appIdx, + return MakeApplicationUpdateTxWithBoxes(appIdx, appArgs, accounts, foreignApps, foreignAssets, + nil, + approvalProg, + clearProg, + sp, + sender, + note, + group, + lease, + rekeyTo, + ) +} + +// MakeApplicationUpdateTxWithBoxes makes a transaction for updating an application's programs (see above for args desc.) +func MakeApplicationUpdateTxWithBoxes( + appIdx uint64, + appArgs [][]byte, + accounts []string, + foreignApps []uint64, + foreignAssets []uint64, + appBoxReferences []types.AppBoxReference, + approvalProg []byte, + clearProg []byte, + sp types.SuggestedParams, + sender types.Address, + note []byte, + group types.Digest, + lease [32]byte, + rekeyTo types.Address) (tx types.Transaction, err error) { + return MakeApplicationCallTxWithBoxes(appIdx, + appArgs, + accounts, + foreignApps, + foreignAssets, + appBoxReferences, types.UpdateApplicationOC, approvalProg, clearProg, emptySchema, emptySchema, + 0, sp, sender, note, @@ -731,6 +812,8 @@ func MakeApplicationUpdateTx( } // MakeApplicationDeleteTx makes a transaction for deleting an application (see above for args desc.) +// +// NOTE: if you need to use boxes, use MakeApplicationDeleteTxWithBoxes instead. func MakeApplicationDeleteTx( appIdx uint64, appArgs [][]byte, @@ -743,16 +826,47 @@ func MakeApplicationDeleteTx( group types.Digest, lease [32]byte, rekeyTo types.Address) (tx types.Transaction, err error) { - return MakeApplicationCallTx(appIdx, + return MakeApplicationDeleteTxWithBoxes(appIdx, + appArgs, + accounts, + foreignApps, + foreignAssets, + nil, + sp, + sender, + note, + group, + lease, + rekeyTo, + ) +} + +// MakeApplicationDeleteTxWithBoxes makes a transaction for deleting an application (see above for args desc.) +func MakeApplicationDeleteTxWithBoxes( + appIdx uint64, + appArgs [][]byte, + accounts []string, + foreignApps []uint64, + foreignAssets []uint64, + appBoxReferences []types.AppBoxReference, + sp types.SuggestedParams, + sender types.Address, + note []byte, + group types.Digest, + lease [32]byte, + rekeyTo types.Address) (tx types.Transaction, err error) { + return MakeApplicationCallTxWithBoxes(appIdx, appArgs, accounts, foreignApps, foreignAssets, + appBoxReferences, types.DeleteApplicationOC, nil, nil, emptySchema, emptySchema, + 0, sp, sender, note, @@ -764,6 +878,8 @@ func MakeApplicationDeleteTx( // MakeApplicationOptInTx makes a transaction for opting in to (allocating // some account-specific state for) an application (see above for args desc.) +// +// NOTE: if you need to use boxes, use MakeApplicationOptInTxWithBoxes instead. func MakeApplicationOptInTx( appIdx uint64, appArgs [][]byte, @@ -776,16 +892,48 @@ func MakeApplicationOptInTx( group types.Digest, lease [32]byte, rekeyTo types.Address) (tx types.Transaction, err error) { - return MakeApplicationCallTx(appIdx, + return MakeApplicationOptInTxWithBoxes(appIdx, appArgs, accounts, foreignApps, foreignAssets, + nil, + sp, + sender, + note, + group, + lease, + rekeyTo, + ) +} + +// MakeApplicationOptInTxWithBoxes makes a transaction for opting in to (allocating +// some account-specific state for) an application (see above for args desc.) +func MakeApplicationOptInTxWithBoxes( + appIdx uint64, + appArgs [][]byte, + accounts []string, + foreignApps []uint64, + foreignAssets []uint64, + appBoxReferences []types.AppBoxReference, + sp types.SuggestedParams, + sender types.Address, + note []byte, + group types.Digest, + lease [32]byte, + rekeyTo types.Address) (tx types.Transaction, err error) { + return MakeApplicationCallTxWithBoxes(appIdx, + appArgs, + accounts, + foreignApps, + foreignAssets, + appBoxReferences, types.OptInOC, nil, nil, emptySchema, emptySchema, + 0, sp, sender, note, @@ -797,6 +945,9 @@ func MakeApplicationOptInTx( // MakeApplicationCloseOutTx makes a transaction for closing out of // (deallocating all account-specific state for) an application (see above for args desc.) +// +// NOTE: if you need to use boxes, use MakeApplicationCloseOutTxWithBoxes +// instead. func MakeApplicationCloseOutTx( appIdx uint64, appArgs [][]byte, @@ -809,16 +960,48 @@ func MakeApplicationCloseOutTx( group types.Digest, lease [32]byte, rekeyTo types.Address) (tx types.Transaction, err error) { - return MakeApplicationCallTx(appIdx, + return MakeApplicationCloseOutTxWithBoxes(appIdx, + appArgs, + accounts, + foreignApps, + foreignAssets, + nil, + sp, + sender, + note, + group, + lease, + rekeyTo, + ) +} + +// MakeApplicationCloseOutTxWithBoxes makes a transaction for closing out of +// (deallocating all account-specific state for) an application (see above for args desc.) +func MakeApplicationCloseOutTxWithBoxes( + appIdx uint64, + appArgs [][]byte, + accounts []string, + foreignApps []uint64, + foreignAssets []uint64, + appBoxReferences []types.AppBoxReference, + sp types.SuggestedParams, + sender types.Address, + note []byte, + group types.Digest, + lease [32]byte, + rekeyTo types.Address) (tx types.Transaction, err error) { + return MakeApplicationCallTxWithBoxes(appIdx, appArgs, accounts, foreignApps, foreignAssets, + appBoxReferences, types.CloseOutOC, nil, nil, emptySchema, emptySchema, + 0, sp, sender, note, @@ -831,6 +1014,9 @@ func MakeApplicationCloseOutTx( // MakeApplicationClearStateTx makes a transaction for clearing out all // account-specific state for an application. It may not be rejected by the // application's logic. (see above for args desc.) +// +// NOTE: if you need to use boxes, use MakeApplicationClearStateTxWithBoxes +// instead. func MakeApplicationClearStateTx( appIdx uint64, appArgs [][]byte, @@ -843,16 +1029,49 @@ func MakeApplicationClearStateTx( group types.Digest, lease [32]byte, rekeyTo types.Address) (tx types.Transaction, err error) { - return MakeApplicationCallTx(appIdx, + return MakeApplicationClearStateTxWithBoxes(appIdx, appArgs, accounts, foreignApps, foreignAssets, + nil, + sp, + sender, + note, + group, + lease, + rekeyTo, + ) +} + +// MakeApplicationClearStateTxWithBoxes makes a transaction for clearing out all +// account-specific state for an application. It may not be rejected by the +// application's logic. (see above for args desc.) +func MakeApplicationClearStateTxWithBoxes( + appIdx uint64, + appArgs [][]byte, + accounts []string, + foreignApps []uint64, + foreignAssets []uint64, + appBoxReferences []types.AppBoxReference, + sp types.SuggestedParams, + sender types.Address, + note []byte, + group types.Digest, + lease [32]byte, + rekeyTo types.Address) (tx types.Transaction, err error) { + return MakeApplicationCallTxWithBoxes(appIdx, + appArgs, + accounts, + foreignApps, + foreignAssets, + appBoxReferences, types.ClearStateOC, nil, nil, emptySchema, emptySchema, + 0, sp, sender, note, @@ -865,6 +1084,8 @@ func MakeApplicationClearStateTx( // MakeApplicationNoOpTx makes a transaction for interacting with an existing // application, potentially updating any account-specific local state and // global state associated with it. (see above for args desc.) +// +// NOTE: if you need to use boxes, use MakeApplicationNoOpTxWithBoxes instead. func MakeApplicationNoOpTx( appIdx uint64, appArgs [][]byte, @@ -877,17 +1098,51 @@ func MakeApplicationNoOpTx( group types.Digest, lease [32]byte, rekeyTo types.Address) (tx types.Transaction, err error) { - return MakeApplicationCallTx( + return MakeApplicationNoOpTxWithBoxes( appIdx, appArgs, accounts, foreignApps, foreignAssets, + nil, + sp, + sender, + note, + group, + lease, + rekeyTo, + ) +} + +// MakeApplicationNoOpTxWithBoxes makes a transaction for interacting with an +// existing application, potentially updating any account-specific local state +// and global state associated with it. (see above for args desc.) +func MakeApplicationNoOpTxWithBoxes( + appIdx uint64, + appArgs [][]byte, + accounts []string, + foreignApps []uint64, + foreignAssets []uint64, + appBoxReferences []types.AppBoxReference, + sp types.SuggestedParams, + sender types.Address, + note []byte, + group types.Digest, + lease [32]byte, + rekeyTo types.Address) (tx types.Transaction, err error) { + return MakeApplicationCallTxWithBoxes( + appIdx, + appArgs, + accounts, + foreignApps, + foreignAssets, + appBoxReferences, types.NoOpOC, nil, nil, emptySchema, emptySchema, + 0, sp, sender, note, @@ -900,6 +1155,9 @@ func MakeApplicationNoOpTx( // MakeApplicationCallTx is a helper for the above ApplicationCall // transaction constructors. A fully custom ApplicationCall transaction may // be constructed using this method. (see above for args desc.) +// +// NOTE: if you need to use boxes or extra program pages, use +// MakeApplicationCallTxWithBoxes instead. func MakeApplicationCallTx( appIdx uint64, appArgs [][]byte, @@ -917,6 +1175,61 @@ func MakeApplicationCallTx( group types.Digest, lease [32]byte, rekeyTo types.Address) (tx types.Transaction, err error) { + return MakeApplicationCallTxWithBoxes( + appIdx, + appArgs, + accounts, + foreignApps, + foreignAssets, + nil, + onCompletion, + approvalProg, + clearProg, + globalSchema, + localSchema, + 0, + sp, + sender, + note, + group, + lease, + rekeyTo, + ) +} + +// MakeApplicationCallTxWithExtraPages sets the ExtraProgramPages on an existing +// application call transaction. +// +// Consider using MakeApplicationCallTxWithBoxes instead if you wish to assign +// the extra pages value at creation. +func MakeApplicationCallTxWithExtraPages( + txn types.Transaction, extraPages uint32) (types.Transaction, error) { + txn.ExtraProgramPages = extraPages + return txn, nil +} + +// MakeApplicationCallTxWithBoxes is a helper for the above ApplicationCall +// transaction constructors. A fully custom ApplicationCall transaction may +// be constructed using this method. (see above for args desc.) +func MakeApplicationCallTxWithBoxes( + appIdx uint64, + appArgs [][]byte, + accounts []string, + foreignApps []uint64, + foreignAssets []uint64, + appBoxReferences []types.AppBoxReference, + onCompletion types.OnCompletion, + approvalProg []byte, + clearProg []byte, + globalSchema types.StateSchema, + localSchema types.StateSchema, + extraPages uint32, + sp types.SuggestedParams, + sender types.Address, + note []byte, + group types.Digest, + lease [32]byte, + rekeyTo types.Address) (tx types.Transaction, err error) { tx.Type = types.ApplicationCallTx tx.ApplicationID = types.AppIndex(appIdx) tx.OnCompletion = onCompletion @@ -929,10 +1242,16 @@ func MakeApplicationCallTx( tx.ForeignApps = parseTxnForeignApps(foreignApps) tx.ForeignAssets = parseTxnForeignAssets(foreignAssets) + tx.BoxReferences, err = parseBoxReferences(appBoxReferences, foreignApps, appIdx) + if err != nil { + return tx, err + } + tx.ApprovalProgram = approvalProg tx.ClearStateProgram = clearProg tx.LocalStateSchema = localSchema tx.GlobalStateSchema = globalSchema + tx.ExtraProgramPages = extraPages var gh types.Digest copy(gh[:], sp.GenesisHash) @@ -954,12 +1273,6 @@ func MakeApplicationCallTx( return setFee(tx, sp) } -func MakeApplicationCallTxWithExtraPages( - txn types.Transaction, extraPages uint32) (types.Transaction, error) { - txn.ExtraProgramPages = extraPages - return txn, nil -} - func parseTxnAccounts(accounts []string) (parsed []types.Address, err error) { for _, acct := range accounts { addr, err := types.DecodeAddress(acct) @@ -985,4 +1298,43 @@ func parseTxnForeignAssets(foreignAssets []uint64) (parsed []types.AssetIndex) { return } +func parseBoxReferences(abrs []types.AppBoxReference, foreignApps []uint64, curAppID uint64) (parsed []types.BoxReference, err error) { + for _, abr := range abrs { + // there are a few unintuitive details to the parsing: + // 1. the AppID of the box must either be in the foreign apps array or + // equal to 0, which references the current app. + // 2. if the box references the current app by its appID rather than 0 AND + // the current appID is explicitly provided in the foreign apps array + // then ForeignAppIdx should be set to its index in the array. + br := types.BoxReference{Name: abr.Name} + found := false + + if abr.AppID == 0 { + found = true + br.ForeignAppIdx = 0 + } else { + for idx, appID := range foreignApps { + if appID == abr.AppID { + found = true + br.ForeignAppIdx = uint64(idx + 1) + break + } + } + } + + if !found && abr.AppID == curAppID { + found = true + br.ForeignAppIdx = 0 + } + + if !found { + return nil, fmt.Errorf("the app id %d provided for this box is not in the foreignApps array", abr.AppID) + } + + parsed = append(parsed, br) + } + + return +} + var emptySchema = types.StateSchema{} diff --git a/future/transaction_test.go b/future/transaction_test.go index 02950b84..7f6da1eb 100644 --- a/future/transaction_test.go +++ b/future/transaction_test.go @@ -57,6 +57,7 @@ func TestMakePaymentTxn(t *testing.T) { require.NoError(t, err) id, bytes, err := crypto.SignTransaction(key, txn) + require.NoError(t, err) stxBytes := byteFromBase64(golden) require.Equal(t, stxBytes, bytes) @@ -102,6 +103,7 @@ func TestMakePaymentTxnWithLease(t *testing.T) { require.NoError(t, err) id, stxBytes, err := crypto.SignTransaction(key, txn) + require.NoError(t, err) goldenBytes := byteFromBase64(golden) require.Equal(t, goldenBytes, stxBytes) @@ -293,6 +295,7 @@ func TestMakeAssetCreateTxn(t *testing.T) { private, err := mnemonic.ToPrivateKey(addrSK) require.NoError(t, err) _, newStxBytes, err := crypto.SignTransaction(private, tx) + require.NoError(t, err) signedGolden := "gqNzaWfEQEDd1OMRoQI/rzNlU4iiF50XQXmup3k5czI9hEsNqHT7K4KsfmA/0DUVkbzOwtJdRsHS8trm3Arjpy9r7AXlbAujdHhuh6RhcGFyiaJhbcQgZkFDUE80blJnTzU1ajFuZEFLM1c2U2djNEFQa2N5RmiiYW6odGVzdGNvaW6iYXWnd2Vic2l0ZaFjxCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aFmxCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aFtxCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aFyxCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aF0ZKJ1bqN0c3SjZmVlzQ+0omZ2zgAE7A+iZ2jEIEhjtRiks8hOyBDyLU8QgcsPcfBZp6wg3sYvf3DlCToiomx2zgAE7/ejc25kxCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aR0eXBlpGFjZmc=" require.EqualValues(t, newStxBytes, byteFromBase64(signedGolden)) } @@ -352,6 +355,7 @@ func TestMakeAssetCreateTxnWithDecimals(t *testing.T) { private, err := mnemonic.ToPrivateKey(addrSK) require.NoError(t, err) _, newStxBytes, err := crypto.SignTransaction(private, tx) + require.NoError(t, err) signedGolden := "gqNzaWfEQCj5xLqNozR5ahB+LNBlTG+d0gl0vWBrGdAXj1ibsCkvAwOsXs5KHZK1YdLgkdJecQiWm4oiZ+pm5Yg0m3KFqgqjdHhuh6RhcGFyiqJhbcQgZkFDUE80blJnTzU1ajFuZEFLM1c2U2djNEFQa2N5RmiiYW6odGVzdGNvaW6iYXWnd2Vic2l0ZaFjxCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aJkYwGhZsQgCfvSdiwI+Gxa5r9t16epAd5mdddQ4H6MXHaYZH224f2hbcQgCfvSdiwI+Gxa5r9t16epAd5mdddQ4H6MXHaYZH224f2hcsQgCfvSdiwI+Gxa5r9t16epAd5mdddQ4H6MXHaYZH224f2hdGSidW6jdHN0o2ZlZc0P3KJmds4ABOwPomdoxCBIY7UYpLPITsgQ8i1PEIHLD3HwWaesIN7GL39w5Qk6IqJsds4ABO/3o3NuZMQgCfvSdiwI+Gxa5r9t16epAd5mdddQ4H6MXHaYZH224f2kdHlwZaRhY2Zn" require.EqualValues(t, newStxBytes, byteFromBase64(signedGolden)) } @@ -401,6 +405,7 @@ func TestMakeAssetConfigTxn(t *testing.T) { private, err := mnemonic.ToPrivateKey(addrSK) require.NoError(t, err) _, newStxBytes, err := crypto.SignTransaction(private, tx) + require.NoError(t, err) signedGolden := "gqNzaWfEQBBkfw5n6UevuIMDo2lHyU4dS80JCCQ/vTRUcTx5m0ivX68zTKyuVRrHaTbxbRRc3YpJ4zeVEnC9Fiw3Wf4REwejdHhuiKRhcGFyhKFjxCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aFmxCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aFtxCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aFyxCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aRjYWlkzQTSo2ZlZc0NSKJmds4ABOwPomdoxCBIY7UYpLPITsgQ8i1PEIHLD3HwWaesIN7GL39w5Qk6IqJsds4ABO/3o3NuZMQgCfvSdiwI+Gxa5r9t16epAd5mdddQ4H6MXHaYZH224f2kdHlwZaRhY2Zn" require.EqualValues(t, newStxBytes, byteFromBase64(signedGolden)) } @@ -463,6 +468,7 @@ func TestMakeAssetDestroyTxn(t *testing.T) { private, err := mnemonic.ToPrivateKey(addrSK) require.NoError(t, err) _, newStxBytes, err := crypto.SignTransaction(private, tx) + require.NoError(t, err) signedGolden := "gqNzaWfEQBSP7HtzD/Lvn4aVvaNpeR4T93dQgo4LvywEwcZgDEoc/WVl3aKsZGcZkcRFoiWk8AidhfOZzZYutckkccB8RgGjdHhuh6RjYWlkAaNmZWXNB1iiZnbOAATsD6JnaMQgSGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiKibHbOAATv96NzbmTEIAn70nYsCPhsWua/bdenqQHeZnXXUOB+jFx2mGR9tuH9pHR5cGWkYWNmZw==" require.EqualValues(t, newStxBytes, byteFromBase64(signedGolden)) } @@ -508,6 +514,7 @@ func TestMakeAssetFreezeTxn(t *testing.T) { private, err := mnemonic.ToPrivateKey(addrSK) require.NoError(t, err) _, newStxBytes, err := crypto.SignTransaction(private, tx) + require.NoError(t, err) signedGolden := "gqNzaWfEQAhru5V2Xvr19s4pGnI0aslqwY4lA2skzpYtDTAN9DKSH5+qsfQQhm4oq+9VHVj7e1rQC49S28vQZmzDTVnYDQGjdHhuiaRhZnJ6w6RmYWRkxCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aRmYWlkAaNmZWXNCRqiZnbOAATsD6JnaMQgSGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiKibHbOAATv+KNzbmTEIAn70nYsCPhsWua/bdenqQHeZnXXUOB+jFx2mGR9tuH9pHR5cGWkYWZyeg==" require.EqualValues(t, newStxBytes, byteFromBase64(signedGolden)) } @@ -613,6 +620,7 @@ func TestMakeAssetAcceptanceTxn(t *testing.T) { private, err := mnemonic.ToPrivateKey(addrSK) require.NoError(t, err) _, newStxBytes, err := crypto.SignTransaction(private, tx) + require.NoError(t, err) signedGolden := "gqNzaWfEQJ7q2rOT8Sb/wB0F87ld+1zMprxVlYqbUbe+oz0WM63FctIi+K9eYFSqT26XBZ4Rr3+VTJpBE+JLKs8nctl9hgijdHhuiKRhcmN2xCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aNmZWXNCOiiZnbOAATsD6JnaMQgSGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiKibHbOAATv96NzbmTEIAn70nYsCPhsWua/bdenqQHeZnXXUOB+jFx2mGR9tuH9pHR5cGWlYXhmZXKkeGFpZAE=" require.EqualValues(t, newStxBytes, byteFromBase64(signedGolden)) } @@ -670,6 +678,7 @@ func TestMakeAssetRevocationTransaction(t *testing.T) { private, err := mnemonic.ToPrivateKey(addrSK) require.NoError(t, err) _, newStxBytes, err := crypto.SignTransaction(private, tx) + require.NoError(t, err) signedGolden := "gqNzaWfEQHsgfEAmEHUxLLLR9s+Y/yq5WeoGo/jAArCbany+7ZYwExMySzAhmV7M7S8+LBtJalB4EhzEUMKmt3kNKk6+vAWjdHhuiqRhYW10AaRhcmN2xCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aRhc25kxCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aNmZWXNCqqiZnbOAATsD6JnaMQgSGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiKibHbOAATv96NzbmTEIAn70nYsCPhsWua/bdenqQHeZnXXUOB+jFx2mGR9tuH9pHR5cGWlYXhmZXKkeGFpZAE=" require.EqualValues(t, newStxBytes, byteFromBase64(signedGolden)) } @@ -698,15 +707,76 @@ func TestMakeApplicationCallTx(t *testing.T) { foreignAssets := foreignApps gSchema := types.StateSchema{NumUint: uint64(1), NumByteSlice: uint64(1)} lSchema := types.StateSchema{NumUint: uint64(1), NumByteSlice: uint64(1)} + extraPages := uint32(2) addr := make([]string, 1) addr[0] = "47YPQTIGQEO7T4Y4RWDYWEKV6RTR2UNBQXBABEEGM72ESWDQNCQ52OPASU" + boxReferences := make([]types.AppBoxReference, 3) + boxReferences[0] = types.AppBoxReference{AppID: 2, Name: []byte("box_name")} + boxReferences[1] = types.AppBoxReference{AppID: 10, Name: []byte("box_name")} + boxReferences[2] = types.AppBoxReference{AppID: 10, Name: []byte("box_name2")} - tx, err := MakeApplicationCallTx(0, args, addr, foreignApps, foreignAssets, types.NoOpOC, program, program, gSchema, lSchema, params, types.Address{}, note, types.Digest{}, [32]byte{}, types.Address{}) - require.NoError(t, err) - require.EqualValues(t, 0, tx.ExtraProgramPages) - tx, err = MakeApplicationCallTxWithExtraPages(tx, 2) + 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{}) require.NoError(t, err) require.EqualValues(t, 2, tx.ExtraProgramPages) + + // verify that the correct app index was calculated + require.EqualValues(t, 0, tx.BoxReferences[0].ForeignAppIdx) + require.EqualValues(t, 1, tx.BoxReferences[1].ForeignAppIdx) + require.EqualValues(t, 1, tx.BoxReferences[2].ForeignAppIdx) + + // the current app can also be referenced with AppID = 0 + boxReferences[0].AppID = 0 + 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{}) + require.NoError(t, err) + require.EqualValues(t, 0, tx.BoxReferences[0].ForeignAppIdx) + require.EqualValues(t, 1, tx.BoxReferences[1].ForeignAppIdx) + require.EqualValues(t, 1, tx.BoxReferences[2].ForeignAppIdx) + + // if the current app's ID is provided explicitly AND is present in the foreignApps array + // then the index in the array should be returned rather than the usual value of 0 + boxReferences[0].AppID = 2 + foreignApps = append(foreignApps, 2) + 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{}) + require.NoError(t, err) + require.EqualValues(t, 2, tx.BoxReferences[0].ForeignAppIdx) + require.EqualValues(t, 1, tx.BoxReferences[1].ForeignAppIdx) + require.EqualValues(t, 1, tx.BoxReferences[2].ForeignAppIdx) +} + +func TestMakeApplicationCallTxInvalidBoxes(t *testing.T) { + const fee = 1000 + const firstRound = 2063137 + const genesisID = "devnet-v1.0" + genesisHash := byteFromBase64("sC3P7e2SdbqKJK0tbiCdK9tdSpbe6XeCGKdoNzmlj0E=") + + params := types.SuggestedParams{ + Fee: fee, + FirstRoundValid: firstRound, + LastRoundValid: firstRound + 1000, + GenesisHash: genesisHash, + GenesisID: genesisID, + FlatFee: true, + } + note := byteFromBase64("8xMCTuLQ810=") + program := []byte{1, 32, 1, 1, 34} + args := make([][]byte, 2) + args[0] = []byte("123") + args[1] = []byte("456") + foreignApps := make([]uint64, 1) + foreignApps[0] = 10 + foreignAssets := foreignApps + gSchema := types.StateSchema{NumUint: uint64(1), NumByteSlice: uint64(1)} + lSchema := types.StateSchema{NumUint: uint64(1), NumByteSlice: uint64(1)} + extraPages := uint32(2) + addr := make([]string, 1) + addr[0] = "47YPQTIGQEO7T4Y4RWDYWEKV6RTR2UNBQXBABEEGM72ESWDQNCQ52OPASU" + boxReferences := make([]types.AppBoxReference, 3) + boxReferences[0] = types.AppBoxReference{AppID: 2, Name: []byte("box_name")} + boxReferences[1] = types.AppBoxReference{AppID: 10, Name: []byte("box_name")} + boxReferences[2] = types.AppBoxReference{AppID: 11, Name: []byte("box_name")} + + _, 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{}) + require.Error(t, err, "the app id 10 provided for this box is not in the foreignApps array") } func TestComputeGroupID(t *testing.T) { @@ -755,6 +825,7 @@ func TestComputeGroupID(t *testing.T) { require.Equal(t, byteFromBase64(goldenTx2), msgpack.Encode(stx2)) gid, err := crypto.ComputeGroupID([]types.Transaction{tx1, tx2}) + require.NoError(t, err) // goal clerk group sets Group to every transaction and concatenate them in output file // simulating that behavior here @@ -910,3 +981,86 @@ func TestFee(t *testing.T) { }) } } + +func TestParseBoxReferences(t *testing.T) { + + genWithAppId := func(appId uint64) types.AppBoxReference { + return types.AppBoxReference{appId, []byte("example")} + } + + genWithNewAppId := func() types.AppBoxReference { + return types.AppBoxReference{0, []byte("example")} + } + + t.Run("appIndexExists", func(t *testing.T) { + appId := uint64(7) + abr := genWithAppId(appId) + + brs, err := parseBoxReferences( + []types.AppBoxReference{abr}, + []uint64{1, 3, 4, appId}, + appId-1) + require.NoError(t, err) + require.Equal(t, + []types.BoxReference{{ + ForeignAppIdx: uint64(4), + Name: abr.Name}}, + brs) + }) + + t.Run("appIndexDoesNotExist", func(t *testing.T) { + appId := uint64(7) + abr := genWithAppId(appId) + + _, err := parseBoxReferences( + []types.AppBoxReference{abr}, + []uint64{1, 3, 4}, + appId-1) + require.Error(t, err) + }) + + t.Run("newAppId", func(t *testing.T) { + abr := genWithNewAppId() + + brs, err := parseBoxReferences( + []types.AppBoxReference{abr}, + []uint64{}, + uint64(1)) + require.NoError(t, err) + require.Equal(t, + []types.BoxReference{{ + ForeignAppIdx: uint64(0), + Name: abr.Name}}, + brs) + }) + + t.Run("fallbackToCurrentApp", func(t *testing.T) { + // Mirrors priority search in goal from `cmd/goal/application.go::translateBoxRefs`. + appId := uint64(7) + abr := genWithAppId(appId) + + // Prefer foreign apps index when present. + brs, err := parseBoxReferences( + []types.AppBoxReference{abr}, + []uint64{1, 3, 4, appId}, + appId) + require.NoError(t, err) + require.Equal(t, + []types.BoxReference{{ + ForeignAppIdx: uint64(4), + Name: abr.Name}}, + brs) + + // Fallback to current app when absent from foreign apps. + brs, err = parseBoxReferences( + []types.AppBoxReference{abr}, + []uint64{1, 3, 4}, + appId) + require.NoError(t, err) + require.Equal(t, + []types.BoxReference{{ + ForeignAppIdx: uint64(0), + Name: abr.Name}}, + brs) + }) +} diff --git a/test/algodclientv2_test.go b/test/algodclientv2_test.go index 3c30812c..25b21a82 100644 --- a/test/algodclientv2_test.go +++ b/test/algodclientv2_test.go @@ -48,6 +48,8 @@ func AlgodClientV2Context(s *godog.Suite) { s.Step(`^we make an Account Information call against account "([^"]*)" with exclude "([^"]*)"$`, weMakeAnAccountInformationCallAgainstAccountWithExclude) s.Step(`^we make an Account Asset Information call against account "([^"]*)" assetID (\d+)$`, weMakeAnAccountAssetInformationCallAgainstAccountAssetID) s.Step(`^we make an Account Application Information call against account "([^"]*)" applicationID (\d+)$`, weMakeAnAccountApplicationInformationCallAgainstAccountApplicationID) + s.Step(`^we make a GetApplicationBoxByName call for applicationID (\d+) with encoded box name "([^"]*)"$`, weMakeAGetApplicationBoxByNameCall) + s.Step(`^we make a GetApplicationBoxes call for applicationID (\d+) with max (\d+)$`, weMakeAGetApplicationBoxesCall) s.Step(`^we make a GetLightBlockHeaderProof call for round (\d+)$`, weMakeAGetLightBlockHeaderProofCallForRound) s.Step(`^we make a GetStateProof call for round (\d+)$`, weMakeAGetStateProofCallForRound) s.Step(`^we make a GetTransactionProof call for round (\d+) txid "([^"]*)" and hashtype "([^"]*)"$`, weMakeAGetTransactionProofCallForRoundTxidAndHashtype) @@ -245,6 +247,28 @@ func weMakeAnAccountApplicationInformationCallAgainstAccountApplicationID(accoun return nil } +func weMakeAGetApplicationBoxByNameCall(appId int, encodedBoxName string) error { + algodClient, err := algod.MakeClient(mockServer.URL, "") + if err != nil { + return err + } + decodedBoxNames, err := parseAppArgs(encodedBoxName) + if err != nil { + return err + } + algodClient.GetApplicationBoxByName(uint64(appId), decodedBoxNames[0]).Do(context.Background()) + return nil +} + +func weMakeAGetApplicationBoxesCall(appId int, max int) error { + algodClient, err := algod.MakeClient(mockServer.URL, "") + if err != nil { + return err + } + algodClient.GetApplicationBoxes(uint64(appId)).Max(uint64(max)).Do(context.Background()) + return nil +} + func weMakeAGetLightBlockHeaderProofCallForRound(round int) error { algodClient, err := algod.MakeClient(mockServer.URL, "") if err != nil { diff --git a/test/applications_integration_test.go b/test/applications_integration_test.go index 6c64b676..be8bad2c 100644 --- a/test/applications_integration_test.go +++ b/test/applications_integration_test.go @@ -10,14 +10,17 @@ import ( "fmt" "reflect" "regexp" + "sort" "strconv" "strings" + "time" "github.com/cucumber/godog" "github.com/algorand/go-algorand-sdk/abi" "github.com/algorand/go-algorand-sdk/client/v2/algod" "github.com/algorand/go-algorand-sdk/client/v2/common/models" + "github.com/algorand/go-algorand-sdk/client/v2/indexer" "github.com/algorand/go-algorand-sdk/crypto" sdkJson "github.com/algorand/go-algorand-sdk/encoding/json" "github.com/algorand/go-algorand-sdk/future" @@ -25,6 +28,7 @@ import ( ) var algodV2client *algod.Client +var indexerV2client *indexer.Client var tx types.Transaction var transientAccount crypto.Account var applicationId uint64 @@ -123,7 +127,8 @@ func readTealProgram(fileName string) ([]byte, error) { func iBuildAnApplicationTransaction( operation, approvalProgram, clearProgram string, globalBytes, globalInts, localBytes, localInts int, - appArgs, foreignApps, foreignAssets, appAccounts string, extraPages int) error { + appArgs, foreignApps, foreignAssets, appAccounts string, + extraPages int, boxes string) error { var clearP []byte var approvalP []byte @@ -169,82 +174,47 @@ func iBuildAnApplicationTransaction( return err } + staticBoxes, err := parseBoxes(boxes) + if err != nil { + return err + } + gSchema := types.StateSchema{NumUint: uint64(globalInts), NumByteSlice: uint64(globalBytes)} lSchema := types.StateSchema{NumUint: uint64(localInts), NumByteSlice: uint64(localBytes)} switch operation { case "create": - if extraPages > 0 { - tx, err = future.MakeApplicationCreateTxWithExtraPages(false, approvalP, clearP, - gSchema, lSchema, args, accs, fApp, fAssets, - suggestedParams, transientAccount.Address, nil, types.Digest{}, [32]byte{}, types.Address{}, uint32(extraPages)) - } else { - tx, err = future.MakeApplicationCreateTx(false, approvalP, clearP, - gSchema, lSchema, args, accs, fApp, fAssets, - suggestedParams, transientAccount.Address, nil, types.Digest{}, [32]byte{}, types.Address{}) - } - - if err != nil { - return err - } - + tx, err = future.MakeApplicationCreateTxWithBoxes(false, approvalP, clearP, + gSchema, lSchema, uint32(extraPages), args, accs, fApp, fAssets, staticBoxes, + suggestedParams, transientAccount.Address, nil, types.Digest{}, [32]byte{}, types.Address{}) case "create_optin": - if extraPages > 0 { - tx, err = future.MakeApplicationCreateTxWithExtraPages(true, approvalP, clearP, - gSchema, lSchema, args, accs, fApp, fAssets, - suggestedParams, transientAccount.Address, nil, types.Digest{}, [32]byte{}, types.Address{}, uint32(extraPages)) - } else { - tx, err = future.MakeApplicationCreateTx(true, approvalP, clearP, - gSchema, lSchema, args, accs, fApp, fAssets, - suggestedParams, transientAccount.Address, nil, types.Digest{}, [32]byte{}, types.Address{}) - } - - if err != nil { - return err - } + tx, err = future.MakeApplicationCreateTxWithBoxes(true, approvalP, clearP, + gSchema, lSchema, uint32(extraPages), args, accs, fApp, fAssets, staticBoxes, + suggestedParams, transientAccount.Address, nil, types.Digest{}, [32]byte{}, types.Address{}) case "update": - tx, err = future.MakeApplicationUpdateTx(applicationId, args, accs, fApp, fAssets, + tx, err = future.MakeApplicationUpdateTxWithBoxes(applicationId, args, accs, fApp, fAssets, staticBoxes, approvalP, clearP, suggestedParams, transientAccount.Address, nil, types.Digest{}, [32]byte{}, types.Address{}) - if err != nil { - return err - } - case "call": - tx, _ = future.MakeApplicationCallTx(applicationId, args, accs, - fApp, fAssets, types.NoOpOC, approvalP, clearP, gSchema, lSchema, + tx, err = future.MakeApplicationNoOpTxWithBoxes(applicationId, args, accs, + fApp, fAssets, staticBoxes, suggestedParams, transientAccount.Address, nil, types.Digest{}, [32]byte{}, types.Address{}) case "optin": - tx, err = future.MakeApplicationOptInTx(applicationId, args, accs, fApp, fAssets, + tx, err = future.MakeApplicationOptInTxWithBoxes(applicationId, args, accs, fApp, fAssets, staticBoxes, suggestedParams, transientAccount.Address, nil, types.Digest{}, [32]byte{}, types.Address{}) - if err != nil { - return err - } - case "clear": - tx, err = future.MakeApplicationClearStateTx(applicationId, args, accs, fApp, fAssets, + tx, err = future.MakeApplicationClearStateTxWithBoxes(applicationId, args, accs, fApp, fAssets, staticBoxes, suggestedParams, transientAccount.Address, nil, types.Digest{}, [32]byte{}, types.Address{}) - if err != nil { - return err - } - case "closeout": - tx, err = future.MakeApplicationCloseOutTx(applicationId, args, accs, fApp, fAssets, + tx, err = future.MakeApplicationCloseOutTxWithBoxes(applicationId, args, accs, fApp, fAssets, staticBoxes, suggestedParams, transientAccount.Address, nil, types.Digest{}, [32]byte{}, types.Address{}) - if err != nil { - return err - } - case "delete": - tx, err = future.MakeApplicationDeleteTx(applicationId, args, accs, fApp, fAssets, + tx, err = future.MakeApplicationDeleteTxWithBoxes(applicationId, args, accs, fApp, fAssets, staticBoxes, suggestedParams, transientAccount.Address, nil, types.Digest{}, [32]byte{}, types.Address{}) - if err != nil { - return err - } default: return fmt.Errorf("unsupported tx type: %s", operation) } - return nil + return err } func iSignAndSubmitTheTransactionSavingTheTxidIfThereIsAnErrorItIs(expectedErr string) error { @@ -321,6 +291,12 @@ func parseAppArgs(appArgsString string) (appArgs [][]byte, err error) { return nil, fmt.Errorf("failed to convert %s to bytes", arg) } resp[idx] = buf.Bytes() + case "b64": + d, err := (base64.StdEncoding.DecodeString(typeArg[1])) + if err != nil { + return nil, fmt.Errorf("failed to b64 decode arg = %s", arg) + } + resp[idx] = d default: return nil, fmt.Errorf("Applications doesn't currently support argument of type %s", typeArg[0]) } @@ -328,6 +304,40 @@ func parseAppArgs(appArgsString string) (appArgs [][]byte, err error) { return resp, err } +func parseBoxes(boxesStr string) (staticBoxes []types.AppBoxReference, err error) { + if boxesStr == "" { + return make([]types.AppBoxReference, 0), nil + } + + boxesArray := strings.Split(boxesStr, ",") + + for i := 0; i < len(boxesArray); i += 2 { + appID, err := strconv.ParseUint(boxesArray[i], 10, 64) + if err != nil { + return nil, err + } + + var nameBytes []byte + nameArray := strings.Split(boxesArray[i+1], ":") + if nameArray[0] == "str" { + nameBytes = []byte(nameArray[1]) + } else { + nameBytes, err = base64.StdEncoding.DecodeString(nameArray[1]) + if err != nil { + return nil, err + } + } + + staticBoxes = append(staticBoxes, + types.AppBoxReference{ + AppID: appID, + Name: nameBytes, + }) + } + + return +} + func splitUint64(uint64s string) ([]uint64, error) { if uint64s == "" { return make([]uint64, 0), nil @@ -756,11 +766,150 @@ func checkRandomElementResult(resultIndex int, input string) error { return nil } -//@applications.verified +func theContentsOfTheBoxWithNameShouldBeIfThereIsAnErrorItIs(fromClient, encodedBoxName, boxContents, errStr string) error { + var box models.Box + decodedBoxNames, err := parseAppArgs(encodedBoxName) + if err != nil { + return err + } + decodedBoxName := decodedBoxNames[0] + if fromClient == "algod" { + box, err = algodV2client.GetApplicationBoxByName(applicationId, decodedBoxName).Do(context.Background()) + } else if fromClient == "indexer" { + box, err = indexerV2client.LookupApplicationBoxByIDAndName(applicationId, decodedBoxName).Do(context.Background()) + } else { + err = fmt.Errorf("expecting algod or indexer, got " + fromClient) + } + if err != nil { + if strings.Contains(err.Error(), errStr) { + return nil + } + return err + } + + b64Value := base64.StdEncoding.EncodeToString(box.Value) + if b64Value != boxContents { + return fmt.Errorf("expected box value %s is not equal to actual box value %s", boxContents, b64Value) + } + + return nil +} + +func currentApplicationShouldHaveFollowingBoxes(fromClient, encodedBoxesRaw string) error { + var expectedNames [][]byte + if len(encodedBoxesRaw) > 0 { + encodedBoxes := strings.Split(encodedBoxesRaw, ":") + expectedNames = make([][]byte, len(encodedBoxes)) + for i, b := range encodedBoxes { + expected, err := base64.StdEncoding.DecodeString(b) + if err != nil { + return err + } + expectedNames[i] = expected + } + } + sort.Slice(expectedNames, func(i, j int) bool { + return bytes.Compare(expectedNames[i], expectedNames[j]) < 0 + }) + + var r models.BoxesResponse + var err error + + if fromClient == "algod" { + r, err = algodV2client.GetApplicationBoxes(applicationId).Do(context.Background()) + } else if fromClient == "indexer" { + r, err = indexerV2client.SearchForApplicationBoxes(applicationId).Do(context.Background()) + } else { + err = fmt.Errorf("expecting algod or indexer, got " + fromClient) + } + if err != nil { + return err + } + + actualNames := make([][]byte, len(r.Boxes)) + for i, b := range r.Boxes { + actualNames[i] = b.Name + } + sort.Slice(actualNames, func(i, j int) bool { + return bytes.Compare(actualNames[i], actualNames[j]) < 0 + }) + + err = sliceOfBytesEqual(expectedNames, actualNames) + if err != nil { + return fmt.Errorf("expected and actual box names do not match: %w", err) + } + + return nil +} + +func sleptForNMilliSecsForIndexer(n int) error { + time.Sleep(time.Duration(n) * time.Millisecond) + return nil +} + +func currentApplicationShouldHaveBoxNum(fromClient string, max int, expectedNum int) error { + var r models.BoxesResponse + var err error + + if fromClient == "algod" { + r, err = algodV2client.GetApplicationBoxes(applicationId).Max(uint64(max)).Do(context.Background()) + } else if fromClient == "indexer" { + r, err = indexerV2client.SearchForApplicationBoxes(applicationId).Limit(uint64(max)).Do(context.Background()) + } else { + err = fmt.Errorf("expecting algod or indexer, got " + fromClient) + } + if err != nil { + return err + } + + if len(r.Boxes) != expectedNum { + return fmt.Errorf("expected and actual box number do not match: %v != %v", expectedNum, len(r.Boxes)) + } + return nil +} + +func indexerSaysCurrentAppShouldHaveTheseBoxes(max int, next string, encodedBoxesRaw string) error { + r, err := indexerV2client.SearchForApplicationBoxes(applicationId).Limit(uint64(max)).Next(next).Do(context.Background()) + if err != nil { + return err + } + actualNames := make([][]byte, len(r.Boxes)) + for i, b := range r.Boxes { + actualNames[i] = b.Name + } + sort.Slice(actualNames, func(i, j int) bool { + return bytes.Compare(actualNames[i], actualNames[j]) < 0 + }) + + var expectedNames [][]byte + if len(encodedBoxesRaw) > 0 { + encodedBoxes := strings.Split(encodedBoxesRaw, ":") + expectedNames = make([][]byte, len(encodedBoxes)) + for i, b := range encodedBoxes { + expected, err := base64.StdEncoding.DecodeString(b) + if err != nil { + return err + } + expectedNames[i] = expected + } + } + sort.Slice(expectedNames, func(i, j int) bool { + return bytes.Compare(expectedNames[i], expectedNames[j]) < 0 + }) + + err = sliceOfBytesEqual(expectedNames, actualNames) + if err != nil { + return fmt.Errorf("expected and actual box names do not match: %w", err) + } + + return nil +} + +// @applications.verified and @applications.boxes func ApplicationsContext(s *godog.Suite) { s.Step(`^an algod v(\d+) client connected to "([^"]*)" port (\d+) with token "([^"]*)"$`, anAlgodVClientConnectedToPortWithToken) s.Step(`^I create a new transient account and fund it with (\d+) microalgos\.$`, iCreateANewTransientAccountAndFundItWithMicroalgos) - s.Step(`^I build an application transaction with the transient account, the current application, suggested params, operation "([^"]*)", approval-program "([^"]*)", clear-program "([^"]*)", global-bytes (\d+), global-ints (\d+), local-bytes (\d+), local-ints (\d+), app-args "([^"]*)", foreign-apps "([^"]*)", foreign-assets "([^"]*)", app-accounts "([^"]*)", extra-pages (\d+)$`, iBuildAnApplicationTransaction) + s.Step(`^I build an application transaction with the transient account, the current application, suggested params, operation "([^"]*)", approval-program "([^"]*)", clear-program "([^"]*)", global-bytes (\d+), global-ints (\d+), local-bytes (\d+), local-ints (\d+), app-args "([^"]*)", foreign-apps "([^"]*)", foreign-assets "([^"]*)", app-accounts "([^"]*)", extra-pages (\d+), boxes "([^"]*)"$`, iBuildAnApplicationTransaction) s.Step(`^I sign and submit the transaction, saving the txid\. If there is an error it is "([^"]*)"\.$`, iSignAndSubmitTheTransactionSavingTheTxidIfThereIsAnErrorItIs) s.Step(`^I wait for the transaction to be confirmed\.$`, iWaitForTheTransactionToBeConfirmed) s.Step(`^I remember the new application ID\.$`, iRememberTheNewApplicationID) @@ -783,4 +932,10 @@ func ApplicationsContext(s *godog.Suite) { s.Step(`^The (\d+)th atomic result for randomInt\((\d+)\) proves correct$`, checkRandomIntResult) s.Step(`^The (\d+)th atomic result for randElement\("([^"]*)"\) proves correct$`, checkRandomElementResult) + + s.Step(`^according to "([^"]*)", the contents of the box with name "([^"]*)" in the current application should be "([^"]*)"\. If there is an error it is "([^"]*)"\.$`, theContentsOfTheBoxWithNameShouldBeIfThereIsAnErrorItIs) + s.Step(`^according to "([^"]*)", the current application should have the following boxes "([^"]*)"\.$`, currentApplicationShouldHaveFollowingBoxes) + s.Step(`^according to "([^"]*)", with (\d+) being the parameter that limits results, the current application should have (\d+) boxes\.$`, currentApplicationShouldHaveBoxNum) + s.Step(`^according to indexer, with (\d+) being the parameter that limits results, and "([^"]*)" being the parameter that sets the next result, the current application should have the following boxes "([^"]*)"\.$`, indexerSaysCurrentAppShouldHaveTheseBoxes) + s.Step(`^I sleep for (\d+) milliseconds for indexer to digest things down\.$`, sleptForNMilliSecsForIndexer) } diff --git a/test/indexer_unit_test.go b/test/indexer_unit_test.go index 2fd9fd31..74b9b7c8 100644 --- a/test/indexer_unit_test.go +++ b/test/indexer_unit_test.go @@ -54,7 +54,8 @@ func IndexerUnitTestContext(s *godog.Suite) { s.Step(`^we make a Lookup Account by ID call against account "([^"]*)" with exclude "([^"]*)"$`, weMakeALookupAccountByIDCallAgainstAccountWithExclude) s.Step(`^we make a SearchForApplications call with creator "([^"]*)"$`, weMakeASearchForApplicationsCallWithCreator) s.Step(`^we make a Lookup Block call against round (\d+) and header "([^"]*)"$`, weMakeALookupBlockCallAgainstRoundAndHeader) - + s.Step(`^we make a LookupApplicationBoxByIDandName call with applicationID (\d+) with encoded box name "([^"]*)"$`, weMakeALookupApplicationBoxByIDandName) + s.Step(`^we make a SearchForApplicationBoxes call with applicationID (\d+) with max (\d+) nextToken "([^"]*)"$`, weMakeASearchForApplicationBoxes) s.BeforeScenario(func(interface{}) { globalErrForExamination = nil }) @@ -370,3 +371,25 @@ func weMakeALookupBlockCallAgainstRoundAndHeader(round int, headerOnly string) e _, globalErrForExamination = indexerClient.LookupBlock(uint64(round)).HeaderOnly(headerOnlyBool).Do(context.Background()) return nil } + +func weMakeALookupApplicationBoxByIDandName(appId int, encodedBoxName string) error { + indexerClient, err := indexer.MakeClient(mockServer.URL, "") + if err != nil { + return err + } + decodedBoxNames, err := parseAppArgs(encodedBoxName) + if err != nil { + return err + } + indexerClient.LookupApplicationBoxByIDAndName(uint64(appId), decodedBoxNames[0]).Do(context.Background()) + return nil +} + +func weMakeASearchForApplicationBoxes(appId int, limit int, next string) error { + indexerClient, err := indexer.MakeClient(mockServer.URL, "") + if err != nil { + return err + } + indexerClient.SearchForApplicationBoxes(uint64(appId)).Limit(uint64(limit)).Next(next).Do(context.Background()) + return nil +} diff --git a/test/integration.tags b/test/integration.tags index e0ea6997..91dace71 100644 --- a/test/integration.tags +++ b/test/integration.tags @@ -1,5 +1,6 @@ @abi @algod +@applications.boxes @applications.verified @assets @auction @@ -10,4 +11,4 @@ @kmd @rekey_v1 @send -@send.keyregtxn \ No newline at end of file +@send.keyregtxn diff --git a/test/steps_test.go b/test/steps_test.go index b35dc7bc..36ba6576 100644 --- a/test/steps_test.go +++ b/test/steps_test.go @@ -29,6 +29,7 @@ import ( algodV2 "github.com/algorand/go-algorand-sdk/client/v2/algod" commonV2 "github.com/algorand/go-algorand-sdk/client/v2/common" modelsV2 "github.com/algorand/go-algorand-sdk/client/v2/common/models" + indexerV2 "github.com/algorand/go-algorand-sdk/client/v2/indexer" "github.com/algorand/go-algorand-sdk/crypto" "github.com/algorand/go-algorand-sdk/encoding/msgpack" "github.com/algorand/go-algorand-sdk/future" @@ -60,6 +61,7 @@ var msigsig types.MultisigSig var kcl kmd.Client var acl algod.Client var aclv2 *algodV2.Client +var iclv2 *indexerV2.Client var walletName string var walletPswd string var walletID string @@ -335,6 +337,7 @@ func FeatureContext(s *godog.Suite) { s.Step(`^base64 encoded program "([^"]*)"$`, baseEncodedProgram) s.Step(`^base64 encoded private key "([^"]*)"$`, baseEncodedPrivateKey) s.Step("an algod v2 client$", algodClientV2) + s.Step("an indexer v2 client$", indexerClientV2) s.Step(`^I compile a teal program "([^"]*)"$`, tealCompile) s.Step(`^it is compiled with (\d+) and "([^"]*)" and "([^"]*)"$`, tealCheckCompile) s.Step(`^base64 decoding the response is the same as the binary "([^"]*)"$`, tealCheckCompileAgainstFile) @@ -901,6 +904,14 @@ func algodClientV2() error { return err } +func indexerClientV2() error { + indexerAddress := "http://localhost:" + "59999" + var err error + iclv2, err = indexerV2.MakeClient(indexerAddress, "") + indexerV2client = iclv2 + return err +} + func walletInfo() error { walletName = "unencrypted-default-wallet" walletPswd = "" diff --git a/test/transactions_test.go b/test/transactions_test.go index ca3e6e72..43be3bfc 100644 --- a/test/transactions_test.go +++ b/test/transactions_test.go @@ -80,7 +80,7 @@ func buildLegacyAppCallTransaction( globalBytes, globalInts, localBytes, localInts int, appArgs, foreignApps, foreignAssets, appAccounts string, fee, firstValid, lastValid int, - genesisHash string, extraPages int) error { + genesisHash string, extraPages int, boxes string) error { if applicationId < 0 || globalBytes < 0 || globalInts < 0 || localBytes < 0 || localInts < 0 || extraPages < 0 || fee < 0 || firstValid < 0 || lastValid < 0 { return fmt.Errorf("Integer arguments cannot be negative") @@ -127,6 +127,11 @@ func buildLegacyAppCallTransaction( return err } + boxReferences, err := parseBoxes(boxes) + if err != nil { + return err + } + gSchema := types.StateSchema{NumUint: uint64(globalInts), NumByteSlice: uint64(globalBytes)} lSchema := types.StateSchema{NumUint: uint64(localInts), NumByteSlice: uint64(localBytes)} @@ -135,7 +140,7 @@ func buildLegacyAppCallTransaction( return err } - // this is only kept to keep compatability with old features + // this is only kept to keep compatibility with old features // going forward, use txnSuggestedParams sugParams = types.SuggestedParams{ Fee: types.MicroAlgos(uint64(fee)), @@ -148,28 +153,28 @@ func buildLegacyAppCallTransaction( switch operation { case "create": - tx, err = future.MakeApplicationCreateTxWithExtraPages(false, approvalP, clearP, - gSchema, lSchema, args, accs, fApp, fAssets, - sugParams, senderAddr, nil, types.Digest{}, [32]byte{}, types.Address{}, uint32(extraPages)) + tx, err = future.MakeApplicationCreateTxWithBoxes(false, approvalP, clearP, + gSchema, lSchema, uint32(extraPages), args, accs, fApp, fAssets, boxReferences, + sugParams, senderAddr, nil, types.Digest{}, [32]byte{}, types.Address{}) case "update": - tx, err = future.MakeApplicationUpdateTx(uint64(applicationId), args, accs, fApp, fAssets, + tx, err = future.MakeApplicationUpdateTxWithBoxes(uint64(applicationId), args, accs, fApp, fAssets, boxReferences, approvalP, clearP, sugParams, senderAddr, nil, types.Digest{}, [32]byte{}, types.Address{}) case "call": - tx, err = future.MakeApplicationCallTx(uint64(applicationId), args, accs, - fApp, fAssets, types.NoOpOC, approvalP, clearP, gSchema, lSchema, + tx, err = future.MakeApplicationNoOpTxWithBoxes(uint64(applicationId), args, accs, + fApp, fAssets, boxReferences, sugParams, senderAddr, nil, types.Digest{}, [32]byte{}, types.Address{}) case "optin": - tx, err = future.MakeApplicationOptInTx(uint64(applicationId), args, accs, fApp, fAssets, + tx, err = future.MakeApplicationOptInTxWithBoxes(uint64(applicationId), args, accs, fApp, fAssets, boxReferences, sugParams, senderAddr, nil, types.Digest{}, [32]byte{}, types.Address{}) case "clear": - tx, err = future.MakeApplicationClearStateTx(uint64(applicationId), args, accs, fApp, fAssets, + tx, err = future.MakeApplicationClearStateTxWithBoxes(uint64(applicationId), args, accs, fApp, fAssets, boxReferences, sugParams, senderAddr, nil, types.Digest{}, [32]byte{}, types.Address{}) case "closeout": - tx, err = future.MakeApplicationCloseOutTx(uint64(applicationId), args, accs, fApp, fAssets, + tx, err = future.MakeApplicationCloseOutTxWithBoxes(uint64(applicationId), args, accs, fApp, fAssets, boxReferences, sugParams, senderAddr, nil, types.Digest{}, [32]byte{}, types.Address{}) case "delete": - tx, err = future.MakeApplicationDeleteTx(uint64(applicationId), args, accs, fApp, fAssets, + tx, err = future.MakeApplicationDeleteTxWithBoxes(uint64(applicationId), args, accs, fApp, fAssets, boxReferences, sugParams, senderAddr, nil, types.Digest{}, [32]byte{}, types.Address{}) default: err = fmt.Errorf("Unknown opperation: %s", operation) @@ -187,5 +192,5 @@ func TransactionsUnitContext(s *godog.Suite) { // @unit.transactions.keyreg s.Step(`^I build a keyreg transaction with sender "([^"]*)", nonparticipation "([^"]*)", vote first (\d+), vote last (\d+), key dilution (\d+), vote public key "([^"]*)", selection public key "([^"]*)", and state proof public key "([^"]*)"$`, buildKeyregTransaction) - s.Step(`^I build an application transaction with operation "([^"]*)", application-id (\d+), sender "([^"]*)", approval-program "([^"]*)", clear-program "([^"]*)", global-bytes (\d+), global-ints (\d+), local-bytes (\d+), local-ints (\d+), app-args "([^"]*)", foreign-apps "([^"]*)", foreign-assets "([^"]*)", app-accounts "([^"]*)", fee (\d+), first-valid (\d+), last-valid (\d+), genesis-hash "([^"]*)", extra-pages (\d+)$`, buildLegacyAppCallTransaction) + s.Step(`^I build an application transaction with operation "([^"]*)", application-id (\d+), sender "([^"]*)", approval-program "([^"]*)", clear-program "([^"]*)", global-bytes (\d+), global-ints (\d+), local-bytes (\d+), local-ints (\d+), app-args "([^"]*)", foreign-apps "([^"]*)", foreign-assets "([^"]*)", app-accounts "([^"]*)", fee (\d+), first-valid (\d+), last-valid (\d+), genesis-hash "([^"]*)", extra-pages (\d+), boxes "([^"]*)"$`, buildLegacyAppCallTransaction) } diff --git a/test/unit.tags b/test/unit.tags index fe2a4d77..905b7e6c 100644 --- a/test/unit.tags +++ b/test/unit.tags @@ -4,6 +4,7 @@ @unit.blocksummary @unit.algod.ledger_refactoring @unit.applications +@unit.applications.boxes @unit.atomic_transaction_composer @unit.dryrun @unit.dryrun.trace.application diff --git a/test/utilities.go b/test/utilities.go index aad46df6..2f647474 100644 --- a/test/utilities.go +++ b/test/utilities.go @@ -1,7 +1,9 @@ package test import ( + "bytes" "encoding/base64" + "encoding/hex" "encoding/json" "errors" "fmt" @@ -311,3 +313,18 @@ func recursiveCompare(field string, expected, actual interface{}) error { return nil } + +func sliceOfBytesEqual(expected [][]byte, actual [][]byte) error { + if len(expected) != len(actual) { + return fmt.Errorf("expected length (%d) does not match actual length (%d)", len(expected), len(actual)) + } + + for i, expectedElement := range expected { + actualElement := actual[i] + if !bytes.Equal(expectedElement, actualElement) { + return fmt.Errorf("elements at index %d are unequal. Expected %s, got %s", i, hex.EncodeToString(expectedElement), hex.EncodeToString(actualElement)) + } + } + + return nil +} diff --git a/types/applications.go b/types/applications.go index 41700185..aaa90f5b 100644 --- a/types/applications.go +++ b/types/applications.go @@ -11,6 +11,24 @@ type ApplicationFields struct { // AppParams type AppIndex uint64 +type AppBoxReference struct { + // The ID of the app that owns the box. Must be converted to BoxReference during transaction submission. + AppID uint64 + + // The Name of the box unique to the app it belongs to + Name []byte +} + +type BoxReference struct { + _struct struct{} `codec:",omitempty,omitemptyarray"` + + // The index of the app in the foreign app array. + ForeignAppIdx uint64 `codec:"i"` + + // The name of the box unique to the app it belongs to + Name []byte `codec:"n"` +} + const ( // encodedMaxApplicationArgs sets the allocation bound for the maximum // number of ApplicationArgs that a transaction decoded off of the wire @@ -35,6 +53,12 @@ const ( // can contain. Its value is verified against consensus parameters in // TestEncodedAppTxnAllocationBounds encodedMaxForeignAssets = 32 + + // encodedMaxBoxReferences sets the allocation bound for the maximum + // number of BoxReferences that a transaction decoded off of the wire + // can contain. Its value is verified against consensus parameters in + // TestEncodedAppTxnAllocationBounds + encodedMaxBoxReferences = 32 ) // OnCompletion is an enum representing some layer 1 side effect that an @@ -75,12 +99,13 @@ const ( type ApplicationCallTxnFields struct { _struct struct{} `codec:",omitempty,omitemptyarray"` - ApplicationID AppIndex `codec:"apid"` - OnCompletion OnCompletion `codec:"apan"` - ApplicationArgs [][]byte `codec:"apaa,allocbound=encodedMaxApplicationArgs"` - Accounts []Address `codec:"apat,allocbound=encodedMaxAccounts"` - ForeignApps []AppIndex `codec:"apfa,allocbound=encodedMaxForeignApps"` - ForeignAssets []AssetIndex `codec:"apas,allocbound=encodedMaxForeignAssets"` + ApplicationID AppIndex `codec:"apid"` + OnCompletion OnCompletion `codec:"apan"` + ApplicationArgs [][]byte `codec:"apaa,allocbound=encodedMaxApplicationArgs"` + Accounts []Address `codec:"apat,allocbound=encodedMaxAccounts"` + ForeignApps []AppIndex `codec:"apfa,allocbound=encodedMaxForeignApps"` + ForeignAssets []AssetIndex `codec:"apas,allocbound=encodedMaxForeignAssets"` + BoxReferences []BoxReference `codec:"apbx,allocbound=encodedMaxBoxReferences"` LocalStateSchema StateSchema `codec:"apls"` GlobalStateSchema StateSchema `codec:"apgs"` @@ -121,6 +146,9 @@ func (ac *ApplicationCallTxnFields) Empty() bool { if ac.ForeignAssets != nil { return false } + if ac.BoxReferences != nil { + return false + } if ac.LocalStateSchema != (StateSchema{}) { return false } From 8a4d6560565bc323f130d82d93dc03944575e1ee Mon Sep 17 00:00:00 2001 From: Arthur Kepler <610274+excalq@users.noreply.github.com> Date: Wed, 2 Nov 2022 18:07:56 -0400 Subject: [PATCH 3/3] Updated CHANGELOG.md --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b78d2384..22108e76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# 1.23.0 +## What's Changed +### New Features +* Boxes: Add support for Boxes by @michaeldiamant in https://github.com/algorand/go-algorand-sdk/pull/341 + +**Full Changelog**: https://github.com/algorand/go-algorand-sdk/compare/v1.22.0...v1.23.0 + # 1.22.0 ## What's Changed ## Enhancements