Skip to content

Commit

Permalink
Fixbug int opcode can't use hex or oct format (#562)
Browse files Browse the repository at this point in the history
* fixbug Int Pseudo-Ops can't start with 0x(hex) or 0(oct) prefix

* fix bug when number = 0 on int opcode

* fix early review

* update CHANGELOG.md
  • Loading branch information
vuvoth authored Jan 27, 2022
1 parent abb5b52 commit cb945de
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 6 deletions.
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@
- Added following functions in `deployer` API
* `getCompiledASC`: compiles a contract in real time, returns info after compilation (eg. bytecode, bytecode hash, timestamp etc).
* `getDeployedASC`: returns cached program (in artifacts/cache) compiled info(bytecode, hash, filename etc).
- Added `sandbox-up-dev` and `sandbox-reset` commands into Makefile in `infrastructure/`.


### Bug Fixes

- Int Pseudo-Ops can't start with 0x(hex) or 0(oct) prefix. (#562)

## v3.1.0 2022-01-25

Expand All @@ -27,7 +33,6 @@ yarn install
- updated sample-project (the one after `algob init`)
- migrate to use yarn v3
- updated dependencies to the latest version (notably: algosdk, typescirpt, eslint, mocha)
- Added `sandbox-up-dev` and `sandbox-reset` commands into Makefile in `infrastructure/`.

### Bug Fixes

Expand Down
6 changes: 3 additions & 3 deletions packages/runtime/src/interpreter/opcode-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
} from "../lib/constants";
import { parseEncodedTxnToExecParams, setInnerTxField } from "../lib/itxn";
import {
assertLen, assertOnlyDigits, bigEndianBytesToBigInt, bigintToBigEndianBytes, convertToBuffer,
assertLen, assertNumber, assertOnlyDigits, bigEndianBytesToBigInt, bigintToBigEndianBytes, convertToBuffer,
convertToString, getEncoding, parseBinaryStrToBigInt
} from "../lib/parsing";
import { Stack } from "../lib/stack";
Expand Down Expand Up @@ -2147,8 +2147,8 @@ export class Int extends Op {
if (intConst !== undefined) {
uint64 = BigInt(intConst);
} else {
assertOnlyDigits(args[0], line);
uint64 = BigInt(args[0]);
const val = assertNumber(args[0], line);
uint64 = BigInt(val);
}

this.checkOverflow(uint64, line, MAX_UINT64);
Expand Down
3 changes: 3 additions & 0 deletions packages/runtime/src/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,9 @@ AssetParamMap[5] = {
AssetParamMap[6] = { ...AssetParamMap[5] };

export const reDigit = /^\d+$/;
export const reDec = /^(0|[1-9]\d*)$/;
export const reHex = /^0x[0-9a-fA-F]+$/;
export const reOct = /^0[0-8]+$/;

/** is Base64 regex
* ^ # Start of input
Expand Down
22 changes: 21 additions & 1 deletion packages/runtime/src/lib/parsing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as base32 from "hi-base32";
import { RUNTIME_ERRORS } from "../errors/errors-list";
import { RuntimeError } from "../errors/runtime-errors";
import { EncodingType } from "../types";
import { reBase32, reBase64, reDigit } from "./constants";
import { reBase32, reBase64, reDec, reDigit, reHex, reOct } from "./constants";

/**
* assert if string contains digits only
Expand All @@ -21,6 +21,26 @@ export function assertOnlyDigits (val: string, line: number): void {
}
}

/**
* assert if string is valid algorand number respesentation (octal / hex / unsigned int).
* return val if format is correct
* @param val : string
*/
export function assertNumber (val: string, line: number): string {
if (reOct.test(val)) {
// typescript use 0o postfix instade of 0 postfix for oct format.
return "0o".concat(val.substring(1));
}

if (reDec.test(val) || reHex.test(val)) return val;

throw new RuntimeError(RUNTIME_ERRORS.TEAL.INVALID_TYPE, {
expected: "unsigned integer (upto 64 bit)",
actual: val,
line: line
});
}

/**
* assert that a line has given number of words
* @param val Comparsion result
Expand Down
35 changes: 35 additions & 0 deletions packages/runtime/test/src/parser/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,20 @@ describe("Parser", function () {
);
});

it("can use prefix 0x(hex) with 'int'", () => {
const valueInHex = "0x02";
const res = opcodeFromSentence(["int", valueInHex], 1, interpreter);
const expected = new Int(["2"], 1);
assert.deepEqual(res, expected);
});

it("can use prefix 0(oct) with 'int'", () => {
const valueInHex = "010";
const res = opcodeFromSentence(["int", valueInHex], 1, interpreter);
const expected = new Int(["8"], 1);
assert.deepEqual(res, expected);
});

it("should return correct opcode object for 'int'", () => {
const value = "812546821";
const res = opcodeFromSentence(["int", value], 1, interpreter);
Expand All @@ -451,6 +465,14 @@ describe("Parser", function () {
assert.deepEqual(res, expected);
});

it("should work when int arg is zero", () => {
const value = "0";
const res = opcodeFromSentence(["int", value], 1, interpreter);
const expected = new Int([value], 1);

assert.deepEqual(res, expected);
});

it("should throw error for wrong field length for 'int'", () => {
expectRuntimeError(
() => opcodeFromSentence(["int"], 1, interpreter),
Expand All @@ -464,11 +486,24 @@ describe("Parser", function () {
RUNTIME_ERRORS.TEAL.INVALID_TYPE
);

// for dec format
expectRuntimeError(
() => opcodeFromSentence(["int", String(MAX_UINT64 + 5n)], 1, interpreter),
RUNTIME_ERRORS.TEAL.UINT64_OVERFLOW
);

// for hex format
expectRuntimeError(
() => opcodeFromSentence(["int", "0x" + (MAX_UINT64 + 5n).toString(16)], 1, interpreter),
RUNTIME_ERRORS.TEAL.UINT64_OVERFLOW
);

// for oct format
expectRuntimeError(
() => opcodeFromSentence(["int", "0" + (MAX_UINT64 + 5n).toString(8)], 1, interpreter),
RUNTIME_ERRORS.TEAL.UINT64_OVERFLOW
);

expectRuntimeError(
() => opcodeFromSentence(["int", String(MIN_UINT64 - 5n)], 1, interpreter),
RUNTIME_ERRORS.TEAL.INVALID_TYPE
Expand Down
35 changes: 34 additions & 1 deletion packages/runtime/test/src/parser/parsing.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { assert } from "chai";

import { RUNTIME_ERRORS } from "../../../src/errors/errors-list";
import { getEncoding, parseBinaryStrToBigInt } from "../../../src/lib/parsing";
import { assertNumber, getEncoding, parseBinaryStrToBigInt } from "../../../src/lib/parsing";
import { EncodingType } from "../../../src/types";
import { expectRuntimeError } from "../../helpers/runtime-errors";

Expand Down Expand Up @@ -84,6 +84,39 @@ describe("Get Encoding for Byte Data", () => {
});
});

describe("assertNumber test cases", function () {
it("should pass with hex and dec", () => {
const hexValue = "0xaa3C3";
assert.equal(assertNumber(hexValue, 1), hexValue);
const decValue = "343434";
assert.equal(assertNumber(decValue, 1), decValue);
});

it("should return right format for OCT", () => {
const oct = "01234";
assert.equal(assertNumber(oct, 1), "0o1234");
});

it("should failed if input invalid", () => {
const failedDatas = [
"0xg",
"3e",
"0e",
"00x3",
"gg",
" ",
"0999",
"1234h3"
];
failedDatas.forEach(data => {
expectRuntimeError(
() => assertNumber(data, 1),
RUNTIME_ERRORS.TEAL.INVALID_TYPE
);
});
});
});

describe("Parse Binary string to BigInt", () => {
it("should parse to bigint", () => {
let res = parseBinaryStrToBigInt(["0"]);
Expand Down

0 comments on commit cb945de

Please sign in to comment.