From 8590499136ca735b8142b0b9cb48014d84880ae3 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Sat, 20 Nov 2021 22:26:01 -0500 Subject: [PATCH 1/6] Three new globals for to help contract-to-contract usability --- data/transactions/logic/README.md | 3 + data/transactions/logic/TEAL_opcodes.md | 3 + data/transactions/logic/assembler_test.go | 3 + data/transactions/logic/doc.go | 3 + data/transactions/logic/eval.go | 16 +++++ data/transactions/logic/evalAppTxn_test.go | 61 +++++++++++++++++++- data/transactions/logic/evalStateful_test.go | 15 +++++ data/transactions/logic/eval_test.go | 20 +++++-- data/transactions/logic/fields.go | 14 +++++ data/transactions/logic/fields_string.go | 9 ++- 10 files changed, 138 insertions(+), 9 deletions(-) diff --git a/data/transactions/logic/README.md b/data/transactions/logic/README.md index a5279327c4..0015bafec0 100644 --- a/data/transactions/logic/README.md +++ b/data/transactions/logic/README.md @@ -341,6 +341,9 @@ Global fields are fields that are common to all the transactions in the group. I | 9 | CreatorAddress | []byte | Address of the creator of the current application. Fails if no such application is executing. LogicSigVersion >= 3. | | 10 | CurrentApplicationAddress | []byte | Address that the current application controls. Fails in LogicSigs. LogicSigVersion >= 5. | | 11 | GroupID | []byte | ID of the transaction group. 32 zero bytes if the transaction is not part of a group. LogicSigVersion >= 5. | +| 12 | OpcodeBudget | uint64 | The remaining cost that can be spent by opcodes in this program. LogicSigVersion >= 6. | +| 13 | CallerApplicationID | uint64 | The application ID of the application that called this application. 0 if this application is at the top-level. LogicSigVersion >= 6. | +| 14 | CallerApplicationAddress | []byte | The application address of the application that called this application. ZeroAddress if this application is at the top-level. LogicSigVersion >= 6. | **Asset Fields** diff --git a/data/transactions/logic/TEAL_opcodes.md b/data/transactions/logic/TEAL_opcodes.md index df6db7e3f2..66ecafe7f3 100644 --- a/data/transactions/logic/TEAL_opcodes.md +++ b/data/transactions/logic/TEAL_opcodes.md @@ -524,6 +524,9 @@ FirstValidTime causes the program to fail. The field is reserved for future use. | 9 | CreatorAddress | []byte | Address of the creator of the current application. Fails if no such application is executing. LogicSigVersion >= 3. | | 10 | CurrentApplicationAddress | []byte | Address that the current application controls. Fails in LogicSigs. LogicSigVersion >= 5. | | 11 | GroupID | []byte | ID of the transaction group. 32 zero bytes if the transaction is not part of a group. LogicSigVersion >= 5. | +| 12 | OpcodeBudget | uint64 | The remaining cost that can be spent by opcodes in this program. LogicSigVersion >= 6. | +| 13 | CallerApplicationID | uint64 | The application ID of the application that called this application. 0 if this application is at the top-level. LogicSigVersion >= 6. | +| 14 | CallerApplicationAddress | []byte | The application address of the application that called this application. ZeroAddress if this application is at the top-level. LogicSigVersion >= 6. | ## gtxn t f diff --git a/data/transactions/logic/assembler_test.go b/data/transactions/logic/assembler_test.go index 10947245cc..152f218718 100644 --- a/data/transactions/logic/assembler_test.go +++ b/data/transactions/logic/assembler_test.go @@ -1315,6 +1315,9 @@ global LatestTimestamp global CurrentApplicationID global CreatorAddress global GroupID +global OpcodeBudget +global CallerApplicationID +global CallerApplicationAddress txn Sender txn Fee bnz label1 diff --git a/data/transactions/logic/doc.go b/data/transactions/logic/doc.go index 6b836760e9..43ba8d2178 100644 --- a/data/transactions/logic/doc.go +++ b/data/transactions/logic/doc.go @@ -463,6 +463,9 @@ var globalFieldDocs = map[string]string{ "CreatorAddress": "Address of the creator of the current application. Fails if no such application is executing", "CurrentApplicationAddress": "Address that the current application controls. Fails in LogicSigs", "GroupID": "ID of the transaction group. 32 zero bytes if the transaction is not part of a group.", + "OpcodeBudget": "The remaining cost that can be spent by opcodes in this program.", + "CallerApplicationID": "The application ID of the application that called this application. 0 if this application is at the top-level.", + "CallerApplicationAddress": "The application address of the application that called this application. ZeroAddress if this application is at the top-level.", } // GlobalFieldDocs are notes on fields available in `global` with extra versioning info if any diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go index 10af327d69..3285884112 100644 --- a/data/transactions/logic/eval.go +++ b/data/transactions/logic/eval.go @@ -2636,6 +2636,22 @@ func (cx *EvalContext) globalFieldToValue(fs globalFieldSpec) (sv stackValue, er sv.Bytes, err = cx.getCreatorAddress() case GroupID: sv.Bytes = cx.Txn.Txn.Group[:] + case OpcodeBudget: + sv.Uint = uint64(cx.budget() - cx.cost) + case CallerApplicationID: + if cx.caller != nil { + sv.Uint, err = cx.caller.getApplicationID() + } else { + sv.Uint = 0 + } + case CallerApplicationAddress: + if cx.caller != nil { + var addr basics.Address + addr, err = cx.caller.getApplicationAddress() + sv.Bytes = addr[:] + } else { + sv.Bytes = zeroAddress[:] + } default: err = fmt.Errorf("invalid global field %d", fs.field) } diff --git a/data/transactions/logic/evalAppTxn_test.go b/data/transactions/logic/evalAppTxn_test.go index 5cd6728c3f..69bb35c9e9 100644 --- a/data/transactions/logic/evalAppTxn_test.go +++ b/data/transactions/logic/evalAppTxn_test.go @@ -845,8 +845,8 @@ int 1 // TestInnerBudgetIncrement ensures that an app can make a (nearly) empty inner // app call in order to get 700 extra opcode budget. Unfortunately, it costs a -// bit to create the call, and the app itself consumes a little, so it's more -// like 690 or so. +// bit to create the call, and the app itself consumes 1, so it ends up being +// about 690 (see next test). func TestInnerBudgetIncrement(t *testing.T) { ep, tx, ledger := makeSampleEnv() gasup := testProg(t, "pushint 1", AssemblerMaxVersion) @@ -874,6 +874,36 @@ itxn_submit; testApp(t, buy+buy+strings.Repeat(waste, 12)+"int 1", ep) } +func TestIncrementCheck(t *testing.T) { + ep, tx, ledger := makeSampleEnv() + gasup := testProg(t, "pushint 1", AssemblerMaxVersion) + ledger.NewApp(tx.Receiver, 222, basics.AppParams{ + ApprovalProgram: gasup.Program, + }) + + source := ` +// 698, not 699, because intcblock happens first +global OpcodeBudget; int 698; ==; assert +// "buy" more +itxn_begin +int appl; itxn_field TypeEnum +int 222; itxn_field ApplicationID +itxn_submit; +global OpcodeBudget; int 1387; ==; assert +itxn_begin +int appl; itxn_field TypeEnum +int 222; itxn_field ApplicationID +itxn_submit; +global OpcodeBudget; int 2076; ==; assert +int 1 +` + + ledger.NewApp(tx.Receiver, 888, basics.AppParams{}) + ledger.NewAccount(ledger.ApplicationID().Address(), 50_000) + tx.ForeignApps = []basics.AppIndex{basics.AppIndex(222)} + testApp(t, source, ep) +} + // TestInnerTxIDs confirms that TxIDs are available and different func TestInnerTxIDs(t *testing.T) { ep, tx, ledger := makeSampleEnv() @@ -1140,3 +1170,30 @@ int 890 == `, ep) } + +// TestCallerGlobals checks that a called app can see its caller. +func TestCallerGlobals(t *testing.T) { + ep, tx, ledger := makeSampleEnv() + globals := testProg(t, fmt.Sprintf(` +global CallerApplicationID +int 888 +== +global CallerApplicationAddress +addr %s +== +&& +`, basics.AppIndex(888).Address()), AssemblerMaxVersion) + ledger.NewApp(tx.Receiver, 222, basics.AppParams{ + ApprovalProgram: globals.Program, + }) + + ledger.NewApp(tx.Receiver, 888, basics.AppParams{}) + ledger.NewAccount(ledger.ApplicationID().Address(), 50_000) + tx.ForeignApps = []basics.AppIndex{basics.AppIndex(222)} + testApp(t, `itxn_begin +int appl; itxn_field TypeEnum +int 222; itxn_field ApplicationID +itxn_submit +int 1 +`, ep) +} diff --git a/data/transactions/logic/evalStateful_test.go b/data/transactions/logic/evalStateful_test.go index 0d8345635b..34eabb135a 100644 --- a/data/transactions/logic/evalStateful_test.go +++ b/data/transactions/logic/evalStateful_test.go @@ -2419,3 +2419,18 @@ func TestAppAddress(t *testing.T) { source = fmt.Sprintf("int 0; app_params_get AppAddress; assert; addr %s; ==;", a) testApp(t, source, ep) } + +func TestBudget(t *testing.T) { + ep, tx, ledger := makeSampleEnv() + ledger.NewApp(tx.Receiver, 888, basics.AppParams{}) + source := ` +global OpcodeBudget +int 699 +== +assert +global OpcodeBudget +int 695 +== +` + testApp(t, source, ep) +} diff --git a/data/transactions/logic/eval_test.go b/data/transactions/logic/eval_test.go index 0ca809264b..684a76e840 100644 --- a/data/transactions/logic/eval_test.go +++ b/data/transactions/logic/eval_test.go @@ -930,7 +930,17 @@ byte 0x0706000000000000000000000000000000000000000000000000000000000000 ` const globalV6TestProgram = globalV5TestProgram + ` -// No new globals in v6 +global OpcodeBudget +int 0 +> +&& +global CallerApplicationAddress +global ZeroAddress +== +&& +global CallerApplicationID +! +&& ` func TestGlobal(t *testing.T) { @@ -941,6 +951,7 @@ func TestGlobal(t *testing.T) { lastField GlobalField program string } + // Associate the highest allowed global constant with each version's test program tests := map[uint64]desc{ 0: {GroupSize, globalV1TestProgram}, 1: {GroupSize, globalV1TestProgram}, @@ -948,10 +959,11 @@ func TestGlobal(t *testing.T) { 3: {CreatorAddress, globalV3TestProgram}, 4: {CreatorAddress, globalV4TestProgram}, 5: {GroupID, globalV5TestProgram}, - 6: {GroupID, globalV6TestProgram}, + 6: {CallerApplicationAddress, globalV6TestProgram}, } // tests keys are versions so they must be in a range 1..AssemblerMaxVersion plus zero version require.LessOrEqual(t, len(tests), AssemblerMaxVersion+1) + require.Len(t, globalFieldSpecs, int(invalidGlobalField)) ledger := MakeLedger(nil) addr, err := basics.UnmarshalChecksumAddress(testAddr) @@ -963,13 +975,13 @@ func TestGlobal(t *testing.T) { t.Run(fmt.Sprintf("v=%d", v), func(t *testing.T) { last := tests[v].lastField testProgram := tests[v].program - for _, globalField := range GlobalFieldNames[:last] { + for _, globalField := range GlobalFieldNames[:last+1] { if !strings.Contains(testProgram, globalField) { t.Errorf("TestGlobal missing field %v", globalField) } } - var txn transactions.SignedTxn + txn := transactions.SignedTxn{} txn.Txn.Group = crypto.Digest{0x07, 0x06} proto := config.ConsensusParams{ diff --git a/data/transactions/logic/fields.go b/data/transactions/logic/fields.go index fe64dd1c80..f226a6f856 100644 --- a/data/transactions/logic/fields.go +++ b/data/transactions/logic/fields.go @@ -371,6 +371,17 @@ const ( // GroupID [32]byte GroupID + // v6 + + // OpcodeBudget The remaining budget available for execution + OpcodeBudget + + // CallerApplicationID + CallerApplicationID + + // CallerApplicationAddress + CallerApplicationAddress + invalidGlobalField ) @@ -400,6 +411,9 @@ var globalFieldSpecs = []globalFieldSpec{ {CreatorAddress, StackBytes, runModeApplication, 3}, {CurrentApplicationAddress, StackBytes, runModeApplication, 5}, {GroupID, StackBytes, modeAny, 5}, + {OpcodeBudget, StackUint64, runModeApplication, 6}, + {CallerApplicationID, StackUint64, runModeApplication, 6}, + {CallerApplicationAddress, StackBytes, runModeApplication, 6}, } // GlobalFieldSpecByField maps GlobalField to spec diff --git a/data/transactions/logic/fields_string.go b/data/transactions/logic/fields_string.go index 82abacb941..31012932cd 100644 --- a/data/transactions/logic/fields_string.go +++ b/data/transactions/logic/fields_string.go @@ -99,12 +99,15 @@ func _() { _ = x[CreatorAddress-9] _ = x[CurrentApplicationAddress-10] _ = x[GroupID-11] - _ = x[invalidGlobalField-12] + _ = x[OpcodeBudget-12] + _ = x[CallerApplicationID-13] + _ = x[CallerApplicationAddress-14] + _ = x[invalidGlobalField-15] } -const _GlobalField_name = "MinTxnFeeMinBalanceMaxTxnLifeZeroAddressGroupSizeLogicSigVersionRoundLatestTimestampCurrentApplicationIDCreatorAddressCurrentApplicationAddressGroupIDinvalidGlobalField" +const _GlobalField_name = "MinTxnFeeMinBalanceMaxTxnLifeZeroAddressGroupSizeLogicSigVersionRoundLatestTimestampCurrentApplicationIDCreatorAddressCurrentApplicationAddressGroupIDOpcodeBudgetCallerApplicationIDCallerApplicationAddressinvalidGlobalField" -var _GlobalField_index = [...]uint8{0, 9, 19, 29, 40, 49, 64, 69, 84, 104, 118, 143, 150, 168} +var _GlobalField_index = [...]uint8{0, 9, 19, 29, 40, 49, 64, 69, 84, 104, 118, 143, 150, 162, 181, 205, 223} func (i GlobalField) String() string { if i >= GlobalField(len(_GlobalField_index)-1) { From 24055a3f8838d59b1281bc0a0bcdbe5b79620e0b Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Sat, 20 Nov 2021 23:44:58 -0500 Subject: [PATCH 2/6] detritis --- data/transactions/logic/eval_test.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/data/transactions/logic/eval_test.go b/data/transactions/logic/eval_test.go index 684a76e840..be4d4527db 100644 --- a/data/transactions/logic/eval_test.go +++ b/data/transactions/logic/eval_test.go @@ -1036,9 +1036,7 @@ int %s == &&`, symbol, string(tt)) ops := testProg(t, text, v) - if v < appsEnabledVersion && tt == protocol.ApplicationCallTx { - } - var txn transactions.SignedTxn + txn := transactions.SignedTxn{} txn.Txn.Type = tt if v < appsEnabledVersion && tt == protocol.ApplicationCallTx { testLogicBytes(t, ops.Program, defaultEvalParams(&txn), From 92a26b62a06ea7a3025554ae0d33f39ea186e6ae Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Mon, 22 Nov 2021 09:08:06 -0500 Subject: [PATCH 3/6] Check error --- ledger/internal/apptxn_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ledger/internal/apptxn_test.go b/ledger/internal/apptxn_test.go index e8ac199985..43a6fda7aa 100644 --- a/ledger/internal/apptxn_test.go +++ b/ledger/internal/apptxn_test.go @@ -475,7 +475,8 @@ func TestClawbackAction(t *testing.T) { Accounts: []basics.Address{addrs[0], addrs[1]}, } eval = nextBlock(t, l, true, nil) - txgroup(t, l, eval, &overpay, &clawmove) + err := txgroup(t, l, eval, &overpay, &clawmove) + require.NoError(t, err) endBlock(t, l, eval) amount, _ := holding(t, l, addrs[1], asaIndex) From d268300fffbb7a163bce94506fc60d188f1c7a79 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Mon, 22 Nov 2021 09:09:32 -0500 Subject: [PATCH 4/6] doc comments --- data/transactions/logic/fields.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/transactions/logic/fields.go b/data/transactions/logic/fields.go index f226a6f856..da2b26e10d 100644 --- a/data/transactions/logic/fields.go +++ b/data/transactions/logic/fields.go @@ -376,10 +376,10 @@ const ( // OpcodeBudget The remaining budget available for execution OpcodeBudget - // CallerApplicationID + // CallerApplicationID The ID of the caller app, else 0 CallerApplicationID - // CallerApplicationAddress + // CallerApplicationAddress The Address of the caller app, else ZeroAddress CallerApplicationAddress invalidGlobalField From b2dd92c80c24946f31c593895bf16052f193e9bd Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Tue, 23 Nov 2021 18:50:42 -0500 Subject: [PATCH 5/6] opcode, docs, and tests --- data/transactions/logic/assembler_test.go | 5 +++- data/transactions/logic/doc.go | 21 +++++++++-------- data/transactions/logic/eval.go | 24 ++++++++++++++++++++ data/transactions/logic/evalStateful_test.go | 3 ++- data/transactions/logic/eval_test.go | 18 ++++++++++++++- data/transactions/logic/opcodes.go | 3 ++- 6 files changed, 60 insertions(+), 14 deletions(-) diff --git a/data/transactions/logic/assembler_test.go b/data/transactions/logic/assembler_test.go index 152f218718..b360991938 100644 --- a/data/transactions/logic/assembler_test.go +++ b/data/transactions/logic/assembler_test.go @@ -346,6 +346,9 @@ const v6Nonsense = v5Nonsense + ` itxn_next gitxn 4 CreatedAssetID gitxna 3 Logs 12 +int 0 +dup +gloadss ` var nonsense = map[uint64]string{ @@ -363,7 +366,7 @@ var compiled = map[uint64]string{ 3: "032008b7a60cf8acd19181cf959a12f8acd19181cf951af8acd19181cf15f8acd191810f01020026050212340c68656c6c6f20776f726c6421208dae2087fbba51304eb02b91f656948397a7946390e8cb70fc9ea4d95f92251d024242047465737400320032013202320328292929292a0431003101310231043105310731083109310a310b310c310d310e310f3111311231133114311533000033000133000233000433000533000733000833000933000a33000b33000c33000d33000e33000f3300113300123300133300143300152d2e0102222324252104082209240a220b230c240d250e230f23102311231223132314181b1c2b171615400003290349483403350222231d4a484848482a50512a63222352410003420000432105602105612105270463484821052b62482b642b65484821052b2106662b21056721072b682b692107210570004848210771004848361c0037001a0031183119311b311d311e311f3120210721051e312131223123312431253126312731283129312a312b312c312d312e312f4478222105531421055427042106552105082106564c4d4b02210538212106391c0081e80780046a6f686e", 4: "042004010200b7a60c26040242420c68656c6c6f20776f726c6421208dae2087fbba51304eb02b91f656948397a7946390e8cb70fc9ea4d95f92251d047465737400320032013202320380021234292929292a0431003101310231043105310731083109310a310b310c310d310e310f3111311231133114311533000033000133000233000433000533000733000833000933000a33000b33000c33000d33000e33000f3300113300123300133300143300152d2e01022581f8acd19181cf959a1281f8acd19181cf951a81f8acd19181cf1581f8acd191810f082209240a220b230c240d250e230f23102311231223132314181b1c28171615400003290349483403350222231d4a484848482a50512a632223524100034200004322602261222b634848222862482864286548482228236628226724286828692422700048482471004848361c0037001a0031183119311b311d311e311f312024221e312131223123312431253126312731283129312a312b312c312d312e312f44782522531422542b2355220823564c4d4b0222382123391c0081e80780046a6f686e2281d00f24231f880003420001892223902291922394239593a0a1a2a3a4a5a6a7a8a9aaabacadae23af3a00003b003c003d8164", 5: "052004010002b7a60c26050242420c68656c6c6f20776f726c6421070123456789abcd208dae2087fbba51304eb02b91f656948397a7946390e8cb70fc9ea4d95f92251d047465737400320032013202320380021234292929292b0431003101310231043105310731083109310a310b310c310d310e310f3111311231133114311533000033000133000233000433000533000733000833000933000a33000b33000c33000d33000e33000f3300113300123300133300143300152d2e01022581f8acd19181cf959a1281f8acd19181cf951a81f8acd19181cf1581f8acd191810f082209240a220b230c240d250e230f23102311231223132314181b1c28171615400003290349483403350222231d4a484848482b50512a632223524100034200004322602261222704634848222862482864286548482228246628226723286828692322700048482371004848361c0037001a0031183119311b311d311e311f312023221e312131223123312431253126312731283129312a312b312c312d312e312f447825225314225427042455220824564c4d4b0222382124391c0081e80780046a6f686e2281d00f23241f880003420001892224902291922494249593a0a1a2a3a4a5a6a7a8a9aaabacadae24af3a00003b003c003d816472064e014f012a57000823810858235b235a2359b03139330039b1b200b322c01a23c1001a2323c21a23c3233e233f8120af06002a494905002a49490700b53a03", - 6: "062004010002b7a60c26050242420c68656c6c6f20776f726c6421070123456789abcd208dae2087fbba51304eb02b91f656948397a7946390e8cb70fc9ea4d95f92251d047465737400320032013202320380021234292929292b0431003101310231043105310731083109310a310b310c310d310e310f3111311231133114311533000033000133000233000433000533000733000833000933000a33000b33000c33000d33000e33000f3300113300123300133300143300152d2e01022581f8acd19181cf959a1281f8acd19181cf951a81f8acd19181cf1581f8acd191810f082209240a220b230c240d250e230f23102311231223132314181b1c28171615400003290349483403350222231d4a484848482b50512a632223524100034200004322602261222704634848222862482864286548482228246628226723286828692322700048482371004848361c0037001a0031183119311b311d311e311f312023221e312131223123312431253126312731283129312a312b312c312d312e312f447825225314225427042455220824564c4d4b0222382124391c0081e80780046a6f686e2281d00f23241f880003420001892224902291922494249593a0a1a2a3a4a5a6a7a8a9aaabacadae24af3a00003b003c003d816472064e014f012a57000823810858235b235a2359b03139330039b1b200b322c01a23c1001a2323c21a23c3233e233f8120af06002a494905002a49490700b53a03b6b7043cb8033a0c}", + 6: "062004010002b7a60c26050242420c68656c6c6f20776f726c6421070123456789abcd208dae2087fbba51304eb02b91f656948397a7946390e8cb70fc9ea4d95f92251d047465737400320032013202320380021234292929292b0431003101310231043105310731083109310a310b310c310d310e310f3111311231133114311533000033000133000233000433000533000733000833000933000a33000b33000c33000d33000e33000f3300113300123300133300143300152d2e01022581f8acd19181cf959a1281f8acd19181cf951a81f8acd19181cf1581f8acd191810f082209240a220b230c240d250e230f23102311231223132314181b1c28171615400003290349483403350222231d4a484848482b50512a632223524100034200004322602261222704634848222862482864286548482228246628226723286828692322700048482371004848361c0037001a0031183119311b311d311e311f312023221e312131223123312431253126312731283129312a312b312c312d312e312f447825225314225427042455220824564c4d4b0222382124391c0081e80780046a6f686e2281d00f23241f880003420001892224902291922494249593a0a1a2a3a4a5a6a7a8a9aaabacadae24af3a00003b003c003d816472064e014f012a57000823810858235b235a2359b03139330039b1b200b322c01a23c1001a2323c21a23c3233e233f8120af06002a494905002a49490700b53a03b6b7043cb8033a0c2349c4}", } func pseudoOp(opcode string) bool { diff --git a/data/transactions/logic/doc.go b/data/transactions/logic/doc.go index 43ba8d2178..481887e520 100644 --- a/data/transactions/logic/doc.go +++ b/data/transactions/logic/doc.go @@ -100,15 +100,16 @@ var opDocByName = map[string]string{ "gitxn": "push field F of the Tth transaction in the last inner group", "gitxna": "push Ith value of the array field F from the Tth transaction in the last inner group", - "global": "push value from globals to stack", - "load": "copy a value from scratch space to the stack. All scratch spaces are 0 at program start.", - "store": "pop value X. store X to the Ith scratch space", - "loads": "copy a value from the Xth scratch space to the stack. All scratch spaces are 0 at program start.", - "stores": "pop indexes A and B. store B to the Ath scratch space", - "gload": "push Ith scratch space index of the Tth transaction in the current group", - "gloads": "push Ith scratch space index of the Xth transaction in the current group", - "gaid": "push the ID of the asset or application created in the Tth transaction of the current group", - "gaids": "push the ID of the asset or application created in the Xth transaction of the current group", + "global": "push value from globals to stack", + "load": "copy a value from scratch space to the stack. All scratch spaces are 0 at program start.", + "store": "pop value X. store X to the Ith scratch space", + "loads": "copy a value from the Xth scratch space to the stack. All scratch spaces are 0 at program start.", + "stores": "pop indexes A and B. store B to the Ath scratch space", + "gload": "push Ith scratch space index of the Tth transaction in the current group", + "gloads": "push Ith scratch space index of the Xth transaction in the current group", + "gloadss": "push Bth scratch space index of the Ath transaction in the current group", + "gaid": "push the ID of the asset or application created in the Tth transaction of the current group", + "gaids": "push the ID of the asset or application created in the Xth transaction of the current group", "bnz": "branch to TARGET if value X is not zero", "bz": "branch to TARGET if value X is zero", @@ -302,7 +303,7 @@ var OpGroups = map[string][]string{ "Byte Array Slicing": {"substring", "substring3", "extract", "extract3", "extract_uint16", "extract_uint32", "extract_uint64"}, "Byte Array Arithmetic": {"b+", "b-", "b/", "b*", "b<", "b>", "b<=", "b>=", "b==", "b!=", "b%"}, "Byte Array Logic": {"b|", "b&", "b^", "b~"}, - "Loading Values": {"intcblock", "intc", "intc_0", "intc_1", "intc_2", "intc_3", "pushint", "bytecblock", "bytec", "bytec_0", "bytec_1", "bytec_2", "bytec_3", "pushbytes", "bzero", "arg", "arg_0", "arg_1", "arg_2", "arg_3", "args", "txn", "gtxn", "txna", "txnas", "gtxna", "gtxnas", "gtxns", "gtxnsa", "gtxnsas", "global", "load", "loads", "store", "stores", "gload", "gloads", "gaid", "gaids"}, + "Loading Values": {"intcblock", "intc", "intc_0", "intc_1", "intc_2", "intc_3", "pushint", "bytecblock", "bytec", "bytec_0", "bytec_1", "bytec_2", "bytec_3", "pushbytes", "bzero", "arg", "arg_0", "arg_1", "arg_2", "arg_3", "args", "txn", "gtxn", "txna", "txnas", "gtxna", "gtxnas", "gtxns", "gtxnsa", "gtxnsas", "global", "load", "loads", "store", "stores", "gload", "gloads", "gloadss", "gaid", "gaids"}, "Flow Control": {"err", "bnz", "bz", "b", "return", "pop", "dup", "dup2", "dig", "cover", "uncover", "swap", "select", "assert", "callsub", "retsub"}, "State Access": {"balance", "min_balance", "app_opted_in", "app_local_get", "app_local_get_ex", "app_global_get", "app_global_get_ex", "app_local_put", "app_global_put", "app_local_del", "app_global_del", "asset_holding_get", "asset_params_get", "app_params_get", "log"}, "Inner Transactions": {"itxn_begin", "itxn_next", "itxn_field", "itxn_submit", "itxn", "itxna", "gitxn", "gitxna"}, diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go index 3285884112..a711a1d10d 100644 --- a/data/transactions/logic/eval.go +++ b/data/transactions/logic/eval.go @@ -2977,6 +2977,30 @@ func opGloads(cx *EvalContext) { cx.stack[last] = scratchValue } +func opGloadss(cx *EvalContext) { + last := len(cx.stack) - 1 + prev := last - 1 + + gi := cx.stack[prev].Uint + if gi >= uint64(len(cx.TxnGroup)) { + cx.err = fmt.Errorf("gloads lookup TxnGroup[%d] but it only has %d", gi, len(cx.TxnGroup)) + return + } + scratchIdx := cx.stack[last].Uint + if scratchIdx >= 256 { + cx.err = fmt.Errorf("gloadss scratch index >= 256 (%d)", scratchIdx) + return + } + scratchValue, err := opGloadImpl(cx, int(gi), byte(scratchIdx), "gloadss") + if err != nil { + cx.err = err + return + } + + cx.stack[prev] = scratchValue + cx.stack = cx.stack[:last] +} + func opConcat(cx *EvalContext) { last := len(cx.stack) - 1 prev := last - 1 diff --git a/data/transactions/logic/evalStateful_test.go b/data/transactions/logic/evalStateful_test.go index 34eabb135a..855c4f05ea 100644 --- a/data/transactions/logic/evalStateful_test.go +++ b/data/transactions/logic/evalStateful_test.go @@ -2244,6 +2244,7 @@ func TestReturnTypes(t *testing.T) { "store": "store 0", "gload": "gload 0 0", "gloads": "gloads 0", + "gloadss": "pop; pop; int 0; int 1; gloadss", // Needs txn index = 0 to work "gaid": "gaid 0", "dig": "dig 0", "cover": "cover 0", @@ -2325,7 +2326,7 @@ func TestReturnTypes(t *testing.T) { require.Equal( t, len(spec.Returns), len(cx.stack), - fmt.Sprintf("\n%s%s expected to return %d values but stack has %d", ep.Trace.String(), spec.Name, len(spec.Returns), len(cx.stack)), + fmt.Sprintf("\n%s%s expected to return %d values but stack is %v", ep.Trace.String(), spec.Name, len(spec.Returns), cx.stack), ) for i := 0; i < len(spec.Returns); i++ { sp := len(cx.stack) - 1 - i diff --git a/data/transactions/logic/eval_test.go b/data/transactions/logic/eval_test.go index be4d4527db..46723c421e 100644 --- a/data/transactions/logic/eval_test.go +++ b/data/transactions/logic/eval_test.go @@ -2468,6 +2468,7 @@ int 1`, } } +// TestGloads tests gloads and gloadss func TestGloads(t *testing.T) { partitiontest.PartitionTest(t) @@ -2487,11 +2488,26 @@ int 0 gloads 0 byte "txn 1" == +assert int 1 gloads 1 byte "txn 2" == -&&` +assert +int 0 +int 0 +gloadss +byte "txn 1" +== +assert +int 1 +int 1 +gloadss +byte "txn 2" +== +assert +int 1 +` sources := []string{source1, source2, source3} diff --git a/data/transactions/logic/opcodes.go b/data/transactions/logic/opcodes.go index d07cb213f5..18feff9546 100644 --- a/data/transactions/logic/opcodes.go +++ b/data/transactions/logic/opcodes.go @@ -221,7 +221,7 @@ var OpSpecs = []OpSpec{ // Group scratch space access {0x3a, "gload", opGload, asmDefault, disDefault, nil, oneAny, 4, runModeApplication, immediates("t", "i")}, {0x3b, "gloads", opGloads, asmDefault, disDefault, oneInt, oneAny, 4, runModeApplication, immediates("i")}, - // Access creatable IDs + // Access creatable IDs (consider deprecating, as txn CreatedAssetID, CreatedApplicationID should be enough {0x3c, "gaid", opGaid, asmDefault, disDefault, nil, oneInt, 4, runModeApplication, immediates("t")}, {0x3d, "gaids", opGaids, asmDefault, disDefault, oneInt, oneInt, 4, runModeApplication, opDefault}, @@ -334,6 +334,7 @@ var OpSpecs = []OpSpec{ {0xc1, "gtxnas", opGtxnas, assembleGtxnas, disGtxn, oneInt, oneAny, 5, modeAny, immediates("t", "f")}, {0xc2, "gtxnsas", opGtxnsas, assembleGtxnsas, disTxn, twoInts, oneAny, 5, modeAny, immediates("f")}, {0xc3, "args", opArgs, asmDefault, disDefault, oneInt, oneBytes, 5, runModeSignature, opDefault}, + {0xc4, "gloadss", opGloadss, asmDefault, disDefault, twoInts, oneAny, 6, runModeApplication, opDefault}, } type sortByOpcode []OpSpec From bdc095f8015b466e648d0c3fbe4ada9998236e3e Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Wed, 24 Nov 2021 09:17:14 -0500 Subject: [PATCH 6/6] specs update --- data/transactions/logic/README.md | 1 + data/transactions/logic/TEAL_opcodes.md | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/data/transactions/logic/README.md b/data/transactions/logic/README.md index 0015bafec0..6a08bb8335 100644 --- a/data/transactions/logic/README.md +++ b/data/transactions/logic/README.md @@ -250,6 +250,7 @@ Some of these have immediate data in the byte or bytes after the opcode. | `stores` | pop indexes A and B. store B to the Ath scratch space | | `gload t i` | push Ith scratch space index of the Tth transaction in the current group | | `gloads i` | push Ith scratch space index of the Xth transaction in the current group | +| `gloadss` | push Bth scratch space index of the Ath transaction in the current group | | `gaid t` | push the ID of the asset or application created in the Tth transaction of the current group | | `gaids` | push the ID of the asset or application created in the Xth transaction of the current group | diff --git a/data/transactions/logic/TEAL_opcodes.md b/data/transactions/logic/TEAL_opcodes.md index 66ecafe7f3..d38645afb3 100644 --- a/data/transactions/logic/TEAL_opcodes.md +++ b/data/transactions/logic/TEAL_opcodes.md @@ -1404,3 +1404,12 @@ bitlen interprets arrays as big-endian integers, unlike setbit/getbit - push Xth LogicSig argument to stack - LogicSigVersion >= 5 - Mode: Signature + +## gloadss + +- Opcode: 0xc4 +- Pops: *... stack*, {uint64 A}, {uint64 B} +- Pushes: any +- push Bth scratch space index of the Ath transaction in the current group +- LogicSigVersion >= 6 +- Mode: Application