From 5c9a2aa6cd3c4309931aa9cb8855488939f5fb29 Mon Sep 17 00:00:00 2001 From: Zeph Grunschlag Date: Tue, 7 Dec 2021 20:35:05 -0600 Subject: [PATCH 1/6] alphabet begone in favor of encoding --- data/transactions/logic/README.md | 2 +- data/transactions/logic/TEAL_opcodes.md | 6 +-- data/transactions/logic/assembler.go | 18 ++++---- data/transactions/logic/assembler_test.go | 2 +- data/transactions/logic/doc.go | 6 +-- data/transactions/logic/eval.go | 8 ++-- data/transactions/logic/evalStateful_test.go | 2 +- data/transactions/logic/eval_test.go | 10 ++--- data/transactions/logic/fields.go | 46 ++++++++++---------- data/transactions/logic/fields_string.go | 18 ++++---- 10 files changed, 59 insertions(+), 59 deletions(-) diff --git a/data/transactions/logic/README.md b/data/transactions/logic/README.md index b76869db1b..e1ca8efa1a 100644 --- a/data/transactions/logic/README.md +++ b/data/transactions/logic/README.md @@ -165,7 +165,7 @@ various sizes. | `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 | | `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 | | `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 | -| `base64_decode e` | decode X which was base64-encoded using _encoding alphabet_ E. Fail if X is not base64 encoded with alphabet E | +| `base64_decode e` | decode X which was base64-encoded using _encoding_ E. Fail if X is not base64 encoded with encoding E | These opcodes take byte-array values that are interpreted as big-endian unsigned integers. For mathematical operators, the diff --git a/data/transactions/logic/TEAL_opcodes.md b/data/transactions/logic/TEAL_opcodes.md index fe667cc781..354cf6f826 100644 --- a/data/transactions/logic/TEAL_opcodes.md +++ b/data/transactions/logic/TEAL_opcodes.md @@ -856,14 +856,14 @@ When A is a uint64, index 0 is the least significant bit. Setting bit 3 to 1 on ## base64_decode e -- Opcode: 0x5c {uint8 alphabet index} +- Opcode: 0x5c {uint8 encoding index} - Pops: *... stack*, []byte - Pushes: []byte -- decode X which was base64-encoded using _encoding alphabet_ E. Fail if X is not base64 encoded with alphabet E +- decode X which was base64-encoded using _encoding_ E. Fail if X is not base64 encoded with encoding E - **Cost**: 25 - LogicSigVersion >= 6 -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 RFC 4648 (sections 4 and 5) +decodes X using the base64 encoding E. Specify the encoding with an immediate arg either as URL and Filename Safe (`URLEncoding`) or Standard (`StdEncoding`). See RFC 4648 (sections 4 and 5) ## balance diff --git a/data/transactions/logic/assembler.go b/data/transactions/logic/assembler.go index 34151d4c0c..613a93650a 100644 --- a/data/transactions/logic/assembler.go +++ b/data/transactions/logic/assembler.go @@ -1249,20 +1249,20 @@ func assembleBase64Decode(ops *OpStream, spec *OpSpec, args []string) error { return ops.errorf("%s expects one argument", spec.Name) } - alph, ok := base64AlphabetSpecByName[args[0]] + encoding, ok := base64EncodingSpecByName[args[0]] if !ok { - return ops.errorf("%s unknown alphabet: %#v", spec.Name, args[0]) + return ops.errorf("%s unknown encoding: %#v", spec.Name, args[0]) } - if alph.version > ops.Version { + if encoding.version > ops.Version { //nolint:errcheck // we continue to maintain typestack - ops.errorf("%s %s available in version %d. Missed #pragma version?", spec.Name, args[0], alph.version) + ops.errorf("%s %s available in version %d. Missed #pragma version?", spec.Name, args[0], encoding.version) } - val := alph.field + val := encoding.field ops.pending.WriteByte(spec.Opcode) ops.pending.WriteByte(uint8(val)) - ops.trace("%s (%s)", alph.field, alph.ftype) - ops.returns(alph.ftype) + ops.trace("%s (%s)", encoding.field, encoding.ftype) + ops.returns(encoding.ftype) return nil } @@ -2698,10 +2698,10 @@ func disBase64Decode(dis *disassembleState, spec *OpSpec) (string, error) { } dis.nextpc = dis.pc + 2 b64dArg := dis.program[dis.pc+1] - if int(b64dArg) >= len(base64AlphabetNames) { + if int(b64dArg) >= len(base64EncodingNames) { return "", fmt.Errorf("invalid base64_decode arg index %d at pc=%d", b64dArg, dis.pc) } - return fmt.Sprintf("%s %s", spec.Name, base64AlphabetNames[b64dArg]), nil + return fmt.Sprintf("%s %s", spec.Name, base64EncodingNames[b64dArg]), nil } type disInfo struct { diff --git a/data/transactions/logic/assembler_test.go b/data/transactions/logic/assembler_test.go index 19abfefb23..1c56ab7a4b 100644 --- a/data/transactions/logic/assembler_test.go +++ b/data/transactions/logic/assembler_test.go @@ -344,7 +344,7 @@ itxna Logs 3 const v6Nonsense = v5Nonsense + ` itxn_next -base64_decode URLAlph +base64_decode URLEncoding ` var nonsense = map[uint64]string{ diff --git a/data/transactions/logic/doc.go b/data/transactions/logic/doc.go index ff258f01e3..941a4b6b3b 100644 --- a/data/transactions/logic/doc.go +++ b/data/transactions/logic/doc.go @@ -133,7 +133,7 @@ var opDocByName = map[string]string{ "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", "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", "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", - "base64_decode": "decode X which was base64-encoded using _encoding alphabet_ E. Fail if X is not base64 encoded with alphabet E", + "base64_decode": "decode X which was base64-encoded using _encoding_ E. Fail if X is not base64 encoded with encoding E", "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.", "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.", @@ -231,7 +231,7 @@ var opcodeImmediateNotes = map[string]string{ "ecdsa_pk_decompress": "{uint8 curve index}", "ecdsa_pk_recover": "{uint8 curve index}", - "base64_decode": "{uint8 alphabet index}", + "base64_decode": "{uint8 encoding index}", } // OpImmediateNote returns a short string about immediate data which follows the op byte @@ -286,7 +286,7 @@ var opDocExtras = map[string]string{ "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.", "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.)", "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.", - "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 RFC 4648 (sections 4 and 5)", + "base64_decode": "decodes X using the base64 encoding E. Specify the encoding with an immediate arg either as URL and Filename Safe (`URLEncoding`) or Standard (`StdEncoding`). See RFC 4648 (sections 4 and 5)", } // OpDocExtra returns extra documentation text about an op diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go index cc7006f03a..842403e9cb 100644 --- a/data/transactions/logic/eval.go +++ b/data/transactions/logic/eval.go @@ -4047,15 +4047,15 @@ func base64Decode(encoded []byte, encoding *base64.Encoding) ([]byte, error) { func opBase64Decode(cx *EvalContext) { last := len(cx.stack) - 1 - alphabetField := Base64Alphabet(cx.program[cx.pc+1]) - fs, ok := base64AlphabetSpecByField[alphabetField] + encodingField := Base64Encoding(cx.program[cx.pc+1]) + fs, ok := base64EncodingSpecByField[encodingField] if !ok || fs.version > cx.version { - cx.err = fmt.Errorf("invalid base64_decode alphabet %d", alphabetField) + cx.err = fmt.Errorf("invalid base64_decode encoding %d", encodingField) return } encoding := base64.URLEncoding - if alphabetField == StdAlph { + if encodingField == StdEncoding { encoding = base64.StdEncoding } cx.stack[last].Bytes, cx.err = base64Decode(cx.stack[last].Bytes, encoding) diff --git a/data/transactions/logic/evalStateful_test.go b/data/transactions/logic/evalStateful_test.go index 2098d3af41..b8e368bf30 100644 --- a/data/transactions/logic/evalStateful_test.go +++ b/data/transactions/logic/evalStateful_test.go @@ -2392,7 +2392,7 @@ func TestReturnTypes(t *testing.T) { "itxn": "itxn_begin; int pay; itxn_field TypeEnum; itxn_submit; itxn CreatedAssetID", // This next one is a cop out. Can't use itxna Logs until we have inner appl "itxna": "itxn_begin; int pay; itxn_field TypeEnum; itxn_submit; itxn NumLogs", - "base64_decode": `pushbytes "YWJjMTIzIT8kKiYoKSctPUB+"; base64_decode StdAlph; pushbytes "abc123!?$*&()'-=@~"; ==; pushbytes "YWJjMTIzIT8kKiYoKSctPUB-"; base64_decode URLAlph; pushbytes "abc123!?$*&()'-=@~"; ==; &&; assert`, + "base64_decode": `pushbytes "YWJjMTIzIT8kKiYoKSctPUB+"; base64_decode StdEncoding; pushbytes "abc123!?$*&()'-=@~"; ==; pushbytes "YWJjMTIzIT8kKiYoKSctPUB-"; base64_decode URLEncoding; pushbytes "abc123!?$*&()'-=@~"; ==; &&; assert`, } // these require special input data and tested separately diff --git a/data/transactions/logic/eval_test.go b/data/transactions/logic/eval_test.go index c374a3022e..800dcf6260 100644 --- a/data/transactions/logic/eval_test.go +++ b/data/transactions/logic/eval_test.go @@ -3738,15 +3738,15 @@ func BenchmarkBase64Decode(b *testing.B) { "keccak256", "sha256", "sha512_256", - "base64_decode StdAlph", - "base64_decode URLAlph", + "base64_decode StdEncoding", + "base64_decode URLEncoding", } benches := [][]string{} for i, tag := range tags { for _, op := range ops { testName := op encoded := stds[i] - if op == "base64_decode URLAlph" { + if op == "base64_decode URLEncoding" { encoded = urls[i] } if len(op) > 0 { @@ -5026,9 +5026,9 @@ base64_decode %s args := []b64DecodeTestArgs{} for _, testCase := range testCases { if testCase.Error == nil { - field := "StdAlph" + field := "StdEncoding" if testCase.IsURL { - field = "URLAlph" + field = "URLEncoding" } source := fmt.Sprintf(sourceTmpl, minB64DecodeVersion, field) ops, err := AssembleStringWithVersion(source, minB64DecodeVersion) diff --git a/data/transactions/logic/fields.go b/data/transactions/logic/fields.go index 37ef079cc5..9863427735 100644 --- a/data/transactions/logic/fields.go +++ b/data/transactions/logic/fields.go @@ -23,7 +23,7 @@ import ( "github.com/algorand/go-algorand/protocol" ) -//go:generate stringer -type=TxnField,GlobalField,AssetParamsField,AppParamsField,AssetHoldingField,OnCompletionConstType,EcdsaCurve,Base64Alphabet -output=fields_string.go +//go:generate stringer -type=TxnField,GlobalField,AssetParamsField,AppParamsField,AssetHoldingField,OnCompletionConstType,EcdsaCurve,Base64Encoding -output=fields_string.go // TxnField is an enum type for `txn` and `gtxn` type TxnField int @@ -448,37 +448,37 @@ func (s ecDsaCurveNameSpecMap) getExtraFor(name string) (extra string) { return } -// Base64Alphabet is an enum for the `base64decode` opcode -type Base64Alphabet int +// Base64Encoding is an enum for the `base64decode` opcode +type Base64Encoding int const ( - // URLAlph represents the base64url alphabet defined in https://www.rfc-editor.org/rfc/rfc4648.html - URLAlph Base64Alphabet = iota - // StdAlph represents the standard alphabet of the RFC - StdAlph + // URLEncoding represents the base64url encoding defined in https://www.rfc-editor.org/rfc/rfc4648.html + URLEncoding Base64Encoding = iota + // StdEncoding represents the standard encoding of the RFC + StdEncoding invalidBase64Alphabet ) // After running `go generate` these strings will be available: -var base64AlphabetNames [2]string = [...]string{URLAlph.String(), StdAlph.String()} +var base64EncodingNames [2]string = [...]string{URLEncoding.String(), StdEncoding.String()} -type base64AlphabetSpec struct { - field Base64Alphabet +type base64EncodingSpec struct { + field Base64Encoding ftype StackType version uint64 } -var base64AlphbetSpecs = []base64AlphabetSpec{ - {URLAlph, StackBytes, 6}, - {StdAlph, StackBytes, 6}, +var base64EncodingSpecs = []base64EncodingSpec{ + {URLEncoding, StackBytes, 6}, + {StdEncoding, StackBytes, 6}, } -var base64AlphabetSpecByField map[Base64Alphabet]base64AlphabetSpec -var base64AlphabetSpecByName base64AlphabetSpecMap +var base64EncodingSpecByField map[Base64Encoding]base64EncodingSpec +var base64EncodingSpecByName base64EncodingSpecMap -type base64AlphabetSpecMap map[string]base64AlphabetSpec +type base64EncodingSpecMap map[string]base64EncodingSpec -func (s base64AlphabetSpecMap) getExtraFor(name string) (extra string) { +func (s base64EncodingSpecMap) getExtraFor(name string) (extra string) { // Uses 6 here because base64_decode fields were introduced in 6 if s[name].version > 6 { extra = fmt.Sprintf("LogicSigVersion >= %d.", s[name].version) @@ -719,14 +719,14 @@ func init() { ecdsaCurveSpecByName[ahfn] = ecdsaCurveSpecByField[EcdsaCurve(i)] } - base64AlphabetSpecByField = make(map[Base64Alphabet]base64AlphabetSpec, len(base64AlphabetNames)) - for _, s := range base64AlphbetSpecs { - base64AlphabetSpecByField[s.field] = s + base64EncodingSpecByField = make(map[Base64Encoding]base64EncodingSpec, len(base64EncodingNames)) + for _, s := range base64EncodingSpecs { + base64EncodingSpecByField[s.field] = s } - base64AlphabetSpecByName = make(base64AlphabetSpecMap, len(base64AlphabetNames)) - for i, alphname := range base64AlphabetNames { - base64AlphabetSpecByName[alphname] = base64AlphabetSpecByField[Base64Alphabet(i)] + base64EncodingSpecByName = make(base64EncodingSpecMap, len(base64EncodingNames)) + for i, encoding := range base64EncodingNames { + base64EncodingSpecByName[encoding] = base64EncodingSpecByField[Base64Encoding(i)] } AssetHoldingFieldNames = make([]string, int(invalidAssetHoldingField)) diff --git a/data/transactions/logic/fields_string.go b/data/transactions/logic/fields_string.go index 5861dedc17..ffe5ed5b3c 100644 --- a/data/transactions/logic/fields_string.go +++ b/data/transactions/logic/fields_string.go @@ -1,4 +1,4 @@ -// Code generated by "stringer -type=TxnField,GlobalField,AssetParamsField,AppParamsField,AssetHoldingField,OnCompletionConstType,EcdsaCurve,Base64Alphabet -output=fields_string.go"; DO NOT EDIT. +// Code generated by "stringer -type=TxnField,GlobalField,AssetParamsField,AppParamsField,AssetHoldingField,OnCompletionConstType,EcdsaCurve,Base64Encoding -output=fields_string.go"; DO NOT EDIT. package logic @@ -231,18 +231,18 @@ func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} - _ = x[URLAlph-0] - _ = x[StdAlph-1] + _ = x[URLEncoding-0] + _ = x[StdEncoding-1] _ = x[invalidBase64Alphabet-2] } -const _Base64Alphabet_name = "URLAlphStdAlphinvalidBase64Alphabet" +const _Base64Encoding_name = "URLEncodingStdEncodinginvalidBase64Alphabet" -var _Base64Alphabet_index = [...]uint8{0, 7, 14, 35} +var _Base64Encoding_index = [...]uint8{0, 11, 22, 43} -func (i Base64Alphabet) String() string { - if i < 0 || i >= Base64Alphabet(len(_Base64Alphabet_index)-1) { - return "Base64Alphabet(" + strconv.FormatInt(int64(i), 10) + ")" +func (i Base64Encoding) String() string { + if i < 0 || i >= Base64Encoding(len(_Base64Encoding_index)-1) { + return "Base64Encoding(" + strconv.FormatInt(int64(i), 10) + ")" } - return _Base64Alphabet_name[_Base64Alphabet_index[i]:_Base64Alphabet_index[i+1]] + return _Base64Encoding_name[_Base64Encoding_index[i]:_Base64Encoding_index[i+1]] } From 8d87423cbe3ad06dd89ea77b533dfbecf289a7bf Mon Sep 17 00:00:00 2001 From: Zeph Grunschlag Date: Wed, 8 Dec 2021 05:39:34 -0600 Subject: [PATCH 2/6] unit test various padding and whitespace scenarios --- data/transactions/logic/eval_test.go | 64 +++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 10 deletions(-) diff --git a/data/transactions/logic/eval_test.go b/data/transactions/logic/eval_test.go index 800dcf6260..0c432990ed 100644 --- a/data/transactions/logic/eval_test.go +++ b/data/transactions/logic/eval_test.go @@ -4961,14 +4961,16 @@ func TestPcDetails(t *testing.T) { var minB64DecodeVersion uint64 = 6 type b64DecodeTestCase struct { - Encoded string - IsURL bool - Decoded string - Error error + Encoded string + IsURL bool + HasExtraNLs bool + Decoded string + Error error } var testCases = []b64DecodeTestCase{ {"TU9CWS1ESUNLOwoKb3IsIFRIRSBXSEFMRS4KCgpCeSBIZXJtYW4gTWVsdmlsbGU=", + false, false, `MOBY-DICK; @@ -4980,6 +4982,7 @@ By Herman Melville`, }, {"TU9CWS1ESUNLOwoKb3IsIFRIRSBXSEFMRS4KCgpCeSBIZXJtYW4gTWVsdmlsbGU=", true, + false, `MOBY-DICK; or, THE WHALE. @@ -4988,10 +4991,47 @@ or, THE WHALE. By Herman Melville`, nil, }, - {"YWJjMTIzIT8kKiYoKSctPUB+", false, "abc123!?$*&()'-=@~", nil}, - {"YWJjMTIzIT8kKiYoKSctPUB-", true, "abc123!?$*&()'-=@~", nil}, - {"YWJjMTIzIT8kKiYoKSctPUB+", true, "", base64.CorruptInputError(23)}, - {"YWJjMTIzIT8kKiYoKSctPUB-", false, "", base64.CorruptInputError(23)}, + {"YWJjMTIzIT8kKiYoKSctPUB+", false, false, "abc123!?$*&()'-=@~", nil}, + {"YWJjMTIzIT8kKiYoKSctPUB-", true, false, "abc123!?$*&()'-=@~", nil}, + {"YWJjMTIzIT8kKiYoKSctPUB+", true, false, "", base64.CorruptInputError(23)}, + {"YWJjMTIzIT8kKiYoKSctPUB-", false, false, "", base64.CorruptInputError(23)}, + + // try extra ='s and various whitespace: + {"", false, false, "", nil}, + {"", true, false, "", nil}, + {"=", false, true, "", base64.CorruptInputError(0)}, + {"=", true, true, "", base64.CorruptInputError(0)}, + {" ", false, true, "", base64.CorruptInputError(0)}, + {" ", true, true, "", base64.CorruptInputError(0)}, + {"\t", false, true, "", base64.CorruptInputError(0)}, + {"\t", true, true, "", base64.CorruptInputError(0)}, + {"\r", false, true, "", nil}, + {"\r", true, true, "", nil}, + {"\n", false, true, "", nil}, + {"\n", true, true, "", nil}, + + {"YWJjMTIzIT8kKiYoKSctPUB+\n", false, true, "abc123!?$*&()'-=@~", nil}, + {"YWJjMTIzIT8kKiYoKSctPUB-\n", true, true, "abc123!?$*&()'-=@~", nil}, + {"YWJjMTIzIT8kK\riYoKSctPUB+\n", false, true, "abc123!?$*&()'-=@~", nil}, + {"YWJjMTIzIT8kK\riYoKSctPUB-\n", true, true, "abc123!?$*&()'-=@~", nil}, + {"\n\rYWJjMTIzIT8\rkKiYoKSctPUB+\n", false, true, "abc123!?$*&()'-=@~", nil}, + {"\n\rYWJjMTIzIT8\rkKiYoKSctPUB-\n", true, true, "abc123!?$*&()'-=@~", nil}, + + // padding and extra legal whitespace + {"SQ==", false, false, "I", nil}, + {"SQ==", true, false, "I", nil}, + {"\rS\r\nQ=\n=\r\r\n", false, true, "I", nil}, + {"\rS\r\nQ=\n=\r\r\n", true, true, "I", nil}, + + // Padding necessary? - Yes it is! And exactly the expected amount. + {"SQ==", false, false, "I", nil}, + {"SQ==", true, false, "I", nil}, + {"SQ", false, false, "", base64.CorruptInputError(0)}, + {"SQ", true, false, "", base64.CorruptInputError(0)}, + {"SQ=", false, false, "", base64.CorruptInputError(3)}, + {"SQ=", true, false, "", base64.CorruptInputError(3)}, + {"SQ===", false, false, "", base64.CorruptInputError(4)}, + {"SQ===", true, false, "", base64.CorruptInputError(4)}, } func TestBase64DecodeFunc(t *testing.T) { @@ -5003,10 +5043,14 @@ func TestBase64DecodeFunc(t *testing.T) { if testCase.IsURL { encoding = base64.URLEncoding } - encoding = encoding.Strict() + // sanity check: + if testCase.Error == nil && !testCase.HasExtraNLs { + require.Equal(t, testCase.Encoded, encoding.EncodeToString([]byte(testCase.Decoded))) + } + decoded, err := base64Decode([]byte(testCase.Encoded), encoding) + require.Equal(t, testCase.Error, err, fmt.Sprintf("Error (%s): case decode [%s] -> [%s]", err, testCase.Encoded, testCase.Decoded)) require.Equal(t, []byte(testCase.Decoded), decoded) - require.Equal(t, testCase.Error, err) } } From c5b1b604b0ffb73d18fbe7b919ea5a81baf65fab Mon Sep 17 00:00:00 2001 From: Zeph Grunschlag Date: Wed, 8 Dec 2021 05:49:02 -0600 Subject: [PATCH 3/6] padding permutations also fail --- data/transactions/logic/eval_test.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/data/transactions/logic/eval_test.go b/data/transactions/logic/eval_test.go index 0c432990ed..ef92c6ce54 100644 --- a/data/transactions/logic/eval_test.go +++ b/data/transactions/logic/eval_test.go @@ -5023,9 +5023,13 @@ By Herman Melville`, {"\rS\r\nQ=\n=\r\r\n", false, true, "I", nil}, {"\rS\r\nQ=\n=\r\r\n", true, true, "I", nil}, - // Padding necessary? - Yes it is! And exactly the expected amount. + // Padding necessary? - Yes it is! And exactly the expected place and amount. {"SQ==", false, false, "I", nil}, {"SQ==", true, false, "I", nil}, + {"S=Q=", false, false, "", base64.CorruptInputError(1)}, + {"S=Q=", true, false, "", base64.CorruptInputError(1)}, + {"=SQ=", false, false, "", base64.CorruptInputError(0)}, + {"=SQ=", true, false, "", base64.CorruptInputError(0)}, {"SQ", false, false, "", base64.CorruptInputError(0)}, {"SQ", true, false, "", base64.CorruptInputError(0)}, {"SQ=", false, false, "", base64.CorruptInputError(3)}, From 708dc9f90cac1046dca2fdc3f2489d0a4bb8ddb0 Mon Sep 17 00:00:00 2001 From: Zeph Grunschlag Date: Wed, 8 Dec 2021 07:17:26 -0600 Subject: [PATCH 4/6] "Slicing" --> "Manipulation" --- cmd/opdoc/tmLanguage.go | 2 +- data/transactions/logic/doc.go | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cmd/opdoc/tmLanguage.go b/cmd/opdoc/tmLanguage.go index 3f5aa8503a..199514b40c 100644 --- a/cmd/opdoc/tmLanguage.go +++ b/cmd/opdoc/tmLanguage.go @@ -175,7 +175,7 @@ func buildSyntaxHighlight() *tmLanguage { // For these, accumulate into allArithmetics, // and only add to keyword.Patterns later, when all // have been collected. - case "Arithmetic", "Byte Array Slicing", "Byte Array Arithmetic", + case "Arithmetic", "Byte Array Manipulation", "Byte Array Arithmetic", "Byte Array Logic", "Inner Transactions": escape := map[rune]bool{ '*': true, diff --git a/data/transactions/logic/doc.go b/data/transactions/logic/doc.go index 941a4b6b3b..cfc020324e 100644 --- a/data/transactions/logic/doc.go +++ b/data/transactions/logic/doc.go @@ -298,14 +298,14 @@ func OpDocExtra(opName string) string { // here is the order args opcodes are presented, so place related // opcodes consecutively, even if their opcode values are not. var OpGroups = map[string][]string{ - "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"}, - "Byte Array Slicing": {"substring", "substring3", "extract", "extract3", "extract_uint16", "extract_uint32", "extract_uint64", "base64_decode"}, - "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"}, - "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"}, + "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"}, + "Byte Array Manipulation": {"substring", "substring3", "extract", "extract3", "extract_uint16", "extract_uint32", "extract_uint64", "base64_decode"}, + "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"}, + "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"}, } // OpCost indicates the cost of an operation over the range of From 56e46287960bfba00527171a58ebd67c60ac4709 Mon Sep 17 00:00:00 2001 From: Zeph Grunschlag Date: Wed, 8 Dec 2021 09:58:12 -0600 Subject: [PATCH 5/6] fix the codegen fail? --- data/transactions/logic/README_in.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/transactions/logic/README_in.md b/data/transactions/logic/README_in.md index 494dd5776f..a70a2b45e7 100644 --- a/data/transactions/logic/README_in.md +++ b/data/transactions/logic/README_in.md @@ -90,7 +90,7 @@ various sizes. ### Byte Array Manipulation -@@ Byte_Array_Slicing.md @@ +@@ Byte_Array_Manipulation.md @@ These opcodes take byte-array values that are interpreted as big-endian unsigned integers. For mathematical operators, the From 9d6d342d248520cb3962a75db9d0d85a572d4f4d Mon Sep 17 00:00:00 2001 From: Zeph Grunschlag Date: Thu, 9 Dec 2021 14:46:13 -0600 Subject: [PATCH 6/6] Documenting padding, whitespace, other character behavior --- data/transactions/logic/TEAL_opcodes.md | 2 +- data/transactions/logic/doc.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data/transactions/logic/TEAL_opcodes.md b/data/transactions/logic/TEAL_opcodes.md index 354cf6f826..69a86492c4 100644 --- a/data/transactions/logic/TEAL_opcodes.md +++ b/data/transactions/logic/TEAL_opcodes.md @@ -863,7 +863,7 @@ When A is a uint64, index 0 is the least significant bit. Setting bit 3 to 1 on - **Cost**: 25 - LogicSigVersion >= 6 -decodes X using the base64 encoding E. Specify the encoding with an immediate arg either as URL and Filename Safe (`URLEncoding`) or Standard (`StdEncoding`). See RFC 4648 (sections 4 and 5) +Decodes X using the base64 encoding E. Specify the encoding with an immediate arg either as URL and Filename Safe (`URLEncoding`) or Standard (`StdEncoding`). See RFC 4648 (sections 4 and 5). It is assumed that the encoding ends with the exact number of `=` padding characters as required by the RFC. When padding occurs, any unused pad bits in the encoding must be set to zero or the decoding will fail. The special cases of `\n` and `\r` are allowed but completely ignored. An error will result when attempting to decode a string with a character that is not in the encoding alphabet or not one of `=`, `\r`, or `\n`. ## balance diff --git a/data/transactions/logic/doc.go b/data/transactions/logic/doc.go index cfc020324e..4887f45f3c 100644 --- a/data/transactions/logic/doc.go +++ b/data/transactions/logic/doc.go @@ -286,7 +286,7 @@ var opDocExtras = map[string]string{ "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.", "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.)", "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.", - "base64_decode": "decodes X using the base64 encoding E. Specify the encoding with an immediate arg either as URL and Filename Safe (`URLEncoding`) or Standard (`StdEncoding`). See RFC 4648 (sections 4 and 5)", + "base64_decode": "Decodes X using the base64 encoding E. Specify the encoding with an immediate arg either as URL and Filename Safe (`URLEncoding`) or Standard (`StdEncoding`). See RFC 4648 (sections 4 and 5). It is assumed that the encoding ends with the exact number of `=` padding characters as required by the RFC. When padding occurs, any unused pad bits in the encoding must be set to zero or the decoding will fail. The special cases of `\\n` and `\\r` are allowed but completely ignored. An error will result when attempting to decode a string with a character that is not in the encoding alphabet or not one of `=`, `\\r`, or `\\n`.", } // OpDocExtra returns extra documentation text about an op