Skip to content

Commit

Permalink
Enhancement: Deprecating use of langspec (#632)
Browse files Browse the repository at this point in the history
  • Loading branch information
ahangsu authored Aug 26, 2022
1 parent 0e9c9fc commit 1c7d9e4
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 20 deletions.
28 changes: 28 additions & 0 deletions src/logic/logic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
* Utilities for working with program bytes.
*/

/** @deprecated langspec.json is deprecated aross all SDKs */
import langspec from './langspec.json';

/**
* Langspec Op Structure
* @deprecated for langspec.json is deprecated aross all SDKs
*/
interface OpStructure {
Opcode: number;
Expand All @@ -23,13 +25,17 @@ interface OpStructure {
Groups: string[];
}

/** @deprecated for langspec.json is deprecated aross all SDKs */
let opcodes: {
[key: number]: OpStructure;
};

/** @deprecated for langspec.json is deprecated aross all SDKs */
const maxCost = 20000;
/** @deprecated for langspec.json is deprecated aross all SDKs */
const maxLength = 1000;

/** @deprecated for langspec.json is deprecated aross all SDKs */
export function parseUvarint(
array: Uint8Array
): [numberFound: number, size: number] {
Expand All @@ -49,6 +55,7 @@ export function parseUvarint(
return [0, 0];
}

/** @deprecated for langspec.json is deprecated aross all SDKs */
function readIntConstBlock(
program: Uint8Array,
pc: number
Expand Down Expand Up @@ -79,6 +86,7 @@ function readIntConstBlock(
return [size, ints];
}

/** @deprecated for langspec.json is deprecated aross all SDKs */
function readByteConstBlock(
program: Uint8Array,
pc: number
Expand Down Expand Up @@ -116,6 +124,7 @@ function readByteConstBlock(
return [size, byteArrays];
}

/** @deprecated for langspec.json is deprecated aross all SDKs */
function readPushIntOp(
program: Uint8Array,
pc: number
Expand All @@ -129,6 +138,7 @@ function readPushIntOp(
return [size, numberFound];
}

/** @deprecated for langspec.json is deprecated aross all SDKs */
function readPushByteOp(
program: Uint8Array,
pc: number
Expand All @@ -151,6 +161,12 @@ function readPushByteOp(

/** readProgram validates program for length and running cost,
* and additionally provides the found int variables and byte blocks
*
* @deprecated Validation relies on metadata (`langspec.json`) that
* does not accurately represent opcode behavior across program versions.
* The behavior of `readProgram` relies on `langspec.json`.
* Thus, this method is being deprecated.
*
* @param program - Program to check
* @param args - Program arguments as array of Uint8Array arrays
* @throws
Expand Down Expand Up @@ -254,6 +270,12 @@ export function readProgram(

/**
* checkProgram validates program for length and running cost
*
* @deprecated Validation relies on metadata (`langspec.json`) that
* does not accurately represent opcode behavior across program versions.
* The behavior of `checkProgram` relies on `langspec.json`.
* Thus, this method is being deprecated.
*
* @param program - Program to check
* @param args - Program arguments as array of Uint8Array arrays
* @throws
Expand All @@ -264,25 +286,31 @@ export function checkProgram(program: Uint8Array, args?: Uint8Array[]) {
return success;
}

/** @deprecated for langspec.json is deprecated aross all SDKs */
export function checkIntConstBlock(program: Uint8Array, pc: number) {
const [size] = readIntConstBlock(program, pc);
return size;
}

/** @deprecated for langspec.json is deprecated aross all SDKs */
export function checkByteConstBlock(program: Uint8Array, pc: number) {
const [size] = readByteConstBlock(program, pc);
return size;
}

/** @deprecated for langspec.json is deprecated aross all SDKs */
export function checkPushIntOp(program: Uint8Array, pc: number) {
const [size] = readPushIntOp(program, pc);
return size;
}

/** @deprecated for langspec.json is deprecated aross all SDKs */
export function checkPushByteOp(program: Uint8Array, pc: number) {
const [size] = readPushByteOp(program, pc);
return size;
}

/** @deprecated for langspec.json is deprecated aross all SDKs */
export const langspecEvalMaxVersion = langspec.EvalMaxVersion;
/** @deprecated for langspec.json is deprecated aross all SDKs */
export const langspecLogicSigVersion = langspec.LogicSigVersion;
40 changes: 35 additions & 5 deletions src/logicsig.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import * as nacl from './nacl/naclWrappers';
import * as address from './encoding/address';
import * as encoding from './encoding/encoding';
import * as logic from './logic/logic';
import { verifyMultisig } from './multisig';
import * as utils from './utils/utils';
import * as txnBuilder from './transaction';
import { isValidAddress } from './encoding/address';
import {
EncodedLogicSig,
EncodedLogicSigAccount,
Expand All @@ -20,6 +20,38 @@ interface LogicSigStorageStructure {
msig?: EncodedMultisig;
}

/** sanityCheckProgram performs heuristic program validation:
* check if passed in bytes are Algorand address or is B64 encoded, rather than Teal bytes
*
* @param program - Program bytes to check
*/
export function sanityCheckProgram(program: Uint8Array) {
if (!program || program.length === 0) throw new Error('empty program');

const lineBreakOrd = '\n'.charCodeAt(0);
const blankSpaceOrd = ' '.charCodeAt(0);
const tildeOrd = '~'.charCodeAt(0);

const isPrintable = (x: number) => blankSpaceOrd <= x && x <= tildeOrd;
const isAsciiPrintable = program.every(
(x: number) => x === lineBreakOrd || isPrintable(x)
);

if (isAsciiPrintable) {
const programStr = Buffer.from(program).toString();

if (isValidAddress(programStr))
throw new Error('requesting program bytes, get Algorand address');

if (Buffer.from(programStr, 'base64').toString('base64') === programStr)
throw new Error('program should not be b64 encoded');

throw new Error(
'program bytes are all ASCII printable characters, not looking like Teal byte code'
);
}
}

/**
LogicSig implementation
*/
Expand Down Expand Up @@ -49,9 +81,7 @@ export class LogicSig implements LogicSigStorageStructure {
if (programArgs != null)
args = programArgs.map((arg) => new Uint8Array(arg));

if (!logic.checkProgram(program, args)) {
throw new Error('Invalid program');
}
sanityCheckProgram(program);

this.logic = program;
this.args = args;
Expand Down Expand Up @@ -93,7 +123,7 @@ export class LogicSig implements LogicSigStorageStructure {
}

try {
logic.checkProgram(this.logic, this.args);
sanityCheckProgram(this.logic);
} catch (e) {
return false;
}
Expand Down
5 changes: 0 additions & 5 deletions tests/7.AlgoSDK.js
Original file line number Diff line number Diff line change
Expand Up @@ -830,11 +830,6 @@ describe('Algosdk (AKA end to end)', () => {
assert.equal(lsig.logic, program);
assert.deepEqual(lsig.args, args);
});
it('should throw on invalid program', () => {
const program = Uint8Array.from([1, 32, 1, 1, 34]);
program[0] = 128;
assert.throws(() => algosdk.makeLogicSig(program));
});
});
describe('Single logic sig', () => {
it('should work on valid program', () => {
Expand Down
10 changes: 0 additions & 10 deletions tests/8.LogicSig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,6 @@ describe('LogicSig', () => {
const verified = lsig.verify(pk);
assert.strictEqual(verified, false);
});
it('should fail on invalid program', () => {
const program = Uint8Array.from([1, 32, 1, 1, 34]);
program[0] = 128;
assert.throws(() => algosdk.makeLogicSig(program));
});
});

describe('address', () => {
Expand Down Expand Up @@ -129,11 +124,6 @@ describe('LogicSigAccount', () => {
const decoded = algosdk.LogicSigAccount.fromByte(encoded);
assert.deepStrictEqual(decoded, lsigAccount);
});
it('should fail on invalid program', () => {
const program = Uint8Array.from([1, 32, 1, 1, 34]);
program[0] = 128;
assert.throws(() => new algosdk.LogicSigAccount(program));
});
});

describe('sign', () => {
Expand Down
26 changes: 26 additions & 0 deletions tests/cucumber/steps/steps.js
Original file line number Diff line number Diff line change
Expand Up @@ -4454,6 +4454,32 @@ module.exports = function getSteps(options) {
}
);

Given(
'a base64 encoded program bytes for heuristic sanity check {string}',
async function (programByteStr) {
this.seeminglyProgram = new Uint8Array(
Buffer.from(programByteStr, 'base64')
);
}
);

When('I start heuristic sanity check over the bytes', async function () {
this.actualErrMsg = undefined;
try {
new algosdk.LogicSigAccount(this.seeminglyProgram); // eslint-disable-line
} catch (e) {
this.actualErrMsg = e.message;
}
});

Then(
'if the heuristic sanity check throws an error, the error contains {string}',
async function (errMsg) {
if (errMsg !== '') assert.ok(this.actualErrMsg.includes(errMsg));
else assert.strictEqual(this.actualErrMsg, undefined);
}
);

if (!options.ignoreReturn) {
return steps;
}
Expand Down
1 change: 1 addition & 0 deletions tests/cucumber/unit.tags
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
@unit.indexer.ledger_refactoring
@unit.indexer.logs
@unit.offline
@unit.program_sanity_check
@unit.rekey
@unit.responses
@unit.responses.231
Expand Down

0 comments on commit 1c7d9e4

Please sign in to comment.