Skip to content

Commit

Permalink
Add opcode ed25519verify_bare (#776)
Browse files Browse the repository at this point in the history
* Add opcode ed25519verify_bare

* update changelog and parser

* update changelog

* update test from go-algorand

* update

* Update capital word

Co-authored-by: MAC <[email protected]>
  • Loading branch information
thdailong and MAC authored Sep 6, 2022
1 parent 359a9a2 commit 43b78de
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ from `algosdk` and sends it to the network.
- Added verification for secret key signatures in `Runtime`.
- Added replace2 and replace3 opcode to `runtime`.
- Added sha3_256 opcode to `Runtime`
- Added ed25519verify_bare opcode to `Runtime`

#### @algo-builder/web
- Added `appendSignMultisigTransaction` function to `WebMode` for appending signature to multisig transaction in the algosigner.
Expand Down
35 changes: 35 additions & 0 deletions packages/runtime/src/interpreter/opcode-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -770,6 +770,41 @@ export class Ed25519verify extends Op {
}
}

// for (data A, signature B, pubkey C) verify the signature of the data against the pubkey => {0 or 1}
// push to stack [...stack, bigint]
export class Ed25519verify_bare extends Ed25519verify {
/**
* Asserts 0 arguments are passed.
* @param args Expected arguments: [] // none
* @param line line number in TEAL file
*/
constructor(args: string[], line: number) {
super(args, line);
}

computeCost(): number {
return OpGasCost[7]["ed25519verify_bare"];
}

execute(stack: TEALStack): number {
this.assertMinStackLen(stack, 3, this.line);
const pubkey = this.assertBytes(stack.pop(), this.line);
const signature = this.assertBytes(stack.pop(), this.line);
const data = this.assertBytes(stack.pop(), this.line);

const addr = encodeAddress(pubkey);
const isValid = verifyBytes(data, signature, addr);
if (isValid) {
stack.push(1n);
} else {
stack.push(0n);
}
return this.computeCost();
}
}



// If A < B pushes '1' else '0'
// push to stack [...stack, bigint]
export class LessThan extends Op {
Expand Down
1 change: 1 addition & 0 deletions packages/runtime/src/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,7 @@ OpGasCost[6] = {
OpGasCost[7] = {
...OpGasCost[6],
sha3_256: 130,
ed25519verify_bare: 1900,
};

export const enum MathOp {
Expand Down
3 changes: 3 additions & 0 deletions packages/runtime/src/parser/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ import {
EcdsaPkRecover,
EcdsaVerify,
Ed25519verify,
Ed25519verify_bare,
EqualTo,
Err,
Exp,
Expand Down Expand Up @@ -402,6 +403,7 @@ opCodeMap[7] = {
replace2: Replace2,
replace3: Replace3,
sha3_256: Sha3_256,
ed25519verify_bare: Ed25519verify_bare,
};

// list of opcodes with exactly one parameter.
Expand Down Expand Up @@ -515,6 +517,7 @@ const commonModeOps = new Set([
"sha3_256",
"sha512_256",
"ed25519verify",
"Ed25519verify_bare",
"ecdsa_verify",
"ecdsa_pk_decompress",
"+",
Expand Down
82 changes: 81 additions & 1 deletion packages/runtime/test/src/interpreter/opcode-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ import {
EcdsaPkRecover,
EcdsaVerify,
Ed25519verify,
Ed25519verify_bare,
EqualTo,
Err,
Exp,
Expand Down Expand Up @@ -136,8 +137,8 @@ import {
Select,
SetBit,
SetByte,
Sha256,
Sha3_256,
Sha256,
Sha512_256,
Shl,
Shr,
Expand Down Expand Up @@ -7203,4 +7204,83 @@ describe("Teal Opcodes", function () {
});
});

describe("Ed25519verify_bare", function () {
const stack = new Stack<StackElem>();

it("Should push 1 to stack if signature is valid", function () {
const account = generateAccount();
const hexStr = "62fdfc072182654f163f5f0f9a621d729566c74d0aa413bf009c9800418c19cd";
const data = Buffer.from(
hexStr,
"hex"
);
const signature = signBytes(data, account.sk);

stack.push(data);
stack.push(signature);
stack.push(decodeAddress(account.addr).publicKey);

const op = new Ed25519verify_bare([], 1);
op.execute(stack);
const top = stack.pop();
assert.equal(top, 1n);
});

it("Should push 0 to stack if signature is invalid", function () {
const account = generateAccount();
let hexStr = "62fdfc072182654f163f5f0f9a621d729566c74d0aa413bf009c9800418c19cd";
let data = Buffer.from(
hexStr,
"hex"
);
const signature = signBytes(data, account.sk);
//flip a bit and it should not pass
hexStr = "52fdfc072182654f163f5f0f9a621d729566c74d0aa413bf009c9800418c19cd"
data = Buffer.from(
hexStr,
"hex"
);
stack.push(data);
stack.push(signature);
stack.push(decodeAddress(account.addr).publicKey);

const op = new Ed25519verify_bare([], 1);
op.execute(stack);
const top = stack.pop();
assert.equal(top, 0n);
});

it(
"Should throw an invalid type error",
execExpectError(
stack,
["1", "1", "1"].map(BigInt),
new Ed25519verify_bare([], 1),
RUNTIME_ERRORS.TEAL.INVALID_TYPE
)
);

it(
"Should throw an error if stack is below min length",
execExpectError(
stack,
[],
new Ed25519verify_bare([], 1),
RUNTIME_ERRORS.TEAL.ASSERT_STACK_LENGTH
)
);

it("Should return correct cost", () => {
const account = generateAccount();
const data = new Uint8Array(Buffer.from([1, 9, 25, 49]));
const signature = signBytes(data, account.sk);

stack.push(data);
stack.push(signature);
stack.push(decodeAddress(account.addr).publicKey);

const op = new Ed25519verify_bare([], 1);
assert.equal(1900, op.execute(stack));
});
});
});

0 comments on commit 43b78de

Please sign in to comment.