Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixbug int opcode can't start with 0x #562

Merged
merged 5 commits into from
Jan 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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