More contained Opcode API.
This commit is contained in:
parent
0296594aba
commit
da8153c877
@ -170,6 +170,35 @@ impossible to synthesize recursive hardware.
|
||||
}
|
||||
```
|
||||
|
||||
Or code that tries to include its own hash internally:
|
||||
|
||||
```
|
||||
@myContract {
|
||||
|
||||
; NOT OK! hash(hash(hash( .... (data))) will never resolve stable bytecode
|
||||
@checksumBad[
|
||||
{{= keccak256(myContract) }}
|
||||
]
|
||||
|
||||
; The hash excluding of bytecode excluding the checksum works.
|
||||
@checksumOk[
|
||||
{{= keccak256(myContract.slice(0, checksumOk.offset)) }}
|
||||
{{= zeroPad("0x", 32) }}
|
||||
{{= keccak256(myContract.slice(checksumOk.offset + 32)) }}
|
||||
]
|
||||
|
||||
; But this is fine... The source code of a file does not change
|
||||
@checksumGood[
|
||||
{{= id(myContract.source) }
|
||||
]
|
||||
|
||||
; Even better; this will make disassembled code look nicer...
|
||||
@checksumBest[
|
||||
{{= concat([ Opcode.from("PUSH32"), id(myContract.source) ]) }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Building
|
||||
========
|
||||
|
||||
|
@ -14,7 +14,7 @@ import vm from "vm";
|
||||
|
||||
import { ethers } from "ethers";
|
||||
|
||||
import { getOpcode, Opcode } from "./opcodes";
|
||||
import { Opcode } from "./opcodes";
|
||||
|
||||
import { parse as _parse, parser as _parser } from "./_parser";
|
||||
|
||||
@ -61,9 +61,14 @@ class Script {
|
||||
ethers: ethers,
|
||||
utils: ethers.utils,
|
||||
|
||||
BigNumber: ethers.BigNumber,
|
||||
|
||||
arrayify: ethers.utils.arrayify,
|
||||
hexlify: ethers.utils.hexlify,
|
||||
concat: hexConcat,
|
||||
hexlify: ethers.utils.hexlify,
|
||||
zeroPad: function(value: ethers.utils.BytesLike, length: number) {
|
||||
return ethers.utils.hexlify(ethers.utils.zeroPad(value, length));
|
||||
},
|
||||
|
||||
id: ethers.utils.id,
|
||||
keccak256: ethers.utils.keccak256,
|
||||
@ -84,7 +89,7 @@ class Script {
|
||||
formatBytes32String: ethers.utils.formatBytes32String,
|
||||
parseBytes32String: ethers.utils.parseBytes32String,
|
||||
|
||||
getOpcode: getOpcode,
|
||||
Opcode: Opcode,
|
||||
|
||||
sighash: function(signature: string): string {
|
||||
return ethers.utils.id(ethers.utils.FunctionFragment.from(signature).format()).substring(0, 10);
|
||||
@ -206,7 +211,7 @@ function pushLiteral(value: ethers.utils.BytesLike | ethers.utils.Hexable | numb
|
||||
const length = ethers.utils.hexDataLength(hex);
|
||||
if (length === 0 || length > 32) { throw new Error(`literal out of range: ${ hex }`); }
|
||||
|
||||
return hexConcat([ getOpcode("PUSH" + String(length)), hex ]);
|
||||
return hexConcat([ Opcode.from("PUSH" + String(length)), hex ]);
|
||||
}
|
||||
|
||||
export class LiteralNode extends ValueNode {
|
||||
@ -320,7 +325,7 @@ export class OpcodeNode extends ValueNode {
|
||||
|
||||
static from(options: any): OpcodeNode {
|
||||
if (options.type !== "opcode") { throw new Error("expected opcode type"); }
|
||||
const opcode = getOpcode(options.mnemonic);
|
||||
const opcode = Opcode.from(options.mnemonic);
|
||||
if (!opcode) { throw new Error("unknown opcode: " + options.mnemonic); }
|
||||
|
||||
// Using the function syntax will check the operand count
|
||||
@ -356,7 +361,7 @@ export abstract class LabelledNode extends CodeNode {
|
||||
export class LabelNode extends LabelledNode {
|
||||
async assemble(assembler: Assembler, visit: AssembleVisitFunc): Promise<void> {
|
||||
assembler.start(this);
|
||||
visit(this, ethers.utils.hexlify(getOpcode("JUMPDEST").value));
|
||||
visit(this, ethers.utils.hexlify(Opcode.from("JUMPDEST").value));
|
||||
assembler.end(this);
|
||||
}
|
||||
|
||||
@ -389,7 +394,7 @@ export class DataNode extends LabelledNode {
|
||||
// Replay the data as bytecode, skipping PUSH data
|
||||
let i = 0;
|
||||
while (i < bytecode.length) {
|
||||
const opcode = getOpcode(bytecode[i++]);
|
||||
const opcode = Opcode.from(bytecode[i++]);
|
||||
if (opcode) {
|
||||
i += opcode.isPush();
|
||||
}
|
||||
@ -398,7 +403,7 @@ export class DataNode extends LabelledNode {
|
||||
// The amount we overshot the data by is how much padding we need
|
||||
const padding = new Uint8Array(i - bytecode.length);
|
||||
// What makes more sense? INVALID or 0 (i.e. STOP)?
|
||||
//padding.fill(getOpcode("INVALID").value);
|
||||
//padding.fill(Opcode.from("INVALID").value);
|
||||
padding.fill(0);
|
||||
visit(this, ethers.utils.hexlify(padding))
|
||||
|
||||
@ -587,7 +592,7 @@ export function disassemble(bytecode: string): Bytecode {
|
||||
let i = 0;
|
||||
let oob = false;
|
||||
while (i < bytes.length) {
|
||||
let opcode = getOpcode(bytes[i]);
|
||||
let opcode = Opcode.from(bytes[i]);
|
||||
if (!opcode) {
|
||||
opcode = new Opcode(`unknown (${ ethers.utils.hexlify(bytes[i]) })`, bytes[i], 0, 0);
|
||||
} else if (oob && opcode.mnemonic === "JUMPDEST") {
|
||||
@ -1017,7 +1022,7 @@ return(0, #myContract)
|
||||
return (0, 0)
|
||||
|
||||
@checksum[
|
||||
{{= (defines.checksum ? concat([ getOpcode("PUSH32"), id(_.source) ]): "0x") }}
|
||||
{{= (defines.checksum ? concat([ Opcode.from("PUSH32"), id(_.source) ]): "0x") }}
|
||||
]
|
||||
}`;
|
||||
|
||||
|
@ -1,13 +1,12 @@
|
||||
"use strict";
|
||||
|
||||
import { assemble, CodeNode, DataNode, disassemble, EvaluationNode, ExecutionNode, formatBytecode, LabelNode, LabelledNode, LinkNode, LiteralNode, Node, OpcodeNode, parse, ScopeNode, ValueNode } from "./assembler";
|
||||
import { getOpcode, Opcode } from "./opcodes";
|
||||
import { Opcode } from "./opcodes";
|
||||
|
||||
import { AssemblerOptions, AssembleVisitFunc, Bytecode, Location, Operation, VisitFunc } from "./assembler";
|
||||
|
||||
export {
|
||||
// Opcodes
|
||||
getOpcode,
|
||||
Opcode,
|
||||
|
||||
// Assembler functions
|
||||
|
@ -55,6 +55,13 @@ export class Opcode {
|
||||
isStatic(): boolean {
|
||||
throw new Error("@TODO: return true if certain non-state-changing");
|
||||
}
|
||||
|
||||
static from(valueOrMnemonic: number | string) {
|
||||
if (typeof(valueOrMnemonic) === "string") {
|
||||
return OpcodeMap[valueOrMnemonic.toLowerCase()] || null;
|
||||
}
|
||||
return (Opcodes[valueOrMnemonic] || null);
|
||||
}
|
||||
}
|
||||
|
||||
type _Opcode = {
|
||||
@ -249,10 +256,4 @@ function repeat(char: string, length: number): string {
|
||||
return result.substring(0, length);
|
||||
}
|
||||
*/
|
||||
export function getOpcode(valueOrMnemonic: number | string) {
|
||||
if (typeof(valueOrMnemonic) === "string") {
|
||||
return OpcodeMap[valueOrMnemonic.toLowerCase()] || null;
|
||||
}
|
||||
return (Opcodes[valueOrMnemonic] || null);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user