211 lines
6.0 KiB
JavaScript
211 lines
6.0 KiB
JavaScript
// Copyright (c) 2018 Jordi Baylina
|
|
// License: LGPL-3.0+
|
|
//
|
|
|
|
|
|
import ethers from "ethers";
|
|
import {Scalar} from "ffjavascript";
|
|
|
|
export default class Contract {
|
|
constructor() {
|
|
this.code = [];
|
|
this.labels = {};
|
|
this.pendingLabels = {};
|
|
}
|
|
|
|
createTxData() {
|
|
let C;
|
|
|
|
// Check all labels are defined
|
|
const pendingLabels = Object.keys(this.pendingLabels);
|
|
if (pendingLabels.length>0) {
|
|
throw new Error("Lables not defined: "+ pendingLabels.join(", "));
|
|
}
|
|
|
|
let setLoaderLength = 0;
|
|
let genLoadedLength = -1;
|
|
|
|
while (genLoadedLength!=setLoaderLength) {
|
|
setLoaderLength = genLoadedLength;
|
|
C = new Contract();
|
|
C.codesize();
|
|
C.push(setLoaderLength);
|
|
C.push(0);
|
|
C.codecopy();
|
|
|
|
C.push(this.code.length);
|
|
C.push(0);
|
|
C.return();
|
|
genLoadedLength = C.code.length;
|
|
}
|
|
|
|
return ethers.utils.hexlify(C.code.concat(this.code));
|
|
}
|
|
|
|
stop() { this.code.push(0x00); }
|
|
add() { this.code.push(0x01); }
|
|
mul() { this.code.push(0x02); }
|
|
sub() { this.code.push(0x03); }
|
|
div() { this.code.push(0x04); }
|
|
sdiv() { this.code.push(0x05); }
|
|
mod() { this.code.push(0x06); }
|
|
smod() { this.code.push(0x07); }
|
|
addmod() { this.code.push(0x08); }
|
|
mulmod() { this.code.push(0x09); }
|
|
exp() { this.code.push(0x0a); }
|
|
signextend() { this.code.push(0x0b); }
|
|
|
|
lt() { this.code.push(0x10); }
|
|
gt() { this.code.push(0x11); }
|
|
slt() { this.code.push(0x12); }
|
|
sgt() { this.code.push(0x13); }
|
|
eq() { this.code.push(0x14); }
|
|
iszero() { this.code.push(0x15); }
|
|
and() { this.code.push(0x16); }
|
|
or() { this.code.push(0x17); }
|
|
shor() { this.code.push(0x18); }
|
|
not() { this.code.push(0x19); }
|
|
byte() { this.code.push(0x1a); }
|
|
|
|
keccak() { this.code.push(0x20); }
|
|
sha3() { this.code.push(0x20); } // alias
|
|
|
|
address() { this.code.push(0x30); }
|
|
balance() { this.code.push(0x31); }
|
|
origin() { this.code.push(0x32); }
|
|
caller() { this.code.push(0x33); }
|
|
callvalue() { this.code.push(0x34); }
|
|
calldataload() { this.code.push(0x35); }
|
|
calldatasize() { this.code.push(0x36); }
|
|
calldatacopy() { this.code.push(0x37); }
|
|
codesize() { this.code.push(0x38); }
|
|
codecopy() { this.code.push(0x39); }
|
|
gasprice() { this.code.push(0x3a); }
|
|
extcodesize() { this.code.push(0x3b); }
|
|
extcodecopy() { this.code.push(0x3c); }
|
|
returndatasize() { this.code.push(0x3d); }
|
|
returndatacopy() { this.code.push(0x3e); }
|
|
|
|
blockhash() { this.code.push(0x40); }
|
|
coinbase() { this.code.push(0x41); }
|
|
timestamp() { this.code.push(0x42); }
|
|
number() { this.code.push(0x43); }
|
|
difficulty() { this.code.push(0x44); }
|
|
gaslimit() { this.code.push(0x45); }
|
|
|
|
pop() { this.code.push(0x50); }
|
|
mload() { this.code.push(0x51); }
|
|
mstore() { this.code.push(0x52); }
|
|
mstore8() { this.code.push(0x53); }
|
|
sload() { this.code.push(0x54); }
|
|
sstore() { this.code.push(0x55); }
|
|
|
|
_pushLabel(label) {
|
|
if (typeof this.labels[label] != "undefined") {
|
|
this.push(this.labels[label]);
|
|
} else {
|
|
this.pendingLabels[label] = this.pendingLabels[label] || [];
|
|
this.pendingLabels[label].push(this.code.length);
|
|
this.push("0x000000");
|
|
}
|
|
}
|
|
|
|
_fillLabel(label) {
|
|
if (!this.pendingLabels[label]) return;
|
|
|
|
let dst = this.labels[label];
|
|
|
|
const dst3 = [dst >> 16, (dst >> 8) & 0xFF, dst & 0xFF];
|
|
|
|
this.pendingLabels[label].forEach((p) => {
|
|
for (let i=0; i<3; i++) {
|
|
this.code[p+i+1] = dst3[i];
|
|
}
|
|
});
|
|
|
|
delete this.pendingLabels[label];
|
|
}
|
|
|
|
|
|
jmp(label) {
|
|
if (typeof label !== "undefined") {
|
|
this._pushLabel(label);
|
|
}
|
|
this.code.push(0x56);
|
|
}
|
|
|
|
jmpi(label) {
|
|
if (typeof label !== "undefined") {
|
|
this._pushLabel(label);
|
|
}
|
|
this.code.push(0x57);
|
|
}
|
|
|
|
pc() { this.code.push(0x58); }
|
|
msize() { this.code.push(0x59); }
|
|
gas() { this.code.push(0x5a); }
|
|
label(name) {
|
|
if (typeof this.labels[name] != "undefined") {
|
|
throw new Error("Label already defined");
|
|
}
|
|
this.labels[name] = this.code.length;
|
|
this.code.push(0x5b);
|
|
|
|
this._fillLabel(name);
|
|
}
|
|
|
|
push(data) {
|
|
if ((typeof data !== "string") || (data.slice(0,2) != "0x")) {
|
|
let v = Scalar.e(data);
|
|
if (Scalar.isNegative(v)) {
|
|
v = Scalar.add(Scalar.shl(Scalar.e(1), 256), v);
|
|
}
|
|
let S = Scalar.toString(v, 16);
|
|
if (S.length % 2) S = "0"+S;
|
|
S = "0x" +S;
|
|
data = S;
|
|
}
|
|
const d = ethers.utils.arrayify(data);
|
|
if (d.length == 0 || d.length > 32) {
|
|
throw new Error("Assertion failed");
|
|
}
|
|
const a = [];
|
|
this.code.push(0x5F + d.length);
|
|
for (let i=0; i<d.length; i++) {
|
|
this.code.push(d[i]);
|
|
}
|
|
}
|
|
|
|
dup(n) {
|
|
if (n < 0 || n >= 16) {
|
|
throw new Error("Assertion failed");
|
|
}
|
|
this.code.push(0x80 + n);
|
|
}
|
|
|
|
swap(n) {
|
|
if (n < 1 || n > 16) {
|
|
throw new Error("Assertion failed");
|
|
}
|
|
this.code.push(0x8f + n);
|
|
}
|
|
|
|
log0() { this.code.push(0xa0); }
|
|
log1() { this.code.push(0xa1); }
|
|
log2() { this.code.push(0xa2); }
|
|
log3() { this.code.push(0xa3); }
|
|
log4() { this.code.push(0xa4); }
|
|
|
|
create() { this.code.push(0xf0); }
|
|
call() { this.code.push(0xf1); }
|
|
callcode() { this.code.push(0xf2); }
|
|
return() { this.code.push(0xf3); }
|
|
delegatecall() { this.code.push(0xf4); }
|
|
|
|
staticcall() { this.code.push(0xfa); }
|
|
revert() { this.code.push(0xfd); }
|
|
invalid() { this.code.push(0xfe); }
|
|
selfdestruct() { this.code.push(0xff); }
|
|
}
|
|
|