Skip to content

Commit 221955f

Browse files
authored
Op base64 decode (#3220)
b64 opcode, tests, and specs
1 parent 850f7c7 commit 221955f

12 files changed

+316
-5
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,5 @@ assets
6565

6666
index.html
6767

68+
# test summary
69+
testresults.json

data/transactions/logic/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ various sizes.
165165
| `extract_uint16` | pop a byte-array A and integer B. Extract a range of bytes from A starting at B up to but not including B+2, convert bytes as big endian and push the uint64 result. If B+2 is larger than the array length, the program fails |
166166
| `extract_uint32` | pop a byte-array A and integer B. Extract a range of bytes from A starting at B up to but not including B+4, convert bytes as big endian and push the uint64 result. If B+4 is larger than the array length, the program fails |
167167
| `extract_uint64` | pop a byte-array A and integer B. Extract a range of bytes from A starting at B up to but not including B+8, convert bytes as big endian and push the uint64 result. If B+8 is larger than the array length, the program fails |
168+
| `base64_decode e` | decode X which was base64-encoded using _encoding alphabet_ E. Fail if X is not base64 encoded with alphabet E |
168169

169170
These opcodes take byte-array values that are interpreted as
170171
big-endian unsigned integers. For mathematical operators, the

data/transactions/logic/TEAL_opcodes.md

+11
Original file line numberDiff line numberDiff line change
@@ -854,6 +854,17 @@ When A is a uint64, index 0 is the least significant bit. Setting bit 3 to 1 on
854854
- pop a byte-array A and integer B. Extract a range of bytes from A starting at B up to but not including B+8, convert bytes as big endian and push the uint64 result. If B+8 is larger than the array length, the program fails
855855
- LogicSigVersion >= 5
856856

857+
## base64_decode e
858+
859+
- Opcode: 0x5c {uint8 alphabet index}
860+
- Pops: *... stack*, []byte
861+
- Pushes: []byte
862+
- decode X which was base64-encoded using _encoding alphabet_ E. Fail if X is not base64 encoded with alphabet E
863+
- **Cost**: 25
864+
- LogicSigVersion >= 6
865+
866+
decodes X using the base64 encoding alphabet E. Specify the alphabet with an immediate arg either as URL and Filename Safe (`URLAlph`) or Standard (`StdAlph`). See <a href="https://rfc-editor.org/rfc/rfc4648.html#section-4">RFC 4648</a> (sections 4 and 5)
867+
857868
## balance
858869

859870
- Opcode: 0x60

data/transactions/logic/assembler.go

+36
Original file line numberDiff line numberDiff line change
@@ -1244,6 +1244,28 @@ func assembleEcdsa(ops *OpStream, spec *OpSpec, args []string) error {
12441244
return nil
12451245
}
12461246

1247+
func assembleBase64Decode(ops *OpStream, spec *OpSpec, args []string) error {
1248+
if len(args) != 1 {
1249+
return ops.errorf("%s expects one argument", spec.Name)
1250+
}
1251+
1252+
alph, ok := base64AlphabetSpecByName[args[0]]
1253+
if !ok {
1254+
return ops.errorf("%s unknown alphabet: %#v", spec.Name, args[0])
1255+
}
1256+
if alph.version > ops.Version {
1257+
//nolint:errcheck // we continue to maintain typestack
1258+
ops.errorf("%s %s available in version %d. Missed #pragma version?", spec.Name, args[0], alph.version)
1259+
}
1260+
1261+
val := alph.field
1262+
ops.pending.WriteByte(spec.Opcode)
1263+
ops.pending.WriteByte(uint8(val))
1264+
ops.trace("%s (%s)", alph.field, alph.ftype)
1265+
ops.returns(alph.ftype)
1266+
return nil
1267+
}
1268+
12471269
type assembleFunc func(*OpStream, *OpSpec, []string) error
12481270

12491271
// Basic assembly. Any extra bytes of opcode are encoded as byte immediates.
@@ -2668,6 +2690,20 @@ func disEcdsa(dis *disassembleState, spec *OpSpec) (string, error) {
26682690
return fmt.Sprintf("%s %s", spec.Name, EcdsaCurveNames[arg]), nil
26692691
}
26702692

2693+
func disBase64Decode(dis *disassembleState, spec *OpSpec) (string, error) {
2694+
lastIdx := dis.pc + 1
2695+
if len(dis.program) <= lastIdx {
2696+
missing := lastIdx - len(dis.program) + 1
2697+
return "", fmt.Errorf("unexpected %s opcode end: missing %d bytes", spec.Name, missing)
2698+
}
2699+
dis.nextpc = dis.pc + 2
2700+
b64dArg := dis.program[dis.pc+1]
2701+
if int(b64dArg) >= len(base64AlphabetNames) {
2702+
return "", fmt.Errorf("invalid base64_decode arg index %d at pc=%d", b64dArg, dis.pc)
2703+
}
2704+
return fmt.Sprintf("%s %s", spec.Name, base64AlphabetNames[b64dArg]), nil
2705+
}
2706+
26712707
type disInfo struct {
26722708
pcOffset []PCOffset
26732709
hasStatefulOps bool

data/transactions/logic/assembler_test.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,7 @@ itxna Logs 3
344344

345345
const v6Nonsense = v5Nonsense + `
346346
itxn_next
347+
base64_decode URLAlph
347348
`
348349

349350
var nonsense = map[uint64]string{
@@ -361,7 +362,7 @@ var compiled = map[uint64]string{
361362
3: "032008b7a60cf8acd19181cf959a12f8acd19181cf951af8acd19181cf15f8acd191810f01020026050212340c68656c6c6f20776f726c6421208dae2087fbba51304eb02b91f656948397a7946390e8cb70fc9ea4d95f92251d024242047465737400320032013202320328292929292a0431003101310231043105310731083109310a310b310c310d310e310f3111311231133114311533000033000133000233000433000533000733000833000933000a33000b33000c33000d33000e33000f3300113300123300133300143300152d2e0102222324252104082209240a220b230c240d250e230f23102311231223132314181b1c2b171615400003290349483403350222231d4a484848482a50512a63222352410003420000432105602105612105270463484821052b62482b642b65484821052b2106662b21056721072b682b692107210570004848210771004848361c0037001a0031183119311b311d311e311f3120210721051e312131223123312431253126312731283129312a312b312c312d312e312f4478222105531421055427042106552105082106564c4d4b02210538212106391c0081e80780046a6f686e",
362363
4: "042004010200b7a60c26040242420c68656c6c6f20776f726c6421208dae2087fbba51304eb02b91f656948397a7946390e8cb70fc9ea4d95f92251d047465737400320032013202320380021234292929292a0431003101310231043105310731083109310a310b310c310d310e310f3111311231133114311533000033000133000233000433000533000733000833000933000a33000b33000c33000d33000e33000f3300113300123300133300143300152d2e01022581f8acd19181cf959a1281f8acd19181cf951a81f8acd19181cf1581f8acd191810f082209240a220b230c240d250e230f23102311231223132314181b1c28171615400003290349483403350222231d4a484848482a50512a632223524100034200004322602261222b634848222862482864286548482228236628226724286828692422700048482471004848361c0037001a0031183119311b311d311e311f312024221e312131223123312431253126312731283129312a312b312c312d312e312f44782522531422542b2355220823564c4d4b0222382123391c0081e80780046a6f686e2281d00f24231f880003420001892223902291922394239593a0a1a2a3a4a5a6a7a8a9aaabacadae23af3a00003b003c003d8164",
363364
5: "052004010002b7a60c26050242420c68656c6c6f20776f726c6421070123456789abcd208dae2087fbba51304eb02b91f656948397a7946390e8cb70fc9ea4d95f92251d047465737400320032013202320380021234292929292b0431003101310231043105310731083109310a310b310c310d310e310f3111311231133114311533000033000133000233000433000533000733000833000933000a33000b33000c33000d33000e33000f3300113300123300133300143300152d2e01022581f8acd19181cf959a1281f8acd19181cf951a81f8acd19181cf1581f8acd191810f082209240a220b230c240d250e230f23102311231223132314181b1c28171615400003290349483403350222231d4a484848482b50512a632223524100034200004322602261222704634848222862482864286548482228246628226723286828692322700048482371004848361c0037001a0031183119311b311d311e311f312023221e312131223123312431253126312731283129312a312b312c312d312e312f447825225314225427042455220824564c4d4b0222382124391c0081e80780046a6f686e2281d00f23241f880003420001892224902291922494249593a0a1a2a3a4a5a6a7a8a9aaabacadae24af3a00003b003c003d816472064e014f012a57000823810858235b235a2359b03139330039b1b200b322c01a23c1001a2323c21a23c3233e233f8120af06002a494905002a49490700b53a03",
364-
6: "062004010002b7a60c26050242420c68656c6c6f20776f726c6421070123456789abcd208dae2087fbba51304eb02b91f656948397a7946390e8cb70fc9ea4d95f92251d047465737400320032013202320380021234292929292b0431003101310231043105310731083109310a310b310c310d310e310f3111311231133114311533000033000133000233000433000533000733000833000933000a33000b33000c33000d33000e33000f3300113300123300133300143300152d2e01022581f8acd19181cf959a1281f8acd19181cf951a81f8acd19181cf1581f8acd191810f082209240a220b230c240d250e230f23102311231223132314181b1c28171615400003290349483403350222231d4a484848482b50512a632223524100034200004322602261222704634848222862482864286548482228246628226723286828692322700048482371004848361c0037001a0031183119311b311d311e311f312023221e312131223123312431253126312731283129312a312b312c312d312e312f447825225314225427042455220824564c4d4b0222382124391c0081e80780046a6f686e2281d00f23241f880003420001892224902291922494249593a0a1a2a3a4a5a6a7a8a9aaabacadae24af3a00003b003c003d816472064e014f012a57000823810858235b235a2359b03139330039b1b200b322c01a23c1001a2323c21a23c3233e233f8120af06002a494905002a49490700b53a03b6",
365+
6: "062004010002b7a60c26050242420c68656c6c6f20776f726c6421070123456789abcd208dae2087fbba51304eb02b91f656948397a7946390e8cb70fc9ea4d95f92251d047465737400320032013202320380021234292929292b0431003101310231043105310731083109310a310b310c310d310e310f3111311231133114311533000033000133000233000433000533000733000833000933000a33000b33000c33000d33000e33000f3300113300123300133300143300152d2e01022581f8acd19181cf959a1281f8acd19181cf951a81f8acd19181cf1581f8acd191810f082209240a220b230c240d250e230f23102311231223132314181b1c28171615400003290349483403350222231d4a484848482b50512a632223524100034200004322602261222704634848222862482864286548482228246628226723286828692322700048482371004848361c0037001a0031183119311b311d311e311f312023221e312131223123312431253126312731283129312a312b312c312d312e312f447825225314225427042455220824564c4d4b0222382124391c0081e80780046a6f686e2281d00f23241f880003420001892224902291922494249593a0a1a2a3a4a5a6a7a8a9aaabacadae24af3a00003b003c003d816472064e014f012a57000823810858235b235a2359b03139330039b1b200b322c01a23c1001a2323c21a23c3233e233f8120af06002a494905002a49490700b53a03b65c00",
365366
}
366367

367368
func pseudoOp(opcode string) bool {

data/transactions/logic/doc.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ var opDocByName = map[string]string{
133133
"extract_uint16": "pop a byte-array A and integer B. Extract a range of bytes from A starting at B up to but not including B+2, convert bytes as big endian and push the uint64 result. If B+2 is larger than the array length, the program fails",
134134
"extract_uint32": "pop a byte-array A and integer B. Extract a range of bytes from A starting at B up to but not including B+4, convert bytes as big endian and push the uint64 result. If B+4 is larger than the array length, the program fails",
135135
"extract_uint64": "pop a byte-array A and integer B. Extract a range of bytes from A starting at B up to but not including B+8, convert bytes as big endian and push the uint64 result. If B+8 is larger than the array length, the program fails",
136+
"base64_decode": "decode X which was base64-encoded using _encoding alphabet_ E. Fail if X is not base64 encoded with alphabet E",
136137

137138
"balance": "get balance for account A, in microalgos. The balance is observed after the effects of previous transactions in the group, and after the fee for the current transaction is deducted.",
138139
"min_balance": "get minimum required balance for account A, in microalgos. Required balance is affected by [ASA](https://developer.algorand.org/docs/features/asa/#assets-overview) and [App](https://developer.algorand.org/docs/features/asc1/stateful/#minimum-balance-requirement-for-a-smart-contract) usage. When creating or opting into an app, the minimum balance grows before the app code runs, therefore the increase is visible there. When deleting or closing out, the minimum balance decreases after the app executes.",
@@ -229,6 +230,8 @@ var opcodeImmediateNotes = map[string]string{
229230
"ecdsa_verify": "{uint8 curve index}",
230231
"ecdsa_pk_decompress": "{uint8 curve index}",
231232
"ecdsa_pk_recover": "{uint8 curve index}",
233+
234+
"base64_decode": "{uint8 alphabet index}",
232235
}
233236

234237
// OpImmediateNote returns a short string about immediate data which follows the op byte
@@ -283,6 +286,7 @@ var opDocExtras = map[string]string{
283286
"itxn_begin": "`itxn_begin` initializes Sender to the application address; Fee to the minimum allowable, taking into account MinTxnFee and credit from overpaying in earlier transactions; FirstValid/LastValid to the values in the top-level transaction, and all other fields to zero values.",
284287
"itxn_field": "`itxn_field` fails if X is of the wrong type for F, including a byte array of the wrong size for use as an address when F is an address field. `itxn_field` also fails if X is an account or asset that does not appear in `txn.Accounts` or `txn.ForeignAssets` of the top-level transaction. (Setting addresses in asset creation are exempted from this requirement.)",
285288
"itxn_submit": "`itxn_submit` resets the current transaction so that it can not be resubmitted. A new `itxn_begin` is required to prepare another inner transaction.",
289+
"base64_decode": "decodes X using the base64 encoding alphabet E. Specify the alphabet with an immediate arg either as URL and Filename Safe (`URLAlph`) or Standard (`StdAlph`). See <a href=\"https://rfc-editor.org/rfc/rfc4648.html#section-4\">RFC 4648</a> (sections 4 and 5)",
286290
}
287291

288292
// OpDocExtra returns extra documentation text about an op
@@ -295,7 +299,7 @@ func OpDocExtra(opName string) string {
295299
// opcodes consecutively, even if their opcode values are not.
296300
var OpGroups = map[string][]string{
297301
"Arithmetic": {"sha256", "keccak256", "sha512_256", "ed25519verify", "ecdsa_verify", "ecdsa_pk_recover", "ecdsa_pk_decompress", "+", "-", "/", "*", "<", ">", "<=", ">=", "&&", "||", "shl", "shr", "sqrt", "bitlen", "exp", "==", "!=", "!", "len", "itob", "btoi", "%", "|", "&", "^", "~", "mulw", "addw", "divmodw", "expw", "getbit", "setbit", "getbyte", "setbyte", "concat"},
298-
"Byte Array Slicing": {"substring", "substring3", "extract", "extract3", "extract_uint16", "extract_uint32", "extract_uint64"},
302+
"Byte Array Slicing": {"substring", "substring3", "extract", "extract3", "extract_uint16", "extract_uint32", "extract_uint64", "base64_decode"},
299303
"Byte Array Arithmetic": {"b+", "b-", "b/", "b*", "b<", "b>", "b<=", "b>=", "b==", "b!=", "b%"},
300304
"Byte Array Logic": {"b|", "b&", "b^", "b~"},
301305
"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"},

data/transactions/logic/eval.go

+26
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"bytes"
2121
"crypto/sha256"
2222
"crypto/sha512"
23+
"encoding/base64"
2324
"encoding/binary"
2425
"encoding/hex"
2526
"errors"
@@ -4034,3 +4035,28 @@ func (cx *EvalContext) PcDetails() (pc int, dis string) {
40344035
}
40354036
return cx.pc, dis
40364037
}
4038+
4039+
func base64Decode(encoded []byte, encoding *base64.Encoding) ([]byte, error) {
4040+
decoded := make([]byte, encoding.DecodedLen(len(encoded)))
4041+
n, err := encoding.Strict().Decode(decoded, encoded)
4042+
if err != nil {
4043+
return decoded[:0], err
4044+
}
4045+
return decoded[:n], err
4046+
}
4047+
4048+
func opBase64Decode(cx *EvalContext) {
4049+
last := len(cx.stack) - 1
4050+
alphabetField := Base64Alphabet(cx.program[cx.pc+1])
4051+
fs, ok := base64AlphabetSpecByField[alphabetField]
4052+
if !ok || fs.version > cx.version {
4053+
cx.err = fmt.Errorf("invalid base64_decode alphabet %d", alphabetField)
4054+
return
4055+
}
4056+
4057+
encoding := base64.URLEncoding
4058+
if alphabetField == StdAlph {
4059+
encoding = base64.StdEncoding
4060+
}
4061+
cx.stack[last].Bytes, cx.err = base64Decode(cx.stack[last].Bytes, encoding)
4062+
}

data/transactions/logic/evalStateful_test.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -2391,7 +2391,8 @@ func TestReturnTypes(t *testing.T) {
23912391
"args": "args",
23922392
"itxn": "itxn_begin; int pay; itxn_field TypeEnum; itxn_submit; itxn CreatedAssetID",
23932393
// This next one is a cop out. Can't use itxna Logs until we have inner appl
2394-
"itxna": "itxn_begin; int pay; itxn_field TypeEnum; itxn_submit; itxn NumLogs",
2394+
"itxna": "itxn_begin; int pay; itxn_field TypeEnum; itxn_submit; itxn NumLogs",
2395+
"base64_decode": `pushbytes "YWJjMTIzIT8kKiYoKSctPUB+"; base64_decode StdAlph; pushbytes "abc123!?$*&()'-=@~"; ==; pushbytes "YWJjMTIzIT8kKiYoKSctPUB-"; base64_decode URLAlph; pushbytes "abc123!?$*&()'-=@~"; ==; &&; assert`,
23952396
}
23962397

23972398
// these require special input data and tested separately

data/transactions/logic/eval_test.go

+160
Original file line numberDiff line numberDiff line change
@@ -3719,6 +3719,54 @@ func BenchmarkBigMath(b *testing.B) {
37193719
}
37203720
}
37213721

3722+
func BenchmarkBase64Decode(b *testing.B) {
3723+
smallStd := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
3724+
smallURL := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
3725+
medStd := strings.Repeat(smallStd, 16)
3726+
medURL := strings.Repeat(smallURL, 16)
3727+
bigStd := strings.Repeat(medStd, 4)
3728+
bigURL := strings.Repeat(medURL, 4)
3729+
3730+
tags := []string{"small", "medium", "large"}
3731+
stds := []string{smallStd, medStd, bigStd}
3732+
urls := []string{smallURL, medURL, bigURL}
3733+
ops := []string{
3734+
"",
3735+
"len",
3736+
"b~",
3737+
"int 1; pop",
3738+
"keccak256",
3739+
"sha256",
3740+
"sha512_256",
3741+
"base64_decode StdAlph",
3742+
"base64_decode URLAlph",
3743+
}
3744+
benches := [][]string{}
3745+
for i, tag := range tags {
3746+
for _, op := range ops {
3747+
testName := op
3748+
encoded := stds[i]
3749+
if op == "base64_decode URLAlph" {
3750+
encoded = urls[i]
3751+
}
3752+
if len(op) > 0 {
3753+
op += "; "
3754+
}
3755+
op += "pop"
3756+
benches = append(benches, []string{
3757+
fmt.Sprintf("%s_%s", testName, tag),
3758+
"",
3759+
fmt.Sprintf(`byte "%s"; %s`, encoded, op),
3760+
"int 1",
3761+
})
3762+
}
3763+
}
3764+
for _, bench := range benches {
3765+
b.Run(bench[0], func(b *testing.B) {
3766+
benchmarkOperation(b, bench[1], bench[2], bench[3])
3767+
})
3768+
}
3769+
}
37223770
func BenchmarkAddx64(b *testing.B) {
37233771
progs := [][]string{
37243772
{"add long stack", addBenchmarkSource},
@@ -4909,3 +4957,115 @@ func TestPcDetails(t *testing.T) {
49094957
})
49104958
}
49114959
}
4960+
4961+
var minB64DecodeVersion uint64 = 6
4962+
4963+
type b64DecodeTestCase struct {
4964+
Encoded string
4965+
IsURL bool
4966+
Decoded string
4967+
Error error
4968+
}
4969+
4970+
var testCases = []b64DecodeTestCase{
4971+
{"TU9CWS1ESUNLOwoKb3IsIFRIRSBXSEFMRS4KCgpCeSBIZXJtYW4gTWVsdmlsbGU=",
4972+
false,
4973+
`MOBY-DICK;
4974+
4975+
or, THE WHALE.
4976+
4977+
4978+
By Herman Melville`,
4979+
nil,
4980+
},
4981+
{"TU9CWS1ESUNLOwoKb3IsIFRIRSBXSEFMRS4KCgpCeSBIZXJtYW4gTWVsdmlsbGU=",
4982+
true,
4983+
`MOBY-DICK;
4984+
4985+
or, THE WHALE.
4986+
4987+
4988+
By Herman Melville`,
4989+
nil,
4990+
},
4991+
{"YWJjMTIzIT8kKiYoKSctPUB+", false, "abc123!?$*&()'-=@~", nil},
4992+
{"YWJjMTIzIT8kKiYoKSctPUB-", true, "abc123!?$*&()'-=@~", nil},
4993+
{"YWJjMTIzIT8kKiYoKSctPUB+", true, "", base64.CorruptInputError(23)},
4994+
{"YWJjMTIzIT8kKiYoKSctPUB-", false, "", base64.CorruptInputError(23)},
4995+
}
4996+
4997+
func TestBase64DecodeFunc(t *testing.T) {
4998+
partitiontest.PartitionTest(t)
4999+
t.Parallel()
5000+
5001+
for _, testCase := range testCases {
5002+
encoding := base64.StdEncoding
5003+
if testCase.IsURL {
5004+
encoding = base64.URLEncoding
5005+
}
5006+
encoding = encoding.Strict()
5007+
decoded, err := base64Decode([]byte(testCase.Encoded), encoding)
5008+
require.Equal(t, []byte(testCase.Decoded), decoded)
5009+
require.Equal(t, testCase.Error, err)
5010+
}
5011+
}
5012+
5013+
type b64DecodeTestArgs struct {
5014+
Raw []byte
5015+
Encoded []byte
5016+
IsURL bool
5017+
Program []byte
5018+
}
5019+
5020+
func b64TestDecodeAssembleWithArgs(t *testing.T) []b64DecodeTestArgs {
5021+
sourceTmpl := `#pragma version %d
5022+
arg 0
5023+
arg 1
5024+
base64_decode %s
5025+
==`
5026+
args := []b64DecodeTestArgs{}
5027+
for _, testCase := range testCases {
5028+
if testCase.Error == nil {
5029+
field := "StdAlph"
5030+
if testCase.IsURL {
5031+
field = "URLAlph"
5032+
}
5033+
source := fmt.Sprintf(sourceTmpl, minB64DecodeVersion, field)
5034+
ops, err := AssembleStringWithVersion(source, minB64DecodeVersion)
5035+
require.NoError(t, err)
5036+
5037+
arg := b64DecodeTestArgs{
5038+
Raw: []byte(testCase.Decoded),
5039+
Encoded: []byte(testCase.Encoded),
5040+
IsURL: testCase.IsURL,
5041+
Program: ops.Program,
5042+
}
5043+
args = append(args, arg)
5044+
}
5045+
}
5046+
return args
5047+
}
5048+
5049+
func b64TestDecodeEval(tb testing.TB, args []b64DecodeTestArgs) {
5050+
for _, data := range args {
5051+
var txn transactions.SignedTxn
5052+
txn.Lsig.Logic = data.Program
5053+
txn.Lsig.Args = [][]byte{data.Raw[:], data.Encoded[:]}
5054+
ep := defaultEvalParams(&strings.Builder{}, &txn)
5055+
pass, err := Eval(data.Program, ep)
5056+
if err != nil {
5057+
require.NoError(tb, err)
5058+
}
5059+
if !pass {
5060+
fmt.Printf("FAILING WITH data = %#v", data)
5061+
require.True(tb, pass)
5062+
}
5063+
}
5064+
}
5065+
5066+
func TestOpBase64Decode(t *testing.T) {
5067+
partitiontest.PartitionTest(t)
5068+
t.Parallel()
5069+
args := b64TestDecodeAssembleWithArgs(t)
5070+
b64TestDecodeEval(t, args)
5071+
}

0 commit comments

Comments
 (0)