circomlib/src/evmasm.js
src/evmasm.jsRaw
1// Copyright (c) 2018 Jordi Baylina
2// License: LGPL-3.0+
3//
4
5
6const Web3Utils = require("web3-utils");
7
8class Contract {
9 constructor() {
10 this.code = [];
11 this.labels = {};
12 this.pendingLabels = {};
13 }
14
15 createTxData() {
16 let C;
17
18 // Check all labels are defined
19 const pendingLabels = Object.keys(this.pendingLabels);
20 if (pendingLabels.length>0) {
21 throw new Error("Lables not defined: "+ pendingLabels.join(", "));
22 }
23
24 let setLoaderLength = 0;
25 let genLoadedLength = -1;
26
27 while (genLoadedLength!=setLoaderLength) {
28 setLoaderLength = genLoadedLength;
29 C = new module.exports();
30 C.codesize();
31 C.push(setLoaderLength);
32 C.push(0);
33 C.codecopy();
34
35 C.push(this.code.length);
36 C.push(0);
37 C.return();
38 genLoadedLength = C.code.length;
39 }
40
41 return Web3Utils.bytesToHex(C.code.concat(this.code));
42 }
43
44 stop() { this.code.push(0x00); }
45 add() { this.code.push(0x01); }
46 mul() { this.code.push(0x02); }
47 sub() { this.code.push(0x03); }
48 div() { this.code.push(0x04); }
49 sdiv() { this.code.push(0x05); }
50 mod() { this.code.push(0x06); }
51 smod() { this.code.push(0x07); }
52 addmod() { this.code.push(0x08); }
53 mulmod() { this.code.push(0x09); }
54 exp() { this.code.push(0x0a); }
55 signextend() { this.code.push(0x0b); }
56
57 lt() { this.code.push(0x10); }
58 gt() { this.code.push(0x11); }
59 slt() { this.code.push(0x12); }
60 sgt() { this.code.push(0x13); }
61 eq() { this.code.push(0x14); }
62 iszero() { this.code.push(0x15); }
63 and() { this.code.push(0x16); }
64 or() { this.code.push(0x17); }
65 shor() { this.code.push(0x18); }
66 not() { this.code.push(0x19); }
67 byte() { this.code.push(0x1a); }
68
69 keccak() { this.code.push(0x20); }
70 sha3() { this.code.push(0x20); } // alias
71
72 address() { this.code.push(0x30); }
73 balance() { this.code.push(0x31); }
74 origin() { this.code.push(0x32); }
75 caller() { this.code.push(0x33); }
76 callvalue() { this.code.push(0x34); }
77 calldataload() { this.code.push(0x35); }
78 calldatasize() { this.code.push(0x36); }
79 calldatacopy() { this.code.push(0x37); }
80 codesize() { this.code.push(0x38); }
81 codecopy() { this.code.push(0x39); }
82 gasprice() { this.code.push(0x3a); }
83 extcodesize() { this.code.push(0x3b); }
84 extcodecopy() { this.code.push(0x3c); }
85 returndatasize() { this.code.push(0x3d); }
86 returndatacopy() { this.code.push(0x3e); }
87
88 blockhash() { this.code.push(0x40); }
89 coinbase() { this.code.push(0x41); }
90 timestamp() { this.code.push(0x42); }
91 number() { this.code.push(0x43); }
92 difficulty() { this.code.push(0x44); }
93 gaslimit() { this.code.push(0x45); }
94
95 pop() { this.code.push(0x50); }
96 mload() { this.code.push(0x51); }
97 mstore() { this.code.push(0x52); }
98 mstore8() { this.code.push(0x53); }
99 sload() { this.code.push(0x54); }
100 sstore() { this.code.push(0x55); }
101
102 _pushLabel(label) {
103 if (typeof this.labels[label] != "undefined") {
104 this.push(this.labels[label]);
105 } else {
106 this.pendingLabels[label] = this.pendingLabels[label] || [];
107 this.pendingLabels[label].push(this.code.length);
108 this.push("0x000000");
109 }
110 }
111
112 _fillLabel(label) {
113 if (!this.pendingLabels[label]) return;
114
115 let dst = this.labels[label];
116
117 const dst3 = [dst >> 16, (dst >> 8) & 0xFF, dst & 0xFF];
118
119 this.pendingLabels[label].forEach((p) => {
120 for (let i=0; i<3; i++) {
121 this.code[p+i+1] = dst3[i];
122 }
123 });
124
125 delete this.pendingLabels[label];
126 }
127
128
129 jmp(label) {
130 if (typeof label !== "undefined") {
131 this._pushLabel(label);
132 }
133 this.code.push(0x56);
134 }
135
136 jmpi(label) {
137 if (typeof label !== "undefined") {
138 this._pushLabel(label);
139 }
140 this.code.push(0x57);
141 }
142
143 pc() { this.code.push(0x58); }
144 msize() { this.code.push(0x59); }
145 gas() { this.code.push(0x5a); }
146 label(name) {
147 if (typeof this.labels[name] != "undefined") {
148 throw new Error("Label already defined");
149 }
150 this.labels[name] = this.code.length;
151 this.code.push(0x5b);
152
153 this._fillLabel(name);
154 }
155
156 push(data) {
157 if (typeof data === "number") {
158 let isNeg;
159 if (data<0) {
160 isNeg = true;
161 data = -data;
162 }
163 data = data.toString(16);
164 if (data.length % 2 == 1) data = "0" + data;
165 data = "0x" + data;
166 if (isNeg) data = "-"+data;
167 }
168 const d = Web3Utils.hexToBytes(Web3Utils.toHex(data));
169 if (d.length == 0 || d.length > 32) {
170 throw new Error("Assertion failed");
171 }
172 this.code = this.code.concat([0x5F + d.length], d);
173 }
174
175 dup(n) {
176 if (n < 0 || n >= 16) {
177 throw new Error("Assertion failed");
178 }
179 this.code.push(0x80 + n);
180 }
181
182 swap(n) {
183 if (n < 1 || n > 16) {
184 throw new Error("Assertion failed");
185 }
186 this.code.push(0x8f + n);
187 }
188
189 log0() { this.code.push(0xa0); }
190 log1() { this.code.push(0xa1); }
191 log2() { this.code.push(0xa2); }
192 log3() { this.code.push(0xa3); }
193 log4() { this.code.push(0xa4); }
194
195 create() { this.code.push(0xf0); }
196 call() { this.code.push(0xf1); }
197 callcode() { this.code.push(0xf2); }
198 return() { this.code.push(0xf3); }
199 delegatecall() { this.code.push(0xf4); }
200
201 staticcall() { this.code.push(0xfa); }
202 revert() { this.code.push(0xfd); }
203 invalid() { this.code.push(0xfe); }
204 selfdestruct() { this.code.push(0xff); }
205}
206
207module.exports = Contract;
208