Created
September 20, 2024 08:27
-
-
Save holgerd77/2c032488196b4afee5d976dc85ee70eb to your computer and use it in GitHub Desktop.
Unminifed JS bundle of the EthereumJS EVM with all dependencies
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const FORMAT = 239; | |
const MAGIC = 0; | |
const VERSION = 1; | |
const MAX_HEADER_SIZE = 49152; | |
const KIND_TYPE = 1; | |
const KIND_CODE = 2; | |
const KIND_CONTAINER = 3; | |
const KIND_DATA = 4; | |
const TERMINATOR = 0; | |
const TYPE_MIN = 4; | |
const TYPE_MAX = 4096; | |
const TYPE_DIVISOR = 4; | |
const CODE_MIN = 1; | |
const CODE_SIZE_MIN = 1; | |
const CONTAINER_MIN = 1; | |
const CONTAINER_MAX = 256; | |
const CONTAINER_SIZE_MIN = 1; | |
const INPUTS_MAX = 127; | |
const OUTPUTS_MAX = 128; | |
const MAX_STACK_HEIGHT = 1023; | |
var EOFError = /* @__PURE__ */ ((EOFError2) => { | |
EOFError2["OutOfBounds"] = "Trying to read out of bounds"; | |
EOFError2["VerifyUint"] = "Uint does not match expected value "; | |
EOFError2["VerifyBytes"] = "Bytes do not match expected value"; | |
EOFError2["FORMAT"] = "err: invalid format"; | |
EOFError2["MAGIC"] = "err: invalid magic"; | |
EOFError2["VERSION"] = `err: invalid eof version`; | |
EOFError2["KIND_TYPE"] = `err: expected kind types`; | |
EOFError2["KIND_CODE"] = `err: expected kind code`; | |
EOFError2["KIND_DATA"] = `err: expected kind data`; | |
EOFError2["TERMINATOR"] = `err: expected terminator`; | |
EOFError2["TypeSize"] = `missing type size`; | |
EOFError2["InvalidTypeSize"] = `err: type section size invalid`; | |
EOFError2["CodeSize"] = `missing code size`; | |
EOFError2["CodeSectionSize"] = `code section should be at least one byte`; | |
EOFError2["InvalidCodeSize"] = `code size does not match type size`; | |
EOFError2["DataSize"] = `missing data size`; | |
EOFError2["ContainerSize"] = "missing container size"; | |
EOFError2["ContainerSectionSize"] = "container section should at least contain one section and at most 255 sections"; | |
EOFError2["TypeSections"] = `err: mismatch of code sections count and type signatures`; | |
EOFError2["Inputs"] = "expected inputs"; | |
EOFError2["Outputs"] = "expected outputs"; | |
EOFError2["MaxInputs"] = "inputs exceeds 127, the maximum, got: "; | |
EOFError2["MaxOutputs"] = "outputs exceeds 127, the maximum, got: "; | |
EOFError2["Code0Inputs"] = "first code section should have 0 inputs"; | |
EOFError2["Code0Outputs"] = "first code section should have 0x80 (terminating section) outputs"; | |
EOFError2["MaxStackHeight"] = `expected maxStackHeight`; | |
EOFError2["MaxStackHeightLimit"] = `stack height limit of 1024 exceeded: `; | |
EOFError2["MinCodeSections"] = `should have at least 1 code section`; | |
EOFError2["MaxCodeSections"] = `can have at most 1024 code sections`; | |
EOFError2["CodeSection"] = `expected a code section`; | |
EOFError2["DataSection"] = `Expected data section`; | |
EOFError2["ContainerSection"] = "expected a container section"; | |
EOFError2["ContainerSectionMin"] = "container section should be at least 1 byte"; | |
EOFError2["InvalidEOFCreateTarget"] = "EOFCREATE targets an undefined container"; | |
EOFError2["InvalidRETURNContractTarget"] = "RETURNCONTRACT targets an undefined container"; | |
EOFError2["ContainerDoubleType"] = "Container is targeted by both EOFCREATE and RETURNCONTRACT"; | |
EOFError2["UnreachableContainerSections"] = "Unreachable containers (by both EOFCREATE and RETURNCONTRACT)"; | |
EOFError2["ContainerTypeError"] = "Container contains opcodes which this mode (deployment mode / init code / runtime mode) cannot have"; | |
EOFError2["DanglingBytes"] = "got dangling bytes in body"; | |
EOFError2["InvalidOpcode"] = "invalid opcode"; | |
EOFError2["InvalidTerminator"] = "invalid terminating opcode"; | |
EOFError2["OpcodeIntermediatesOOB"] = "invalid opcode: intermediates out-of-bounds"; | |
EOFError2["InvalidRJUMP"] = "invalid rjump* target"; | |
EOFError2["InvalidCallTarget"] = "invalid callf/jumpf target"; | |
EOFError2["InvalidCALLFReturning"] = "invalid callf: calls to non-returning function"; | |
EOFError2["InvalidStackHeight"] = "invalid stack height"; | |
EOFError2["InvalidJUMPF"] = "invalid jumpf target (output count)"; | |
EOFError2["InvalidReturningSection"] = "invalid returning code section: section is not returning"; | |
EOFError2["RJUMPVTableSize0"] = "invalid RJUMPV: table size 0"; | |
EOFError2["UnreachableCodeSections"] = "unreachable code sections"; | |
EOFError2["UnreachableCode"] = "unreachable code (by forward jumps)"; | |
EOFError2["DataLoadNOutOfBounds"] = "DATALOADN reading out of bounds"; | |
EOFError2["MaxStackHeightViolation"] = "Max stack height does not match the reported max stack height"; | |
EOFError2["StackUnderflow"] = "Stack underflow"; | |
EOFError2["StackOverflow"] = "Stack overflow"; | |
EOFError2["UnstableStack"] = "Unstable stack (can reach stack under/overflow by jumps)"; | |
EOFError2["RetfNoReturn"] = "Trying to return to undefined function"; | |
EOFError2["ReturnStackOverflow"] = "Return stack overflow"; | |
EOFError2["InvalidExtcallTarget"] = "invalid extcall target: address > 20 bytes"; | |
EOFError2["InvalidReturnContractDataSize"] = "invalid RETURNCONTRACT: data size lower than expected"; | |
EOFError2["InvalidEofFormat"] = "invalid EOF format"; | |
return EOFError2; | |
})(EOFError || {}); | |
function validationError(type, ...args) { | |
switch (type) { | |
case "Trying to read out of bounds": { | |
const pos = args[0]; | |
if (pos === 0 || pos === 2 || pos === 3 || pos === 6) { | |
throw new Error(args[1]); | |
} | |
throw new Error(`Trying to read out of bounds `); | |
} | |
case "Bytes do not match expected value": { | |
const pos = args[0]; | |
if (pos === 0 || pos === 2 || pos === 3 || pos === 6) { | |
throw new Error(args[1]); | |
} | |
throw new Error(`Bytes do not match expected value at pos: ${args[0]}: ${args[1]}`); | |
} | |
case "Uint does not match expected value ": { | |
const pos = args[0]; | |
if (pos === 0 || pos === 2 || pos === 3 || pos === 6 || pos === 18) { | |
throw new Error(args[1]); | |
} | |
throw new Error(`Uint does not match expected value at pos: ${args[0]}: ${args[1]}`); | |
} | |
case "missing type size": { | |
throw new Error("missing type size" + args[0]); | |
} | |
case "err: mismatch of code sections count and type signatures": { | |
throw new Error(`${"err: mismatch of code sections count and type signatures"} (types ${args[0]} code ${args[1]})`); | |
} | |
case "err: type section size invalid": { | |
throw new Error( | |
"err: type section size invalid" | |
/* InvalidTypeSize */ | |
); | |
} | |
case "code size does not match type size": { | |
throw new Error("code size does not match type size" + args[0]); | |
} | |
case "expected inputs": { | |
throw new Error(`${"expected inputs"} - typeSection ${args[0]}`); | |
} | |
case "expected outputs": { | |
throw new Error(`${"expected outputs"} - typeSection ${args[0]}`); | |
} | |
case "first code section should have 0 inputs": { | |
throw new Error(`first code section should have 0 inputs`); | |
} | |
case "first code section should have 0x80 (terminating section) outputs": { | |
throw new Error(`first code section should have 0 outputs`); | |
} | |
case "inputs exceeds 127, the maximum, got: ": { | |
throw new Error(`inputs exceeds 127, the maximum, got: ${args[1]} - code section ${args[0]}`); | |
} | |
case "outputs exceeds 127, the maximum, got: ": { | |
throw new Error(`outputs exceeds 127, the maximum, got: ${args[1]} - code section ${args[0]}`); | |
} | |
case "expected a code section": { | |
throw new Error(`expected code: codeSection ${args[0]}: `); | |
} | |
case "Expected data section": { | |
throw new Error( | |
"Expected data section" | |
/* DataSection */ | |
); | |
} | |
case "expected maxStackHeight": { | |
throw new Error(`${"expected maxStackHeight"} - typeSection ${args[0]}: `); | |
} | |
case "stack height limit of 1024 exceeded: ": { | |
throw new Error(`${"stack height limit of 1024 exceeded: "}, got: ${args[1]} - typeSection ${args[0]}`); | |
} | |
case "got dangling bytes in body": { | |
throw new Error( | |
"got dangling bytes in body" | |
/* DanglingBytes */ | |
); | |
} | |
default: { | |
throw new Error(type); | |
} | |
} | |
} | |
const stackDelta = { | |
0: { inputs: 0, outputs: 0, name: "STOP", intermediates: 0, terminating: true }, | |
1: { inputs: 2, outputs: 1, name: "ADD", intermediates: 0 }, | |
2: { inputs: 2, outputs: 1, name: "MUL", intermediates: 0 }, | |
3: { inputs: 2, outputs: 1, name: "SUB", intermediates: 0 }, | |
4: { inputs: 2, outputs: 1, name: "DIV", intermediates: 0 }, | |
5: { inputs: 2, outputs: 1, name: "SDIV", intermediates: 0 }, | |
6: { inputs: 2, outputs: 1, name: "MOD", intermediates: 0 }, | |
7: { inputs: 2, outputs: 1, name: "SMOD", intermediates: 0 }, | |
8: { inputs: 3, outputs: 1, name: "ADDMOD", intermediates: 0 }, | |
9: { inputs: 3, outputs: 1, name: "MULMOD", intermediates: 0 }, | |
10: { inputs: 2, outputs: 1, name: "EXP", intermediates: 0 }, | |
11: { inputs: 2, outputs: 1, name: "SIGNEXTEND", intermediates: 0 }, | |
16: { inputs: 2, outputs: 1, name: "LT", intermediates: 0 }, | |
17: { inputs: 2, outputs: 1, name: "GT", intermediates: 0 }, | |
18: { inputs: 2, outputs: 1, name: "SLT", intermediates: 0 }, | |
19: { inputs: 2, outputs: 1, name: "SGT", intermediates: 0 }, | |
20: { inputs: 2, outputs: 1, name: "EQ", intermediates: 0 }, | |
21: { inputs: 1, outputs: 1, name: "ISZERO", intermediates: 0 }, | |
22: { inputs: 2, outputs: 1, name: "AND", intermediates: 0 }, | |
23: { inputs: 2, outputs: 1, name: "OR", intermediates: 0 }, | |
24: { inputs: 2, outputs: 1, name: "XOR", intermediates: 0 }, | |
25: { inputs: 1, outputs: 1, name: "NOT", intermediates: 0 }, | |
26: { inputs: 2, outputs: 1, name: "BYTE", intermediates: 0 }, | |
27: { inputs: 2, outputs: 1, name: "SHL", intermediates: 0 }, | |
28: { inputs: 2, outputs: 1, name: "SHR", intermediates: 0 }, | |
29: { inputs: 2, outputs: 1, name: "SAR", intermediates: 0 }, | |
32: { inputs: 2, outputs: 1, name: "SHA3", intermediates: 0 }, | |
48: { inputs: 0, outputs: 1, name: "ADDRESS", intermediates: 0 }, | |
49: { inputs: 1, outputs: 1, name: "BALANCE", intermediates: 0 }, | |
50: { inputs: 0, outputs: 1, name: "ORIGIN", intermediates: 0 }, | |
51: { inputs: 0, outputs: 1, name: "CALLER", intermediates: 0 }, | |
52: { inputs: 0, outputs: 1, name: "CALLVALUE", intermediates: 0 }, | |
53: { inputs: 1, outputs: 1, name: "CALLDATALOAD", intermediates: 0 }, | |
54: { inputs: 0, outputs: 1, name: "CALLDATASIZE", intermediates: 0 }, | |
55: { inputs: 3, outputs: 0, name: "CALLDATACOPY", intermediates: 0 }, | |
58: { inputs: 0, outputs: 1, name: "GASPRICE", intermediates: 0 }, | |
61: { inputs: 0, outputs: 1, name: "RETURNDATASIZE", intermediates: 0 }, | |
62: { inputs: 3, outputs: 0, name: "RETURNDATACOPY", intermediates: 0 }, | |
64: { inputs: 1, outputs: 1, name: "BLOCKHASH", intermediates: 0 }, | |
65: { inputs: 0, outputs: 1, name: "COINBASE", intermediates: 0 }, | |
66: { inputs: 0, outputs: 1, name: "TIMESTAMP", intermediates: 0 }, | |
67: { inputs: 0, outputs: 1, name: "NUMBER", intermediates: 0 }, | |
68: { inputs: 0, outputs: 1, name: "PREVRANDAO", intermediates: 0 }, | |
69: { inputs: 0, outputs: 1, name: "GASLIMIT", intermediates: 0 }, | |
70: { inputs: 0, outputs: 1, name: "CHAINID", intermediates: 0 }, | |
71: { inputs: 0, outputs: 1, name: "SELFBALANCE", intermediates: 0 }, | |
72: { inputs: 0, outputs: 1, name: "BASEFEE", intermediates: 0 }, | |
73: { inputs: 1, outputs: 1, name: "BLOBAHASH", intermediates: 0 }, | |
74: { inputs: 0, outputs: 1, name: "BLOBBASEFEE", intermediates: 0 }, | |
80: { inputs: 1, outputs: 0, name: "POP", intermediates: 0 }, | |
81: { inputs: 1, outputs: 1, name: "MLOAD", intermediates: 0 }, | |
82: { inputs: 2, outputs: 0, name: "MSTORE", intermediates: 0 }, | |
83: { inputs: 2, outputs: 0, name: "MSTORE8", intermediates: 0 }, | |
84: { inputs: 1, outputs: 1, name: "SLOAD", intermediates: 0 }, | |
85: { inputs: 2, outputs: 0, name: "SSTORE", intermediates: 0 }, | |
89: { inputs: 0, outputs: 1, name: "MSIZE", intermediates: 0 }, | |
91: { inputs: 0, outputs: 0, name: "NOOP", intermediates: 0 }, | |
92: { inputs: 1, outputs: 1, name: "TLOAD", intermediates: 0 }, | |
93: { inputs: 2, outputs: 0, name: "TSTORE", intermediates: 0 }, | |
94: { inputs: 3, outputs: 0, name: "MCOPY", intermediates: 0 }, | |
95: { inputs: 0, outputs: 1, name: "PUSH0", intermediates: 0 }, | |
96: { inputs: 0, outputs: 1, name: "PUSH1", intermediates: 1 }, | |
97: { inputs: 0, outputs: 1, name: "PUSH2", intermediates: 2 }, | |
98: { inputs: 0, outputs: 1, name: "PUSH3", intermediates: 3 }, | |
99: { inputs: 0, outputs: 1, name: "PUSH4", intermediates: 4 }, | |
100: { inputs: 0, outputs: 1, name: "PUSH5", intermediates: 5 }, | |
101: { inputs: 0, outputs: 1, name: "PUSH6", intermediates: 6 }, | |
102: { inputs: 0, outputs: 1, name: "PUSH7", intermediates: 7 }, | |
103: { inputs: 0, outputs: 1, name: "PUSH8", intermediates: 8 }, | |
104: { inputs: 0, outputs: 1, name: "PUSH9", intermediates: 9 }, | |
105: { inputs: 0, outputs: 1, name: "PUSH10", intermediates: 10 }, | |
106: { inputs: 0, outputs: 1, name: "PUSH11", intermediates: 11 }, | |
107: { inputs: 0, outputs: 1, name: "PUSH12", intermediates: 12 }, | |
108: { inputs: 0, outputs: 1, name: "PUSH13", intermediates: 13 }, | |
109: { inputs: 0, outputs: 1, name: "PUSH14", intermediates: 14 }, | |
110: { inputs: 0, outputs: 1, name: "PUSH15", intermediates: 15 }, | |
111: { inputs: 0, outputs: 1, name: "PUSH16", intermediates: 16 }, | |
112: { inputs: 0, outputs: 1, name: "PUSH17", intermediates: 17 }, | |
113: { inputs: 0, outputs: 1, name: "PUSH18", intermediates: 18 }, | |
114: { inputs: 0, outputs: 1, name: "PUSH19", intermediates: 19 }, | |
115: { inputs: 0, outputs: 1, name: "PUSH20", intermediates: 20 }, | |
116: { inputs: 0, outputs: 1, name: "PUSH21", intermediates: 21 }, | |
117: { inputs: 0, outputs: 1, name: "PUSH22", intermediates: 22 }, | |
118: { inputs: 0, outputs: 1, name: "PUSH23", intermediates: 23 }, | |
119: { inputs: 0, outputs: 1, name: "PUSH24", intermediates: 24 }, | |
120: { inputs: 0, outputs: 1, name: "PUSH25", intermediates: 25 }, | |
121: { inputs: 0, outputs: 1, name: "PUSH26", intermediates: 26 }, | |
122: { inputs: 0, outputs: 1, name: "PUSH27", intermediates: 27 }, | |
123: { inputs: 0, outputs: 1, name: "PUSH28", intermediates: 28 }, | |
124: { inputs: 0, outputs: 1, name: "PUSH29", intermediates: 29 }, | |
125: { inputs: 0, outputs: 1, name: "PUSH30", intermediates: 30 }, | |
126: { inputs: 0, outputs: 1, name: "PUSH31", intermediates: 31 }, | |
127: { inputs: 0, outputs: 1, name: "PUSH32", intermediates: 32 }, | |
128: { inputs: 1, outputs: 2, name: "DUP1", intermediates: 0 }, | |
129: { inputs: 2, outputs: 3, name: "DUP2", intermediates: 0 }, | |
130: { inputs: 3, outputs: 4, name: "DUP3", intermediates: 0 }, | |
131: { inputs: 4, outputs: 5, name: "DUP4", intermediates: 0 }, | |
132: { inputs: 5, outputs: 6, name: "DUP5", intermediates: 0 }, | |
133: { inputs: 6, outputs: 7, name: "DUP6", intermediates: 0 }, | |
134: { inputs: 7, outputs: 8, name: "DUP7", intermediates: 0 }, | |
135: { inputs: 8, outputs: 9, name: "DUP8", intermediates: 0 }, | |
136: { inputs: 9, outputs: 10, name: "DUP9", intermediates: 0 }, | |
137: { inputs: 10, outputs: 11, name: "DUP10", intermediates: 0 }, | |
138: { inputs: 11, outputs: 12, name: "DUP11", intermediates: 0 }, | |
139: { inputs: 12, outputs: 13, name: "DUP12", intermediates: 0 }, | |
140: { inputs: 13, outputs: 14, name: "DUP13", intermediates: 0 }, | |
141: { inputs: 14, outputs: 15, name: "DUP14", intermediates: 0 }, | |
142: { inputs: 15, outputs: 16, name: "DUP15", intermediates: 0 }, | |
143: { inputs: 16, outputs: 17, name: "DUP16", intermediates: 0 }, | |
144: { inputs: 2, outputs: 2, name: "SWAP1", intermediates: 0 }, | |
145: { inputs: 3, outputs: 3, name: "SWAP2", intermediates: 0 }, | |
146: { inputs: 4, outputs: 4, name: "SWAP3", intermediates: 0 }, | |
147: { inputs: 5, outputs: 5, name: "SWAP4", intermediates: 0 }, | |
148: { inputs: 6, outputs: 6, name: "SWAP5", intermediates: 0 }, | |
149: { inputs: 7, outputs: 7, name: "SWAP6", intermediates: 0 }, | |
150: { inputs: 8, outputs: 8, name: "SWAP7", intermediates: 0 }, | |
151: { inputs: 9, outputs: 9, name: "SWAP8", intermediates: 0 }, | |
152: { inputs: 10, outputs: 10, name: "SWAP9", intermediates: 0 }, | |
153: { inputs: 11, outputs: 11, name: "SWAP10", intermediates: 0 }, | |
154: { inputs: 12, outputs: 12, name: "SWAP11", intermediates: 0 }, | |
155: { inputs: 13, outputs: 13, name: "SWAP12", intermediates: 0 }, | |
156: { inputs: 14, outputs: 14, name: "SWAP13", intermediates: 0 }, | |
157: { inputs: 15, outputs: 15, name: "SWAP14", intermediates: 0 }, | |
158: { inputs: 16, outputs: 16, name: "SWAP15", intermediates: 0 }, | |
159: { inputs: 17, outputs: 17, name: "SWAP16", intermediates: 0 }, | |
160: { inputs: 2, outputs: 0, name: "LOG0", intermediates: 0 }, | |
161: { inputs: 3, outputs: 0, name: "LOG1", intermediates: 0 }, | |
162: { inputs: 4, outputs: 0, name: "LOG2", intermediates: 0 }, | |
163: { inputs: 5, outputs: 0, name: "LOG3", intermediates: 0 }, | |
164: { inputs: 6, outputs: 0, name: "LOG4", intermediates: 0 }, | |
208: { inputs: 1, outputs: 1, name: "DATALOAD", intermediates: 0 }, | |
209: { inputs: 0, outputs: 1, name: "DATALOADN", intermediates: 2 }, | |
210: { inputs: 0, outputs: 1, name: "DATASIZE", intermediates: 0 }, | |
211: { inputs: 3, outputs: 0, name: "DATACOPY", intermediates: 0 }, | |
224: { inputs: 0, outputs: 0, name: "RJUMP", intermediates: 2 }, | |
225: { inputs: 1, outputs: 0, name: "RJUMPI", intermediates: 2 }, | |
// NOTE: for RJUMPV the intermediate byte is set to 0, this has to do with the validation algorithm specifics | |
// This has to do with the dynamic intermediate size of RJUMPV, which depends upon the table size byte right after RJUMPV | |
226: { inputs: 1, outputs: 0, name: "RJUMPV", intermediates: 0 }, | |
// CALLF special case for stack validation algorithm: the inputs and outputs MUST stay 0 | |
// (this is currently the case also in EVM) | |
227: { inputs: 0, outputs: 0, name: "CALLF", intermediates: 2 }, | |
228: { inputs: 0, outputs: 0, name: "RETF", intermediates: 0, terminating: true }, | |
229: { inputs: 0, outputs: 0, name: "JUMPF", intermediates: 2, terminating: true }, | |
230: { inputs: 0, outputs: 1, name: "DUPN", intermediates: 1 }, | |
231: { inputs: 0, outputs: 0, name: "SWAPN", intermediates: 1 }, | |
232: { inputs: 0, outputs: 0, name: "EXCHANGE", intermediates: 1 }, | |
236: { inputs: 4, outputs: 1, name: "EOFCREATE", intermediates: 1 }, | |
238: { inputs: 2, outputs: 0, name: "RETURNCONTRACT", intermediates: 1, terminating: true }, | |
243: { inputs: 2, outputs: 0, name: "RETURN", intermediates: 0, terminating: true }, | |
247: { inputs: 1, outputs: 1, name: "RETURNDATALOAD", intermediates: 0 }, | |
248: { inputs: 4, outputs: 1, name: "EXTCALL", intermediates: 0 }, | |
249: { inputs: 3, outputs: 1, name: "EXTDELEGATECALL", intermediates: 0 }, | |
251: { inputs: 3, outputs: 1, name: "EXTSTATICCALL", intermediates: 0 }, | |
253: { inputs: 2, outputs: 0, name: "REVERT", intermediates: 0, terminating: true }, | |
254: { inputs: 0, outputs: 0, name: "INVALID", intermediates: 0, terminating: true } | |
}; | |
var ContainerSectionType = /* @__PURE__ */ ((ContainerSectionType2) => { | |
ContainerSectionType2[ContainerSectionType2["InitCode"] = 0] = "InitCode"; | |
ContainerSectionType2[ContainerSectionType2["DeploymentCode"] = 1] = "DeploymentCode"; | |
ContainerSectionType2[ContainerSectionType2["RuntimeCode"] = 2] = "RuntimeCode"; | |
return ContainerSectionType2; | |
})(ContainerSectionType || {}); | |
function verifyCode(container, evm, mode = 2) { | |
return validateOpcodes(container, evm, mode); | |
} | |
function readInt16(code, start) { | |
return new DataView(code.buffer).getInt16(start); | |
} | |
function readUint16(code, start) { | |
return new DataView(code.buffer).getUint16(start); | |
} | |
function validateOpcodes(container, evm, mode = 2) { | |
const intermediateBytes = /* @__PURE__ */ new Set(); | |
const jumpLocations = /* @__PURE__ */ new Set(); | |
const containerTypeMap = /* @__PURE__ */ new Map(); | |
function addJump(location) { | |
if (intermediateBytes.has(location)) { | |
validationError(EOFError.InvalidRJUMP); | |
} | |
jumpLocations.add(location); | |
} | |
function addIntermediate(location) { | |
if (jumpLocations.has(location)) { | |
validationError(EOFError.InvalidRJUMP); | |
} | |
intermediateBytes.add(location); | |
} | |
const opcodes2 = evm.getActiveOpcodes(); | |
const opcodeNumbers = /* @__PURE__ */ new Set(); | |
for (const [key] of opcodes2) { | |
opcodeNumbers.add(key); | |
} | |
opcodeNumbers.add(254); | |
opcodeNumbers.delete(56); | |
opcodeNumbers.delete(57); | |
opcodeNumbers.delete(90); | |
opcodeNumbers.delete(59); | |
opcodeNumbers.delete(60); | |
opcodeNumbers.delete(63); | |
opcodeNumbers.delete(242); | |
opcodeNumbers.delete(255); | |
opcodeNumbers.delete(86); | |
opcodeNumbers.delete(87); | |
opcodeNumbers.delete(88); | |
opcodeNumbers.delete(240); | |
opcodeNumbers.delete(245); | |
const terminatingOpcodes = /* @__PURE__ */ new Set(); | |
terminatingOpcodes.add(0); | |
terminatingOpcodes.add(243); | |
terminatingOpcodes.add(253); | |
terminatingOpcodes.add(254); | |
terminatingOpcodes.add(238); | |
terminatingOpcodes.add(228); | |
terminatingOpcodes.add(229); | |
terminatingOpcodes.add(224); | |
for (const opcode of terminatingOpcodes) { | |
if (!opcodeNumbers.has(opcode)) { | |
terminatingOpcodes.delete(opcode); | |
} | |
} | |
const validJumps = /* @__PURE__ */ new Set(); | |
const reachableSections = {}; | |
let codeSection = -1; | |
for (const code of container.body.codeSections) { | |
codeSection++; | |
reachableSections[codeSection] = /* @__PURE__ */ new Set(); | |
const returningFunction = container.body.typeSections[codeSection].outputs === 128; | |
const reachableOpcodes = /* @__PURE__ */ new Set(); | |
reachableOpcodes.add(0); | |
let ptr = 0; | |
let lastOpcode = 0; | |
const inputs = container.body.typeSections[codeSection].inputs; | |
let maxStackHeight = inputs; | |
const stackHeightMin = [inputs]; | |
const stackHeightMax = [inputs]; | |
while (ptr < code.length) { | |
const successorSet = /* @__PURE__ */ new Set(); | |
if (!reachableOpcodes.has(ptr)) { | |
validationError(EOFError.UnreachableCode); | |
} | |
if (stackHeightMin[ptr] === void 0 || stackHeightMax[ptr] === void 0) { | |
validationError(EOFError.UnreachableCode); | |
} | |
validJumps.add(ptr); | |
const opcode = code[ptr]; | |
const minStackCurrent = stackHeightMin[ptr]; | |
const maxStackCurrent = stackHeightMax[ptr]; | |
const opcodeInputs = stackDelta[opcode].inputs; | |
const opcodeOutputs = stackDelta[opcode].outputs; | |
if (minStackCurrent - opcodeInputs < 0) { | |
validationError(EOFError.StackUnderflow); | |
} | |
const delta = opcodeOutputs - opcodeInputs; | |
let minStackNext = minStackCurrent + delta; | |
let maxStackNext = maxStackCurrent + delta; | |
if (maxStackNext > 1023) { | |
validationError(EOFError.StackOverflow); | |
} | |
if (returningFunction && opcode === 228) { | |
validationError(EOFError.InvalidReturningSection); | |
} | |
lastOpcode = opcode; | |
if (!opcodeNumbers.has(opcode)) { | |
validationError(EOFError.InvalidOpcode); | |
} | |
if (opcode === 224 || opcode === 225) { | |
const target = readInt16(code, ptr + 1) + ptr + 3; | |
if (target < 0 || target >= code.length) { | |
validationError(EOFError.InvalidRJUMP); | |
} | |
successorSet.add(target); | |
addJump(target); | |
reachableOpcodes.add(target); | |
if (opcode === 224) { | |
if (!reachableOpcodes.has(ptr + 3) && ptr + 3 < code.length) { | |
validationError(EOFError.UnreachableCode); | |
} | |
} | |
} else if (opcode === 226) { | |
const tableSize = code[ptr + 1] + 1; | |
if (tableSize === void 0) { | |
validationError(EOFError.OpcodeIntermediatesOOB); | |
} else if (tableSize === 0) { | |
validationError(EOFError.RJUMPVTableSize0); | |
} | |
if (ptr + tableSize * 2 + 2 >= code.length) { | |
validationError(EOFError.OpcodeIntermediatesOOB); | |
} | |
const newPc = ptr + 2 + tableSize * 2; | |
for (let i = 0; i < tableSize; i++) { | |
const newPtr = ptr + 2 + i * 2; | |
addIntermediate(newPtr); | |
addIntermediate(newPtr + 1); | |
const target = readInt16(code, newPtr) + newPc; | |
if (target < 0 || target >= code.length) { | |
validationError(EOFError.OpcodeIntermediatesOOB); | |
} | |
successorSet.add(target); | |
addJump(target); | |
reachableOpcodes.add(target); | |
} | |
addIntermediate(ptr + 1); | |
ptr += 2 * tableSize + 1; | |
} else if (opcode === 227 || opcode === 229) { | |
const target = readUint16(code, ptr + 1); | |
reachableSections[codeSection].add(target); | |
if (target >= container.header.codeSizes.length) { | |
validationError(EOFError.InvalidCallTarget); | |
} | |
if (opcode === 227) { | |
const targetOutputs = container.body.typeSections[target].outputs; | |
const targetInputs = container.body.typeSections[target].inputs; | |
if (targetOutputs === 128) { | |
validationError(EOFError.InvalidCALLFReturning); | |
} | |
if (minStackCurrent < targetInputs) { | |
validationError(EOFError.StackUnderflow); | |
} | |
if (maxStackCurrent + container.body.typeSections[target].maxStackHeight - targetInputs > 1024) { | |
validationError(EOFError.StackOverflow); | |
} | |
minStackNext += targetOutputs - targetInputs; | |
maxStackNext += targetOutputs - targetInputs; | |
} else { | |
const currentOutputs = container.body.typeSections[codeSection].outputs; | |
const targetOutputs = container.body.typeSections[target].outputs; | |
const targetInputs = container.body.typeSections[target].inputs; | |
const targetNonReturning = targetOutputs === 128; | |
if (targetOutputs > currentOutputs && !targetNonReturning) { | |
validationError(EOFError.InvalidJUMPF); | |
} | |
if (returningFunction && targetOutputs <= 127) { | |
validationError(EOFError.InvalidReturningSection); | |
} | |
if (targetNonReturning) { | |
if (minStackCurrent < targetInputs) { | |
validationError(EOFError.StackUnderflow); | |
} | |
} else { | |
const expectedStack = currentOutputs + targetInputs - targetOutputs; | |
if (!(minStackCurrent === maxStackCurrent && maxStackCurrent === expectedStack)) { | |
validationError(EOFError.InvalidStackHeight); | |
} | |
} | |
if (maxStackCurrent + container.body.typeSections[target].maxStackHeight - targetInputs > 1024) { | |
validationError(EOFError.StackOverflow); | |
} | |
} | |
} else if (opcode === 228) { | |
const outputs = container.body.typeSections[codeSection].outputs; | |
if (!(minStackCurrent === maxStackCurrent && maxStackCurrent === outputs)) { | |
validationError(EOFError.InvalidStackHeight); | |
} | |
} else if (opcode === 230) { | |
const toDup = code[ptr + 1]; | |
if (toDup + 1 > minStackCurrent) { | |
validationError(EOFError.StackUnderflow); | |
} | |
} else if (opcode === 231) { | |
const toSwap = code[ptr + 1]; | |
if (toSwap + 1 > minStackCurrent) { | |
validationError(EOFError.StackUnderflow); | |
} | |
} else if (opcode === 232) { | |
const exchangeRaw = code[ptr + 1]; | |
const n = (exchangeRaw >> 4) + 1; | |
const m = (exchangeRaw & 15) + 1; | |
if (n + m + 1 > minStackCurrent) { | |
validationError(EOFError.StackUnderflow); | |
} | |
} else if (opcode === 236) { | |
const target = code[ptr + 1]; | |
if (target >= container.header.containerSizes.length) { | |
validationError(EOFError.InvalidEOFCreateTarget); | |
} | |
if (containerTypeMap.has(target)) { | |
if (containerTypeMap.get(target) !== 0) { | |
validationError(EOFError.ContainerDoubleType); | |
} | |
} | |
containerTypeMap.set( | |
target, | |
0 | |
/* InitCode */ | |
); | |
} else if (opcode === 238) { | |
if (mode !== 0) { | |
validationError(EOFError.ContainerTypeError); | |
} | |
const target = code[ptr + 1]; | |
if (target >= container.header.containerSizes.length) { | |
validationError(EOFError.InvalidRETURNContractTarget); | |
} | |
if (containerTypeMap.has(target)) { | |
if (containerTypeMap.get(target) !== 1) { | |
validationError(EOFError.ContainerDoubleType); | |
} | |
} | |
containerTypeMap.set( | |
target, | |
1 | |
/* DeploymentCode */ | |
); | |
} else if (opcode === 209) { | |
const dataTarget = readUint16(code, ptr + 1); | |
const endOfSlice = dataTarget + 32; | |
if (container.header.dataSize < endOfSlice) { | |
validationError(EOFError.DataLoadNOutOfBounds); | |
} | |
} else if (opcode === 0 || opcode === 243) { | |
if (mode === 0) { | |
validationError(EOFError.ContainerTypeError); | |
} | |
} | |
const intermediates = stackDelta[opcode].intermediates; | |
if (intermediates > 0) { | |
for (let i = 1; i <= intermediates; i++) { | |
addIntermediate(ptr + i); | |
} | |
ptr += intermediates; | |
} | |
if (ptr >= code.length) { | |
validationError(EOFError.OpcodeIntermediatesOOB); | |
} | |
ptr++; | |
if (stackDelta[opcode].terminating === void 0) { | |
reachableOpcodes.add(ptr); | |
if (opcode !== 224) { | |
successorSet.add(ptr); | |
} | |
} | |
for (const successor of successorSet) { | |
if (successor < ptr) { | |
if (stackHeightMin[successor] !== minStackNext || stackHeightMax[successor] !== maxStackNext) { | |
validationError(EOFError.UnstableStack); | |
} | |
} | |
if (stackHeightMax[successor] === void 0) { | |
stackHeightMin[successor] = minStackNext; | |
stackHeightMax[successor] = maxStackNext; | |
} else { | |
stackHeightMin[successor] = Math.min(stackHeightMin[successor], minStackNext); | |
stackHeightMax[successor] = Math.max(stackHeightMax[successor], maxStackNext); | |
} | |
} | |
maxStackHeight = Math.max(maxStackNext, maxStackHeight); | |
} | |
if (!terminatingOpcodes.has(lastOpcode)) { | |
validationError(EOFError.InvalidTerminator); | |
} | |
if (container.body.typeSections[codeSection].maxStackHeight !== maxStackHeight) { | |
validationError(EOFError.MaxStackHeightViolation); | |
} | |
if (maxStackHeight > 1023) { | |
validationError(EOFError.MaxStackHeightLimit); | |
} | |
} | |
const sectionAccumulator = /* @__PURE__ */ new Set(); | |
sectionAccumulator.add(0); | |
const toCheck = [0]; | |
while (toCheck.length > 0) { | |
const checkArray = reachableSections[toCheck.pop()]; | |
for (const checkSection of checkArray) { | |
if (!sectionAccumulator.has(checkSection)) { | |
sectionAccumulator.add(checkSection); | |
toCheck.push(checkSection); | |
} | |
} | |
} | |
if (sectionAccumulator.size !== container.header.codeSizes.length) { | |
validationError(EOFError.UnreachableCodeSections); | |
} | |
if (containerTypeMap.size !== container.header.containerSizes.length) { | |
validationError(EOFError.UnreachableContainerSections); | |
} | |
return containerTypeMap; | |
} | |
var EOFContainerMode = /* @__PURE__ */ ((EOFContainerMode2) => { | |
EOFContainerMode2[EOFContainerMode2["Default"] = 0] = "Default"; | |
EOFContainerMode2[EOFContainerMode2["Initmode"] = 1] = "Initmode"; | |
EOFContainerMode2[EOFContainerMode2["TxInitmode"] = 2] = "TxInitmode"; | |
return EOFContainerMode2; | |
})(EOFContainerMode || {}); | |
class StreamReader { | |
// Current pointer to where the stream is being read | |
constructor(stream) { | |
this.data = stream; | |
this.ptr = 0; | |
} | |
/** | |
* Read `amount` bytes from the stream. Throws when trying to read out of bounds with an optional error string. | |
* This also updates the internal pointer | |
* @param amount Bytes to read | |
* @param errorStr Optional error string to throw when trying to read out-of-bounds | |
* @returns The byte array with length `amount` | |
*/ | |
readBytes(amount, errorStr) { | |
const end = this.ptr + amount; | |
if (end > this.data.length) { | |
validationError(EOFError.OutOfBounds, this.ptr, errorStr); | |
} | |
const ptr = this.ptr; | |
this.ptr += amount; | |
return this.data.slice(ptr, end); | |
} | |
/** | |
* Reads an Uint8. Also updates the pointer. | |
* @param errorStr Optional error string | |
* @returns The uint8 | |
*/ | |
readUint(errorStr) { | |
if (this.ptr >= this.data.length) { | |
validationError(EOFError.OutOfBounds, this.ptr, errorStr); | |
} | |
return this.data[this.ptr++]; | |
} | |
/** | |
* Verify that the current uint8 pointed to by the pointer is the expected uint8 | |
* Also updates the pointer | |
* @param expect The uint to expect | |
* @param errorStr Optional error string when the read uint is not the expected uint | |
*/ | |
verifyUint(expect, errorStr) { | |
if (this.readUint() !== expect) { | |
validationError(EOFError.VerifyUint, this.ptr - 1, errorStr); | |
} | |
} | |
/** | |
* Same as readUint, except this reads an uint16 | |
* @param errorStr | |
* @returns | |
*/ | |
readUint16(errorStr) { | |
const end = this.ptr + 2; | |
if (end > this.data.length) { | |
validationError(EOFError.OutOfBounds, this.ptr, errorStr); | |
} | |
const ptr = this.ptr; | |
this.ptr += 2; | |
return new DataView(this.data.buffer).getUint16(ptr); | |
} | |
/** | |
* Get the current pointer of the stream | |
* @returns The pointer | |
*/ | |
getPtr() { | |
return this.ptr; | |
} | |
// Get the remainder bytes of the current stream | |
readRemainder() { | |
return this.data.slice(this.ptr); | |
} | |
// Returns `true` if the stream is fully read, or false if there are dangling bytes | |
isAtEnd() { | |
return this.ptr === this.data.length; | |
} | |
} | |
class EOFHeader { | |
// Internal array to track at which byte of the container the code starts (per section) | |
/** | |
* Create an EOF header. Performs various validation checks inside the constructor | |
* @param input either a raw header or a complete container | |
*/ | |
constructor(input) { | |
if (input.length > MAX_HEADER_SIZE) { | |
throw new Error("err: container size more than maximum valid size"); | |
} | |
const stream = new StreamReader(input); | |
stream.verifyUint(FORMAT, EOFError.FORMAT); | |
stream.verifyUint(MAGIC, EOFError.MAGIC); | |
stream.verifyUint(VERSION, EOFError.VERSION); | |
if (input.length < 15) { | |
throw new Error("err: container size less than minimum valid size"); | |
} | |
stream.verifyUint(KIND_TYPE, EOFError.KIND_TYPE); | |
const typeSize = stream.readUint16(EOFError.TypeSize); | |
if (typeSize < TYPE_MIN) { | |
validationError(EOFError.InvalidTypeSize, typeSize); | |
} | |
if (typeSize % TYPE_DIVISOR !== 0) { | |
validationError(EOFError.InvalidTypeSize, typeSize); | |
} | |
if (typeSize > TYPE_MAX) { | |
throw new Error(`err: number of code sections must not exceed 1024 (got ${typeSize})`); | |
} | |
stream.verifyUint(KIND_CODE, EOFError.KIND_CODE); | |
const codeSize = stream.readUint16(EOFError.CodeSize); | |
if (codeSize < CODE_MIN) { | |
validationError(EOFError.MinCodeSections); | |
} | |
if (codeSize !== typeSize / TYPE_DIVISOR) { | |
validationError(EOFError.TypeSections, typeSize / TYPE_DIVISOR, codeSize); | |
} | |
const codeSizes = []; | |
for (let i = 0; i < codeSize; i++) { | |
const codeSectionSize = stream.readUint16(EOFError.CodeSection); | |
if (codeSectionSize < CODE_SIZE_MIN) { | |
validationError(EOFError.CodeSectionSize); | |
} | |
codeSizes.push(codeSectionSize); | |
} | |
let nextSection = stream.readUint(); | |
const containerSizes = []; | |
if (nextSection === KIND_CONTAINER) { | |
const containerSectionSize = stream.readUint16(EOFError.ContainerSize); | |
if (containerSectionSize < CONTAINER_MIN) { | |
validationError(EOFError.ContainerSectionSize); | |
} | |
if (containerSectionSize > CONTAINER_MAX) { | |
validationError(EOFError.ContainerSectionSize); | |
} | |
for (let i = 0; i < containerSectionSize; i++) { | |
const containerSize = stream.readUint16(EOFError.ContainerSection); | |
if (containerSize < CONTAINER_SIZE_MIN) { | |
validationError(EOFError.ContainerSectionMin); | |
} | |
containerSizes.push(containerSize); | |
} | |
nextSection = stream.readUint(); | |
} | |
if (nextSection !== KIND_DATA) { | |
validationError(EOFError.KIND_DATA); | |
} | |
this.dataSizePtr = stream.getPtr(); | |
const dataSize = stream.readUint16(EOFError.DataSize); | |
stream.verifyUint(TERMINATOR, EOFError.TERMINATOR); | |
this.typeSize = typeSize; | |
this.codeSizes = codeSizes; | |
this.containerSizes = containerSizes; | |
this.dataSize = dataSize; | |
this.buffer = input.slice(0, stream.getPtr()); | |
const relativeOffset = this.buffer.length + this.typeSize; | |
this.codeStartPos = [relativeOffset]; | |
} | |
sections() { | |
return [this.typeSize, this.codeSizes, this.containerSizes, this.dataSize]; | |
} | |
sectionSizes() { | |
return [1, this.codeSizes.length, this.containerSizes.length, 1]; | |
} | |
// Returns the code position in the container for the requested section | |
// Setting the Program Counter in the EVM to a number of this array would start executing the bytecode of the indexed section | |
getCodePosition(section) { | |
if (this.codeStartPos[section]) { | |
return this.codeStartPos[section]; | |
} | |
const start = this.codeStartPos.length; | |
let offset = this.codeStartPos[start - 1]; | |
for (let i = start; i <= section; i++) { | |
offset += this.codeSizes[i - 1]; | |
this.codeStartPos[i] = offset; | |
} | |
return offset; | |
} | |
} | |
class EOFBody { | |
// Only available in TxInitmode. The `txCallData` are the dangling bytes after parsing the container, | |
// and these are used for the CALLDATA in the EVM when trying to create a contract via a transaction, and the deployment code is an EOF container | |
constructor(buf, header, eofMode = 0, dataSectionAllowedSmaller = false) { | |
const stream = new StreamReader(buf); | |
const typeSections = []; | |
for (let i = 0; i < header.typeSize / 4; i++) { | |
const inputs = stream.readUint(EOFError.Inputs); | |
const outputs = stream.readUint(EOFError.Outputs); | |
const maxStackHeight = stream.readUint16(EOFError.MaxStackHeight); | |
if (i === 0) { | |
if (inputs !== 0) { | |
validationError(EOFError.Code0Inputs); | |
} | |
if (outputs !== 128) { | |
validationError(EOFError.Code0Outputs); | |
} | |
} | |
if (inputs > INPUTS_MAX) { | |
validationError(EOFError.MaxInputs, i, inputs); | |
} | |
if (outputs > OUTPUTS_MAX) { | |
validationError(EOFError.MaxOutputs, i, outputs); | |
} | |
if (maxStackHeight > MAX_STACK_HEIGHT) { | |
validationError(EOFError.MaxStackHeightLimit, i, maxStackHeight); | |
} | |
typeSections.push({ | |
inputs, | |
outputs, | |
maxStackHeight | |
}); | |
} | |
const codeStartPtr = stream.getPtr(); | |
const codes = []; | |
for (const [i, codeSize] of header.codeSizes.entries()) { | |
try { | |
const code = stream.readBytes(codeSize); | |
codes.push(code); | |
} catch { | |
validationError(EOFError.CodeSection, i); | |
} | |
} | |
const entireCodeSection = buf.slice(codeStartPtr, stream.getPtr()); | |
const containers = []; | |
for (const [i, containerSize] of header.containerSizes.entries()) { | |
try { | |
const container = stream.readBytes(containerSize); | |
containers.push(container); | |
} catch { | |
validationError(EOFError.ContainerSection, i); | |
} | |
} | |
let dataSection; | |
if (eofMode !== 1 && !dataSectionAllowedSmaller) { | |
dataSection = stream.readBytes(header.dataSize, EOFError.DataSection); | |
if (eofMode === 0) { | |
if (!stream.isAtEnd()) { | |
validationError(EOFError.DanglingBytes); | |
} | |
} else { | |
this.txCallData = stream.readRemainder(); | |
} | |
} else { | |
dataSection = stream.readRemainder(); | |
} | |
this.typeSections = typeSections; | |
this.codeSections = codes; | |
this.containerSections = containers; | |
this.entireCode = entireCodeSection; | |
this.dataSection = dataSection; | |
this.buffer = buf; | |
} | |
sections() { | |
return [this.typeSections, this.codeSections, this.dataSection]; | |
} | |
size() { | |
return { | |
typeSize: this.typeSections.length, | |
codeSize: this.codeSections.length, | |
dataSize: this.dataSection.length | |
}; | |
} | |
sectionSizes() { | |
return [ | |
this.typeSections.map(() => 4), | |
this.codeSections.map((b) => b.length), | |
this.dataSection.length | |
]; | |
} | |
} | |
class EOFContainer { | |
/** | |
* | |
* @param buf Entire container buffer | |
* @param eofMode Container mode to validate the container on | |
* @param dataSectionAllowedSmaller `true` if the data section is allowed to be smaller than the data section size in the header | |
*/ | |
constructor(buf, eofMode = 0, dataSectionAllowedSmaller = false) { | |
this.eofMode = eofMode; | |
this.header = new EOFHeader(buf); | |
this.body = new EOFBody( | |
buf.slice(this.header.buffer.length), | |
this.header, | |
eofMode, | |
dataSectionAllowedSmaller | |
); | |
this.buffer = buf; | |
} | |
} | |
function validateEOF(input, evm, containerMode = ContainerSectionType.RuntimeCode, eofMode = 0) { | |
const container = new EOFContainer( | |
input, | |
eofMode, | |
containerMode === ContainerSectionType.DeploymentCode | |
); | |
const containerMap = verifyCode(container, evm, containerMode); | |
for (let i = 0; i < container.body.containerSections.length; i++) { | |
const subContainer = container.body.containerSections[i]; | |
const mode = containerMap.get(i); | |
validateEOF(subContainer, evm, mode); | |
} | |
return container; | |
} | |
const Mainnet = { | |
name: "mainnet", | |
chainId: 1, | |
defaultHardfork: "cancun", | |
consensus: { | |
type: "pow", | |
algorithm: "ethash", | |
ethash: {} | |
}, | |
comment: "The Ethereum main chain", | |
url: "https://ethstats.net/", | |
genesis: { | |
gasLimit: 5e3, | |
difficulty: 17179869184, | |
nonce: "0x0000000000000042", | |
extraData: "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa" | |
}, | |
depositContractAddress: "0x00000000219ab540356cBB839Cbe05303d7705Fa", | |
hardforks: [ | |
{ | |
name: "chainstart", | |
block: 0, | |
forkHash: "0xfc64ec04" | |
}, | |
{ | |
name: "homestead", | |
block: 115e4, | |
forkHash: "0x97c2c34c" | |
}, | |
{ | |
name: "dao", | |
block: 192e4, | |
forkHash: "0x91d1f948" | |
}, | |
{ | |
name: "tangerineWhistle", | |
block: 2463e3, | |
forkHash: "0x7a64da13" | |
}, | |
{ | |
name: "spuriousDragon", | |
block: 2675e3, | |
forkHash: "0x3edd5b10" | |
}, | |
{ | |
name: "byzantium", | |
block: 437e4, | |
forkHash: "0xa00bc324" | |
}, | |
{ | |
name: "constantinople", | |
block: 728e4, | |
forkHash: "0x668db0af" | |
}, | |
{ | |
name: "petersburg", | |
block: 728e4, | |
forkHash: "0x668db0af" | |
}, | |
{ | |
name: "istanbul", | |
block: 9069e3, | |
forkHash: "0x879d6e30" | |
}, | |
{ | |
name: "muirGlacier", | |
block: 92e5, | |
forkHash: "0xe029e991" | |
}, | |
{ | |
name: "berlin", | |
block: 12244e3, | |
forkHash: "0x0eb440f6" | |
}, | |
{ | |
name: "london", | |
block: 12965e3, | |
forkHash: "0xb715077d" | |
}, | |
{ | |
name: "arrowGlacier", | |
block: 13773e3, | |
forkHash: "0x20c327fc" | |
}, | |
{ | |
name: "grayGlacier", | |
block: 1505e4, | |
forkHash: "0xf0afd0e3" | |
}, | |
{ | |
// The forkHash will remain same as mergeForkIdTransition is post merge | |
// terminal block: https://etherscan.io/block/15537393 | |
name: "paris", | |
block: 15537394, | |
forkHash: "0xf0afd0e3" | |
}, | |
{ | |
name: "mergeForkIdTransition", | |
block: null, | |
forkHash: null | |
}, | |
{ | |
name: "shanghai", | |
block: null, | |
timestamp: "1681338455", | |
forkHash: "0xdce96c2d" | |
}, | |
{ | |
name: "cancun", | |
block: null, | |
timestamp: "1710338135", | |
forkHash: "0x9f3d2254" | |
}, | |
{ | |
name: "prague", | |
block: null | |
} | |
], | |
bootstrapNodes: [ | |
{ | |
ip: "18.138.108.67", | |
port: 30303, | |
id: "d860a01f9722d78051619d1e2351aba3f43f943f6f00718d1b9baa4101932a1f5011f16bb2b1bb35db20d6fe28fa0bf09636d26a87d31de9ec6203eeedb1f666", | |
location: "ap-southeast-1-001", | |
comment: "bootnode-aws-ap-southeast-1-001" | |
}, | |
{ | |
ip: "3.209.45.79", | |
port: 30303, | |
id: "22a8232c3abc76a16ae9d6c3b164f98775fe226f0917b0ca871128a74a8e9630b458460865bab457221f1d448dd9791d24c4e5d88786180ac185df813a68d4de", | |
location: "us-east-1-001", | |
comment: "bootnode-aws-us-east-1-001" | |
}, | |
{ | |
ip: "65.108.70.101", | |
port: 30303, | |
id: "2b252ab6a1d0f971d9722cb839a42cb81db019ba44c08754628ab4a823487071b5695317c8ccd085219c3a03af063495b2f1da8d18218da2d6a82981b45e6ffc", | |
location: "eu-west-1-001", | |
comment: "bootnode-hetzner-hel" | |
}, | |
{ | |
ip: "157.90.35.166", | |
port: 30303, | |
id: "4aeb4ab6c14b23e2c4cfdce879c04b0748a20d8e9b59e25ded2a08143e265c6c25936e74cbc8e641e3312ca288673d91f2f93f8e277de3cfa444ecdaaf982052", | |
location: "eu-central-1-001", | |
comment: "bootnode-hetzner-fsn" | |
} | |
], | |
dnsNetworks: [ | |
"enrtree://AKA3AM6LPBYEUDMVNU3BSVQJ5AD45Y7YPOHJLEF6W26QOE4VTUDPE@all.mainnet.ethdisco.net" | |
] | |
}; | |
function number(n) { | |
if (!Number.isSafeInteger(n) || n < 0) | |
throw new Error(`positive integer expected, not ${n}`); | |
} | |
function bool(b) { | |
if (typeof b !== "boolean") | |
throw new Error(`boolean expected, not ${b}`); | |
} | |
function isBytes$1(a) { | |
return a instanceof Uint8Array || a != null && typeof a === "object" && a.constructor.name === "Uint8Array"; | |
} | |
function bytes(b, ...lengths) { | |
if (!isBytes$1(b)) | |
throw new Error("Uint8Array expected"); | |
if (lengths.length > 0 && !lengths.includes(b.length)) | |
throw new Error(`Uint8Array expected of length ${lengths}, not of length=${b.length}`); | |
} | |
function hash(h) { | |
if (typeof h !== "function" || typeof h.create !== "function") | |
throw new Error("Hash should be wrapped by utils.wrapConstructor"); | |
number(h.outputLen); | |
number(h.blockLen); | |
} | |
function exists(instance, checkFinished = true) { | |
if (instance.destroyed) | |
throw new Error("Hash instance has been destroyed"); | |
if (checkFinished && instance.finished) | |
throw new Error("Hash#digest() has already been called"); | |
} | |
function output(out, instance) { | |
bytes(out); | |
const min = instance.outputLen; | |
if (out.length < min) { | |
throw new Error(`digestInto() expects output buffer of length at least ${min}`); | |
} | |
} | |
const assert = { number, bool, bytes, hash, exists, output }; | |
const crypto = typeof globalThis === "object" && "crypto" in globalThis ? globalThis.crypto : void 0; | |
/*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | |
const u32 = (arr) => new Uint32Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 4)); | |
const createView = (arr) => new DataView(arr.buffer, arr.byteOffset, arr.byteLength); | |
const rotr = (word, shift) => word << 32 - shift | word >>> shift; | |
const rotl = (word, shift) => word << shift | word >>> 32 - shift >>> 0; | |
const isLE = new Uint8Array(new Uint32Array([287454020]).buffer)[0] === 68; | |
const byteSwap = (word) => word << 24 & 4278190080 | word << 8 & 16711680 | word >>> 8 & 65280 | word >>> 24 & 255; | |
function byteSwap32(arr) { | |
for (let i = 0; i < arr.length; i++) { | |
arr[i] = byteSwap(arr[i]); | |
} | |
} | |
const hexes$1 = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, "0")); | |
function bytesToHex$3(bytes$1) { | |
bytes(bytes$1); | |
let hex = ""; | |
for (let i = 0; i < bytes$1.length; i++) { | |
hex += hexes$1[bytes$1[i]]; | |
} | |
return hex; | |
} | |
const asciis$1 = { _0: 48, _9: 57, _A: 65, _F: 70, _a: 97, _f: 102 }; | |
function asciiToBase16$1(char) { | |
if (char >= asciis$1._0 && char <= asciis$1._9) | |
return char - asciis$1._0; | |
if (char >= asciis$1._A && char <= asciis$1._F) | |
return char - (asciis$1._A - 10); | |
if (char >= asciis$1._a && char <= asciis$1._f) | |
return char - (asciis$1._a - 10); | |
return; | |
} | |
function hexToBytes$4(hex) { | |
if (typeof hex !== "string") | |
throw new Error("hex string expected, got " + typeof hex); | |
const hl = hex.length; | |
const al = hl / 2; | |
if (hl % 2) | |
throw new Error("padded hex string expected, got unpadded hex of length " + hl); | |
const array = new Uint8Array(al); | |
for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) { | |
const n1 = asciiToBase16$1(hex.charCodeAt(hi)); | |
const n2 = asciiToBase16$1(hex.charCodeAt(hi + 1)); | |
if (n1 === void 0 || n2 === void 0) { | |
const char = hex[hi] + hex[hi + 1]; | |
throw new Error('hex string expected, got non-hex character "' + char + '" at index ' + hi); | |
} | |
array[ai] = n1 * 16 + n2; | |
} | |
return array; | |
} | |
function utf8ToBytes$2(str) { | |
if (typeof str !== "string") | |
throw new Error(`utf8ToBytes expected string, got ${typeof str}`); | |
return new Uint8Array(new TextEncoder().encode(str)); | |
} | |
function toBytes$2(data) { | |
if (typeof data === "string") | |
data = utf8ToBytes$2(data); | |
bytes(data); | |
return data; | |
} | |
function concatBytes$3(...arrays) { | |
let sum = 0; | |
for (let i = 0; i < arrays.length; i++) { | |
const a = arrays[i]; | |
bytes(a); | |
sum += a.length; | |
} | |
const res = new Uint8Array(sum); | |
for (let i = 0, pad = 0; i < arrays.length; i++) { | |
const a = arrays[i]; | |
res.set(a, pad); | |
pad += a.length; | |
} | |
return res; | |
} | |
class Hash { | |
// Safe version that clones internal state | |
clone() { | |
return this._cloneInto(); | |
} | |
} | |
function wrapConstructor(hashCons) { | |
const hashC = (msg) => hashCons().update(toBytes$2(msg)).digest(); | |
const tmp = hashCons(); | |
hashC.outputLen = tmp.outputLen; | |
hashC.blockLen = tmp.blockLen; | |
hashC.create = () => hashCons(); | |
return hashC; | |
} | |
function randomBytes(bytesLength = 32) { | |
if (crypto && typeof crypto.getRandomValues === "function") { | |
return crypto.getRandomValues(new Uint8Array(bytesLength)); | |
} | |
if (crypto && typeof crypto.randomBytes === "function") { | |
return crypto.randomBytes(bytesLength); | |
} | |
throw new Error("crypto.getRandomValues must be defined"); | |
} | |
function setBigUint64(view, byteOffset, value, isLE2) { | |
if (typeof view.setBigUint64 === "function") | |
return view.setBigUint64(byteOffset, value, isLE2); | |
const _32n2 = BigInt(32); | |
const _u32_max = BigInt(4294967295); | |
const wh = Number(value >> _32n2 & _u32_max); | |
const wl = Number(value & _u32_max); | |
const h = isLE2 ? 4 : 0; | |
const l = isLE2 ? 0 : 4; | |
view.setUint32(byteOffset + h, wh, isLE2); | |
view.setUint32(byteOffset + l, wl, isLE2); | |
} | |
const Chi = (a, b, c) => a & b ^ ~a & c; | |
const Maj = (a, b, c) => a & b ^ a & c ^ b & c; | |
class HashMD extends Hash { | |
constructor(blockLen, outputLen, padOffset, isLE2) { | |
super(); | |
this.blockLen = blockLen; | |
this.outputLen = outputLen; | |
this.padOffset = padOffset; | |
this.isLE = isLE2; | |
this.finished = false; | |
this.length = 0; | |
this.pos = 0; | |
this.destroyed = false; | |
this.buffer = new Uint8Array(blockLen); | |
this.view = createView(this.buffer); | |
} | |
update(data) { | |
exists(this); | |
const { view, buffer, blockLen } = this; | |
data = toBytes$2(data); | |
const len = data.length; | |
for (let pos = 0; pos < len; ) { | |
const take = Math.min(blockLen - this.pos, len - pos); | |
if (take === blockLen) { | |
const dataView = createView(data); | |
for (; blockLen <= len - pos; pos += blockLen) | |
this.process(dataView, pos); | |
continue; | |
} | |
buffer.set(data.subarray(pos, pos + take), this.pos); | |
this.pos += take; | |
pos += take; | |
if (this.pos === blockLen) { | |
this.process(view, 0); | |
this.pos = 0; | |
} | |
} | |
this.length += data.length; | |
this.roundClean(); | |
return this; | |
} | |
digestInto(out) { | |
exists(this); | |
output(out, this); | |
this.finished = true; | |
const { buffer, view, blockLen, isLE: isLE2 } = this; | |
let { pos } = this; | |
buffer[pos++] = 128; | |
this.buffer.subarray(pos).fill(0); | |
if (this.padOffset > blockLen - pos) { | |
this.process(view, 0); | |
pos = 0; | |
} | |
for (let i = pos; i < blockLen; i++) | |
buffer[i] = 0; | |
setBigUint64(view, blockLen - 8, BigInt(this.length * 8), isLE2); | |
this.process(view, 0); | |
const oview = createView(out); | |
const len = this.outputLen; | |
if (len % 4) | |
throw new Error("_sha2: outputLen should be aligned to 32bit"); | |
const outLen = len / 4; | |
const state = this.get(); | |
if (outLen > state.length) | |
throw new Error("_sha2: outputLen bigger than state"); | |
for (let i = 0; i < outLen; i++) | |
oview.setUint32(4 * i, state[i], isLE2); | |
} | |
digest() { | |
const { buffer, outputLen } = this; | |
this.digestInto(buffer); | |
const res = buffer.slice(0, outputLen); | |
this.destroy(); | |
return res; | |
} | |
_cloneInto(to) { | |
to || (to = new this.constructor()); | |
to.set(...this.get()); | |
const { blockLen, buffer, length, finished, destroyed, pos } = this; | |
to.length = length; | |
to.pos = pos; | |
to.finished = finished; | |
to.destroyed = destroyed; | |
if (length % blockLen) | |
to.buffer.set(buffer); | |
return to; | |
} | |
} | |
const SHA256_K = /* @__PURE__ */ new Uint32Array([ | |
1116352408, | |
1899447441, | |
3049323471, | |
3921009573, | |
961987163, | |
1508970993, | |
2453635748, | |
2870763221, | |
3624381080, | |
310598401, | |
607225278, | |
1426881987, | |
1925078388, | |
2162078206, | |
2614888103, | |
3248222580, | |
3835390401, | |
4022224774, | |
264347078, | |
604807628, | |
770255983, | |
1249150122, | |
1555081692, | |
1996064986, | |
2554220882, | |
2821834349, | |
2952996808, | |
3210313671, | |
3336571891, | |
3584528711, | |
113926993, | |
338241895, | |
666307205, | |
773529912, | |
1294757372, | |
1396182291, | |
1695183700, | |
1986661051, | |
2177026350, | |
2456956037, | |
2730485921, | |
2820302411, | |
3259730800, | |
3345764771, | |
3516065817, | |
3600352804, | |
4094571909, | |
275423344, | |
430227734, | |
506948616, | |
659060556, | |
883997877, | |
958139571, | |
1322822218, | |
1537002063, | |
1747873779, | |
1955562222, | |
2024104815, | |
2227730452, | |
2361852424, | |
2428436474, | |
2756734187, | |
3204031479, | |
3329325298 | |
]); | |
const SHA256_IV = /* @__PURE__ */ new Uint32Array([ | |
1779033703, | |
3144134277, | |
1013904242, | |
2773480762, | |
1359893119, | |
2600822924, | |
528734635, | |
1541459225 | |
]); | |
const SHA256_W = /* @__PURE__ */ new Uint32Array(64); | |
class SHA256 extends HashMD { | |
constructor() { | |
super(64, 32, 8, false); | |
this.A = SHA256_IV[0] | 0; | |
this.B = SHA256_IV[1] | 0; | |
this.C = SHA256_IV[2] | 0; | |
this.D = SHA256_IV[3] | 0; | |
this.E = SHA256_IV[4] | 0; | |
this.F = SHA256_IV[5] | 0; | |
this.G = SHA256_IV[6] | 0; | |
this.H = SHA256_IV[7] | 0; | |
} | |
get() { | |
const { A, B, C, D, E, F: F2, G, H } = this; | |
return [A, B, C, D, E, F2, G, H]; | |
} | |
// prettier-ignore | |
set(A, B, C, D, E, F2, G, H) { | |
this.A = A | 0; | |
this.B = B | 0; | |
this.C = C | 0; | |
this.D = D | 0; | |
this.E = E | 0; | |
this.F = F2 | 0; | |
this.G = G | 0; | |
this.H = H | 0; | |
} | |
process(view, offset) { | |
for (let i = 0; i < 16; i++, offset += 4) | |
SHA256_W[i] = view.getUint32(offset, false); | |
for (let i = 16; i < 64; i++) { | |
const W15 = SHA256_W[i - 15]; | |
const W2 = SHA256_W[i - 2]; | |
const s0 = rotr(W15, 7) ^ rotr(W15, 18) ^ W15 >>> 3; | |
const s1 = rotr(W2, 17) ^ rotr(W2, 19) ^ W2 >>> 10; | |
SHA256_W[i] = s1 + SHA256_W[i - 7] + s0 + SHA256_W[i - 16] | 0; | |
} | |
let { A, B, C, D, E, F: F2, G, H } = this; | |
for (let i = 0; i < 64; i++) { | |
const sigma1 = rotr(E, 6) ^ rotr(E, 11) ^ rotr(E, 25); | |
const T1 = H + sigma1 + Chi(E, F2, G) + SHA256_K[i] + SHA256_W[i] | 0; | |
const sigma0 = rotr(A, 2) ^ rotr(A, 13) ^ rotr(A, 22); | |
const T2 = sigma0 + Maj(A, B, C) | 0; | |
H = G; | |
G = F2; | |
F2 = E; | |
E = D + T1 | 0; | |
D = C; | |
C = B; | |
B = A; | |
A = T1 + T2 | 0; | |
} | |
A = A + this.A | 0; | |
B = B + this.B | 0; | |
C = C + this.C | 0; | |
D = D + this.D | 0; | |
E = E + this.E | 0; | |
F2 = F2 + this.F | 0; | |
G = G + this.G | 0; | |
H = H + this.H | 0; | |
this.set(A, B, C, D, E, F2, G, H); | |
} | |
roundClean() { | |
SHA256_W.fill(0); | |
} | |
destroy() { | |
this.set(0, 0, 0, 0, 0, 0, 0, 0); | |
this.buffer.fill(0); | |
} | |
} | |
const sha256$1 = /* @__PURE__ */ wrapConstructor(() => new SHA256()); | |
class HMAC extends Hash { | |
constructor(hash$1, _key) { | |
super(); | |
this.finished = false; | |
this.destroyed = false; | |
hash(hash$1); | |
const key = toBytes$2(_key); | |
this.iHash = hash$1.create(); | |
if (typeof this.iHash.update !== "function") | |
throw new Error("Expected instance of class which extends utils.Hash"); | |
this.blockLen = this.iHash.blockLen; | |
this.outputLen = this.iHash.outputLen; | |
const blockLen = this.blockLen; | |
const pad = new Uint8Array(blockLen); | |
pad.set(key.length > blockLen ? hash$1.create().update(key).digest() : key); | |
for (let i = 0; i < pad.length; i++) | |
pad[i] ^= 54; | |
this.iHash.update(pad); | |
this.oHash = hash$1.create(); | |
for (let i = 0; i < pad.length; i++) | |
pad[i] ^= 54 ^ 92; | |
this.oHash.update(pad); | |
pad.fill(0); | |
} | |
update(buf) { | |
exists(this); | |
this.iHash.update(buf); | |
return this; | |
} | |
digestInto(out) { | |
exists(this); | |
bytes(out, this.outputLen); | |
this.finished = true; | |
this.iHash.digestInto(out); | |
this.oHash.update(out); | |
this.oHash.digestInto(out); | |
this.destroy(); | |
} | |
digest() { | |
const out = new Uint8Array(this.oHash.outputLen); | |
this.digestInto(out); | |
return out; | |
} | |
_cloneInto(to) { | |
to || (to = Object.create(Object.getPrototypeOf(this), {})); | |
const { oHash, iHash, finished, destroyed, blockLen, outputLen } = this; | |
to = to; | |
to.finished = finished; | |
to.destroyed = destroyed; | |
to.blockLen = blockLen; | |
to.outputLen = outputLen; | |
to.oHash = oHash._cloneInto(to.oHash); | |
to.iHash = iHash._cloneInto(to.iHash); | |
return to; | |
} | |
destroy() { | |
this.destroyed = true; | |
this.oHash.destroy(); | |
this.iHash.destroy(); | |
} | |
} | |
const hmac = (hash2, key, message) => new HMAC(hash2, key).update(message).digest(); | |
hmac.create = (hash2, key) => new HMAC(hash2, key); | |
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | |
const _0n$7 = /* @__PURE__ */ BigInt(0); | |
const _1n$9 = /* @__PURE__ */ BigInt(1); | |
const _2n$8 = /* @__PURE__ */ BigInt(2); | |
function isBytes(a) { | |
return a instanceof Uint8Array || a != null && typeof a === "object" && a.constructor.name === "Uint8Array"; | |
} | |
function abytes(item) { | |
if (!isBytes(item)) | |
throw new Error("Uint8Array expected"); | |
} | |
function abool(title, value) { | |
if (typeof value !== "boolean") | |
throw new Error(`${title} must be valid boolean, got "${value}".`); | |
} | |
const hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, "0")); | |
function bytesToHex$2(bytes2) { | |
abytes(bytes2); | |
let hex = ""; | |
for (let i = 0; i < bytes2.length; i++) { | |
hex += hexes[bytes2[i]]; | |
} | |
return hex; | |
} | |
function numberToHexUnpadded(num) { | |
const hex = num.toString(16); | |
return hex.length & 1 ? `0${hex}` : hex; | |
} | |
function hexToNumber(hex) { | |
if (typeof hex !== "string") | |
throw new Error("hex string expected, got " + typeof hex); | |
return BigInt(hex === "" ? "0" : `0x${hex}`); | |
} | |
const asciis = { _0: 48, _9: 57, _A: 65, _F: 70, _a: 97, _f: 102 }; | |
function asciiToBase16(char) { | |
if (char >= asciis._0 && char <= asciis._9) | |
return char - asciis._0; | |
if (char >= asciis._A && char <= asciis._F) | |
return char - (asciis._A - 10); | |
if (char >= asciis._a && char <= asciis._f) | |
return char - (asciis._a - 10); | |
return; | |
} | |
function hexToBytes$3(hex) { | |
if (typeof hex !== "string") | |
throw new Error("hex string expected, got " + typeof hex); | |
const hl = hex.length; | |
const al = hl / 2; | |
if (hl % 2) | |
throw new Error("padded hex string expected, got unpadded hex of length " + hl); | |
const array = new Uint8Array(al); | |
for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) { | |
const n1 = asciiToBase16(hex.charCodeAt(hi)); | |
const n2 = asciiToBase16(hex.charCodeAt(hi + 1)); | |
if (n1 === void 0 || n2 === void 0) { | |
const char = hex[hi] + hex[hi + 1]; | |
throw new Error('hex string expected, got non-hex character "' + char + '" at index ' + hi); | |
} | |
array[ai] = n1 * 16 + n2; | |
} | |
return array; | |
} | |
function bytesToNumberBE(bytes2) { | |
return hexToNumber(bytesToHex$2(bytes2)); | |
} | |
function bytesToNumberLE(bytes2) { | |
abytes(bytes2); | |
return hexToNumber(bytesToHex$2(Uint8Array.from(bytes2).reverse())); | |
} | |
function numberToBytesBE(n, len) { | |
return hexToBytes$3(n.toString(16).padStart(len * 2, "0")); | |
} | |
function numberToBytesLE(n, len) { | |
return numberToBytesBE(n, len).reverse(); | |
} | |
function numberToVarBytesBE(n) { | |
return hexToBytes$3(numberToHexUnpadded(n)); | |
} | |
function ensureBytes(title, hex, expectedLength) { | |
let res; | |
if (typeof hex === "string") { | |
try { | |
res = hexToBytes$3(hex); | |
} catch (e) { | |
throw new Error(`${title} must be valid hex string, got "${hex}". Cause: ${e}`); | |
} | |
} else if (isBytes(hex)) { | |
res = Uint8Array.from(hex); | |
} else { | |
throw new Error(`${title} must be hex string or Uint8Array`); | |
} | |
const len = res.length; | |
if (typeof expectedLength === "number" && len !== expectedLength) | |
throw new Error(`${title} expected ${expectedLength} bytes, got ${len}`); | |
return res; | |
} | |
function concatBytes$2(...arrays) { | |
let sum = 0; | |
for (let i = 0; i < arrays.length; i++) { | |
const a = arrays[i]; | |
abytes(a); | |
sum += a.length; | |
} | |
const res = new Uint8Array(sum); | |
for (let i = 0, pad = 0; i < arrays.length; i++) { | |
const a = arrays[i]; | |
res.set(a, pad); | |
pad += a.length; | |
} | |
return res; | |
} | |
function equalBytes(a, b) { | |
if (a.length !== b.length) | |
return false; | |
let diff = 0; | |
for (let i = 0; i < a.length; i++) | |
diff |= a[i] ^ b[i]; | |
return diff === 0; | |
} | |
function utf8ToBytes$1(str) { | |
if (typeof str !== "string") | |
throw new Error(`utf8ToBytes expected string, got ${typeof str}`); | |
return new Uint8Array(new TextEncoder().encode(str)); | |
} | |
const isPosBig = (n) => typeof n === "bigint" && _0n$7 <= n; | |
function inRange(n, min, max) { | |
return isPosBig(n) && isPosBig(min) && isPosBig(max) && min <= n && n < max; | |
} | |
function aInRange(title, n, min, max) { | |
if (!inRange(n, min, max)) | |
throw new Error(`expected valid ${title}: ${min} <= n < ${max}, got ${typeof n} ${n}`); | |
} | |
function bitLen(n) { | |
let len; | |
for (len = 0; n > _0n$7; n >>= _1n$9, len += 1) | |
; | |
return len; | |
} | |
function bitGet(n, pos) { | |
return n >> BigInt(pos) & _1n$9; | |
} | |
function bitSet(n, pos, value) { | |
return n | (value ? _1n$9 : _0n$7) << BigInt(pos); | |
} | |
const bitMask = (n) => (_2n$8 << BigInt(n - 1)) - _1n$9; | |
const u8n = (data) => new Uint8Array(data); | |
const u8fr = (arr) => Uint8Array.from(arr); | |
function createHmacDrbg(hashLen, qByteLen, hmacFn) { | |
if (typeof hashLen !== "number" || hashLen < 2) | |
throw new Error("hashLen must be a number"); | |
if (typeof qByteLen !== "number" || qByteLen < 2) | |
throw new Error("qByteLen must be a number"); | |
if (typeof hmacFn !== "function") | |
throw new Error("hmacFn must be a function"); | |
let v = u8n(hashLen); | |
let k = u8n(hashLen); | |
let i = 0; | |
const reset = () => { | |
v.fill(1); | |
k.fill(0); | |
i = 0; | |
}; | |
const h = (...b) => hmacFn(k, v, ...b); | |
const reseed = (seed = u8n()) => { | |
k = h(u8fr([0]), seed); | |
v = h(); | |
if (seed.length === 0) | |
return; | |
k = h(u8fr([1]), seed); | |
v = h(); | |
}; | |
const gen2 = () => { | |
if (i++ >= 1e3) | |
throw new Error("drbg: tried 1000 values"); | |
let len = 0; | |
const out = []; | |
while (len < qByteLen) { | |
v = h(); | |
const sl = v.slice(); | |
out.push(sl); | |
len += v.length; | |
} | |
return concatBytes$2(...out); | |
}; | |
const genUntil = (seed, pred) => { | |
reset(); | |
reseed(seed); | |
let res = void 0; | |
while (!(res = pred(gen2()))) | |
reseed(); | |
reset(); | |
return res; | |
}; | |
return genUntil; | |
} | |
const validatorFns = { | |
bigint: (val) => typeof val === "bigint", | |
function: (val) => typeof val === "function", | |
boolean: (val) => typeof val === "boolean", | |
string: (val) => typeof val === "string", | |
stringOrUint8Array: (val) => typeof val === "string" || isBytes(val), | |
isSafeInteger: (val) => Number.isSafeInteger(val), | |
array: (val) => Array.isArray(val), | |
field: (val, object) => object.Fp.isValid(val), | |
hash: (val) => typeof val === "function" && Number.isSafeInteger(val.outputLen) | |
}; | |
function validateObject(object, validators, optValidators = {}) { | |
const checkField = (fieldName, type, isOptional) => { | |
const checkVal = validatorFns[type]; | |
if (typeof checkVal !== "function") | |
throw new Error(`Invalid validator "${type}", expected function`); | |
const val = object[fieldName]; | |
if (isOptional && val === void 0) | |
return; | |
if (!checkVal(val, object)) { | |
throw new Error(`Invalid param ${String(fieldName)}=${val} (${typeof val}), expected ${type}`); | |
} | |
}; | |
for (const [fieldName, type] of Object.entries(validators)) | |
checkField(fieldName, type, false); | |
for (const [fieldName, type] of Object.entries(optValidators)) | |
checkField(fieldName, type, true); | |
return object; | |
} | |
const notImplemented = () => { | |
throw new Error("not implemented"); | |
}; | |
function memoized(fn) { | |
const map = /* @__PURE__ */ new WeakMap(); | |
return (arg, ...args) => { | |
const val = map.get(arg); | |
if (val !== void 0) | |
return val; | |
const computed = fn(arg, ...args); | |
map.set(arg, computed); | |
return computed; | |
}; | |
} | |
const ut = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ | |
__proto__: null, | |
aInRange, | |
abool, | |
abytes, | |
bitGet, | |
bitLen, | |
bitMask, | |
bitSet, | |
bytesToHex: bytesToHex$2, | |
bytesToNumberBE, | |
bytesToNumberLE, | |
concatBytes: concatBytes$2, | |
createHmacDrbg, | |
ensureBytes, | |
equalBytes, | |
hexToBytes: hexToBytes$3, | |
hexToNumber, | |
inRange, | |
isBytes, | |
memoized, | |
notImplemented, | |
numberToBytesBE, | |
numberToBytesLE, | |
numberToHexUnpadded, | |
numberToVarBytesBE, | |
utf8ToBytes: utf8ToBytes$1, | |
validateObject | |
}, Symbol.toStringTag, { value: "Module" })); | |
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | |
const _0n$6 = BigInt(0), _1n$8 = BigInt(1), _2n$7 = BigInt(2), _3n$5 = BigInt(3); | |
const _4n$2 = BigInt(4), _5n = BigInt(5), _8n = BigInt(8); | |
BigInt(9); | |
BigInt(16); | |
function mod$1(a, b) { | |
const result = a % b; | |
return result >= _0n$6 ? result : b + result; | |
} | |
function pow(num, power, modulo) { | |
if (modulo <= _0n$6 || power < _0n$6) | |
throw new Error("Expected power/modulo > 0"); | |
if (modulo === _1n$8) | |
return _0n$6; | |
let res = _1n$8; | |
while (power > _0n$6) { | |
if (power & _1n$8) | |
res = res * num % modulo; | |
num = num * num % modulo; | |
power >>= _1n$8; | |
} | |
return res; | |
} | |
function pow2(x, power, modulo) { | |
let res = x; | |
while (power-- > _0n$6) { | |
res *= res; | |
res %= modulo; | |
} | |
return res; | |
} | |
function invert(number2, modulo) { | |
if (number2 === _0n$6 || modulo <= _0n$6) { | |
throw new Error(`invert: expected positive integers, got n=${number2} mod=${modulo}`); | |
} | |
let a = mod$1(number2, modulo); | |
let b = modulo; | |
let x = _0n$6, u = _1n$8; | |
while (a !== _0n$6) { | |
const q = b / a; | |
const r = b % a; | |
const m = x - u * q; | |
b = a, a = r, x = u, u = m; | |
} | |
const gcd = b; | |
if (gcd !== _1n$8) | |
throw new Error("invert: does not exist"); | |
return mod$1(x, modulo); | |
} | |
function tonelliShanks(P) { | |
const legendreC = (P - _1n$8) / _2n$7; | |
let Q, S, Z; | |
for (Q = P - _1n$8, S = 0; Q % _2n$7 === _0n$6; Q /= _2n$7, S++) | |
; | |
for (Z = _2n$7; Z < P && pow(Z, legendreC, P) !== P - _1n$8; Z++) | |
; | |
if (S === 1) { | |
const p1div4 = (P + _1n$8) / _4n$2; | |
return function tonelliFast(Fp3, n) { | |
const root = Fp3.pow(n, p1div4); | |
if (!Fp3.eql(Fp3.sqr(root), n)) | |
throw new Error("Cannot find square root"); | |
return root; | |
}; | |
} | |
const Q1div2 = (Q + _1n$8) / _2n$7; | |
return function tonelliSlow(Fp3, n) { | |
if (Fp3.pow(n, legendreC) === Fp3.neg(Fp3.ONE)) | |
throw new Error("Cannot find square root"); | |
let r = S; | |
let g = Fp3.pow(Fp3.mul(Fp3.ONE, Z), Q); | |
let x = Fp3.pow(n, Q1div2); | |
let b = Fp3.pow(n, Q); | |
while (!Fp3.eql(b, Fp3.ONE)) { | |
if (Fp3.eql(b, Fp3.ZERO)) | |
return Fp3.ZERO; | |
let m = 1; | |
for (let t2 = Fp3.sqr(b); m < r; m++) { | |
if (Fp3.eql(t2, Fp3.ONE)) | |
break; | |
t2 = Fp3.sqr(t2); | |
} | |
const ge = Fp3.pow(g, _1n$8 << BigInt(r - m - 1)); | |
g = Fp3.sqr(ge); | |
x = Fp3.mul(x, ge); | |
b = Fp3.mul(b, g); | |
r = m; | |
} | |
return x; | |
}; | |
} | |
function FpSqrt(P) { | |
if (P % _4n$2 === _3n$5) { | |
const p1div4 = (P + _1n$8) / _4n$2; | |
return function sqrt3mod4(Fp3, n) { | |
const root = Fp3.pow(n, p1div4); | |
if (!Fp3.eql(Fp3.sqr(root), n)) | |
throw new Error("Cannot find square root"); | |
return root; | |
}; | |
} | |
if (P % _8n === _5n) { | |
const c1 = (P - _5n) / _8n; | |
return function sqrt5mod8(Fp3, n) { | |
const n2 = Fp3.mul(n, _2n$7); | |
const v = Fp3.pow(n2, c1); | |
const nv = Fp3.mul(n, v); | |
const i = Fp3.mul(Fp3.mul(nv, _2n$7), v); | |
const root = Fp3.mul(nv, Fp3.sub(i, Fp3.ONE)); | |
if (!Fp3.eql(Fp3.sqr(root), n)) | |
throw new Error("Cannot find square root"); | |
return root; | |
}; | |
} | |
return tonelliShanks(P); | |
} | |
const FIELD_FIELDS = [ | |
"create", | |
"isValid", | |
"is0", | |
"neg", | |
"inv", | |
"sqrt", | |
"sqr", | |
"eql", | |
"add", | |
"sub", | |
"mul", | |
"pow", | |
"div", | |
"addN", | |
"subN", | |
"mulN", | |
"sqrN" | |
]; | |
function validateField(field) { | |
const initial = { | |
ORDER: "bigint", | |
MASK: "bigint", | |
BYTES: "isSafeInteger", | |
BITS: "isSafeInteger" | |
}; | |
const opts = FIELD_FIELDS.reduce((map, val) => { | |
map[val] = "function"; | |
return map; | |
}, initial); | |
return validateObject(field, opts); | |
} | |
function FpPow(f2, num, power) { | |
if (power < _0n$6) | |
throw new Error("Expected power > 0"); | |
if (power === _0n$6) | |
return f2.ONE; | |
if (power === _1n$8) | |
return num; | |
let p = f2.ONE; | |
let d = num; | |
while (power > _0n$6) { | |
if (power & _1n$8) | |
p = f2.mul(p, d); | |
d = f2.sqr(d); | |
power >>= _1n$8; | |
} | |
return p; | |
} | |
function FpInvertBatch(f2, nums) { | |
const tmp = new Array(nums.length); | |
const lastMultiplied = nums.reduce((acc, num, i) => { | |
if (f2.is0(num)) | |
return acc; | |
tmp[i] = acc; | |
return f2.mul(acc, num); | |
}, f2.ONE); | |
const inverted = f2.inv(lastMultiplied); | |
nums.reduceRight((acc, num, i) => { | |
if (f2.is0(num)) | |
return acc; | |
tmp[i] = f2.mul(acc, tmp[i]); | |
return f2.mul(acc, num); | |
}, inverted); | |
return tmp; | |
} | |
function FpLegendre(order) { | |
const legendreConst = (order - _1n$8) / _2n$7; | |
return (f2, x) => f2.pow(x, legendreConst); | |
} | |
function nLength(n, nBitLength) { | |
const _nBitLength = nBitLength !== void 0 ? nBitLength : n.toString(2).length; | |
const nByteLength = Math.ceil(_nBitLength / 8); | |
return { nBitLength: _nBitLength, nByteLength }; | |
} | |
function Field(ORDER, bitLen2, isLE2 = false, redef = {}) { | |
if (ORDER <= _0n$6) | |
throw new Error(`Expected Field ORDER > 0, got ${ORDER}`); | |
const { nBitLength: BITS, nByteLength: BYTES } = nLength(ORDER, bitLen2); | |
if (BYTES > 2048) | |
throw new Error("Field lengths over 2048 bytes are not supported"); | |
const sqrtP = FpSqrt(ORDER); | |
const f2 = Object.freeze({ | |
ORDER, | |
BITS, | |
BYTES, | |
MASK: bitMask(BITS), | |
ZERO: _0n$6, | |
ONE: _1n$8, | |
create: (num) => mod$1(num, ORDER), | |
isValid: (num) => { | |
if (typeof num !== "bigint") | |
throw new Error(`Invalid field element: expected bigint, got ${typeof num}`); | |
return _0n$6 <= num && num < ORDER; | |
}, | |
is0: (num) => num === _0n$6, | |
isOdd: (num) => (num & _1n$8) === _1n$8, | |
neg: (num) => mod$1(-num, ORDER), | |
eql: (lhs, rhs) => lhs === rhs, | |
sqr: (num) => mod$1(num * num, ORDER), | |
add: (lhs, rhs) => mod$1(lhs + rhs, ORDER), | |
sub: (lhs, rhs) => mod$1(lhs - rhs, ORDER), | |
mul: (lhs, rhs) => mod$1(lhs * rhs, ORDER), | |
pow: (num, power) => FpPow(f2, num, power), | |
div: (lhs, rhs) => mod$1(lhs * invert(rhs, ORDER), ORDER), | |
// Same as above, but doesn't normalize | |
sqrN: (num) => num * num, | |
addN: (lhs, rhs) => lhs + rhs, | |
subN: (lhs, rhs) => lhs - rhs, | |
mulN: (lhs, rhs) => lhs * rhs, | |
inv: (num) => invert(num, ORDER), | |
sqrt: redef.sqrt || ((n) => sqrtP(f2, n)), | |
invertBatch: (lst) => FpInvertBatch(f2, lst), | |
// TODO: do we really need constant cmov? | |
// We don't have const-time bigints anyway, so probably will be not very useful | |
cmov: (a, b, c) => c ? b : a, | |
toBytes: (num) => isLE2 ? numberToBytesLE(num, BYTES) : numberToBytesBE(num, BYTES), | |
fromBytes: (bytes2) => { | |
if (bytes2.length !== BYTES) | |
throw new Error(`Fp.fromBytes: expected ${BYTES}, got ${bytes2.length}`); | |
return isLE2 ? bytesToNumberLE(bytes2) : bytesToNumberBE(bytes2); | |
} | |
}); | |
return Object.freeze(f2); | |
} | |
function getFieldBytesLength(fieldOrder) { | |
if (typeof fieldOrder !== "bigint") | |
throw new Error("field order must be bigint"); | |
const bitLength = fieldOrder.toString(2).length; | |
return Math.ceil(bitLength / 8); | |
} | |
function getMinHashLength(fieldOrder) { | |
const length = getFieldBytesLength(fieldOrder); | |
return length + Math.ceil(length / 2); | |
} | |
function mapHashToField(key, fieldOrder, isLE2 = false) { | |
const len = key.length; | |
const fieldLen = getFieldBytesLength(fieldOrder); | |
const minLen = getMinHashLength(fieldOrder); | |
if (len < 16 || len < minLen || len > 1024) | |
throw new Error(`expected ${minLen}-1024 bytes of input, got ${len}`); | |
const num = isLE2 ? bytesToNumberBE(key) : bytesToNumberLE(key); | |
const reduced = mod$1(num, fieldOrder - _1n$8) + _1n$8; | |
return isLE2 ? numberToBytesLE(reduced, fieldLen) : numberToBytesBE(reduced, fieldLen); | |
} | |
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | |
const _0n$5 = BigInt(0); | |
const _1n$7 = BigInt(1); | |
const pointPrecomputes = /* @__PURE__ */ new WeakMap(); | |
const pointWindowSizes = /* @__PURE__ */ new WeakMap(); | |
function wNAF(c, bits) { | |
const constTimeNegate = (condition, item) => { | |
const neg = item.negate(); | |
return condition ? neg : item; | |
}; | |
const validateW = (W) => { | |
if (!Number.isSafeInteger(W) || W <= 0 || W > bits) | |
throw new Error(`Wrong window size=${W}, should be [1..${bits}]`); | |
}; | |
const opts = (W) => { | |
validateW(W); | |
const windows = Math.ceil(bits / W) + 1; | |
const windowSize = 2 ** (W - 1); | |
return { windows, windowSize }; | |
}; | |
return { | |
constTimeNegate, | |
// non-const time multiplication ladder | |
unsafeLadder(elm, n) { | |
let p = c.ZERO; | |
let d = elm; | |
while (n > _0n$5) { | |
if (n & _1n$7) | |
p = p.add(d); | |
d = d.double(); | |
n >>= _1n$7; | |
} | |
return p; | |
}, | |
/** | |
* Creates a wNAF precomputation window. Used for caching. | |
* Default window size is set by `utils.precompute()` and is equal to 8. | |
* Number of precomputed points depends on the curve size: | |
* 2^(𝑊−1) * (Math.ceil(𝑛 / 𝑊) + 1), where: | |
* - 𝑊 is the window size | |
* - 𝑛 is the bitlength of the curve order. | |
* For a 256-bit curve and window size 8, the number of precomputed points is 128 * 33 = 4224. | |
* @returns precomputed point tables flattened to a single array | |
*/ | |
precomputeWindow(elm, W) { | |
const { windows, windowSize } = opts(W); | |
const points = []; | |
let p = elm; | |
let base = p; | |
for (let window2 = 0; window2 < windows; window2++) { | |
base = p; | |
points.push(base); | |
for (let i = 1; i < windowSize; i++) { | |
base = base.add(p); | |
points.push(base); | |
} | |
p = base.double(); | |
} | |
return points; | |
}, | |
/** | |
* Implements ec multiplication using precomputed tables and w-ary non-adjacent form. | |
* @param W window size | |
* @param precomputes precomputed tables | |
* @param n scalar (we don't check here, but should be less than curve order) | |
* @returns real and fake (for const-time) points | |
*/ | |
wNAF(W, precomputes, n) { | |
const { windows, windowSize } = opts(W); | |
let p = c.ZERO; | |
let f2 = c.BASE; | |
const mask = BigInt(2 ** W - 1); | |
const maxNumber = 2 ** W; | |
const shiftBy = BigInt(W); | |
for (let window2 = 0; window2 < windows; window2++) { | |
const offset = window2 * windowSize; | |
let wbits = Number(n & mask); | |
n >>= shiftBy; | |
if (wbits > windowSize) { | |
wbits -= maxNumber; | |
n += _1n$7; | |
} | |
const offset1 = offset; | |
const offset2 = offset + Math.abs(wbits) - 1; | |
const cond1 = window2 % 2 !== 0; | |
const cond2 = wbits < 0; | |
if (wbits === 0) { | |
f2 = f2.add(constTimeNegate(cond1, precomputes[offset1])); | |
} else { | |
p = p.add(constTimeNegate(cond2, precomputes[offset2])); | |
} | |
} | |
return { p, f: f2 }; | |
}, | |
wNAFCached(P, n, transform) { | |
const W = pointWindowSizes.get(P) || 1; | |
let comp = pointPrecomputes.get(P); | |
if (!comp) { | |
comp = this.precomputeWindow(P, W); | |
if (W !== 1) | |
pointPrecomputes.set(P, transform(comp)); | |
} | |
return this.wNAF(W, comp, n); | |
}, | |
// We calculate precomputes for elliptic curve point multiplication | |
// using windowed method. This specifies window size and | |
// stores precomputed values. Usually only base point would be precomputed. | |
setWindowSize(P, W) { | |
validateW(W); | |
pointWindowSizes.set(P, W); | |
pointPrecomputes.delete(P); | |
} | |
}; | |
} | |
function pippenger(c, field, points, scalars) { | |
if (!Array.isArray(points) || !Array.isArray(scalars) || scalars.length !== points.length) | |
throw new Error("arrays of points and scalars must have equal length"); | |
scalars.forEach((s, i) => { | |
if (!field.isValid(s)) | |
throw new Error(`wrong scalar at index ${i}`); | |
}); | |
points.forEach((p, i) => { | |
if (!(p instanceof c)) | |
throw new Error(`wrong point at index ${i}`); | |
}); | |
const wbits = bitLen(BigInt(points.length)); | |
const windowSize = wbits > 12 ? wbits - 3 : wbits > 4 ? wbits - 2 : wbits ? 2 : 1; | |
const MASK = (1 << windowSize) - 1; | |
const buckets = new Array(MASK + 1).fill(c.ZERO); | |
const lastBits = Math.floor((field.BITS - 1) / windowSize) * windowSize; | |
let sum = c.ZERO; | |
for (let i = lastBits; i >= 0; i -= windowSize) { | |
buckets.fill(c.ZERO); | |
for (let j = 0; j < scalars.length; j++) { | |
const scalar = scalars[j]; | |
const wbits2 = Number(scalar >> BigInt(i) & BigInt(MASK)); | |
buckets[wbits2] = buckets[wbits2].add(points[j]); | |
} | |
let resI = c.ZERO; | |
for (let j = buckets.length - 1, sumI = c.ZERO; j > 0; j--) { | |
sumI = sumI.add(buckets[j]); | |
resI = resI.add(sumI); | |
} | |
sum = sum.add(resI); | |
if (i !== 0) | |
for (let j = 0; j < windowSize; j++) | |
sum = sum.double(); | |
} | |
return sum; | |
} | |
function validateBasic(curve) { | |
validateField(curve.Fp); | |
validateObject(curve, { | |
n: "bigint", | |
h: "bigint", | |
Gx: "field", | |
Gy: "field" | |
}, { | |
nBitLength: "isSafeInteger", | |
nByteLength: "isSafeInteger" | |
}); | |
return Object.freeze({ | |
...nLength(curve.n, curve.nBitLength), | |
...curve, | |
...{ p: curve.Fp.ORDER } | |
}); | |
} | |
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | |
function validateSigVerOpts(opts) { | |
if (opts.lowS !== void 0) | |
abool("lowS", opts.lowS); | |
if (opts.prehash !== void 0) | |
abool("prehash", opts.prehash); | |
} | |
function validatePointOpts(curve) { | |
const opts = validateBasic(curve); | |
validateObject(opts, { | |
a: "field", | |
b: "field" | |
}, { | |
allowedPrivateKeyLengths: "array", | |
wrapPrivateKey: "boolean", | |
isTorsionFree: "function", | |
clearCofactor: "function", | |
allowInfinityPoint: "boolean", | |
fromBytes: "function", | |
toBytes: "function" | |
}); | |
const { endo, Fp: Fp3, a } = opts; | |
if (endo) { | |
if (!Fp3.eql(a, Fp3.ZERO)) { | |
throw new Error("Endomorphism can only be defined for Koblitz curves that have a=0"); | |
} | |
if (typeof endo !== "object" || typeof endo.beta !== "bigint" || typeof endo.splitScalar !== "function") { | |
throw new Error("Expected endomorphism with beta: bigint and splitScalar: function"); | |
} | |
} | |
return Object.freeze({ ...opts }); | |
} | |
const { bytesToNumberBE: b2n, hexToBytes: h2b } = ut; | |
const DER = { | |
// asn.1 DER encoding utils | |
Err: class DERErr extends Error { | |
constructor(m = "") { | |
super(m); | |
} | |
}, | |
// Basic building block is TLV (Tag-Length-Value) | |
_tlv: { | |
encode: (tag, data) => { | |
const { Err: E } = DER; | |
if (tag < 0 || tag > 256) | |
throw new E("tlv.encode: wrong tag"); | |
if (data.length & 1) | |
throw new E("tlv.encode: unpadded data"); | |
const dataLen = data.length / 2; | |
const len = numberToHexUnpadded(dataLen); | |
if (len.length / 2 & 128) | |
throw new E("tlv.encode: long form length too big"); | |
const lenLen = dataLen > 127 ? numberToHexUnpadded(len.length / 2 | 128) : ""; | |
return `${numberToHexUnpadded(tag)}${lenLen}${len}${data}`; | |
}, | |
// v - value, l - left bytes (unparsed) | |
decode(tag, data) { | |
const { Err: E } = DER; | |
let pos = 0; | |
if (tag < 0 || tag > 256) | |
throw new E("tlv.encode: wrong tag"); | |
if (data.length < 2 || data[pos++] !== tag) | |
throw new E("tlv.decode: wrong tlv"); | |
const first = data[pos++]; | |
const isLong = !!(first & 128); | |
let length = 0; | |
if (!isLong) | |
length = first; | |
else { | |
const lenLen = first & 127; | |
if (!lenLen) | |
throw new E("tlv.decode(long): indefinite length not supported"); | |
if (lenLen > 4) | |
throw new E("tlv.decode(long): byte length is too big"); | |
const lengthBytes = data.subarray(pos, pos + lenLen); | |
if (lengthBytes.length !== lenLen) | |
throw new E("tlv.decode: length bytes not complete"); | |
if (lengthBytes[0] === 0) | |
throw new E("tlv.decode(long): zero leftmost byte"); | |
for (const b of lengthBytes) | |
length = length << 8 | b; | |
pos += lenLen; | |
if (length < 128) | |
throw new E("tlv.decode(long): not minimal encoding"); | |
} | |
const v = data.subarray(pos, pos + length); | |
if (v.length !== length) | |
throw new E("tlv.decode: wrong value length"); | |
return { v, l: data.subarray(pos + length) }; | |
} | |
}, | |
// https://crypto.stackexchange.com/a/57734 Leftmost bit of first byte is 'negative' flag, | |
// since we always use positive integers here. It must always be empty: | |
// - add zero byte if exists | |
// - if next byte doesn't have a flag, leading zero is not allowed (minimal encoding) | |
_int: { | |
encode(num) { | |
const { Err: E } = DER; | |
if (num < _0n$4) | |
throw new E("integer: negative integers are not allowed"); | |
let hex = numberToHexUnpadded(num); | |
if (Number.parseInt(hex[0], 16) & 8) | |
hex = "00" + hex; | |
if (hex.length & 1) | |
throw new E("unexpected assertion"); | |
return hex; | |
}, | |
decode(data) { | |
const { Err: E } = DER; | |
if (data[0] & 128) | |
throw new E("Invalid signature integer: negative"); | |
if (data[0] === 0 && !(data[1] & 128)) | |
throw new E("Invalid signature integer: unnecessary leading zero"); | |
return b2n(data); | |
} | |
}, | |
toSig(hex) { | |
const { Err: E, _int: int, _tlv: tlv } = DER; | |
const data = typeof hex === "string" ? h2b(hex) : hex; | |
abytes(data); | |
const { v: seqBytes, l: seqLeftBytes } = tlv.decode(48, data); | |
if (seqLeftBytes.length) | |
throw new E("Invalid signature: left bytes after parsing"); | |
const { v: rBytes, l: rLeftBytes } = tlv.decode(2, seqBytes); | |
const { v: sBytes, l: sLeftBytes } = tlv.decode(2, rLeftBytes); | |
if (sLeftBytes.length) | |
throw new E("Invalid signature: left bytes after parsing"); | |
return { r: int.decode(rBytes), s: int.decode(sBytes) }; | |
}, | |
hexFromSig(sig) { | |
const { _tlv: tlv, _int: int } = DER; | |
const seq = `${tlv.encode(2, int.encode(sig.r))}${tlv.encode(2, int.encode(sig.s))}`; | |
return tlv.encode(48, seq); | |
} | |
}; | |
const _0n$4 = BigInt(0), _1n$6 = BigInt(1), _2n$6 = BigInt(2), _3n$4 = BigInt(3), _4n$1 = BigInt(4); | |
function weierstrassPoints(opts) { | |
const CURVE = validatePointOpts(opts); | |
const { Fp: Fp3 } = CURVE; | |
const Fn = Field(CURVE.n, CURVE.nBitLength); | |
const toBytes2 = CURVE.toBytes || ((_c, point, _isCompressed) => { | |
const a = point.toAffine(); | |
return concatBytes$2(Uint8Array.from([4]), Fp3.toBytes(a.x), Fp3.toBytes(a.y)); | |
}); | |
const fromBytes = CURVE.fromBytes || ((bytes2) => { | |
const tail = bytes2.subarray(1); | |
const x = Fp3.fromBytes(tail.subarray(0, Fp3.BYTES)); | |
const y = Fp3.fromBytes(tail.subarray(Fp3.BYTES, 2 * Fp3.BYTES)); | |
return { x, y }; | |
}); | |
function weierstrassEquation(x) { | |
const { a, b } = CURVE; | |
const x2 = Fp3.sqr(x); | |
const x3 = Fp3.mul(x2, x); | |
return Fp3.add(Fp3.add(x3, Fp3.mul(x, a)), b); | |
} | |
if (!Fp3.eql(Fp3.sqr(CURVE.Gy), weierstrassEquation(CURVE.Gx))) | |
throw new Error("bad generator point: equation left != right"); | |
function isWithinCurveOrder(num) { | |
return inRange(num, _1n$6, CURVE.n); | |
} | |
function normPrivateKeyToScalar(key) { | |
const { allowedPrivateKeyLengths: lengths, nByteLength, wrapPrivateKey, n: N2 } = CURVE; | |
if (lengths && typeof key !== "bigint") { | |
if (isBytes(key)) | |
key = bytesToHex$2(key); | |
if (typeof key !== "string" || !lengths.includes(key.length)) | |
throw new Error("Invalid key"); | |
key = key.padStart(nByteLength * 2, "0"); | |
} | |
let num; | |
try { | |
num = typeof key === "bigint" ? key : bytesToNumberBE(ensureBytes("private key", key, nByteLength)); | |
} catch (error) { | |
throw new Error(`private key must be ${nByteLength} bytes, hex or bigint, not ${typeof key}`); | |
} | |
if (wrapPrivateKey) | |
num = mod$1(num, N2); | |
aInRange("private key", num, _1n$6, N2); | |
return num; | |
} | |
function assertPrjPoint(other) { | |
if (!(other instanceof Point)) | |
throw new Error("ProjectivePoint expected"); | |
} | |
const toAffineMemo = memoized((p, iz) => { | |
const { px: x, py: y, pz: z } = p; | |
if (Fp3.eql(z, Fp3.ONE)) | |
return { x, y }; | |
const is0 = p.is0(); | |
if (iz == null) | |
iz = is0 ? Fp3.ONE : Fp3.inv(z); | |
const ax = Fp3.mul(x, iz); | |
const ay = Fp3.mul(y, iz); | |
const zz = Fp3.mul(z, iz); | |
if (is0) | |
return { x: Fp3.ZERO, y: Fp3.ZERO }; | |
if (!Fp3.eql(zz, Fp3.ONE)) | |
throw new Error("invZ was invalid"); | |
return { x: ax, y: ay }; | |
}); | |
const assertValidMemo = memoized((p) => { | |
if (p.is0()) { | |
if (CURVE.allowInfinityPoint && !Fp3.is0(p.py)) | |
return; | |
throw new Error("bad point: ZERO"); | |
} | |
const { x, y } = p.toAffine(); | |
if (!Fp3.isValid(x) || !Fp3.isValid(y)) | |
throw new Error("bad point: x or y not FE"); | |
const left = Fp3.sqr(y); | |
const right = weierstrassEquation(x); | |
if (!Fp3.eql(left, right)) | |
throw new Error("bad point: equation left != right"); | |
if (!p.isTorsionFree()) | |
throw new Error("bad point: not in prime-order subgroup"); | |
return true; | |
}); | |
class Point { | |
constructor(px, py, pz) { | |
this.px = px; | |
this.py = py; | |
this.pz = pz; | |
if (px == null || !Fp3.isValid(px)) | |
throw new Error("x required"); | |
if (py == null || !Fp3.isValid(py)) | |
throw new Error("y required"); | |
if (pz == null || !Fp3.isValid(pz)) | |
throw new Error("z required"); | |
Object.freeze(this); | |
} | |
// Does not validate if the point is on-curve. | |
// Use fromHex instead, or call assertValidity() later. | |
static fromAffine(p) { | |
const { x, y } = p || {}; | |
if (!p || !Fp3.isValid(x) || !Fp3.isValid(y)) | |
throw new Error("invalid affine point"); | |
if (p instanceof Point) | |
throw new Error("projective point not allowed"); | |
const is0 = (i) => Fp3.eql(i, Fp3.ZERO); | |
if (is0(x) && is0(y)) | |
return Point.ZERO; | |
return new Point(x, y, Fp3.ONE); | |
} | |
get x() { | |
return this.toAffine().x; | |
} | |
get y() { | |
return this.toAffine().y; | |
} | |
/** | |
* Takes a bunch of Projective Points but executes only one | |
* inversion on all of them. Inversion is very slow operation, | |
* so this improves performance massively. | |
* Optimization: converts a list of projective points to a list of identical points with Z=1. | |
*/ | |
static normalizeZ(points) { | |
const toInv = Fp3.invertBatch(points.map((p) => p.pz)); | |
return points.map((p, i) => p.toAffine(toInv[i])).map(Point.fromAffine); | |
} | |
/** | |
* Converts hash string or Uint8Array to Point. | |
* @param hex short/long ECDSA hex | |
*/ | |
static fromHex(hex) { | |
const P = Point.fromAffine(fromBytes(ensureBytes("pointHex", hex))); | |
P.assertValidity(); | |
return P; | |
} | |
// Multiplies generator point by privateKey. | |
static fromPrivateKey(privateKey) { | |
return Point.BASE.multiply(normPrivateKeyToScalar(privateKey)); | |
} | |
// Multiscalar Multiplication | |
static msm(points, scalars) { | |
return pippenger(Point, Fn, points, scalars); | |
} | |
// "Private method", don't use it directly | |
_setWindowSize(windowSize) { | |
wnaf.setWindowSize(this, windowSize); | |
} | |
// A point on curve is valid if it conforms to equation. | |
assertValidity() { | |
assertValidMemo(this); | |
} | |
hasEvenY() { | |
const { y } = this.toAffine(); | |
if (Fp3.isOdd) | |
return !Fp3.isOdd(y); | |
throw new Error("Field doesn't support isOdd"); | |
} | |
/** | |
* Compare one point to another. | |
*/ | |
equals(other) { | |
assertPrjPoint(other); | |
const { px: X1, py: Y1, pz: Z1 } = this; | |
const { px: X2, py: Y2, pz: Z2 } = other; | |
const U1 = Fp3.eql(Fp3.mul(X1, Z2), Fp3.mul(X2, Z1)); | |
const U2 = Fp3.eql(Fp3.mul(Y1, Z2), Fp3.mul(Y2, Z1)); | |
return U1 && U2; | |
} | |
/** | |
* Flips point to one corresponding to (x, -y) in Affine coordinates. | |
*/ | |
negate() { | |
return new Point(this.px, Fp3.neg(this.py), this.pz); | |
} | |
// Renes-Costello-Batina exception-free doubling formula. | |
// There is 30% faster Jacobian formula, but it is not complete. | |
// https://eprint.iacr.org/2015/1060, algorithm 3 | |
// Cost: 8M + 3S + 3*a + 2*b3 + 15add. | |
double() { | |
const { a, b } = CURVE; | |
const b3 = Fp3.mul(b, _3n$4); | |
const { px: X1, py: Y1, pz: Z1 } = this; | |
let X3 = Fp3.ZERO, Y3 = Fp3.ZERO, Z3 = Fp3.ZERO; | |
let t0 = Fp3.mul(X1, X1); | |
let t1 = Fp3.mul(Y1, Y1); | |
let t2 = Fp3.mul(Z1, Z1); | |
let t3 = Fp3.mul(X1, Y1); | |
t3 = Fp3.add(t3, t3); | |
Z3 = Fp3.mul(X1, Z1); | |
Z3 = Fp3.add(Z3, Z3); | |
X3 = Fp3.mul(a, Z3); | |
Y3 = Fp3.mul(b3, t2); | |
Y3 = Fp3.add(X3, Y3); | |
X3 = Fp3.sub(t1, Y3); | |
Y3 = Fp3.add(t1, Y3); | |
Y3 = Fp3.mul(X3, Y3); | |
X3 = Fp3.mul(t3, X3); | |
Z3 = Fp3.mul(b3, Z3); | |
t2 = Fp3.mul(a, t2); | |
t3 = Fp3.sub(t0, t2); | |
t3 = Fp3.mul(a, t3); | |
t3 = Fp3.add(t3, Z3); | |
Z3 = Fp3.add(t0, t0); | |
t0 = Fp3.add(Z3, t0); | |
t0 = Fp3.add(t0, t2); | |
t0 = Fp3.mul(t0, t3); | |
Y3 = Fp3.add(Y3, t0); | |
t2 = Fp3.mul(Y1, Z1); | |
t2 = Fp3.add(t2, t2); | |
t0 = Fp3.mul(t2, t3); | |
X3 = Fp3.sub(X3, t0); | |
Z3 = Fp3.mul(t2, t1); | |
Z3 = Fp3.add(Z3, Z3); | |
Z3 = Fp3.add(Z3, Z3); | |
return new Point(X3, Y3, Z3); | |
} | |
// Renes-Costello-Batina exception-free addition formula. | |
// There is 30% faster Jacobian formula, but it is not complete. | |
// https://eprint.iacr.org/2015/1060, algorithm 1 | |
// Cost: 12M + 0S + 3*a + 3*b3 + 23add. | |
add(other) { | |
assertPrjPoint(other); | |
const { px: X1, py: Y1, pz: Z1 } = this; | |
const { px: X2, py: Y2, pz: Z2 } = other; | |
let X3 = Fp3.ZERO, Y3 = Fp3.ZERO, Z3 = Fp3.ZERO; | |
const a = CURVE.a; | |
const b3 = Fp3.mul(CURVE.b, _3n$4); | |
let t0 = Fp3.mul(X1, X2); | |
let t1 = Fp3.mul(Y1, Y2); | |
let t2 = Fp3.mul(Z1, Z2); | |
let t3 = Fp3.add(X1, Y1); | |
let t4 = Fp3.add(X2, Y2); | |
t3 = Fp3.mul(t3, t4); | |
t4 = Fp3.add(t0, t1); | |
t3 = Fp3.sub(t3, t4); | |
t4 = Fp3.add(X1, Z1); | |
let t5 = Fp3.add(X2, Z2); | |
t4 = Fp3.mul(t4, t5); | |
t5 = Fp3.add(t0, t2); | |
t4 = Fp3.sub(t4, t5); | |
t5 = Fp3.add(Y1, Z1); | |
X3 = Fp3.add(Y2, Z2); | |
t5 = Fp3.mul(t5, X3); | |
X3 = Fp3.add(t1, t2); | |
t5 = Fp3.sub(t5, X3); | |
Z3 = Fp3.mul(a, t4); | |
X3 = Fp3.mul(b3, t2); | |
Z3 = Fp3.add(X3, Z3); | |
X3 = Fp3.sub(t1, Z3); | |
Z3 = Fp3.add(t1, Z3); | |
Y3 = Fp3.mul(X3, Z3); | |
t1 = Fp3.add(t0, t0); | |
t1 = Fp3.add(t1, t0); | |
t2 = Fp3.mul(a, t2); | |
t4 = Fp3.mul(b3, t4); | |
t1 = Fp3.add(t1, t2); | |
t2 = Fp3.sub(t0, t2); | |
t2 = Fp3.mul(a, t2); | |
t4 = Fp3.add(t4, t2); | |
t0 = Fp3.mul(t1, t4); | |
Y3 = Fp3.add(Y3, t0); | |
t0 = Fp3.mul(t5, t4); | |
X3 = Fp3.mul(t3, X3); | |
X3 = Fp3.sub(X3, t0); | |
t0 = Fp3.mul(t3, t1); | |
Z3 = Fp3.mul(t5, Z3); | |
Z3 = Fp3.add(Z3, t0); | |
return new Point(X3, Y3, Z3); | |
} | |
subtract(other) { | |
return this.add(other.negate()); | |
} | |
is0() { | |
return this.equals(Point.ZERO); | |
} | |
wNAF(n) { | |
return wnaf.wNAFCached(this, n, Point.normalizeZ); | |
} | |
/** | |
* Non-constant-time multiplication. Uses double-and-add algorithm. | |
* It's faster, but should only be used when you don't care about | |
* an exposed private key e.g. sig verification, which works over *public* keys. | |
*/ | |
multiplyUnsafe(sc) { | |
aInRange("scalar", sc, _0n$4, CURVE.n); | |
const I = Point.ZERO; | |
if (sc === _0n$4) | |
return I; | |
if (sc === _1n$6) | |
return this; | |
const { endo } = CURVE; | |
if (!endo) | |
return wnaf.unsafeLadder(this, sc); | |
let { k1neg, k1, k2neg, k2 } = endo.splitScalar(sc); | |
let k1p = I; | |
let k2p = I; | |
let d = this; | |
while (k1 > _0n$4 || k2 > _0n$4) { | |
if (k1 & _1n$6) | |
k1p = k1p.add(d); | |
if (k2 & _1n$6) | |
k2p = k2p.add(d); | |
d = d.double(); | |
k1 >>= _1n$6; | |
k2 >>= _1n$6; | |
} | |
if (k1neg) | |
k1p = k1p.negate(); | |
if (k2neg) | |
k2p = k2p.negate(); | |
k2p = new Point(Fp3.mul(k2p.px, endo.beta), k2p.py, k2p.pz); | |
return k1p.add(k2p); | |
} | |
/** | |
* Constant time multiplication. | |
* Uses wNAF method. Windowed method may be 10% faster, | |
* but takes 2x longer to generate and consumes 2x memory. | |
* Uses precomputes when available. | |
* Uses endomorphism for Koblitz curves. | |
* @param scalar by which the point would be multiplied | |
* @returns New point | |
*/ | |
multiply(scalar) { | |
const { endo, n: N2 } = CURVE; | |
aInRange("scalar", scalar, _1n$6, N2); | |
let point, fake; | |
if (endo) { | |
const { k1neg, k1, k2neg, k2 } = endo.splitScalar(scalar); | |
let { p: k1p, f: f1p } = this.wNAF(k1); | |
let { p: k2p, f: f2p } = this.wNAF(k2); | |
k1p = wnaf.constTimeNegate(k1neg, k1p); | |
k2p = wnaf.constTimeNegate(k2neg, k2p); | |
k2p = new Point(Fp3.mul(k2p.px, endo.beta), k2p.py, k2p.pz); | |
point = k1p.add(k2p); | |
fake = f1p.add(f2p); | |
} else { | |
const { p, f: f2 } = this.wNAF(scalar); | |
point = p; | |
fake = f2; | |
} | |
return Point.normalizeZ([point, fake])[0]; | |
} | |
/** | |
* Efficiently calculate `aP + bQ`. Unsafe, can expose private key, if used incorrectly. | |
* Not using Strauss-Shamir trick: precomputation tables are faster. | |
* The trick could be useful if both P and Q are not G (not in our case). | |
* @returns non-zero affine point | |
*/ | |
multiplyAndAddUnsafe(Q, a, b) { | |
const G = Point.BASE; | |
const mul = (P, a2) => a2 === _0n$4 || a2 === _1n$6 || !P.equals(G) ? P.multiplyUnsafe(a2) : P.multiply(a2); | |
const sum = mul(this, a).add(mul(Q, b)); | |
return sum.is0() ? void 0 : sum; | |
} | |
// Converts Projective point to affine (x, y) coordinates. | |
// Can accept precomputed Z^-1 - for example, from invertBatch. | |
// (x, y, z) ∋ (x=x/z, y=y/z) | |
toAffine(iz) { | |
return toAffineMemo(this, iz); | |
} | |
isTorsionFree() { | |
const { h: cofactor, isTorsionFree } = CURVE; | |
if (cofactor === _1n$6) | |
return true; | |
if (isTorsionFree) | |
return isTorsionFree(Point, this); | |
throw new Error("isTorsionFree() has not been declared for the elliptic curve"); | |
} | |
clearCofactor() { | |
const { h: cofactor, clearCofactor } = CURVE; | |
if (cofactor === _1n$6) | |
return this; | |
if (clearCofactor) | |
return clearCofactor(Point, this); | |
return this.multiplyUnsafe(CURVE.h); | |
} | |
toRawBytes(isCompressed = true) { | |
abool("isCompressed", isCompressed); | |
this.assertValidity(); | |
return toBytes2(Point, this, isCompressed); | |
} | |
toHex(isCompressed = true) { | |
abool("isCompressed", isCompressed); | |
return bytesToHex$2(this.toRawBytes(isCompressed)); | |
} | |
} | |
Point.BASE = new Point(CURVE.Gx, CURVE.Gy, Fp3.ONE); | |
Point.ZERO = new Point(Fp3.ZERO, Fp3.ONE, Fp3.ZERO); | |
const _bits = CURVE.nBitLength; | |
const wnaf = wNAF(Point, CURVE.endo ? Math.ceil(_bits / 2) : _bits); | |
return { | |
CURVE, | |
ProjectivePoint: Point, | |
normPrivateKeyToScalar, | |
weierstrassEquation, | |
isWithinCurveOrder | |
}; | |
} | |
function validateOpts(curve) { | |
const opts = validateBasic(curve); | |
validateObject(opts, { | |
hash: "hash", | |
hmac: "function", | |
randomBytes: "function" | |
}, { | |
bits2int: "function", | |
bits2int_modN: "function", | |
lowS: "boolean" | |
}); | |
return Object.freeze({ lowS: true, ...opts }); | |
} | |
function weierstrass(curveDef) { | |
const CURVE = validateOpts(curveDef); | |
const { Fp: Fp3, n: CURVE_ORDER } = CURVE; | |
const compressedLen = Fp3.BYTES + 1; | |
const uncompressedLen = 2 * Fp3.BYTES + 1; | |
function modN(a) { | |
return mod$1(a, CURVE_ORDER); | |
} | |
function invN(a) { | |
return invert(a, CURVE_ORDER); | |
} | |
const { ProjectivePoint: Point, normPrivateKeyToScalar, weierstrassEquation, isWithinCurveOrder } = weierstrassPoints({ | |
...CURVE, | |
toBytes(_c, point, isCompressed) { | |
const a = point.toAffine(); | |
const x = Fp3.toBytes(a.x); | |
const cat = concatBytes$2; | |
abool("isCompressed", isCompressed); | |
if (isCompressed) { | |
return cat(Uint8Array.from([point.hasEvenY() ? 2 : 3]), x); | |
} else { | |
return cat(Uint8Array.from([4]), x, Fp3.toBytes(a.y)); | |
} | |
}, | |
fromBytes(bytes2) { | |
const len = bytes2.length; | |
const head = bytes2[0]; | |
const tail = bytes2.subarray(1); | |
if (len === compressedLen && (head === 2 || head === 3)) { | |
const x = bytesToNumberBE(tail); | |
if (!inRange(x, _1n$6, Fp3.ORDER)) | |
throw new Error("Point is not on curve"); | |
const y2 = weierstrassEquation(x); | |
let y; | |
try { | |
y = Fp3.sqrt(y2); | |
} catch (sqrtError) { | |
const suffix = sqrtError instanceof Error ? ": " + sqrtError.message : ""; | |
throw new Error("Point is not on curve" + suffix); | |
} | |
const isYOdd = (y & _1n$6) === _1n$6; | |
const isHeadOdd = (head & 1) === 1; | |
if (isHeadOdd !== isYOdd) | |
y = Fp3.neg(y); | |
return { x, y }; | |
} else if (len === uncompressedLen && head === 4) { | |
const x = Fp3.fromBytes(tail.subarray(0, Fp3.BYTES)); | |
const y = Fp3.fromBytes(tail.subarray(Fp3.BYTES, 2 * Fp3.BYTES)); | |
return { x, y }; | |
} else { | |
throw new Error(`Point of length ${len} was invalid. Expected ${compressedLen} compressed bytes or ${uncompressedLen} uncompressed bytes`); | |
} | |
} | |
}); | |
const numToNByteStr = (num) => bytesToHex$2(numberToBytesBE(num, CURVE.nByteLength)); | |
function isBiggerThanHalfOrder(number2) { | |
const HALF = CURVE_ORDER >> _1n$6; | |
return number2 > HALF; | |
} | |
function normalizeS(s) { | |
return isBiggerThanHalfOrder(s) ? modN(-s) : s; | |
} | |
const slcNum = (b, from, to) => bytesToNumberBE(b.slice(from, to)); | |
class Signature { | |
constructor(r, s, recovery) { | |
this.r = r; | |
this.s = s; | |
this.recovery = recovery; | |
this.assertValidity(); | |
} | |
// pair (bytes of r, bytes of s) | |
static fromCompact(hex) { | |
const l = CURVE.nByteLength; | |
hex = ensureBytes("compactSignature", hex, l * 2); | |
return new Signature(slcNum(hex, 0, l), slcNum(hex, l, 2 * l)); | |
} | |
// DER encoded ECDSA signature | |
// https://bitcoin.stackexchange.com/questions/57644/what-are-the-parts-of-a-bitcoin-transaction-input-script | |
static fromDER(hex) { | |
const { r, s } = DER.toSig(ensureBytes("DER", hex)); | |
return new Signature(r, s); | |
} | |
assertValidity() { | |
aInRange("r", this.r, _1n$6, CURVE_ORDER); | |
aInRange("s", this.s, _1n$6, CURVE_ORDER); | |
} | |
addRecoveryBit(recovery) { | |
return new Signature(this.r, this.s, recovery); | |
} | |
recoverPublicKey(msgHash) { | |
const { r, s, recovery: rec } = this; | |
const h = bits2int_modN(ensureBytes("msgHash", msgHash)); | |
if (rec == null || ![0, 1, 2, 3].includes(rec)) | |
throw new Error("recovery id invalid"); | |
const radj = rec === 2 || rec === 3 ? r + CURVE.n : r; | |
if (radj >= Fp3.ORDER) | |
throw new Error("recovery id 2 or 3 invalid"); | |
const prefix = (rec & 1) === 0 ? "02" : "03"; | |
const R2 = Point.fromHex(prefix + numToNByteStr(radj)); | |
const ir = invN(radj); | |
const u1 = modN(-h * ir); | |
const u2 = modN(s * ir); | |
const Q = Point.BASE.multiplyAndAddUnsafe(R2, u1, u2); | |
if (!Q) | |
throw new Error("point at infinify"); | |
Q.assertValidity(); | |
return Q; | |
} | |
// Signatures should be low-s, to prevent malleability. | |
hasHighS() { | |
return isBiggerThanHalfOrder(this.s); | |
} | |
normalizeS() { | |
return this.hasHighS() ? new Signature(this.r, modN(-this.s), this.recovery) : this; | |
} | |
// DER-encoded | |
toDERRawBytes() { | |
return hexToBytes$3(this.toDERHex()); | |
} | |
toDERHex() { | |
return DER.hexFromSig({ r: this.r, s: this.s }); | |
} | |
// padded bytes of r, then padded bytes of s | |
toCompactRawBytes() { | |
return hexToBytes$3(this.toCompactHex()); | |
} | |
toCompactHex() { | |
return numToNByteStr(this.r) + numToNByteStr(this.s); | |
} | |
} | |
const utils = { | |
isValidPrivateKey(privateKey) { | |
try { | |
normPrivateKeyToScalar(privateKey); | |
return true; | |
} catch (error) { | |
return false; | |
} | |
}, | |
normPrivateKeyToScalar, | |
/** | |
* Produces cryptographically secure private key from random of size | |
* (groupLen + ceil(groupLen / 2)) with modulo bias being negligible. | |
*/ | |
randomPrivateKey: () => { | |
const length = getMinHashLength(CURVE.n); | |
return mapHashToField(CURVE.randomBytes(length), CURVE.n); | |
}, | |
/** | |
* Creates precompute table for an arbitrary EC point. Makes point "cached". | |
* Allows to massively speed-up `point.multiply(scalar)`. | |
* @returns cached point | |
* @example | |
* const fast = utils.precompute(8, ProjectivePoint.fromHex(someonesPubKey)); | |
* fast.multiply(privKey); // much faster ECDH now | |
*/ | |
precompute(windowSize = 8, point = Point.BASE) { | |
point._setWindowSize(windowSize); | |
point.multiply(BigInt(3)); | |
return point; | |
} | |
}; | |
function getPublicKey(privateKey, isCompressed = true) { | |
return Point.fromPrivateKey(privateKey).toRawBytes(isCompressed); | |
} | |
function isProbPub(item) { | |
const arr = isBytes(item); | |
const str = typeof item === "string"; | |
const len = (arr || str) && item.length; | |
if (arr) | |
return len === compressedLen || len === uncompressedLen; | |
if (str) | |
return len === 2 * compressedLen || len === 2 * uncompressedLen; | |
if (item instanceof Point) | |
return true; | |
return false; | |
} | |
function getSharedSecret(privateA, publicB, isCompressed = true) { | |
if (isProbPub(privateA)) | |
throw new Error("first arg must be private key"); | |
if (!isProbPub(publicB)) | |
throw new Error("second arg must be public key"); | |
const b = Point.fromHex(publicB); | |
return b.multiply(normPrivateKeyToScalar(privateA)).toRawBytes(isCompressed); | |
} | |
const bits2int = CURVE.bits2int || function(bytes2) { | |
const num = bytesToNumberBE(bytes2); | |
const delta = bytes2.length * 8 - CURVE.nBitLength; | |
return delta > 0 ? num >> BigInt(delta) : num; | |
}; | |
const bits2int_modN = CURVE.bits2int_modN || function(bytes2) { | |
return modN(bits2int(bytes2)); | |
}; | |
const ORDER_MASK = bitMask(CURVE.nBitLength); | |
function int2octets(num) { | |
aInRange(`num < 2^${CURVE.nBitLength}`, num, _0n$4, ORDER_MASK); | |
return numberToBytesBE(num, CURVE.nByteLength); | |
} | |
function prepSig(msgHash, privateKey, opts = defaultSigOpts) { | |
if (["recovered", "canonical"].some((k) => k in opts)) | |
throw new Error("sign() legacy options not supported"); | |
const { hash: hash2, randomBytes: randomBytes2 } = CURVE; | |
let { lowS, prehash, extraEntropy: ent } = opts; | |
if (lowS == null) | |
lowS = true; | |
msgHash = ensureBytes("msgHash", msgHash); | |
validateSigVerOpts(opts); | |
if (prehash) | |
msgHash = ensureBytes("prehashed msgHash", hash2(msgHash)); | |
const h1int = bits2int_modN(msgHash); | |
const d = normPrivateKeyToScalar(privateKey); | |
const seedArgs = [int2octets(d), int2octets(h1int)]; | |
if (ent != null && ent !== false) { | |
const e = ent === true ? randomBytes2(Fp3.BYTES) : ent; | |
seedArgs.push(ensureBytes("extraEntropy", e)); | |
} | |
const seed = concatBytes$2(...seedArgs); | |
const m = h1int; | |
function k2sig(kBytes) { | |
const k = bits2int(kBytes); | |
if (!isWithinCurveOrder(k)) | |
return; | |
const ik = invN(k); | |
const q = Point.BASE.multiply(k).toAffine(); | |
const r = modN(q.x); | |
if (r === _0n$4) | |
return; | |
const s = modN(ik * modN(m + r * d)); | |
if (s === _0n$4) | |
return; | |
let recovery = (q.x === r ? 0 : 2) | Number(q.y & _1n$6); | |
let normS = s; | |
if (lowS && isBiggerThanHalfOrder(s)) { | |
normS = normalizeS(s); | |
recovery ^= 1; | |
} | |
return new Signature(r, normS, recovery); | |
} | |
return { seed, k2sig }; | |
} | |
const defaultSigOpts = { lowS: CURVE.lowS, prehash: false }; | |
const defaultVerOpts = { lowS: CURVE.lowS, prehash: false }; | |
function sign(msgHash, privKey, opts = defaultSigOpts) { | |
const { seed, k2sig } = prepSig(msgHash, privKey, opts); | |
const C = CURVE; | |
const drbg = createHmacDrbg(C.hash.outputLen, C.nByteLength, C.hmac); | |
return drbg(seed, k2sig); | |
} | |
Point.BASE._setWindowSize(8); | |
function verify(signature, msgHash, publicKey, opts = defaultVerOpts) { | |
var _a; | |
const sg = signature; | |
msgHash = ensureBytes("msgHash", msgHash); | |
publicKey = ensureBytes("publicKey", publicKey); | |
if ("strict" in opts) | |
throw new Error("options.strict was renamed to lowS"); | |
validateSigVerOpts(opts); | |
const { lowS, prehash } = opts; | |
let _sig = void 0; | |
let P; | |
try { | |
if (typeof sg === "string" || isBytes(sg)) { | |
try { | |
_sig = Signature.fromDER(sg); | |
} catch (derError) { | |
if (!(derError instanceof DER.Err)) | |
throw derError; | |
_sig = Signature.fromCompact(sg); | |
} | |
} else if (typeof sg === "object" && typeof sg.r === "bigint" && typeof sg.s === "bigint") { | |
const { r: r2, s: s2 } = sg; | |
_sig = new Signature(r2, s2); | |
} else { | |
throw new Error("PARSE"); | |
} | |
P = Point.fromHex(publicKey); | |
} catch (error) { | |
if (error.message === "PARSE") | |
throw new Error(`signature must be Signature instance, Uint8Array or hex string`); | |
return false; | |
} | |
if (lowS && _sig.hasHighS()) | |
return false; | |
if (prehash) | |
msgHash = CURVE.hash(msgHash); | |
const { r, s } = _sig; | |
const h = bits2int_modN(msgHash); | |
const is = invN(s); | |
const u1 = modN(h * is); | |
const u2 = modN(r * is); | |
const R2 = (_a = Point.BASE.multiplyAndAddUnsafe(P, u1, u2)) == null ? void 0 : _a.toAffine(); | |
if (!R2) | |
return false; | |
const v = modN(R2.x); | |
return v === r; | |
} | |
return { | |
CURVE, | |
getPublicKey, | |
getSharedSecret, | |
sign, | |
verify, | |
ProjectivePoint: Point, | |
Signature, | |
utils | |
}; | |
} | |
function SWUFpSqrtRatio(Fp3, Z) { | |
const q = Fp3.ORDER; | |
let l = _0n$4; | |
for (let o = q - _1n$6; o % _2n$6 === _0n$4; o /= _2n$6) | |
l += _1n$6; | |
const c1 = l; | |
const _2n_pow_c1_1 = _2n$6 << c1 - _1n$6 - _1n$6; | |
const _2n_pow_c1 = _2n_pow_c1_1 * _2n$6; | |
const c2 = (q - _1n$6) / _2n_pow_c1; | |
const c3 = (c2 - _1n$6) / _2n$6; | |
const c4 = _2n_pow_c1 - _1n$6; | |
const c5 = _2n_pow_c1_1; | |
const c6 = Fp3.pow(Z, c2); | |
const c7 = Fp3.pow(Z, (c2 + _1n$6) / _2n$6); | |
let sqrtRatio = (u, v) => { | |
let tv1 = c6; | |
let tv2 = Fp3.pow(v, c4); | |
let tv3 = Fp3.sqr(tv2); | |
tv3 = Fp3.mul(tv3, v); | |
let tv5 = Fp3.mul(u, tv3); | |
tv5 = Fp3.pow(tv5, c3); | |
tv5 = Fp3.mul(tv5, tv2); | |
tv2 = Fp3.mul(tv5, v); | |
tv3 = Fp3.mul(tv5, u); | |
let tv4 = Fp3.mul(tv3, tv2); | |
tv5 = Fp3.pow(tv4, c5); | |
let isQR = Fp3.eql(tv5, Fp3.ONE); | |
tv2 = Fp3.mul(tv3, c7); | |
tv5 = Fp3.mul(tv4, tv1); | |
tv3 = Fp3.cmov(tv2, tv3, isQR); | |
tv4 = Fp3.cmov(tv5, tv4, isQR); | |
for (let i = c1; i > _1n$6; i--) { | |
let tv52 = i - _2n$6; | |
tv52 = _2n$6 << tv52 - _1n$6; | |
let tvv5 = Fp3.pow(tv4, tv52); | |
const e1 = Fp3.eql(tvv5, Fp3.ONE); | |
tv2 = Fp3.mul(tv3, tv1); | |
tv1 = Fp3.mul(tv1, tv1); | |
tvv5 = Fp3.mul(tv4, tv1); | |
tv3 = Fp3.cmov(tv2, tv3, e1); | |
tv4 = Fp3.cmov(tvv5, tv4, e1); | |
} | |
return { isValid: isQR, value: tv3 }; | |
}; | |
if (Fp3.ORDER % _4n$1 === _3n$4) { | |
const c12 = (Fp3.ORDER - _3n$4) / _4n$1; | |
const c22 = Fp3.sqrt(Fp3.neg(Z)); | |
sqrtRatio = (u, v) => { | |
let tv1 = Fp3.sqr(v); | |
const tv2 = Fp3.mul(u, v); | |
tv1 = Fp3.mul(tv1, tv2); | |
let y1 = Fp3.pow(tv1, c12); | |
y1 = Fp3.mul(y1, tv2); | |
const y2 = Fp3.mul(y1, c22); | |
const tv3 = Fp3.mul(Fp3.sqr(y1), v); | |
const isQR = Fp3.eql(tv3, u); | |
let y = Fp3.cmov(y2, y1, isQR); | |
return { isValid: isQR, value: y }; | |
}; | |
} | |
return sqrtRatio; | |
} | |
function mapToCurveSimpleSWU(Fp3, opts) { | |
validateField(Fp3); | |
if (!Fp3.isValid(opts.A) || !Fp3.isValid(opts.B) || !Fp3.isValid(opts.Z)) | |
throw new Error("mapToCurveSimpleSWU: invalid opts"); | |
const sqrtRatio = SWUFpSqrtRatio(Fp3, opts.Z); | |
if (!Fp3.isOdd) | |
throw new Error("Fp.isOdd is not implemented!"); | |
return (u) => { | |
let tv1, tv2, tv3, tv4, tv5, tv6, x, y; | |
tv1 = Fp3.sqr(u); | |
tv1 = Fp3.mul(tv1, opts.Z); | |
tv2 = Fp3.sqr(tv1); | |
tv2 = Fp3.add(tv2, tv1); | |
tv3 = Fp3.add(tv2, Fp3.ONE); | |
tv3 = Fp3.mul(tv3, opts.B); | |
tv4 = Fp3.cmov(opts.Z, Fp3.neg(tv2), !Fp3.eql(tv2, Fp3.ZERO)); | |
tv4 = Fp3.mul(tv4, opts.A); | |
tv2 = Fp3.sqr(tv3); | |
tv6 = Fp3.sqr(tv4); | |
tv5 = Fp3.mul(tv6, opts.A); | |
tv2 = Fp3.add(tv2, tv5); | |
tv2 = Fp3.mul(tv2, tv3); | |
tv6 = Fp3.mul(tv6, tv4); | |
tv5 = Fp3.mul(tv6, opts.B); | |
tv2 = Fp3.add(tv2, tv5); | |
x = Fp3.mul(tv1, tv3); | |
const { isValid, value } = sqrtRatio(tv2, tv6); | |
y = Fp3.mul(tv1, u); | |
y = Fp3.mul(y, value); | |
x = Fp3.cmov(x, tv3, isValid); | |
y = Fp3.cmov(y, value, isValid); | |
const e1 = Fp3.isOdd(u) === Fp3.isOdd(y); | |
y = Fp3.cmov(Fp3.neg(y), y, e1); | |
x = Fp3.div(x, tv4); | |
return { x, y }; | |
}; | |
} | |
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | |
function getHash(hash2) { | |
return { | |
hash: hash2, | |
hmac: (key, ...msgs) => hmac(hash2, key, concatBytes$3(...msgs)), | |
randomBytes | |
}; | |
} | |
function createCurve(curveDef, defHash) { | |
const create = (hash2) => weierstrass({ ...curveDef, ...getHash(hash2) }); | |
return Object.freeze({ ...create(defHash), create }); | |
} | |
const os2ip = bytesToNumberBE; | |
function i2osp(value, length) { | |
anum(value); | |
anum(length); | |
if (value < 0 || value >= 1 << 8 * length) { | |
throw new Error(`bad I2OSP call: value=${value} length=${length}`); | |
} | |
const res = Array.from({ length }).fill(0); | |
for (let i = length - 1; i >= 0; i--) { | |
res[i] = value & 255; | |
value >>>= 8; | |
} | |
return new Uint8Array(res); | |
} | |
function strxor(a, b) { | |
const arr = new Uint8Array(a.length); | |
for (let i = 0; i < a.length; i++) { | |
arr[i] = a[i] ^ b[i]; | |
} | |
return arr; | |
} | |
function anum(item) { | |
if (!Number.isSafeInteger(item)) | |
throw new Error("number expected"); | |
} | |
function expand_message_xmd(msg, DST, lenInBytes, H) { | |
abytes(msg); | |
abytes(DST); | |
anum(lenInBytes); | |
if (DST.length > 255) | |
DST = H(concatBytes$2(utf8ToBytes$1("H2C-OVERSIZE-DST-"), DST)); | |
const { outputLen: b_in_bytes, blockLen: r_in_bytes } = H; | |
const ell = Math.ceil(lenInBytes / b_in_bytes); | |
if (lenInBytes > 65535 || ell > 255) | |
throw new Error("expand_message_xmd: invalid lenInBytes"); | |
const DST_prime = concatBytes$2(DST, i2osp(DST.length, 1)); | |
const Z_pad = i2osp(0, r_in_bytes); | |
const l_i_b_str = i2osp(lenInBytes, 2); | |
const b = new Array(ell); | |
const b_0 = H(concatBytes$2(Z_pad, msg, l_i_b_str, i2osp(0, 1), DST_prime)); | |
b[0] = H(concatBytes$2(b_0, i2osp(1, 1), DST_prime)); | |
for (let i = 1; i <= ell; i++) { | |
const args = [strxor(b_0, b[i - 1]), i2osp(i + 1, 1), DST_prime]; | |
b[i] = H(concatBytes$2(...args)); | |
} | |
const pseudo_random_bytes = concatBytes$2(...b); | |
return pseudo_random_bytes.slice(0, lenInBytes); | |
} | |
function expand_message_xof(msg, DST, lenInBytes, k, H) { | |
abytes(msg); | |
abytes(DST); | |
anum(lenInBytes); | |
if (DST.length > 255) { | |
const dkLen = Math.ceil(2 * k / 8); | |
DST = H.create({ dkLen }).update(utf8ToBytes$1("H2C-OVERSIZE-DST-")).update(DST).digest(); | |
} | |
if (lenInBytes > 65535 || DST.length > 255) | |
throw new Error("expand_message_xof: invalid lenInBytes"); | |
return H.create({ dkLen: lenInBytes }).update(msg).update(i2osp(lenInBytes, 2)).update(DST).update(i2osp(DST.length, 1)).digest(); | |
} | |
function hash_to_field(msg, count, options) { | |
validateObject(options, { | |
DST: "stringOrUint8Array", | |
p: "bigint", | |
m: "isSafeInteger", | |
k: "isSafeInteger", | |
hash: "hash" | |
}); | |
const { p, k, m, hash: hash2, expand, DST: _DST } = options; | |
abytes(msg); | |
anum(count); | |
const DST = typeof _DST === "string" ? utf8ToBytes$1(_DST) : _DST; | |
const log2p = p.toString(2).length; | |
const L = Math.ceil((log2p + k) / 8); | |
const len_in_bytes = count * m * L; | |
let prb; | |
if (expand === "xmd") { | |
prb = expand_message_xmd(msg, DST, len_in_bytes, hash2); | |
} else if (expand === "xof") { | |
prb = expand_message_xof(msg, DST, len_in_bytes, k, hash2); | |
} else if (expand === "_internal_pass") { | |
prb = msg; | |
} else { | |
throw new Error('expand must be "xmd" or "xof"'); | |
} | |
const u = new Array(count); | |
for (let i = 0; i < count; i++) { | |
const e = new Array(m); | |
for (let j = 0; j < m; j++) { | |
const elm_offset = L * (j + i * m); | |
const tv = prb.subarray(elm_offset, elm_offset + L); | |
e[j] = mod$1(os2ip(tv), p); | |
} | |
u[i] = e; | |
} | |
return u; | |
} | |
function isogenyMap(field, map) { | |
const COEFF = map.map((i) => Array.from(i).reverse()); | |
return (x, y) => { | |
const [xNum, xDen, yNum, yDen] = COEFF.map((val) => val.reduce((acc, i) => field.add(field.mul(acc, x), i))); | |
x = field.div(xNum, xDen); | |
y = field.mul(y, field.div(yNum, yDen)); | |
return { x, y }; | |
}; | |
} | |
function createHasher(Point, mapToCurve, def) { | |
if (typeof mapToCurve !== "function") | |
throw new Error("mapToCurve() must be defined"); | |
return { | |
// Encodes byte string to elliptic curve. | |
// hash_to_curve from https://www.rfc-editor.org/rfc/rfc9380#section-3 | |
hashToCurve(msg, options) { | |
const u = hash_to_field(msg, 2, { ...def, DST: def.DST, ...options }); | |
const u0 = Point.fromAffine(mapToCurve(u[0])); | |
const u1 = Point.fromAffine(mapToCurve(u[1])); | |
const P = u0.add(u1).clearCofactor(); | |
P.assertValidity(); | |
return P; | |
}, | |
// Encodes byte string to elliptic curve. | |
// encode_to_curve from https://www.rfc-editor.org/rfc/rfc9380#section-3 | |
encodeToCurve(msg, options) { | |
const u = hash_to_field(msg, 1, { ...def, DST: def.encodeDST, ...options }); | |
const P = Point.fromAffine(mapToCurve(u[0])).clearCofactor(); | |
P.assertValidity(); | |
return P; | |
}, | |
// Same as encodeToCurve, but without hash | |
mapToCurve(scalars) { | |
if (!Array.isArray(scalars)) | |
throw new Error("mapToCurve: expected array of bigints"); | |
for (const i of scalars) | |
if (typeof i !== "bigint") | |
throw new Error(`mapToCurve: expected array of bigints, got ${i} in array`); | |
const P = Point.fromAffine(mapToCurve(scalars)).clearCofactor(); | |
P.assertValidity(); | |
return P; | |
} | |
}; | |
} | |
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | |
const secp256k1P = BigInt("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f"); | |
const secp256k1N = BigInt("0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141"); | |
const _1n$5 = BigInt(1); | |
const _2n$5 = BigInt(2); | |
const divNearest = (a, b) => (a + b / _2n$5) / b; | |
function sqrtMod(y) { | |
const P = secp256k1P; | |
const _3n2 = BigInt(3), _6n2 = BigInt(6), _11n = BigInt(11), _22n = BigInt(22); | |
const _23n = BigInt(23), _44n = BigInt(44), _88n = BigInt(88); | |
const b2 = y * y * y % P; | |
const b3 = b2 * b2 * y % P; | |
const b6 = pow2(b3, _3n2, P) * b3 % P; | |
const b9 = pow2(b6, _3n2, P) * b3 % P; | |
const b11 = pow2(b9, _2n$5, P) * b2 % P; | |
const b22 = pow2(b11, _11n, P) * b11 % P; | |
const b44 = pow2(b22, _22n, P) * b22 % P; | |
const b88 = pow2(b44, _44n, P) * b44 % P; | |
const b176 = pow2(b88, _88n, P) * b88 % P; | |
const b220 = pow2(b176, _44n, P) * b44 % P; | |
const b223 = pow2(b220, _3n2, P) * b3 % P; | |
const t1 = pow2(b223, _23n, P) * b22 % P; | |
const t2 = pow2(t1, _6n2, P) * b2 % P; | |
const root = pow2(t2, _2n$5, P); | |
if (!Fp$2.eql(Fp$2.sqr(root), y)) | |
throw new Error("Cannot find square root"); | |
return root; | |
} | |
const Fp$2 = Field(secp256k1P, void 0, void 0, { sqrt: sqrtMod }); | |
const secp256k1 = createCurve({ | |
a: BigInt(0), | |
// equation params: a, b | |
b: BigInt(7), | |
// Seem to be rigid: bitcointalk.org/index.php?topic=289795.msg3183975#msg3183975 | |
Fp: Fp$2, | |
// Field's prime: 2n**256n - 2n**32n - 2n**9n - 2n**8n - 2n**7n - 2n**6n - 2n**4n - 1n | |
n: secp256k1N, | |
// Curve order, total count of valid points in the field | |
// Base point (x, y) aka generator point | |
Gx: BigInt("55066263022277343669578718895168534326250603453777594175500187360389116729240"), | |
Gy: BigInt("32670510020758816978083085130507043184471273380659243275938904335757337482424"), | |
h: BigInt(1), | |
// Cofactor | |
lowS: true, | |
// Allow only low-S signatures by default in sign() and verify() | |
/** | |
* secp256k1 belongs to Koblitz curves: it has efficiently computable endomorphism. | |
* Endomorphism uses 2x less RAM, speeds up precomputation by 2x and ECDH / key recovery by 20%. | |
* For precomputed wNAF it trades off 1/2 init time & 1/3 ram for 20% perf hit. | |
* Explanation: https://gist.github.com/paulmillr/eb670806793e84df628a7c434a873066 | |
*/ | |
endo: { | |
beta: BigInt("0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee"), | |
splitScalar: (k) => { | |
const n = secp256k1N; | |
const a1 = BigInt("0x3086d221a7d46bcde86c90e49284eb15"); | |
const b1 = -_1n$5 * BigInt("0xe4437ed6010e88286f547fa90abfe4c3"); | |
const a2 = BigInt("0x114ca50f7a8e2f3f657c1108d9d44cfd8"); | |
const b2 = a1; | |
const POW_2_128 = BigInt("0x100000000000000000000000000000000"); | |
const c1 = divNearest(b2 * k, n); | |
const c2 = divNearest(-b1 * k, n); | |
let k1 = mod$1(k - c1 * a1 - c2 * a2, n); | |
let k2 = mod$1(-c1 * b1 - c2 * b2, n); | |
const k1neg = k1 > POW_2_128; | |
const k2neg = k2 > POW_2_128; | |
if (k1neg) | |
k1 = n - k1; | |
if (k2neg) | |
k2 = n - k2; | |
if (k1 > POW_2_128 || k2 > POW_2_128) { | |
throw new Error("splitScalar: Endomorphism failed, k=" + k); | |
} | |
return { k1neg, k1, k2neg, k2 }; | |
} | |
} | |
}, sha256$1); | |
BigInt(0); | |
secp256k1.ProjectivePoint; | |
function hexToBytes$2(data) { | |
const sliced = data.startsWith("0x") ? data.substring(2) : data; | |
return hexToBytes$4(sliced); | |
} | |
function equalsBytes(a, b) { | |
if (a.length !== b.length) { | |
return false; | |
} | |
for (let i = 0; i < a.length; i++) { | |
if (a[i] !== b[i]) { | |
return false; | |
} | |
} | |
return true; | |
} | |
function wrapHash(hash2) { | |
return (msg) => { | |
assert.bytes(msg); | |
return hash2(msg); | |
}; | |
} | |
function isHexString$1(value, length) { | |
if (typeof value !== "string" || !value.match(/^0x[0-9A-Fa-f]*$/)) | |
return false; | |
return true; | |
} | |
const stripHexPrefix$1 = (str) => { | |
if (typeof str !== "string") | |
throw new Error(`[stripHexPrefix] input must be type 'string', received ${typeof str}`); | |
return isHexString$1(str) ? str.slice(2) : str; | |
}; | |
function padToEven$1(value) { | |
let a = value; | |
if (typeof a !== "string") { | |
throw new Error(`[padToEven] value must be type 'string', received ${typeof a}`); | |
} | |
if (a.length % 2) | |
a = `0${a}`; | |
return a; | |
} | |
const assertIsBytes = function(input) { | |
if (!(input instanceof Uint8Array)) { | |
const msg = `This method only supports Uint8Array but input was: ${input}`; | |
throw new Error(msg); | |
} | |
}; | |
const BIGINT_0$1 = BigInt(0); | |
const bytesToUnprefixedHex = bytesToHex$3; | |
const hexToBytesMapFirstKey = {}; | |
const hexToBytesMapSecondKey = {}; | |
for (let i = 0; i < 16; i++) { | |
const vSecondKey = i; | |
const vFirstKey = i * 16; | |
const key = i.toString(16).toLowerCase(); | |
hexToBytesMapSecondKey[key] = vSecondKey; | |
hexToBytesMapSecondKey[key.toUpperCase()] = vSecondKey; | |
hexToBytesMapFirstKey[key] = vFirstKey; | |
hexToBytesMapFirstKey[key.toUpperCase()] = vFirstKey; | |
} | |
const unprefixedHexToBytes = (inp) => { | |
if (inp.slice(0, 2) === "0x") { | |
throw new Error("hex string is prefixed with 0x, should be unprefixed"); | |
} else { | |
inp = padToEven$1(inp); | |
const byteLen = inp.length; | |
const bytes2 = new Uint8Array(byteLen / 2); | |
for (let i = 0; i < byteLen; i += 2) { | |
bytes2[i / 2] = hexToBytesMapFirstKey[inp[i]] + hexToBytesMapSecondKey[inp[i + 1]]; | |
} | |
return bytes2; | |
} | |
}; | |
const hexByByte = Array.from({ length: 256 }, (v, i) => i.toString(16).padStart(2, "0")); | |
const bytesToHex$1 = (bytes2) => { | |
let hex = `0x`; | |
if (bytes2 === void 0 || bytes2.length === 0) | |
return hex; | |
for (const byte of bytes2) { | |
hex = `${hex}${hexByByte[byte]}`; | |
} | |
return hex; | |
}; | |
const BIGINT_CACHE = []; | |
for (let i = 0; i <= 256 * 256 - 1; i++) { | |
BIGINT_CACHE[i] = BigInt(i); | |
} | |
const bytesToBigInt = (bytes2, littleEndian = false) => { | |
if (littleEndian) { | |
bytes2.reverse(); | |
} | |
const hex = bytesToHex$1(bytes2); | |
if (hex === "0x") { | |
return BIGINT_0$1; | |
} | |
if (hex.length === 4) { | |
return BIGINT_CACHE[bytes2[0]]; | |
} | |
if (hex.length === 6) { | |
return BIGINT_CACHE[bytes2[0] * 256 + bytes2[1]]; | |
} | |
return BigInt(hex); | |
}; | |
const bytesToInt = (bytes2) => { | |
const res = Number(bytesToBigInt(bytes2)); | |
if (!Number.isSafeInteger(res)) | |
throw new Error("Number exceeds 53 bits"); | |
return res; | |
}; | |
const hexToBytes$1 = (hex) => { | |
if (typeof hex !== "string") { | |
throw new Error(`hex argument type ${typeof hex} must be of type string`); | |
} | |
if (!/^0x[0-9a-fA-F]*$/.test(hex)) { | |
throw new Error(`Input must be a 0x-prefixed hexadecimal string, got ${hex}`); | |
} | |
const unprefixedHex = hex.slice(2); | |
return unprefixedHexToBytes(unprefixedHex); | |
}; | |
const intToHex = (i) => { | |
if (!Number.isSafeInteger(i) || i < 0) { | |
throw new Error(`Received an invalid integer type: ${i}`); | |
} | |
return `0x${i.toString(16)}`; | |
}; | |
const intToBytes = (i) => { | |
const hex = intToHex(i); | |
return hexToBytes$1(hex); | |
}; | |
const bigIntToBytes = (num, littleEndian = false) => { | |
const bytes2 = toBytes$1(`0x${padToEven$1(num.toString(16))}`); | |
return littleEndian ? bytes2.reverse() : bytes2; | |
}; | |
const zeros = (bytes2) => { | |
return new Uint8Array(bytes2); | |
}; | |
const setLength = (msg, length, right) => { | |
if (right) { | |
if (msg.length < length) { | |
return new Uint8Array([...msg, ...zeros(length - msg.length)]); | |
} | |
return msg.subarray(0, length); | |
} else { | |
if (msg.length < length) { | |
return new Uint8Array([...zeros(length - msg.length), ...msg]); | |
} | |
return msg.subarray(-length); | |
} | |
}; | |
const setLengthLeft = (msg, length) => { | |
assertIsBytes(msg); | |
return setLength(msg, length, false); | |
}; | |
const setLengthRight = (msg, length) => { | |
assertIsBytes(msg); | |
return setLength(msg, length, true); | |
}; | |
const stripZeros = (a) => { | |
let first = a[0]; | |
while (a.length > 0 && first.toString() === "0") { | |
a = a.slice(1); | |
first = a[0]; | |
} | |
return a; | |
}; | |
const unpadBytes = (a) => { | |
assertIsBytes(a); | |
return stripZeros(a); | |
}; | |
const toBytes$1 = (v) => { | |
if (v === null || v === void 0) { | |
return new Uint8Array(); | |
} | |
if (Array.isArray(v) || v instanceof Uint8Array) { | |
return Uint8Array.from(v); | |
} | |
if (typeof v === "string") { | |
if (!isHexString$1(v)) { | |
throw new Error(`Cannot convert string to Uint8Array. toBytes only supports 0x-prefixed hex strings and this string was given: ${v}`); | |
} | |
return hexToBytes$1(v); | |
} | |
if (typeof v === "number") { | |
return intToBytes(v); | |
} | |
if (typeof v === "bigint") { | |
if (v < BIGINT_0$1) { | |
throw new Error(`Cannot convert negative bigint to Uint8Array. Given: ${v}`); | |
} | |
let n = v.toString(16); | |
if (n.length % 2) | |
n = "0" + n; | |
return unprefixedHexToBytes(n); | |
} | |
if (v.toBytes !== void 0) { | |
return v.toBytes(); | |
} | |
throw new Error("invalid type"); | |
}; | |
const short = (bytes2, maxLength = 50) => { | |
const byteStr = bytes2 instanceof Uint8Array ? bytesToHex$1(bytes2) : bytes2; | |
const len = byteStr.slice(0, 2) === "0x" ? maxLength + 2 : maxLength; | |
if (byteStr.length <= len) { | |
return byteStr; | |
} | |
return byteStr.slice(0, len) + "\u2026"; | |
}; | |
const bigIntToHex = (num) => { | |
return `0x${num.toString(16)}`; | |
}; | |
const bigIntToUnpaddedBytes = (value) => { | |
return unpadBytes(bigIntToBytes(value)); | |
}; | |
const bigIntToAddressBytes = (value, strict = true) => { | |
const addressBytes = bigIntToBytes(value); | |
if (strict && addressBytes.length > 20) { | |
throw Error(`Invalid address bytes length=${addressBytes.length} strict=${strict}`); | |
} | |
return setLengthLeft(addressBytes, 20); | |
}; | |
const intToUnpaddedBytes = (value) => { | |
return unpadBytes(intToBytes(value)); | |
}; | |
const concatBytes$1 = (...arrays) => { | |
if (arrays.length === 1) | |
return arrays[0]; | |
const length = arrays.reduce((a, arr) => a + arr.length, 0); | |
const result = new Uint8Array(length); | |
for (let i = 0, pad = 0; i < arrays.length; i++) { | |
const arr = arrays[i]; | |
result.set(arr, pad); | |
pad += arr.length; | |
} | |
return result; | |
}; | |
const MAX_UINT64 = BigInt("0xffffffffffffffff"); | |
const MAX_INTEGER = BigInt("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); | |
const MAX_INTEGER_BIGINT = BigInt("115792089237316195423570985008687907853269984665640564039457584007913129639935"); | |
secp256k1.CURVE.n; | |
secp256k1.CURVE.n / BigInt(2); | |
const TWO_POW256 = BigInt("0x10000000000000000000000000000000000000000000000000000000000000000"); | |
const KECCAK256_NULL_S = "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"; | |
const KECCAK256_NULL = hexToBytes$1(KECCAK256_NULL_S); | |
const KECCAK256_RLP_ARRAY_S = "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"; | |
hexToBytes$1(KECCAK256_RLP_ARRAY_S); | |
const KECCAK256_RLP_S = "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"; | |
const KECCAK256_RLP = hexToBytes$1(KECCAK256_RLP_S); | |
Uint8Array.from([128]); | |
const RIPEMD160_ADDRESS_STRING = "0000000000000000000000000000000000000003"; | |
BigInt(-1); | |
const BIGINT_0 = BigInt(0); | |
const BIGINT_1 = BigInt(1); | |
const BIGINT_2 = BigInt(2); | |
const BIGINT_3 = BigInt(3); | |
const BIGINT_7 = BigInt(7); | |
const BIGINT_8 = BigInt(8); | |
const BIGINT_27 = BigInt(27); | |
const BIGINT_28 = BigInt(28); | |
const BIGINT_31 = BigInt(31); | |
const BIGINT_32 = BigInt(32); | |
const BIGINT_64 = BigInt(64); | |
BigInt(128); | |
const BIGINT_255 = BigInt(255); | |
const BIGINT_256 = BigInt(256); | |
const BIGINT_96 = BigInt(96); | |
BigInt(100); | |
const BIGINT_160 = BigInt(160); | |
const BIGINT_224 = BigInt(224); | |
const BIGINT_2EXP96 = BigInt(7922816251426434e13); | |
const BIGINT_2EXP160 = BigInt(1461501637330903e33); | |
const BIGINT_2EXP224 = BigInt(2695994666715064e52); | |
function encode(input) { | |
if (Array.isArray(input)) { | |
const output2 = []; | |
let outputLength = 0; | |
for (let i = 0; i < input.length; i++) { | |
const encoded = encode(input[i]); | |
output2.push(encoded); | |
outputLength += encoded.length; | |
} | |
return concatBytes(encodeLength(outputLength, 192), ...output2); | |
} | |
const inputBuf = toBytes(input); | |
if (inputBuf.length === 1 && inputBuf[0] < 128) { | |
return inputBuf; | |
} | |
return concatBytes(encodeLength(inputBuf.length, 128), inputBuf); | |
} | |
function safeSlice(input, start, end) { | |
if (end > input.length) { | |
throw new Error("invalid RLP (safeSlice): end slice of Uint8Array out-of-bounds"); | |
} | |
return input.slice(start, end); | |
} | |
function decodeLength(v) { | |
if (v[0] === 0) { | |
throw new Error("invalid RLP: extra zeros"); | |
} | |
return parseHexByte(bytesToHex(v)); | |
} | |
function encodeLength(len, offset) { | |
if (len < 56) { | |
return Uint8Array.from([len + offset]); | |
} | |
const hexLength = numberToHex(len); | |
const lLength = hexLength.length / 2; | |
const firstByte = numberToHex(offset + 55 + lLength); | |
return Uint8Array.from(hexToBytes(firstByte + hexLength)); | |
} | |
function decode(input, stream = false) { | |
if (typeof input === "undefined" || input === null || input.length === 0) { | |
return Uint8Array.from([]); | |
} | |
const inputBytes = toBytes(input); | |
const decoded = _decode(inputBytes); | |
if (stream) { | |
return { | |
data: decoded.data, | |
remainder: decoded.remainder.slice() | |
}; | |
} | |
if (decoded.remainder.length !== 0) { | |
throw new Error("invalid RLP: remainder must be zero"); | |
} | |
return decoded.data; | |
} | |
function _decode(input) { | |
let length, lLength, data, innerRemainder, d; | |
const decoded = []; | |
const firstByte = input[0]; | |
if (firstByte <= 127) { | |
return { | |
data: input.slice(0, 1), | |
remainder: input.subarray(1) | |
}; | |
} else if (firstByte <= 183) { | |
length = firstByte - 127; | |
if (firstByte === 128) { | |
data = Uint8Array.from([]); | |
} else { | |
data = safeSlice(input, 1, length); | |
} | |
if (length === 2 && data[0] < 128) { | |
throw new Error("invalid RLP encoding: invalid prefix, single byte < 0x80 are not prefixed"); | |
} | |
return { | |
data, | |
remainder: input.subarray(length) | |
}; | |
} else if (firstByte <= 191) { | |
lLength = firstByte - 182; | |
if (input.length - 1 < lLength) { | |
throw new Error("invalid RLP: not enough bytes for string length"); | |
} | |
length = decodeLength(safeSlice(input, 1, lLength)); | |
if (length <= 55) { | |
throw new Error("invalid RLP: expected string length to be greater than 55"); | |
} | |
data = safeSlice(input, lLength, length + lLength); | |
return { | |
data, | |
remainder: input.subarray(length + lLength) | |
}; | |
} else if (firstByte <= 247) { | |
length = firstByte - 191; | |
innerRemainder = safeSlice(input, 1, length); | |
while (innerRemainder.length) { | |
d = _decode(innerRemainder); | |
decoded.push(d.data); | |
innerRemainder = d.remainder; | |
} | |
return { | |
data: decoded, | |
remainder: input.subarray(length) | |
}; | |
} else { | |
lLength = firstByte - 246; | |
length = decodeLength(safeSlice(input, 1, lLength)); | |
if (length < 56) { | |
throw new Error("invalid RLP: encoded list too short"); | |
} | |
const totalLength = lLength + length; | |
if (totalLength > input.length) { | |
throw new Error("invalid RLP: total length is larger than the data"); | |
} | |
innerRemainder = safeSlice(input, lLength, totalLength); | |
while (innerRemainder.length) { | |
d = _decode(innerRemainder); | |
decoded.push(d.data); | |
innerRemainder = d.remainder; | |
} | |
return { | |
data: decoded, | |
remainder: input.subarray(totalLength) | |
}; | |
} | |
} | |
const cachedHexes = Array.from({ length: 256 }, (_v, i) => i.toString(16).padStart(2, "0")); | |
function bytesToHex(uint8a) { | |
let hex = ""; | |
for (let i = 0; i < uint8a.length; i++) { | |
hex += cachedHexes[uint8a[i]]; | |
} | |
return hex; | |
} | |
function parseHexByte(hexByte) { | |
const byte = Number.parseInt(hexByte, 16); | |
if (Number.isNaN(byte)) | |
throw new Error("Invalid byte sequence"); | |
return byte; | |
} | |
function hexToBytes(hex) { | |
if (typeof hex !== "string") { | |
throw new TypeError("hexToBytes: expected string, got " + typeof hex); | |
} | |
if (hex.length % 2) | |
throw new Error("hexToBytes: received invalid unpadded hex"); | |
const array = new Uint8Array(hex.length / 2); | |
for (let i = 0; i < array.length; i++) { | |
const j = i * 2; | |
array[i] = parseHexByte(hex.slice(j, j + 2)); | |
} | |
return array; | |
} | |
function concatBytes(...arrays) { | |
if (arrays.length === 1) | |
return arrays[0]; | |
const length = arrays.reduce((a, arr) => a + arr.length, 0); | |
const result = new Uint8Array(length); | |
for (let i = 0, pad = 0; i < arrays.length; i++) { | |
const arr = arrays[i]; | |
result.set(arr, pad); | |
pad += arr.length; | |
} | |
return result; | |
} | |
function utf8ToBytes(utf) { | |
return new TextEncoder().encode(utf); | |
} | |
function numberToHex(integer) { | |
if (integer < 0) { | |
throw new Error("Invalid integer as argument, must be unsigned!"); | |
} | |
const hex = integer.toString(16); | |
return hex.length % 2 ? `0${hex}` : hex; | |
} | |
function padToEven(a) { | |
return a.length % 2 ? `0${a}` : a; | |
} | |
function isHexString(str) { | |
return str.length >= 2 && str[0] === "0" && str[1] === "x"; | |
} | |
function stripHexPrefix(str) { | |
if (typeof str !== "string") { | |
return str; | |
} | |
return isHexString(str) ? str.slice(2) : str; | |
} | |
function toBytes(v) { | |
if (v instanceof Uint8Array) { | |
return v; | |
} | |
if (typeof v === "string") { | |
if (isHexString(v)) { | |
return hexToBytes(padToEven(stripHexPrefix(v))); | |
} | |
return utf8ToBytes(v); | |
} | |
if (typeof v === "number" || typeof v === "bigint") { | |
if (!v) { | |
return Uint8Array.from([]); | |
} | |
return hexToBytes(numberToHex(v)); | |
} | |
if (v === null || v === void 0) { | |
return Uint8Array.from([]); | |
} | |
throw new Error("toBytes: received unsupported type " + typeof v); | |
} | |
const RLP = { encode, decode }; | |
const U32_MASK64 = /* @__PURE__ */ BigInt(2 ** 32 - 1); | |
const _32n = /* @__PURE__ */ BigInt(32); | |
function fromBig(n, le = false) { | |
if (le) | |
return { h: Number(n & U32_MASK64), l: Number(n >> _32n & U32_MASK64) }; | |
return { h: Number(n >> _32n & U32_MASK64) | 0, l: Number(n & U32_MASK64) | 0 }; | |
} | |
function split(lst, le = false) { | |
let Ah = new Uint32Array(lst.length); | |
let Al = new Uint32Array(lst.length); | |
for (let i = 0; i < lst.length; i++) { | |
const { h, l } = fromBig(lst[i], le); | |
[Ah[i], Al[i]] = [h, l]; | |
} | |
return [Ah, Al]; | |
} | |
const rotlSH = (h, l, s) => h << s | l >>> 32 - s; | |
const rotlSL = (h, l, s) => l << s | h >>> 32 - s; | |
const rotlBH = (h, l, s) => l << s - 32 | h >>> 64 - s; | |
const rotlBL = (h, l, s) => h << s - 32 | l >>> 64 - s; | |
const SHA3_PI = []; | |
const SHA3_ROTL = []; | |
const _SHA3_IOTA = []; | |
const _0n$3 = /* @__PURE__ */ BigInt(0); | |
const _1n$4 = /* @__PURE__ */ BigInt(1); | |
const _2n$4 = /* @__PURE__ */ BigInt(2); | |
const _7n = /* @__PURE__ */ BigInt(7); | |
const _256n = /* @__PURE__ */ BigInt(256); | |
const _0x71n = /* @__PURE__ */ BigInt(113); | |
for (let round = 0, R2 = _1n$4, x = 1, y = 0; round < 24; round++) { | |
[x, y] = [y, (2 * x + 3 * y) % 5]; | |
SHA3_PI.push(2 * (5 * y + x)); | |
SHA3_ROTL.push((round + 1) * (round + 2) / 2 % 64); | |
let t = _0n$3; | |
for (let j = 0; j < 7; j++) { | |
R2 = (R2 << _1n$4 ^ (R2 >> _7n) * _0x71n) % _256n; | |
if (R2 & _2n$4) | |
t ^= _1n$4 << (_1n$4 << /* @__PURE__ */ BigInt(j)) - _1n$4; | |
} | |
_SHA3_IOTA.push(t); | |
} | |
const [SHA3_IOTA_H, SHA3_IOTA_L] = /* @__PURE__ */ split(_SHA3_IOTA, true); | |
const rotlH = (h, l, s) => s > 32 ? rotlBH(h, l, s) : rotlSH(h, l, s); | |
const rotlL = (h, l, s) => s > 32 ? rotlBL(h, l, s) : rotlSL(h, l, s); | |
function keccakP(s, rounds = 24) { | |
const B = new Uint32Array(5 * 2); | |
for (let round = 24 - rounds; round < 24; round++) { | |
for (let x = 0; x < 10; x++) | |
B[x] = s[x] ^ s[x + 10] ^ s[x + 20] ^ s[x + 30] ^ s[x + 40]; | |
for (let x = 0; x < 10; x += 2) { | |
const idx1 = (x + 8) % 10; | |
const idx0 = (x + 2) % 10; | |
const B0 = B[idx0]; | |
const B1 = B[idx0 + 1]; | |
const Th = rotlH(B0, B1, 1) ^ B[idx1]; | |
const Tl = rotlL(B0, B1, 1) ^ B[idx1 + 1]; | |
for (let y = 0; y < 50; y += 10) { | |
s[x + y] ^= Th; | |
s[x + y + 1] ^= Tl; | |
} | |
} | |
let curH = s[2]; | |
let curL = s[3]; | |
for (let t = 0; t < 24; t++) { | |
const shift = SHA3_ROTL[t]; | |
const Th = rotlH(curH, curL, shift); | |
const Tl = rotlL(curH, curL, shift); | |
const PI = SHA3_PI[t]; | |
curH = s[PI]; | |
curL = s[PI + 1]; | |
s[PI] = Th; | |
s[PI + 1] = Tl; | |
} | |
for (let y = 0; y < 50; y += 10) { | |
for (let x = 0; x < 10; x++) | |
B[x] = s[y + x]; | |
for (let x = 0; x < 10; x++) | |
s[y + x] ^= ~B[(x + 2) % 10] & B[(x + 4) % 10]; | |
} | |
s[0] ^= SHA3_IOTA_H[round]; | |
s[1] ^= SHA3_IOTA_L[round]; | |
} | |
B.fill(0); | |
} | |
class Keccak extends Hash { | |
// NOTE: we accept arguments in bytes instead of bits here. | |
constructor(blockLen, suffix, outputLen, enableXOF = false, rounds = 24) { | |
super(); | |
this.blockLen = blockLen; | |
this.suffix = suffix; | |
this.outputLen = outputLen; | |
this.enableXOF = enableXOF; | |
this.rounds = rounds; | |
this.pos = 0; | |
this.posOut = 0; | |
this.finished = false; | |
this.destroyed = false; | |
number(outputLen); | |
if (0 >= this.blockLen || this.blockLen >= 200) | |
throw new Error("Sha3 supports only keccak-f1600 function"); | |
this.state = new Uint8Array(200); | |
this.state32 = u32(this.state); | |
} | |
keccak() { | |
if (!isLE) | |
byteSwap32(this.state32); | |
keccakP(this.state32, this.rounds); | |
if (!isLE) | |
byteSwap32(this.state32); | |
this.posOut = 0; | |
this.pos = 0; | |
} | |
update(data) { | |
exists(this); | |
const { blockLen, state } = this; | |
data = toBytes$2(data); | |
const len = data.length; | |
for (let pos = 0; pos < len; ) { | |
const take = Math.min(blockLen - this.pos, len - pos); | |
for (let i = 0; i < take; i++) | |
state[this.pos++] ^= data[pos++]; | |
if (this.pos === blockLen) | |
this.keccak(); | |
} | |
return this; | |
} | |
finish() { | |
if (this.finished) | |
return; | |
this.finished = true; | |
const { state, suffix, pos, blockLen } = this; | |
state[pos] ^= suffix; | |
if ((suffix & 128) !== 0 && pos === blockLen - 1) | |
this.keccak(); | |
state[blockLen - 1] ^= 128; | |
this.keccak(); | |
} | |
writeInto(out) { | |
exists(this, false); | |
bytes(out); | |
this.finish(); | |
const bufferOut = this.state; | |
const { blockLen } = this; | |
for (let pos = 0, len = out.length; pos < len; ) { | |
if (this.posOut >= blockLen) | |
this.keccak(); | |
const take = Math.min(blockLen - this.posOut, len - pos); | |
out.set(bufferOut.subarray(this.posOut, this.posOut + take), pos); | |
this.posOut += take; | |
pos += take; | |
} | |
return out; | |
} | |
xofInto(out) { | |
if (!this.enableXOF) | |
throw new Error("XOF is not possible for this instance"); | |
return this.writeInto(out); | |
} | |
xof(bytes2) { | |
number(bytes2); | |
return this.xofInto(new Uint8Array(bytes2)); | |
} | |
digestInto(out) { | |
output(out, this); | |
if (this.finished) | |
throw new Error("digest() was already called"); | |
this.writeInto(out); | |
this.destroy(); | |
return out; | |
} | |
digest() { | |
return this.digestInto(new Uint8Array(this.outputLen)); | |
} | |
destroy() { | |
this.destroyed = true; | |
this.state.fill(0); | |
} | |
_cloneInto(to) { | |
const { blockLen, suffix, outputLen, rounds, enableXOF } = this; | |
to || (to = new Keccak(blockLen, suffix, outputLen, enableXOF, rounds)); | |
to.state32.set(this.state32); | |
to.pos = this.pos; | |
to.posOut = this.posOut; | |
to.finished = this.finished; | |
to.rounds = rounds; | |
to.suffix = suffix; | |
to.outputLen = outputLen; | |
to.enableXOF = enableXOF; | |
to.destroyed = this.destroyed; | |
return to; | |
} | |
} | |
const gen = (suffix, blockLen, outputLen) => wrapConstructor(() => new Keccak(blockLen, suffix, outputLen)); | |
const keccak_256 = /* @__PURE__ */ gen(1, 136, 256 / 8); | |
const keccak256 = (() => { | |
const k = wrapHash(keccak_256); | |
k.create = keccak_256.create; | |
return k; | |
})(); | |
class Account { | |
get version() { | |
if (this._version !== null) { | |
return this._version; | |
} else { | |
throw Error(`version=${this._version} not loaded`); | |
} | |
} | |
set version(_version) { | |
this._version = _version; | |
} | |
get nonce() { | |
if (this._nonce !== null) { | |
return this._nonce; | |
} else { | |
throw Error(`nonce=${this._nonce} not loaded`); | |
} | |
} | |
set nonce(_nonce) { | |
this._nonce = _nonce; | |
} | |
get balance() { | |
if (this._balance !== null) { | |
return this._balance; | |
} else { | |
throw Error(`balance=${this._balance} not loaded`); | |
} | |
} | |
set balance(_balance) { | |
this._balance = _balance; | |
} | |
get storageRoot() { | |
if (this._storageRoot !== null) { | |
return this._storageRoot; | |
} else { | |
throw Error(`storageRoot=${this._storageRoot} not loaded`); | |
} | |
} | |
set storageRoot(_storageRoot) { | |
this._storageRoot = _storageRoot; | |
} | |
get codeHash() { | |
if (this._codeHash !== null) { | |
return this._codeHash; | |
} else { | |
throw Error(`codeHash=${this._codeHash} not loaded`); | |
} | |
} | |
set codeHash(_codeHash) { | |
this._codeHash = _codeHash; | |
} | |
get codeSize() { | |
if (this._codeSize !== null) { | |
return this._codeSize; | |
} else { | |
throw Error(`codeSize=${this._codeSize} not loaded`); | |
} | |
} | |
set codeSize(_codeSize) { | |
this._codeSize = _codeSize; | |
} | |
/** | |
* This constructor assigns and validates the values. | |
* Use the static factory methods to assist in creating an Account from varying data types. | |
* undefined get assigned with the defaults present, but null args are retained as is | |
*/ | |
constructor(nonce = BIGINT_0, balance = BIGINT_0, storageRoot = KECCAK256_RLP, codeHash = KECCAK256_NULL, codeSize = 0, version = 0) { | |
this._nonce = null; | |
this._balance = null; | |
this._storageRoot = null; | |
this._codeHash = null; | |
this._codeSize = null; | |
this._version = null; | |
this._nonce = nonce; | |
this._balance = balance; | |
this._storageRoot = storageRoot; | |
this._codeHash = codeHash; | |
if (codeSize === null && codeHash !== null && !this.isContract()) { | |
codeSize = 0; | |
} | |
this._codeSize = codeSize; | |
this._version = version; | |
this._validate(); | |
} | |
_validate() { | |
if (this._nonce !== null && this._nonce < BIGINT_0) { | |
throw new Error("nonce must be greater than zero"); | |
} | |
if (this._balance !== null && this._balance < BIGINT_0) { | |
throw new Error("balance must be greater than zero"); | |
} | |
if (this._storageRoot !== null && this._storageRoot.length !== 32) { | |
throw new Error("storageRoot must have a length of 32"); | |
} | |
if (this._codeHash !== null && this._codeHash.length !== 32) { | |
throw new Error("codeHash must have a length of 32"); | |
} | |
if (this._codeSize !== null && this._codeSize < BIGINT_0) { | |
throw new Error("codeSize must be greater than zero"); | |
} | |
} | |
/** | |
* Returns an array of Uint8Arrays of the raw bytes for the account, in order. | |
*/ | |
raw() { | |
return [ | |
bigIntToUnpaddedBytes(this.nonce), | |
bigIntToUnpaddedBytes(this.balance), | |
this.storageRoot, | |
this.codeHash | |
]; | |
} | |
/** | |
* Returns the RLP serialization of the account as a `Uint8Array`. | |
*/ | |
serialize() { | |
return RLP.encode(this.raw()); | |
} | |
serializeWithPartialInfo() { | |
const partialData = []; | |
const zeroEncoded = intToUnpaddedBytes(0); | |
const oneEncoded = intToUnpaddedBytes(1); | |
if (this._nonce !== null) { | |
partialData.push([oneEncoded, bigIntToUnpaddedBytes(this._nonce)]); | |
} else { | |
partialData.push([zeroEncoded]); | |
} | |
if (this._balance !== null) { | |
partialData.push([oneEncoded, bigIntToUnpaddedBytes(this._balance)]); | |
} else { | |
partialData.push([zeroEncoded]); | |
} | |
if (this._storageRoot !== null) { | |
partialData.push([oneEncoded, this._storageRoot]); | |
} else { | |
partialData.push([zeroEncoded]); | |
} | |
if (this._codeHash !== null) { | |
partialData.push([oneEncoded, this._codeHash]); | |
} else { | |
partialData.push([zeroEncoded]); | |
} | |
if (this._codeSize !== null) { | |
partialData.push([oneEncoded, intToUnpaddedBytes(this._codeSize)]); | |
} else { | |
partialData.push([zeroEncoded]); | |
} | |
if (this._version !== null) { | |
partialData.push([oneEncoded, intToUnpaddedBytes(this._version)]); | |
} else { | |
partialData.push([zeroEncoded]); | |
} | |
return RLP.encode(partialData); | |
} | |
/** | |
* Returns a `Boolean` determining if the account is a contract. | |
*/ | |
isContract() { | |
if (this._codeHash === null && this._codeSize === null) { | |
throw Error(`Insufficient data as codeHash=null and codeSize=null`); | |
} | |
return this._codeHash !== null && !equalsBytes(this._codeHash, KECCAK256_NULL) || this._codeSize !== null && this._codeSize !== 0; | |
} | |
/** | |
* Returns a `Boolean` determining if the account is empty complying to the definition of | |
* account emptiness in [EIP-161](https://eips.ethereum.org/EIPS/eip-161): | |
* "An account is considered empty when it has no code and zero nonce and zero balance." | |
*/ | |
isEmpty() { | |
if (this._balance !== null && this.balance !== BIGINT_0 || this._nonce === null && this.nonce !== BIGINT_0 || this._codeHash !== null && !equalsBytes(this.codeHash, KECCAK256_NULL)) { | |
return false; | |
} | |
return this.balance === BIGINT_0 && this.nonce === BIGINT_0 && equalsBytes(this.codeHash, KECCAK256_NULL); | |
} | |
} | |
const generateAddress = function(from, nonce) { | |
assertIsBytes(from); | |
assertIsBytes(nonce); | |
if (bytesToBigInt(nonce) === BIGINT_0) { | |
return keccak256(RLP.encode([from, Uint8Array.from([])])).subarray(-20); | |
} | |
return keccak256(RLP.encode([from, nonce])).subarray(-20); | |
}; | |
const generateAddress2 = function(from, salt, initCode) { | |
assertIsBytes(from); | |
assertIsBytes(salt); | |
assertIsBytes(initCode); | |
if (from.length !== 20) { | |
throw new Error("Expected from to be of length 20"); | |
} | |
if (salt.length !== 32) { | |
throw new Error("Expected salt to be of length 32"); | |
} | |
const address = keccak256(concatBytes$1(hexToBytes$1("0xff"), from, salt, keccak256(initCode))); | |
return address.subarray(-20); | |
}; | |
const pubToAddress = function(pubKey, sanitize = false) { | |
assertIsBytes(pubKey); | |
if (sanitize && pubKey.length !== 64) { | |
pubKey = secp256k1.ProjectivePoint.fromHex(pubKey).toRawBytes(false).slice(1); | |
} | |
if (pubKey.length !== 64) { | |
throw new Error("Expected pubKey to be of length 64"); | |
} | |
return keccak256(pubKey).subarray(-20); | |
}; | |
const publicToAddress = pubToAddress; | |
class Address { | |
constructor(bytes2) { | |
if (bytes2.length !== 20) { | |
throw new Error("Invalid address length"); | |
} | |
this.bytes = bytes2; | |
} | |
/** | |
* Is address equal to another. | |
*/ | |
equals(address) { | |
return equalsBytes(this.bytes, address.bytes); | |
} | |
/** | |
* Is address zero. | |
*/ | |
isZero() { | |
return this.equals(new Address(zeros(20))); | |
} | |
/** | |
* True if address is in the address range defined | |
* by EIP-1352 | |
*/ | |
isPrecompileOrSystemAddress() { | |
const address = bytesToBigInt(this.bytes); | |
const rangeMin = BIGINT_0; | |
const rangeMax = BigInt("0xffff"); | |
return address >= rangeMin && address <= rangeMax; | |
} | |
/** | |
* Returns hex encoding of address. | |
*/ | |
toString() { | |
return bytesToHex$1(this.bytes); | |
} | |
/** | |
* Returns a new Uint8Array representation of address. | |
*/ | |
toBytes() { | |
return new Uint8Array(this.bytes); | |
} | |
} | |
function createZeroAddress() { | |
return new Address(zeros(20)); | |
} | |
function createAddressFromBigInt(value) { | |
const bytes2 = bigIntToBytes(value); | |
if (bytes2.length > 20) { | |
throw new Error(`Invalid address, too long: ${bytes2.length}`); | |
} | |
return new Address(setLengthLeft(bytes2, 20)); | |
} | |
var TypeOutput; | |
(function(TypeOutput2) { | |
TypeOutput2[TypeOutput2["Number"] = 0] = "Number"; | |
TypeOutput2[TypeOutput2["BigInt"] = 1] = "BigInt"; | |
TypeOutput2[TypeOutput2["Uint8Array"] = 2] = "Uint8Array"; | |
TypeOutput2[TypeOutput2["PrefixedHexString"] = 3] = "PrefixedHexString"; | |
})(TypeOutput = TypeOutput || (TypeOutput = {})); | |
function toType(input, outputType) { | |
if (input === null) { | |
return null; | |
} | |
if (input === void 0) { | |
return void 0; | |
} | |
if (typeof input === "string" && !isHexString$1(input)) { | |
throw new Error(`A string must be provided with a 0x-prefix, given: ${input}`); | |
} else if (typeof input === "number" && !Number.isSafeInteger(input)) { | |
throw new Error("The provided number is greater than MAX_SAFE_INTEGER (please use an alternative input type)"); | |
} | |
const output2 = toBytes$1(input); | |
switch (outputType) { | |
case TypeOutput.Uint8Array: | |
return output2; | |
case TypeOutput.BigInt: | |
return bytesToBigInt(output2); | |
case TypeOutput.Number: { | |
const bigInt = bytesToBigInt(output2); | |
if (bigInt > BigInt(Number.MAX_SAFE_INTEGER)) { | |
throw new Error("The provided number is greater than MAX_SAFE_INTEGER (please use an alternative output type)"); | |
} | |
return Number(bigInt); | |
} | |
case TypeOutput.PrefixedHexString: | |
return bytesToHex$1(output2); | |
default: | |
throw new Error("unknown outputType"); | |
} | |
} | |
function calculateSigRecovery(v, chainId) { | |
if (v === BIGINT_0 || v === BIGINT_1) | |
return v; | |
if (chainId === void 0) { | |
return v - BIGINT_27; | |
} | |
return v - (chainId * BIGINT_2 + BigInt(35)); | |
} | |
function isValidSigRecovery(recovery) { | |
return recovery === BIGINT_0 || recovery === BIGINT_1; | |
} | |
const ecrecover = function(msgHash, v, r, s, chainId) { | |
const signature = concatBytes$1(setLengthLeft(r, 32), setLengthLeft(s, 32)); | |
const recovery = calculateSigRecovery(v, chainId); | |
if (!isValidSigRecovery(recovery)) { | |
throw new Error("Invalid signature v value"); | |
} | |
const sig = secp256k1.Signature.fromCompact(signature).addRecoveryBit(Number(recovery)); | |
const senderPubKey = sig.recoverPublicKey(msgHash); | |
return senderPubKey.toRawBytes(false).slice(1); | |
}; | |
function getDefaultExportFromCjs(x) { | |
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x; | |
} | |
var events = { exports: {} }; | |
events.exports; | |
var R = typeof Reflect === "object" ? Reflect : null; | |
var ReflectApply = R && typeof R.apply === "function" ? R.apply : function ReflectApply2(target, receiver, args) { | |
return Function.prototype.apply.call(target, receiver, args); | |
}; | |
var ReflectOwnKeys; | |
if (R && typeof R.ownKeys === "function") { | |
ReflectOwnKeys = R.ownKeys; | |
} else if (Object.getOwnPropertySymbols) { | |
ReflectOwnKeys = function ReflectOwnKeys2(target) { | |
return Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target)); | |
}; | |
} else { | |
ReflectOwnKeys = function ReflectOwnKeys2(target) { | |
return Object.getOwnPropertyNames(target); | |
}; | |
} | |
function ProcessEmitWarning(warning) { | |
if (console && console.warn) console.warn(warning); | |
} | |
var NumberIsNaN = Number.isNaN || function NumberIsNaN2(value) { | |
return value !== value; | |
}; | |
function EventEmitter() { | |
EventEmitter.init.call(this); | |
} | |
events.exports = EventEmitter; | |
events.exports.once = once2; | |
EventEmitter.EventEmitter = EventEmitter; | |
EventEmitter.prototype._events = void 0; | |
EventEmitter.prototype._eventsCount = 0; | |
EventEmitter.prototype._maxListeners = void 0; | |
var defaultMaxListeners = 10; | |
function checkListener(listener) { | |
if (typeof listener !== "function") { | |
throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof listener); | |
} | |
} | |
Object.defineProperty(EventEmitter, "defaultMaxListeners", { | |
enumerable: true, | |
get: function() { | |
return defaultMaxListeners; | |
}, | |
set: function(arg) { | |
if (typeof arg !== "number" || arg < 0 || NumberIsNaN(arg)) { | |
throw new RangeError('The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received ' + arg + "."); | |
} | |
defaultMaxListeners = arg; | |
} | |
}); | |
EventEmitter.init = function() { | |
if (this._events === void 0 || this._events === Object.getPrototypeOf(this)._events) { | |
this._events = /* @__PURE__ */ Object.create(null); | |
this._eventsCount = 0; | |
} | |
this._maxListeners = this._maxListeners || void 0; | |
}; | |
EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) { | |
if (typeof n !== "number" || n < 0 || NumberIsNaN(n)) { | |
throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received ' + n + "."); | |
} | |
this._maxListeners = n; | |
return this; | |
}; | |
function _getMaxListeners(that) { | |
if (that._maxListeners === void 0) | |
return EventEmitter.defaultMaxListeners; | |
return that._maxListeners; | |
} | |
EventEmitter.prototype.getMaxListeners = function getMaxListeners() { | |
return _getMaxListeners(this); | |
}; | |
EventEmitter.prototype.emit = function emit(type) { | |
var args = []; | |
for (var i = 1; i < arguments.length; i++) args.push(arguments[i]); | |
var doError = type === "error"; | |
var events2 = this._events; | |
if (events2 !== void 0) | |
doError = doError && events2.error === void 0; | |
else if (!doError) | |
return false; | |
if (doError) { | |
var er; | |
if (args.length > 0) | |
er = args[0]; | |
if (er instanceof Error) { | |
throw er; | |
} | |
var err = new Error("Unhandled error." + (er ? " (" + er.message + ")" : "")); | |
err.context = er; | |
throw err; | |
} | |
var handler = events2[type]; | |
if (handler === void 0) | |
return false; | |
if (typeof handler === "function") { | |
ReflectApply(handler, this, args); | |
} else { | |
var len = handler.length; | |
var listeners2 = arrayClone(handler, len); | |
for (var i = 0; i < len; ++i) | |
ReflectApply(listeners2[i], this, args); | |
} | |
return true; | |
}; | |
function _addListener(target, type, listener, prepend) { | |
var m; | |
var events2; | |
var existing; | |
checkListener(listener); | |
events2 = target._events; | |
if (events2 === void 0) { | |
events2 = target._events = /* @__PURE__ */ Object.create(null); | |
target._eventsCount = 0; | |
} else { | |
if (events2.newListener !== void 0) { | |
target.emit( | |
"newListener", | |
type, | |
listener.listener ? listener.listener : listener | |
); | |
events2 = target._events; | |
} | |
existing = events2[type]; | |
} | |
if (existing === void 0) { | |
existing = events2[type] = listener; | |
++target._eventsCount; | |
} else { | |
if (typeof existing === "function") { | |
existing = events2[type] = prepend ? [listener, existing] : [existing, listener]; | |
} else if (prepend) { | |
existing.unshift(listener); | |
} else { | |
existing.push(listener); | |
} | |
m = _getMaxListeners(target); | |
if (m > 0 && existing.length > m && !existing.warned) { | |
existing.warned = true; | |
var w = new Error("Possible EventEmitter memory leak detected. " + existing.length + " " + String(type) + " listeners added. Use emitter.setMaxListeners() to increase limit"); | |
w.name = "MaxListenersExceededWarning"; | |
w.emitter = target; | |
w.type = type; | |
w.count = existing.length; | |
ProcessEmitWarning(w); | |
} | |
} | |
return target; | |
} | |
EventEmitter.prototype.addListener = function addListener(type, listener) { | |
return _addListener(this, type, listener, false); | |
}; | |
EventEmitter.prototype.on = EventEmitter.prototype.addListener; | |
EventEmitter.prototype.prependListener = function prependListener(type, listener) { | |
return _addListener(this, type, listener, true); | |
}; | |
function onceWrapper() { | |
if (!this.fired) { | |
this.target.removeListener(this.type, this.wrapFn); | |
this.fired = true; | |
if (arguments.length === 0) | |
return this.listener.call(this.target); | |
return this.listener.apply(this.target, arguments); | |
} | |
} | |
function _onceWrap(target, type, listener) { | |
var state = { fired: false, wrapFn: void 0, target, type, listener }; | |
var wrapped = onceWrapper.bind(state); | |
wrapped.listener = listener; | |
state.wrapFn = wrapped; | |
return wrapped; | |
} | |
EventEmitter.prototype.once = function once(type, listener) { | |
checkListener(listener); | |
this.on(type, _onceWrap(this, type, listener)); | |
return this; | |
}; | |
EventEmitter.prototype.prependOnceListener = function prependOnceListener(type, listener) { | |
checkListener(listener); | |
this.prependListener(type, _onceWrap(this, type, listener)); | |
return this; | |
}; | |
EventEmitter.prototype.removeListener = function removeListener(type, listener) { | |
var list, events2, position, i, originalListener; | |
checkListener(listener); | |
events2 = this._events; | |
if (events2 === void 0) | |
return this; | |
list = events2[type]; | |
if (list === void 0) | |
return this; | |
if (list === listener || list.listener === listener) { | |
if (--this._eventsCount === 0) | |
this._events = /* @__PURE__ */ Object.create(null); | |
else { | |
delete events2[type]; | |
if (events2.removeListener) | |
this.emit("removeListener", type, list.listener || listener); | |
} | |
} else if (typeof list !== "function") { | |
position = -1; | |
for (i = list.length - 1; i >= 0; i--) { | |
if (list[i] === listener || list[i].listener === listener) { | |
originalListener = list[i].listener; | |
position = i; | |
break; | |
} | |
} | |
if (position < 0) | |
return this; | |
if (position === 0) | |
list.shift(); | |
else { | |
spliceOne(list, position); | |
} | |
if (list.length === 1) | |
events2[type] = list[0]; | |
if (events2.removeListener !== void 0) | |
this.emit("removeListener", type, originalListener || listener); | |
} | |
return this; | |
}; | |
EventEmitter.prototype.off = EventEmitter.prototype.removeListener; | |
EventEmitter.prototype.removeAllListeners = function removeAllListeners(type) { | |
var listeners2, events2, i; | |
events2 = this._events; | |
if (events2 === void 0) | |
return this; | |
if (events2.removeListener === void 0) { | |
if (arguments.length === 0) { | |
this._events = /* @__PURE__ */ Object.create(null); | |
this._eventsCount = 0; | |
} else if (events2[type] !== void 0) { | |
if (--this._eventsCount === 0) | |
this._events = /* @__PURE__ */ Object.create(null); | |
else | |
delete events2[type]; | |
} | |
return this; | |
} | |
if (arguments.length === 0) { | |
var keys = Object.keys(events2); | |
var key; | |
for (i = 0; i < keys.length; ++i) { | |
key = keys[i]; | |
if (key === "removeListener") continue; | |
this.removeAllListeners(key); | |
} | |
this.removeAllListeners("removeListener"); | |
this._events = /* @__PURE__ */ Object.create(null); | |
this._eventsCount = 0; | |
return this; | |
} | |
listeners2 = events2[type]; | |
if (typeof listeners2 === "function") { | |
this.removeListener(type, listeners2); | |
} else if (listeners2 !== void 0) { | |
for (i = listeners2.length - 1; i >= 0; i--) { | |
this.removeListener(type, listeners2[i]); | |
} | |
} | |
return this; | |
}; | |
function _listeners(target, type, unwrap) { | |
var events2 = target._events; | |
if (events2 === void 0) | |
return []; | |
var evlistener = events2[type]; | |
if (evlistener === void 0) | |
return []; | |
if (typeof evlistener === "function") | |
return unwrap ? [evlistener.listener || evlistener] : [evlistener]; | |
return unwrap ? unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length); | |
} | |
EventEmitter.prototype.listeners = function listeners(type) { | |
return _listeners(this, type, true); | |
}; | |
EventEmitter.prototype.rawListeners = function rawListeners(type) { | |
return _listeners(this, type, false); | |
}; | |
EventEmitter.listenerCount = function(emitter, type) { | |
if (typeof emitter.listenerCount === "function") { | |
return emitter.listenerCount(type); | |
} else { | |
return listenerCount.call(emitter, type); | |
} | |
}; | |
EventEmitter.prototype.listenerCount = listenerCount; | |
function listenerCount(type) { | |
var events2 = this._events; | |
if (events2 !== void 0) { | |
var evlistener = events2[type]; | |
if (typeof evlistener === "function") { | |
return 1; | |
} else if (evlistener !== void 0) { | |
return evlistener.length; | |
} | |
} | |
return 0; | |
} | |
EventEmitter.prototype.eventNames = function eventNames() { | |
return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : []; | |
}; | |
function arrayClone(arr, n) { | |
var copy = new Array(n); | |
for (var i = 0; i < n; ++i) | |
copy[i] = arr[i]; | |
return copy; | |
} | |
function spliceOne(list, index) { | |
for (; index + 1 < list.length; index++) | |
list[index] = list[index + 1]; | |
list.pop(); | |
} | |
function unwrapListeners(arr) { | |
var ret = new Array(arr.length); | |
for (var i = 0; i < ret.length; ++i) { | |
ret[i] = arr[i].listener || arr[i]; | |
} | |
return ret; | |
} | |
function once2(emitter, name) { | |
return new Promise(function(resolve, reject) { | |
function errorListener(err) { | |
emitter.removeListener(name, resolver); | |
reject(err); | |
} | |
function resolver() { | |
if (typeof emitter.removeListener === "function") { | |
emitter.removeListener("error", errorListener); | |
} | |
resolve([].slice.call(arguments)); | |
} | |
eventTargetAgnosticAddListener(emitter, name, resolver, { once: true }); | |
if (name !== "error") { | |
addErrorHandlerIfEventEmitter(emitter, errorListener, { once: true }); | |
} | |
}); | |
} | |
function addErrorHandlerIfEventEmitter(emitter, handler, flags) { | |
if (typeof emitter.on === "function") { | |
eventTargetAgnosticAddListener(emitter, "error", handler, flags); | |
} | |
} | |
function eventTargetAgnosticAddListener(emitter, name, listener, flags) { | |
if (typeof emitter.on === "function") { | |
if (flags.once) { | |
emitter.once(name, listener); | |
} else { | |
emitter.on(name, listener); | |
} | |
} else if (typeof emitter.addEventListener === "function") { | |
emitter.addEventListener(name, function wrapListener(arg) { | |
if (flags.once) { | |
emitter.removeEventListener(name, wrapListener); | |
} | |
listener(arg); | |
}); | |
} else { | |
throw new TypeError('The "emitter" argument must be of type EventEmitter. Received type ' + typeof emitter); | |
} | |
} | |
var eventsExports = events.exports; | |
async function runInSeries(context, tasks, data) { | |
let error; | |
for await (const task of tasks) { | |
try { | |
if (task.length < 2) { | |
task.call(context, data); | |
} else { | |
await new Promise((resolve, reject) => { | |
task.call(context, data, (error2) => { | |
if (error2) { | |
reject(error2); | |
} else { | |
resolve(); | |
} | |
}); | |
}); | |
} | |
} catch (e) { | |
error = e; | |
} | |
} | |
if (error) { | |
throw error; | |
} | |
} | |
class AsyncEventEmitter extends eventsExports.EventEmitter { | |
emit(event, ...args) { | |
let [data, callback] = args; | |
const self = this; | |
let listeners2 = self._events[event] ?? []; | |
if (callback === void 0 && typeof data === "function") { | |
callback = data; | |
data = void 0; | |
} | |
if (event === "newListener" || event === "removeListener") { | |
data = { | |
event: data, | |
fn: callback | |
}; | |
callback = void 0; | |
} | |
listeners2 = Array.isArray(listeners2) ? listeners2 : [listeners2]; | |
runInSeries(self, listeners2.slice(), data).then(callback).catch(callback); | |
return self.listenerCount(event) > 0; | |
} | |
once(event, listener) { | |
const self = this; | |
let g; | |
if (typeof listener !== "function") { | |
throw new TypeError("listener must be a function"); | |
} | |
if (listener.length >= 2) { | |
g = function(e, next) { | |
self.removeListener(event, g); | |
void listener(e, next); | |
}; | |
} else { | |
g = function(e) { | |
self.removeListener(event, g); | |
void listener(e, g); | |
}; | |
} | |
self.on(event, g); | |
return self; | |
} | |
first(event, listener) { | |
let listeners2 = this._events[event] ?? []; | |
if (typeof listener !== "function") { | |
throw new TypeError("listener must be a function"); | |
} | |
if (!Array.isArray(listeners2)) { | |
this._events[event] = listeners2 = [listeners2]; | |
} | |
listeners2.unshift(listener); | |
return this; | |
} | |
before(event, target, listener) { | |
return this.beforeOrAfter(event, target, listener); | |
} | |
after(event, target, listener) { | |
return this.beforeOrAfter(event, target, listener, "after"); | |
} | |
beforeOrAfter(event, target, listener, beforeOrAfter) { | |
let listeners2 = this._events[event] ?? []; | |
let i; | |
let index; | |
const add = beforeOrAfter === "after" ? 1 : 0; | |
if (typeof listener !== "function") { | |
throw new TypeError("listener must be a function"); | |
} | |
if (typeof target !== "function") { | |
throw new TypeError("target must be a function"); | |
} | |
if (!Array.isArray(listeners2)) { | |
this._events[event] = listeners2 = [listeners2]; | |
} | |
index = listeners2.length; | |
for (i = listeners2.length; i--; ) { | |
if (listeners2[i] === target) { | |
index = i + add; | |
break; | |
} | |
} | |
listeners2.splice(index, 0, listener); | |
return this; | |
} | |
on(event, listener) { | |
return super.on(event, listener); | |
} | |
addListener(event, listener) { | |
return super.addListener(event, listener); | |
} | |
prependListener(event, listener) { | |
return super.prependListener(event, listener); | |
} | |
prependOnceListener(event, listener) { | |
return super.prependOnceListener(event, listener); | |
} | |
removeAllListeners(event) { | |
return super.removeAllListeners(event); | |
} | |
removeListener(event, listener) { | |
return super.removeListener(event, listener); | |
} | |
eventNames() { | |
return super.eventNames(); | |
} | |
listeners(event) { | |
return super.listeners(event); | |
} | |
listenerCount(event) { | |
return super.listenerCount(event); | |
} | |
getMaxListeners() { | |
return super.getMaxListeners(); | |
} | |
setMaxListeners(maxListeners) { | |
return super.setMaxListeners(maxListeners); | |
} | |
} | |
const sha256 = wrapHash(sha256$1); | |
const computeVersionedHash = (commitment, blobCommitmentVersion) => { | |
const computedVersionedHash = new Uint8Array(32); | |
computedVersionedHash.set([blobCommitmentVersion], 0); | |
computedVersionedHash.set(sha256(commitment).subarray(1), 1); | |
return computedVersionedHash; | |
}; | |
var VerkleLeafType; | |
(function(VerkleLeafType2) { | |
VerkleLeafType2[VerkleLeafType2["BasicData"] = 0] = "BasicData"; | |
VerkleLeafType2[VerkleLeafType2["CodeHash"] = 1] = "CodeHash"; | |
})(VerkleLeafType = VerkleLeafType || (VerkleLeafType = {})); | |
const VERKLE_BASIC_DATA_LEAF_KEY = intToBytes(VerkleLeafType.BasicData); | |
const VERKLE_CODE_HASH_LEAF_KEY = intToBytes(VerkleLeafType.CodeHash); | |
const VERKLE_CODE_CHUNK_SIZE = 31; | |
const VERKLE_HEADER_STORAGE_OFFSET = 64; | |
const VERKLE_CODE_OFFSET = 128; | |
const VERKLE_NODE_WIDTH = 256; | |
const VERKLE_MAIN_STORAGE_OFFSET = BigInt(256) ** BigInt(VERKLE_CODE_CHUNK_SIZE); | |
function getVerkleTreeIndicesForStorageSlot(storageKey) { | |
let position; | |
if (storageKey < VERKLE_CODE_OFFSET - VERKLE_HEADER_STORAGE_OFFSET) { | |
position = BigInt(VERKLE_HEADER_STORAGE_OFFSET) + storageKey; | |
} else { | |
position = VERKLE_MAIN_STORAGE_OFFSET + storageKey; | |
} | |
const treeIndex = position / BigInt(VERKLE_NODE_WIDTH); | |
const subIndex = Number(position % BigInt(VERKLE_NODE_WIDTH)); | |
return { treeIndex, subIndex }; | |
} | |
let TABLE = [ | |
0, | |
1996959894, | |
3993919788, | |
2567524794, | |
124634137, | |
1886057615, | |
3915621685, | |
2657392035, | |
249268274, | |
2044508324, | |
3772115230, | |
2547177864, | |
162941995, | |
2125561021, | |
3887607047, | |
2428444049, | |
498536548, | |
1789927666, | |
4089016648, | |
2227061214, | |
450548861, | |
1843258603, | |
4107580753, | |
2211677639, | |
325883990, | |
1684777152, | |
4251122042, | |
2321926636, | |
335633487, | |
1661365465, | |
4195302755, | |
2366115317, | |
997073096, | |
1281953886, | |
3579855332, | |
2724688242, | |
1006888145, | |
1258607687, | |
3524101629, | |
2768942443, | |
901097722, | |
1119000684, | |
3686517206, | |
2898065728, | |
853044451, | |
1172266101, | |
3705015759, | |
2882616665, | |
651767980, | |
1373503546, | |
3369554304, | |
3218104598, | |
565507253, | |
1454621731, | |
3485111705, | |
3099436303, | |
671266974, | |
1594198024, | |
3322730930, | |
2970347812, | |
795835527, | |
1483230225, | |
3244367275, | |
3060149565, | |
1994146192, | |
31158534, | |
2563907772, | |
4023717930, | |
1907459465, | |
112637215, | |
2680153253, | |
3904427059, | |
2013776290, | |
251722036, | |
2517215374, | |
3775830040, | |
2137656763, | |
141376813, | |
2439277719, | |
3865271297, | |
1802195444, | |
476864866, | |
2238001368, | |
4066508878, | |
1812370925, | |
453092731, | |
2181625025, | |
4111451223, | |
1706088902, | |
314042704, | |
2344532202, | |
4240017532, | |
1658658271, | |
366619977, | |
2362670323, | |
4224994405, | |
1303535960, | |
984961486, | |
2747007092, | |
3569037538, | |
1256170817, | |
1037604311, | |
2765210733, | |
3554079995, | |
1131014506, | |
879679996, | |
2909243462, | |
3663771856, | |
1141124467, | |
855842277, | |
2852801631, | |
3708648649, | |
1342533948, | |
654459306, | |
3188396048, | |
3373015174, | |
1466479909, | |
544179635, | |
3110523913, | |
3462522015, | |
1591671054, | |
702138776, | |
2966460450, | |
3352799412, | |
1504918807, | |
783551873, | |
3082640443, | |
3233442989, | |
3988292384, | |
2596254646, | |
62317068, | |
1957810842, | |
3939845945, | |
2647816111, | |
81470997, | |
1943803523, | |
3814918930, | |
2489596804, | |
225274430, | |
2053790376, | |
3826175755, | |
2466906013, | |
167816743, | |
2097651377, | |
4027552580, | |
2265490386, | |
503444072, | |
1762050814, | |
4150417245, | |
2154129355, | |
426522225, | |
1852507879, | |
4275313526, | |
2312317920, | |
282753626, | |
1742555852, | |
4189708143, | |
2394877945, | |
397917763, | |
1622183637, | |
3604390888, | |
2714866558, | |
953729732, | |
1340076626, | |
3518719985, | |
2797360999, | |
1068828381, | |
1219638859, | |
3624741850, | |
2936675148, | |
906185462, | |
1090812512, | |
3747672003, | |
2825379669, | |
829329135, | |
1181335161, | |
3412177804, | |
3160834842, | |
628085408, | |
1382605366, | |
3423369109, | |
3138078467, | |
570562233, | |
1426400815, | |
3317316542, | |
2998733608, | |
733239954, | |
1555261956, | |
3268935591, | |
3050360625, | |
752459403, | |
1541320221, | |
2607071920, | |
3965973030, | |
1969922972, | |
40735498, | |
2617837225, | |
3943577151, | |
1913087877, | |
83908371, | |
2512341634, | |
3803740692, | |
2075208622, | |
213261112, | |
2463272603, | |
3855990285, | |
2094854071, | |
198958881, | |
2262029012, | |
4057260610, | |
1759359992, | |
534414190, | |
2176718541, | |
4139329115, | |
1873836001, | |
414664567, | |
2282248934, | |
4279200368, | |
1711684554, | |
285281116, | |
2405801727, | |
4167216745, | |
1634467795, | |
376229701, | |
2685067896, | |
3608007406, | |
1308918612, | |
956543938, | |
2808555105, | |
3495958263, | |
1231636301, | |
1047427035, | |
2932959818, | |
3654703836, | |
1088359270, | |
936918e3, | |
2847714899, | |
3736837829, | |
1202900863, | |
817233897, | |
3183342108, | |
3401237130, | |
1404277552, | |
615818150, | |
3134207493, | |
3453421203, | |
1423857449, | |
601450431, | |
3009837614, | |
3294710456, | |
1567103746, | |
711928724, | |
3020668471, | |
3272380065, | |
1510334235, | |
755167117 | |
]; | |
if (typeof Int32Array !== "undefined") { | |
TABLE = new Int32Array(TABLE); | |
} | |
const crc = (current, previous) => { | |
let crc2 = ~~previous ^ -1; | |
for (let index = 0; index < current.length; index++) { | |
crc2 = TABLE[(crc2 ^ current[index]) & 255] ^ crc2 >>> 8; | |
} | |
return crc2 ^ -1; | |
}; | |
const crc32 = (current, previous) => { | |
return crc(current, previous) >>> 0; | |
}; | |
var Chain; | |
(function(Chain2) { | |
Chain2[Chain2["Mainnet"] = 1] = "Mainnet"; | |
Chain2[Chain2["Goerli"] = 5] = "Goerli"; | |
Chain2[Chain2["Sepolia"] = 11155111] = "Sepolia"; | |
Chain2[Chain2["Holesky"] = 17e3] = "Holesky"; | |
Chain2[Chain2["Kaustinen6"] = 69420] = "Kaustinen6"; | |
})(Chain = Chain || (Chain = {})); | |
({ | |
[Chain.Mainnet]: { | |
name: "mainnet", | |
blockNumber: BIGINT_0, | |
stateRoot: hexToBytes$1("0xd7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544") | |
}, | |
[Chain.Goerli]: { | |
name: "goerli", | |
blockNumber: BIGINT_0, | |
stateRoot: hexToBytes$1("0x5d6cded585e73c4e322c30c2f782a336316f17dd85a4863b9d838d2d4b8b3008") | |
}, | |
[Chain.Sepolia]: { | |
name: "sepolia", | |
blockNumber: BIGINT_0, | |
stateRoot: hexToBytes$1("0x5eb6e371a698b8d68f665192350ffcecbbbf322916f4b51bd79bb6887da3f494") | |
}, | |
[Chain.Holesky]: { | |
name: "holesky", | |
blockNumber: BIGINT_0, | |
stateRoot: hexToBytes$1("0x69d8c9d72f6fa4ad42d4702b433707212f90db395eb54dc20bc85de253788783") | |
}, | |
[Chain.Kaustinen6]: { | |
name: "kaustinen6", | |
blockNumber: BIGINT_0, | |
stateRoot: hexToBytes$1("0x1fbf85345a3cbba9a6d44f991b721e55620a22397c2a93ee8d5011136ac300ee") | |
} | |
}); | |
var Hardfork; | |
(function(Hardfork2) { | |
Hardfork2["Chainstart"] = "chainstart"; | |
Hardfork2["Homestead"] = "homestead"; | |
Hardfork2["Dao"] = "dao"; | |
Hardfork2["TangerineWhistle"] = "tangerineWhistle"; | |
Hardfork2["SpuriousDragon"] = "spuriousDragon"; | |
Hardfork2["Byzantium"] = "byzantium"; | |
Hardfork2["Constantinople"] = "constantinople"; | |
Hardfork2["Petersburg"] = "petersburg"; | |
Hardfork2["Istanbul"] = "istanbul"; | |
Hardfork2["MuirGlacier"] = "muirGlacier"; | |
Hardfork2["Berlin"] = "berlin"; | |
Hardfork2["London"] = "london"; | |
Hardfork2["ArrowGlacier"] = "arrowGlacier"; | |
Hardfork2["GrayGlacier"] = "grayGlacier"; | |
Hardfork2["MergeForkIdTransition"] = "mergeForkIdTransition"; | |
Hardfork2["Paris"] = "paris"; | |
Hardfork2["Shanghai"] = "shanghai"; | |
Hardfork2["Cancun"] = "cancun"; | |
Hardfork2["Prague"] = "prague"; | |
Hardfork2["Osaka"] = "osaka"; | |
})(Hardfork = Hardfork || (Hardfork = {})); | |
var ConsensusType; | |
(function(ConsensusType2) { | |
ConsensusType2["ProofOfStake"] = "pos"; | |
ConsensusType2["ProofOfWork"] = "pow"; | |
ConsensusType2["ProofOfAuthority"] = "poa"; | |
})(ConsensusType = ConsensusType || (ConsensusType = {})); | |
var ConsensusAlgorithm; | |
(function(ConsensusAlgorithm2) { | |
ConsensusAlgorithm2["Ethash"] = "ethash"; | |
ConsensusAlgorithm2["Clique"] = "clique"; | |
ConsensusAlgorithm2["Casper"] = "casper"; | |
})(ConsensusAlgorithm = ConsensusAlgorithm || (ConsensusAlgorithm = {})); | |
const eipsDict = { | |
/** | |
* Frontier/Chainstart | |
* (there is no Meta-EIP currently for Frontier, so 1 was chosen) | |
*/ | |
1: { | |
minimumHardfork: Hardfork.Chainstart | |
}, | |
/** | |
* Homestead HF Meta EIP | |
*/ | |
606: { | |
minimumHardfork: Hardfork.Chainstart | |
}, | |
/** | |
* TangerineWhistle HF Meta EIP | |
*/ | |
608: { | |
minimumHardfork: Hardfork.Homestead | |
}, | |
/** | |
* Spurious Dragon HF Meta EIP | |
*/ | |
607: { | |
minimumHardfork: Hardfork.TangerineWhistle | |
}, | |
/** | |
* Byzantium HF Meta EIP | |
*/ | |
609: { | |
minimumHardfork: Hardfork.SpuriousDragon | |
}, | |
/** | |
* Constantinople HF Meta EIP | |
*/ | |
1013: { | |
minimumHardfork: Hardfork.Constantinople | |
}, | |
/** | |
* Petersburg HF Meta EIP | |
*/ | |
1716: { | |
minimumHardfork: Hardfork.Constantinople | |
}, | |
/** | |
* Istanbul HF Meta EIP | |
*/ | |
1679: { | |
minimumHardfork: Hardfork.Constantinople | |
}, | |
/** | |
* MuirGlacier HF Meta EIP | |
*/ | |
2384: { | |
minimumHardfork: Hardfork.Istanbul | |
}, | |
/** | |
* Description : SWAPN, DUPN and EXCHANGE instructions | |
* URL : https://github.com/ethereum/EIPs/blob/bd421962b4e241aa2b00a85d9cf4e57770bdb954/EIPS/eip-663.md | |
* Status : Review | |
*/ | |
663: { | |
minimumHardfork: Hardfork.Chainstart, | |
requiredEIPs: [3540, 5450] | |
}, | |
/** | |
* Description : Transient storage opcodes | |
* URL : https://eips.ethereum.org/EIPS/eip-1153 | |
* Status : Final | |
*/ | |
1153: { | |
minimumHardfork: Hardfork.Chainstart | |
}, | |
/** | |
* Description : Fee market change for ETH 1.0 chain | |
* URL : https://eips.ethereum.org/EIPS/eip-1559 | |
* Status : Final | |
*/ | |
1559: { | |
minimumHardfork: Hardfork.Berlin, | |
requiredEIPs: [2930] | |
}, | |
/** | |
* Description : ModExp gas cost | |
* URL : https://eips.ethereum.org/EIPS/eip-2565 | |
* Status : Final | |
*/ | |
2565: { | |
minimumHardfork: Hardfork.Byzantium | |
}, | |
/** | |
* Description : BLS12-381 precompiles | |
* URL : https://eips.ethereum.org/EIPS/eip-2537 | |
* Status : Review | |
*/ | |
2537: { | |
minimumHardfork: Hardfork.Chainstart | |
}, | |
/** | |
* Description : Typed Transaction Envelope | |
* URL : https://eips.ethereum.org/EIPS/eip-2718 | |
* Status : Final | |
*/ | |
2718: { | |
minimumHardfork: Hardfork.Chainstart | |
}, | |
/** | |
* Description : Gas cost increases for state access opcodes | |
* URL : https://eips.ethereum.org/EIPS/eip-2929 | |
* Status : Final | |
*/ | |
2929: { | |
minimumHardfork: Hardfork.Chainstart | |
}, | |
/** | |
* Description : Optional access lists | |
* URL : https://eips.ethereum.org/EIPS/eip-2930 | |
* Status : Final | |
*/ | |
2930: { | |
minimumHardfork: Hardfork.Istanbul, | |
requiredEIPs: [2718, 2929] | |
}, | |
/** | |
* Description : Save historical block hashes in state (Verkle related usage, UNSTABLE) | |
* URL : https://github.com/gballet/EIPs/pull/3/commits/2e9ac09a142b0d9fb4db0b8d4609f92e5d9990c5 | |
* Status : Draft | |
*/ | |
2935: { | |
minimumHardfork: Hardfork.Chainstart | |
}, | |
/** | |
* Description : BASEFEE opcode | |
* URL : https://eips.ethereum.org/EIPS/eip-3198 | |
* Status : Final | |
*/ | |
3198: { | |
minimumHardfork: Hardfork.London | |
}, | |
/** | |
* Description : Reduction in refunds | |
* URL : https://eips.ethereum.org/EIPS/eip-3529 | |
* Status : Final | |
*/ | |
3529: { | |
minimumHardfork: Hardfork.Berlin, | |
requiredEIPs: [2929] | |
}, | |
/** | |
* Description : EVM Object Format (EOF) v1 | |
* URL : https://github.com/ethereum/EIPs/blob/bd421962b4e241aa2b00a85d9cf4e57770bdb954/EIPS/eip-3540.md | |
* Status : Review | |
*/ | |
3540: { | |
minimumHardfork: Hardfork.London, | |
requiredEIPs: [3541, 3860] | |
}, | |
/** | |
* Description : Reject new contracts starting with the 0xEF byte | |
* URL : https://eips.ethereum.org/EIPS/eip-3541 | |
* Status : Final | |
*/ | |
3541: { | |
minimumHardfork: Hardfork.Berlin | |
}, | |
/** | |
* Description : Difficulty Bomb Delay to December 1st 2021 | |
* URL : https://eips.ethereum.org/EIPS/eip-3554 | |
* Status : Final | |
*/ | |
3554: { | |
minimumHardfork: Hardfork.MuirGlacier | |
}, | |
/** | |
* Description : Reject transactions from senders with deployed code | |
* URL : https://eips.ethereum.org/EIPS/eip-3607 | |
* Status : Final | |
*/ | |
3607: { | |
minimumHardfork: Hardfork.Chainstart | |
}, | |
/** | |
* Description : Warm COINBASE | |
* URL : https://eips.ethereum.org/EIPS/eip-3651 | |
* Status : Final | |
*/ | |
3651: { | |
minimumHardfork: Hardfork.London, | |
requiredEIPs: [2929] | |
}, | |
/** | |
* Description : EOF - Code Validation | |
* URL : https://github.com/ethereum/EIPs/blob/bd421962b4e241aa2b00a85d9cf4e57770bdb954/EIPS/eip-3670.md | |
* Status : Review | |
*/ | |
3670: { | |
minimumHardfork: Hardfork.London, | |
requiredEIPs: [3540] | |
}, | |
/** | |
* Description : Upgrade consensus to Proof-of-Stake | |
* URL : https://eips.ethereum.org/EIPS/eip-3675 | |
* Status : Final | |
*/ | |
3675: { | |
minimumHardfork: Hardfork.London | |
}, | |
/** | |
* Description : PUSH0 instruction | |
* URL : https://eips.ethereum.org/EIPS/eip-3855 | |
* Status : Final | |
*/ | |
3855: { | |
minimumHardfork: Hardfork.Chainstart | |
}, | |
/** | |
* Description : Limit and meter initcode | |
* URL : https://eips.ethereum.org/EIPS/eip-3860 | |
* Status : Final | |
*/ | |
3860: { | |
minimumHardfork: Hardfork.SpuriousDragon | |
}, | |
/** | |
* Description : EOF - Static relative jumps | |
* URL : https://github.com/ethereum/EIPs/blob/bd421962b4e241aa2b00a85d9cf4e57770bdb954/EIPS/eip-4200.md | |
* Status : Review | |
*/ | |
4200: { | |
minimumHardfork: Hardfork.London, | |
requiredEIPs: [3540, 3670] | |
}, | |
/** | |
* Description : Difficulty Bomb Delay to June 2022 | |
* URL : https://eips.ethereum.org/EIPS/eip-4345 | |
* Status : Final | |
*/ | |
4345: { | |
minimumHardfork: Hardfork.London | |
}, | |
/** | |
* Description : Supplant DIFFICULTY opcode with PREVRANDAO | |
* URL : https://eips.ethereum.org/EIPS/eip-4399 | |
* Status : Final | |
*/ | |
4399: { | |
minimumHardfork: Hardfork.London | |
}, | |
/** | |
* Description : EOF - Functions | |
* URL : https://github.com/ethereum/EIPs/blob/bd421962b4e241aa2b00a85d9cf4e57770bdb954/EIPS/eip-4750.md | |
* Status : Review | |
*/ | |
4750: { | |
minimumHardfork: Hardfork.London, | |
requiredEIPs: [3540, 3670, 5450] | |
}, | |
/** | |
* Description : Beacon block root in the EVM | |
* URL : https://eips.ethereum.org/EIPS/eip-4788 | |
* Status : Final | |
*/ | |
4788: { | |
minimumHardfork: Hardfork.Cancun | |
}, | |
/** | |
* Description : Shard Blob Transactions | |
* URL : https://eips.ethereum.org/EIPS/eip-4844 | |
* Status : Final | |
*/ | |
4844: { | |
minimumHardfork: Hardfork.Paris, | |
requiredEIPs: [1559, 2718, 2930, 4895] | |
}, | |
/** | |
* Description : Beacon chain push withdrawals as operations | |
* URL : https://eips.ethereum.org/EIPS/eip-4895 | |
* Status : Final | |
*/ | |
4895: { | |
minimumHardfork: Hardfork.Paris | |
}, | |
/** | |
* Description : Delaying Difficulty Bomb to mid-September 2022 | |
* URL : https://eips.ethereum.org/EIPS/eip-5133 | |
* Status : Final | |
*/ | |
5133: { | |
minimumHardfork: Hardfork.GrayGlacier | |
}, | |
/** | |
* Description : EOF - Stack Validation | |
* URL : https://github.com/ethereum/EIPs/blob/bd421962b4e241aa2b00a85d9cf4e57770bdb954/EIPS/eip-5450.md | |
* Status : Review | |
*/ | |
5450: { | |
minimumHardfork: Hardfork.London, | |
requiredEIPs: [3540, 3670, 4200, 4750] | |
}, | |
/** | |
* Description : MCOPY - Memory copying instruction | |
* URL : https://eips.ethereum.org/EIPS/eip-5656 | |
* Status : Final | |
*/ | |
5656: { | |
minimumHardfork: Hardfork.Shanghai | |
}, | |
/** | |
* Description : Supply validator deposits on chain | |
* URL : https://eips.ethereum.org/EIPS/eip-6110 | |
* Status : Review | |
*/ | |
6110: { | |
minimumHardfork: Hardfork.Cancun, | |
requiredEIPs: [7685] | |
}, | |
/** | |
* Description : EOF - JUMPF and non-returning functions | |
* URL : https://github.com/ethereum/EIPs/blob/bd421962b4e241aa2b00a85d9cf4e57770bdb954/EIPS/eip-6206.md | |
* Status : Review | |
*/ | |
6206: { | |
minimumHardfork: Hardfork.London, | |
requiredEIPs: [4750, 5450] | |
}, | |
/** | |
* Description : SELFDESTRUCT only in same transaction | |
* URL : https://eips.ethereum.org/EIPS/eip-6780 | |
* Status : Final | |
*/ | |
6780: { | |
minimumHardfork: Hardfork.London | |
}, | |
/** | |
* Description : Ethereum state using a unified verkle tree (experimental) | |
* URL : https://github.com/ethereum/EIPs/pull/6800 | |
* Status : Draft | |
*/ | |
6800: { | |
minimumHardfork: Hardfork.London | |
}, | |
/** | |
* Description : Execution layer triggerable withdrawals (experimental) | |
* URL : https://github.com/ethereum/EIPs/blob/3b5fcad6b35782f8aaeba7d4ac26004e8fbd720f/EIPS/eip-7002.md | |
* Status : Review | |
*/ | |
7002: { | |
minimumHardfork: Hardfork.Paris, | |
requiredEIPs: [7685] | |
}, | |
/** | |
* Description : Revamped CALL instructions | |
* URL : https://github.com/ethereum/EIPs/blob/bd421962b4e241aa2b00a85d9cf4e57770bdb954/EIPS/eip-7069.md | |
* Status : Review | |
*/ | |
7069: { | |
minimumHardfork: Hardfork.Berlin, | |
/* Note: per EIP these are the additionally required EIPs: | |
EIP 150 - This is the entire Tangerine Whistle hardfork | |
EIP 211 - (RETURNDATASIZE / RETURNDATACOPY) - Included in Byzantium | |
EIP 214 - (STATICCALL) - Included in Byzantium | |
*/ | |
requiredEIPs: [2929] | |
}, | |
/** | |
* Description : Increase the MAX_EFFECTIVE_BALANCE -> Execution layer triggered consolidations (experimental) | |
* URL : https://eips.ethereum.org/EIPS/eip-7251 | |
* Status : Draft | |
*/ | |
7251: { | |
minimumHardfork: Hardfork.Paris, | |
requiredEIPs: [7685] | |
}, | |
/** | |
* Description : EOF - Data section access instructions | |
* URL : https://github.com/ethereum/EIPs/blob/bd421962b4e241aa2b00a85d9cf4e57770bdb954/EIPS/eip-7480.md | |
* Status : Review | |
*/ | |
7480: { | |
minimumHardfork: Hardfork.London, | |
requiredEIPs: [3540, 3670] | |
}, | |
/** | |
* Description : BLOBBASEFEE opcode | |
* URL : https://eips.ethereum.org/EIPS/eip-7516 | |
* Status : Final | |
*/ | |
7516: { | |
minimumHardfork: Hardfork.Paris, | |
requiredEIPs: [4844] | |
}, | |
/** | |
* Description : EOF Contract Creation | |
* URL : https://github.com/ethereum/EIPs/blob/dd32a34cfe4473bce143641bfffe4fd67e1987ab/EIPS/eip-7620.md | |
* Status : Review | |
*/ | |
7620: { | |
minimumHardfork: Hardfork.London, | |
/* Note: per EIP these are the additionally required EIPs: | |
EIP 170 - (Max contract size) - Included in Spurious Dragon | |
*/ | |
requiredEIPs: [3540, 3541, 3670] | |
}, | |
/** | |
* Description : General purpose execution layer requests | |
* URL : https://eips.ethereum.org/EIPS/eip-7685 | |
* Status : Review | |
*/ | |
7685: { | |
// TODO: Set correct minimum hardfork | |
minimumHardfork: Hardfork.Cancun, | |
requiredEIPs: [3675] | |
}, | |
/** | |
* Description : EVM Object Format (EOFv1) Meta | |
* URL : https://github.com/ethereum/EIPs/blob/4153e95befd0264082de3c4c2fe3a85cc74d3152/EIPS/eip-7692.md | |
* Status : Draft | |
*/ | |
7692: { | |
minimumHardfork: Hardfork.Cancun, | |
requiredEIPs: [663, 3540, 3670, 4200, 4750, 5450, 6206, 7069, 7480, 7620, 7698] | |
}, | |
/** | |
* Description : EOF - Creation transaction | |
* URL : https://github.com/ethereum/EIPs/blob/bd421962b4e241aa2b00a85d9cf4e57770bdb954/EIPS/eip-7698.md | |
* Status : Draft | |
*/ | |
7698: { | |
minimumHardfork: Hardfork.London, | |
requiredEIPs: [3540, 7620] | |
}, | |
/** | |
* Description : Set EOA account code for one transaction | |
* URL : https://github.com/ethereum/EIPs/blob/62419ca3f45375db00b04a368ea37c0bfb05386a/EIPS/eip-7702.md | |
* Status : Review | |
*/ | |
7702: { | |
// TODO: Set correct minimum hardfork | |
minimumHardfork: Hardfork.Cancun, | |
requiredEIPs: [2718, 2929, 2930] | |
}, | |
/** | |
* Description : Use historical block hashes saved in state for BLOCKHASH | |
* URL : https://eips.ethereum.org/EIPS/eip-7709 | |
* Status : Final | |
*/ | |
7709: { | |
minimumHardfork: Hardfork.Chainstart, | |
requiredEIPs: [2935] | |
} | |
}; | |
const hardforksDict = { | |
/** | |
* Description: Start of the Ethereum main chain | |
* URL : - | |
* Status : Final | |
*/ | |
chainstart: { | |
eips: [1] | |
}, | |
/** | |
* Description: Homestead hardfork with protocol and network changes | |
* URL : https://eips.ethereum.org/EIPS/eip-606 | |
* Status : Final | |
*/ | |
homestead: { | |
eips: [606] | |
}, | |
/** | |
* Description: DAO rescue hardfork | |
* URL : https://eips.ethereum.org/EIPS/eip-779 | |
* Status : Final | |
*/ | |
dao: { | |
eips: [] | |
}, | |
/** | |
* Description: Hardfork with gas cost changes for IO-heavy operations | |
* URL : https://eips.ethereum.org/EIPS/eip-608 | |
* Status : Final | |
*/ | |
tangerineWhistle: { | |
eips: [608] | |
}, | |
/** | |
* Description: HF with EIPs for simple replay attack protection, EXP cost increase, state trie clearing, contract code size limit | |
* URL : https://eips.ethereum.org/EIPS/eip-607 | |
* Status : Final | |
*/ | |
spuriousDragon: { | |
eips: [607] | |
}, | |
/** | |
* Description: Hardfork with new precompiles, instructions and other protocol changes | |
* URL : https://eips.ethereum.org/EIPS/eip-609 | |
* Status : Final | |
*/ | |
byzantium: { | |
eips: [609] | |
}, | |
/** | |
* Description: Postponed hardfork including EIP-1283 (SSTORE gas metering changes) | |
* URL : https://eips.ethereum.org/EIPS/eip-1013 | |
* Status : Final | |
*/ | |
constantinople: { | |
eips: [1013] | |
}, | |
/** | |
* Description: Aka constantinopleFix, removes EIP-1283, activate together with or after constantinople | |
* URL : https://eips.ethereum.org/EIPS/eip-1716 | |
* Status : Final | |
*/ | |
petersburg: { | |
eips: [1716] | |
}, | |
/** | |
* Description: HF targeted for December 2019 following the Constantinople/Petersburg HF | |
* URL : https://eips.ethereum.org/EIPS/eip-1679 | |
* Status : Final | |
*/ | |
istanbul: { | |
eips: [1679] | |
}, | |
/** | |
* Description: HF to delay the difficulty bomb | |
* URL : https://eips.ethereum.org/EIPS/eip-2384 | |
* Status : Final | |
*/ | |
muirGlacier: { | |
eips: [2384] | |
}, | |
/** | |
* Description: HF targeted for July 2020 following the Muir Glacier HF | |
* URL : https://eips.ethereum.org/EIPS/eip-2070 | |
* Status : Final | |
*/ | |
berlin: { | |
eips: [2565, 2929, 2718, 2930] | |
}, | |
/** | |
* Description: HF targeted for July 2021 following the Berlin fork | |
* URL : https://github.com/ethereum/eth1.0-specs/blob/master/network-upgrades/mainnet-upgrades/london.md | |
* Status : Final | |
*/ | |
london: { | |
eips: [1559, 3198, 3529, 3541] | |
}, | |
/** | |
* Description: HF to delay the difficulty bomb | |
* URL : https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/arrow-glacier.md | |
* Status : Final | |
*/ | |
arrowGlacier: { | |
eips: [4345] | |
}, | |
/** | |
* Description: Delaying the difficulty bomb to Mid September 2022 | |
* URL : https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/gray-glacier.md | |
* Status : Final | |
*/ | |
grayGlacier: { | |
eips: [5133] | |
}, | |
/** | |
* Description: Hardfork to upgrade the consensus mechanism to Proof-of-Stake | |
* URL : https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/merge.md | |
* Status : Final | |
*/ | |
paris: { | |
consensus: { | |
type: "pos", | |
algorithm: "casper", | |
casper: {} | |
}, | |
eips: [3675, 4399] | |
}, | |
/** | |
* Description: Pre-merge hardfork to fork off non-upgraded clients | |
* URL : https://eips.ethereum.org/EIPS/eip-3675 | |
* Status : Final | |
*/ | |
mergeForkIdTransition: { | |
eips: [] | |
}, | |
/** | |
* Description: Next feature hardfork after the merge hardfork having withdrawals, warm coinbase, push0, limit/meter initcode | |
* URL : https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/shanghai.md | |
* Status : Final | |
*/ | |
shanghai: { | |
eips: [3651, 3855, 3860, 4895] | |
}, | |
/** | |
* Description: Next feature hardfork after shanghai, includes proto-danksharding EIP 4844 blobs | |
* (still WIP hence not for production use), transient storage opcodes, parent beacon block root | |
* availability in EVM, selfdestruct only in same transaction, and blob base fee opcode | |
* URL : https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/cancun.md | |
* Status : Final | |
*/ | |
cancun: { | |
eips: [1153, 4844, 4788, 5656, 6780, 7516] | |
}, | |
/** | |
* Description: Next feature hardfork after cancun, internally used for pectra testing/implementation (incomplete/experimental) | |
* URL : https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/prague.md | |
* Status : Final | |
*/ | |
prague: { | |
// TODO update this accordingly to the right devnet setup | |
//eips: [663, 3540, 3670, 4200, 4750, 5450, 6206, 7069, 7480, 7620, 7692, 7698], // This is EOF-only | |
eips: [2537, 2935, 6110, 7002, 7251, 7685, 7702] | |
// This is current prague without EOF | |
}, | |
/** | |
* Description: Next feature hardfork after prague, internally used for verkle testing/implementation (incomplete/experimental) | |
* URL : https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/osaka.md | |
* Status : Final | |
*/ | |
osaka: { | |
eips: [2935, 6800] | |
} | |
}; | |
class Common { | |
constructor(opts) { | |
this._eips = []; | |
this._paramsCache = {}; | |
this._activatedEIPsCache = []; | |
this.events = new eventsExports.EventEmitter(); | |
this._chainParams = JSON.parse(JSON.stringify(opts.chain)); | |
this.DEFAULT_HARDFORK = this._chainParams.defaultHardfork ?? Hardfork.Cancun; | |
this.HARDFORK_CHANGES = this.hardforks().map((hf) => [ | |
hf.name, | |
hardforksDict[hf.name] ?? (this._chainParams.customHardforks && this._chainParams.customHardforks[hf.name]) | |
]); | |
this._hardfork = this.DEFAULT_HARDFORK; | |
this._params = opts.params ? JSON.parse(JSON.stringify(opts.params)) : {}; | |
if (opts.hardfork !== void 0) { | |
this.setHardfork(opts.hardfork); | |
} | |
if (opts.eips) { | |
this.setEIPs(opts.eips); | |
} | |
this.customCrypto = opts.customCrypto ?? {}; | |
if (Object.keys(this._paramsCache).length === 0) { | |
this._buildParamsCache(); | |
this._buildActivatedEIPsCache(); | |
} | |
} | |
/** | |
* Update the internal Common EIP params set. Existing values | |
* will get preserved unless there is a new value for a parameter | |
* provided with params. | |
* | |
* Example Format: | |
* | |
* ```ts | |
* { | |
* 1559: { | |
* initialBaseFee: 1000000000, | |
* } | |
* } | |
* ``` | |
* | |
* @param params | |
*/ | |
updateParams(params) { | |
for (const [eip, paramsConfig] of Object.entries(params)) { | |
if (!(eip in this._params)) { | |
this._params[eip] = JSON.parse(JSON.stringify(paramsConfig)); | |
} else { | |
this._params[eip] = JSON.parse(JSON.stringify({ ...this._params[eip], ...params[eip] })); | |
} | |
} | |
this._buildParamsCache(); | |
} | |
/** | |
* Fully resets the internal Common EIP params set with the values provided. | |
* | |
* Example Format: | |
* | |
* ```ts | |
* { | |
* 1559: { | |
* initialBaseFee: 1000000000, | |
* } | |
* } | |
* ``` | |
* | |
* @param params | |
*/ | |
resetParams(params) { | |
this._params = JSON.parse(JSON.stringify(params)); | |
this._buildParamsCache(); | |
} | |
/** | |
* Sets the hardfork to get params for | |
* @param hardfork String identifier (e.g. 'byzantium') or {@link Hardfork} enum | |
*/ | |
setHardfork(hardfork) { | |
let existing = false; | |
for (const hfChanges of this.HARDFORK_CHANGES) { | |
if (hfChanges[0] === hardfork) { | |
if (this._hardfork !== hardfork) { | |
this._hardfork = hardfork; | |
this._buildParamsCache(); | |
this._buildActivatedEIPsCache(); | |
this.events.emit("hardforkChanged", hardfork); | |
} | |
existing = true; | |
} | |
} | |
if (!existing) { | |
throw new Error(`Hardfork with name ${hardfork} not supported`); | |
} | |
} | |
/** | |
* Returns the hardfork either based on block number (older HFs) or | |
* timestamp (Shanghai upwards). | |
* | |
* @param Opts Block number or timestamp | |
* @returns The name of the HF | |
*/ | |
getHardforkBy(opts) { | |
const blockNumber = toType(opts.blockNumber, TypeOutput.BigInt); | |
const timestamp = toType(opts.timestamp, TypeOutput.BigInt); | |
const hfs = this.hardforks().filter((hf) => hf.block !== null || hf.timestamp !== void 0); | |
let hfIndex = hfs.findIndex((hf) => blockNumber !== void 0 && hf.block !== null && BigInt(hf.block) > blockNumber || timestamp !== void 0 && hf.timestamp !== void 0 && BigInt(hf.timestamp) > timestamp); | |
if (hfIndex === -1) { | |
hfIndex = hfs.length; | |
} else if (hfIndex === 0) { | |
throw Error("Must have at least one hardfork at block 0"); | |
} | |
if (timestamp === void 0) { | |
const stepBack = hfs.slice(0, hfIndex).reverse().findIndex((hf) => hf.block !== null); | |
hfIndex = hfIndex - stepBack; | |
} | |
hfIndex = hfIndex - 1; | |
const hfStartIndex = hfIndex; | |
for (; hfIndex < hfs.length - 1; hfIndex++) { | |
if (hfs[hfIndex].block !== hfs[hfIndex + 1].block || hfs[hfIndex].timestamp !== hfs[hfIndex + 1].timestamp) { | |
break; | |
} | |
} | |
if (timestamp !== void 0) { | |
const minTimeStamp = hfs.slice(0, hfStartIndex).reduce((acc, hf) => Math.max(Number(hf.timestamp ?? "0"), acc), 0); | |
if (minTimeStamp > timestamp) { | |
throw Error(`Maximum HF determined by timestamp is lower than the block number HF`); | |
} | |
const maxTimeStamp = hfs.slice(hfIndex + 1).reduce((acc, hf) => Math.min(Number(hf.timestamp ?? timestamp), acc), Number(timestamp)); | |
if (maxTimeStamp < timestamp) { | |
throw Error(`Maximum HF determined by block number is lower than timestamp HF`); | |
} | |
} | |
const hardfork = hfs[hfIndex]; | |
return hardfork.name; | |
} | |
/** | |
* Sets a new hardfork either based on block number (older HFs) or | |
* timestamp (Shanghai upwards). | |
* | |
* @param Opts Block number or timestamp | |
* @returns The name of the HF set | |
*/ | |
setHardforkBy(opts) { | |
const hardfork = this.getHardforkBy(opts); | |
this.setHardfork(hardfork); | |
return hardfork; | |
} | |
/** | |
* Internal helper function, returns the params for the given hardfork for the chain set | |
* @param hardfork Hardfork name | |
* @returns Dictionary with hardfork params or null if hardfork not on chain | |
*/ | |
_getHardfork(hardfork) { | |
const hfs = this.hardforks(); | |
for (const hf of hfs) { | |
if (hf["name"] === hardfork) | |
return hf; | |
} | |
return null; | |
} | |
/** | |
* Sets the active EIPs | |
* @param eips | |
*/ | |
setEIPs(eips = []) { | |
for (const eip of eips) { | |
if (!(eip in eipsDict)) { | |
throw new Error(`${eip} not supported`); | |
} | |
const minHF = this.gteHardfork(eipsDict[eip]["minimumHardfork"]); | |
if (!minHF) { | |
throw new Error(`${eip} cannot be activated on hardfork ${this.hardfork()}, minimumHardfork: ${minHF}`); | |
} | |
} | |
this._eips = eips; | |
this._buildParamsCache(); | |
this._buildActivatedEIPsCache(); | |
for (const eip of eips) { | |
if (eipsDict[eip].requiredEIPs !== void 0) { | |
for (const elem of eipsDict[eip].requiredEIPs) { | |
if (!(eips.includes(elem) || this.isActivatedEIP(elem))) { | |
throw new Error(`${eip} requires EIP ${elem}, but is not included in the EIP list`); | |
} | |
} | |
} | |
} | |
} | |
/** | |
* Internal helper for _buildParamsCache() | |
*/ | |
_mergeWithParamsCache(params) { | |
this._paramsCache = { | |
...this._paramsCache, | |
...params | |
}; | |
} | |
/** | |
* Build up a cache for all parameter values for the current HF and all activated EIPs | |
*/ | |
_buildParamsCache() { | |
this._paramsCache = {}; | |
const hardfork = this.hardfork(); | |
for (const hfChanges of this.HARDFORK_CHANGES) { | |
if ("eips" in hfChanges[1]) { | |
const hfEIPs = hfChanges[1]["eips"]; | |
for (const eip of hfEIPs) { | |
this._mergeWithParamsCache(this._params[eip] ?? {}); | |
} | |
} | |
this._mergeWithParamsCache(hfChanges[1].params ?? {}); | |
if (hfChanges[0] === hardfork) | |
break; | |
} | |
for (const eip of this._eips) { | |
this._mergeWithParamsCache(this._params[eip] ?? {}); | |
} | |
} | |
_buildActivatedEIPsCache() { | |
this._activatedEIPsCache = []; | |
for (const [name, hf] of this.HARDFORK_CHANGES) { | |
if (this.gteHardfork(name) && "eips" in hf) { | |
this._activatedEIPsCache = this._activatedEIPsCache.concat(hf["eips"]); | |
} | |
} | |
this._activatedEIPsCache = this._activatedEIPsCache.concat(this._eips); | |
} | |
/** | |
* Returns a parameter for the current chain setup | |
* | |
* If the parameter is present in an EIP, the EIP always takes precedence. | |
* Otherwise the parameter is taken from the latest applied HF with | |
* a change on the respective parameter. | |
* | |
* @param name Parameter name (e.g. 'minGasLimit') | |
* @returns The value requested (throws if not found) | |
*/ | |
param(name) { | |
if (!(name in this._paramsCache)) { | |
throw new Error(`Missing parameter value for ${name}`); | |
} | |
const value = this._paramsCache[name]; | |
return BigInt(value ?? 0); | |
} | |
/** | |
* Returns the parameter corresponding to a hardfork | |
* @param name Parameter name (e.g. 'minGasLimit') | |
* @param hardfork Hardfork name | |
* @returns The value requested (throws if not found) | |
*/ | |
paramByHardfork(name, hardfork) { | |
var _a; | |
let value; | |
for (const hfChanges of this.HARDFORK_CHANGES) { | |
if ("eips" in hfChanges[1]) { | |
const hfEIPs = hfChanges[1]["eips"]; | |
for (const eip of hfEIPs) { | |
const eipParams = this._params[eip]; | |
const eipValue = eipParams == null ? void 0 : eipParams[name]; | |
if (eipValue !== void 0) { | |
value = eipValue; | |
} | |
} | |
} else { | |
const hfValue = (_a = hfChanges[1].params) == null ? void 0 : _a[name]; | |
if (hfValue !== void 0) { | |
value = hfValue; | |
} | |
} | |
if (hfChanges[0] === hardfork) | |
break; | |
} | |
if (value === void 0) { | |
throw new Error(`Missing parameter value for ${name}`); | |
} | |
return BigInt(value ?? 0); | |
} | |
/** | |
* Returns a parameter corresponding to an EIP | |
* @param name Parameter name (e.g. 'minGasLimit' for 'gasConfig' topic) | |
* @param eip Number of the EIP | |
* @returns The value requested (throws if not found) | |
*/ | |
paramByEIP(name, eip) { | |
if (!(eip in eipsDict)) { | |
throw new Error(`${eip} not supported`); | |
} | |
const eipParams = this._params[eip]; | |
if ((eipParams == null ? void 0 : eipParams[name]) === void 0) { | |
throw new Error(`Missing parameter value for ${name}`); | |
} | |
const value = eipParams[name]; | |
return BigInt(value ?? 0); | |
} | |
/** | |
* Returns a parameter for the hardfork active on block number or | |
* optional provided total difficulty (Merge HF) | |
* @param name Parameter name | |
* @param blockNumber Block number | |
* * @returns The value requested or `BigInt(0)` if not found | |
*/ | |
paramByBlock(name, blockNumber, timestamp) { | |
const hardfork = this.getHardforkBy({ blockNumber, timestamp }); | |
return this.paramByHardfork(name, hardfork); | |
} | |
/** | |
* Checks if an EIP is activated by either being included in the EIPs | |
* manually passed in with the {@link CommonOpts.eips} or in a | |
* hardfork currently being active | |
* | |
* Note: this method only works for EIPs being supported | |
* by the {@link CommonOpts.eips} constructor option | |
* @param eip | |
*/ | |
isActivatedEIP(eip) { | |
if (this._activatedEIPsCache.includes(eip)) { | |
return true; | |
} | |
return false; | |
} | |
/** | |
* Checks if set or provided hardfork is active on block number | |
* @param hardfork Hardfork name or null (for HF set) | |
* @param blockNumber | |
* @returns True if HF is active on block number | |
*/ | |
hardforkIsActiveOnBlock(hardfork, blockNumber) { | |
blockNumber = toType(blockNumber, TypeOutput.BigInt); | |
hardfork = hardfork ?? this._hardfork; | |
const hfBlock = this.hardforkBlock(hardfork); | |
if (typeof hfBlock === "bigint" && hfBlock !== BIGINT_0 && blockNumber >= hfBlock) { | |
return true; | |
} | |
return false; | |
} | |
/** | |
* Alias to hardforkIsActiveOnBlock when hardfork is set | |
* @param blockNumber | |
* @returns True if HF is active on block number | |
*/ | |
activeOnBlock(blockNumber) { | |
return this.hardforkIsActiveOnBlock(null, blockNumber); | |
} | |
/** | |
* Sequence based check if given or set HF1 is greater than or equal HF2 | |
* @param hardfork1 Hardfork name or null (if set) | |
* @param hardfork2 Hardfork name | |
* @param opts Hardfork options | |
* @returns True if HF1 gte HF2 | |
*/ | |
hardforkGteHardfork(hardfork1, hardfork2) { | |
hardfork1 = hardfork1 ?? this._hardfork; | |
const hardforks = this.hardforks(); | |
let posHf1 = -1, posHf2 = -1; | |
let index = 0; | |
for (const hf of hardforks) { | |
if (hf["name"] === hardfork1) | |
posHf1 = index; | |
if (hf["name"] === hardfork2) | |
posHf2 = index; | |
index += 1; | |
} | |
return posHf1 >= posHf2 && posHf2 !== -1; | |
} | |
/** | |
* Alias to hardforkGteHardfork when hardfork is set | |
* @param hardfork Hardfork name | |
* @returns True if hardfork set is greater than hardfork provided | |
*/ | |
gteHardfork(hardfork) { | |
return this.hardforkGteHardfork(null, hardfork); | |
} | |
/** | |
* Returns the hardfork change block for hardfork provided or set | |
* @param hardfork Hardfork name, optional if HF set | |
* @returns Block number or null if unscheduled | |
*/ | |
hardforkBlock(hardfork) { | |
var _a; | |
hardfork = hardfork ?? this._hardfork; | |
const block = (_a = this._getHardfork(hardfork)) == null ? void 0 : _a["block"]; | |
if (block === void 0 || block === null) { | |
return null; | |
} | |
return BigInt(block); | |
} | |
hardforkTimestamp(hardfork) { | |
var _a; | |
hardfork = hardfork ?? this._hardfork; | |
const timestamp = (_a = this._getHardfork(hardfork)) == null ? void 0 : _a["timestamp"]; | |
if (timestamp === void 0 || timestamp === null) { | |
return null; | |
} | |
return BigInt(timestamp); | |
} | |
/** | |
* Returns the hardfork change block for eip | |
* @param eip EIP number | |
* @returns Block number or null if unscheduled | |
*/ | |
eipBlock(eip) { | |
for (const hfChanges of this.HARDFORK_CHANGES) { | |
const hf = hfChanges[1]; | |
if ("eips" in hf) { | |
if (hf["eips"].includes(eip)) { | |
return this.hardforkBlock(hfChanges[0]); | |
} | |
} | |
} | |
return null; | |
} | |
/** | |
* Returns the scheduled timestamp of the EIP (if scheduled and scheduled by timestamp) | |
* @param eip EIP number | |
* @returns Scheduled timestamp. If this EIP is unscheduled, or the EIP is scheduled by block number, then it returns `null`. | |
*/ | |
eipTimestamp(eip) { | |
for (const hfChanges of this.HARDFORK_CHANGES) { | |
const hf = hfChanges[1]; | |
if ("eips" in hf) { | |
if (hf["eips"].includes(eip)) { | |
return this.hardforkTimestamp(hfChanges[0]); | |
} | |
} | |
} | |
return null; | |
} | |
/** | |
* Returns the change block for the next hardfork after the hardfork provided or set | |
* @param hardfork Hardfork name, optional if HF set | |
* @returns Block timestamp, number or null if not available | |
*/ | |
nextHardforkBlockOrTimestamp(hardfork) { | |
hardfork = hardfork ?? this._hardfork; | |
const hfs = this.hardforks(); | |
let hfIndex = hfs.findIndex((hf) => hf.name === hardfork); | |
if (hardfork === Hardfork.Paris) { | |
hfIndex -= 1; | |
} | |
if (hfIndex < 0) { | |
return null; | |
} | |
let currHfTimeOrBlock = hfs[hfIndex].timestamp ?? hfs[hfIndex].block; | |
currHfTimeOrBlock = currHfTimeOrBlock !== null && currHfTimeOrBlock !== void 0 ? Number(currHfTimeOrBlock) : null; | |
const nextHf = hfs.slice(hfIndex + 1).find((hf) => { | |
let hfTimeOrBlock = hf.timestamp ?? hf.block; | |
hfTimeOrBlock = hfTimeOrBlock !== null && hfTimeOrBlock !== void 0 ? Number(hfTimeOrBlock) : null; | |
return hf.name !== Hardfork.Paris && hfTimeOrBlock !== null && hfTimeOrBlock !== void 0 && hfTimeOrBlock !== currHfTimeOrBlock; | |
}); | |
if (nextHf === void 0) { | |
return null; | |
} | |
const nextHfBlock = nextHf.timestamp ?? nextHf.block; | |
if (nextHfBlock === null || nextHfBlock === void 0) { | |
return null; | |
} | |
return BigInt(nextHfBlock); | |
} | |
/** | |
* Internal helper function to calculate a fork hash | |
* @param hardfork Hardfork name | |
* @param genesisHash Genesis block hash of the chain | |
* @returns Fork hash as hex string | |
*/ | |
_calcForkHash(hardfork, genesisHash) { | |
let hfBytes = new Uint8Array(0); | |
let prevBlockOrTime = 0; | |
for (const hf of this.hardforks()) { | |
const { block, timestamp, name } = hf; | |
let blockOrTime = timestamp ?? block; | |
blockOrTime = blockOrTime !== null ? Number(blockOrTime) : null; | |
if (typeof blockOrTime === "number" && blockOrTime !== 0 && blockOrTime !== prevBlockOrTime && name !== Hardfork.Paris) { | |
const hfBlockBytes = hexToBytes$1(`0x${blockOrTime.toString(16).padStart(16, "0")}`); | |
hfBytes = concatBytes$1(hfBytes, hfBlockBytes); | |
prevBlockOrTime = blockOrTime; | |
} | |
if (hf.name === hardfork) | |
break; | |
} | |
const inputBytes = concatBytes$1(genesisHash, hfBytes); | |
const forkhash = bytesToHex$1(intToBytes(crc32(inputBytes) >>> 0)); | |
return forkhash; | |
} | |
/** | |
* Returns an eth/64 compliant fork hash (EIP-2124) | |
* @param hardfork Hardfork name, optional if HF set | |
* @param genesisHash Genesis block hash of the network, optional if already defined and not needed to be calculated | |
*/ | |
forkHash(hardfork, genesisHash) { | |
hardfork = hardfork ?? this._hardfork; | |
const data = this._getHardfork(hardfork); | |
if (data === null || (data == null ? void 0 : data.block) === null && (data == null ? void 0 : data.timestamp) === void 0) { | |
const msg = "No fork hash calculation possible for future hardfork"; | |
throw new Error(msg); | |
} | |
if ((data == null ? void 0 : data.forkHash) !== null && (data == null ? void 0 : data.forkHash) !== void 0) { | |
return data.forkHash; | |
} | |
if (!genesisHash) | |
throw new Error("genesisHash required for forkHash calculation"); | |
return this._calcForkHash(hardfork, genesisHash); | |
} | |
/** | |
* | |
* @param forkHash Fork hash as a hex string | |
* @returns Array with hardfork data (name, block, forkHash) | |
*/ | |
hardforkForForkHash(forkHash) { | |
const resArray = this.hardforks().filter((hf) => { | |
return hf.forkHash === forkHash; | |
}); | |
return resArray.length >= 1 ? resArray[resArray.length - 1] : null; | |
} | |
/** | |
* Sets any missing forkHashes on the passed-in {@link Common} instance | |
* @param common The {@link Common} to set the forkHashes for | |
* @param genesisHash The genesis block hash | |
*/ | |
setForkHashes(genesisHash) { | |
for (const hf of this.hardforks()) { | |
const blockOrTime = hf.timestamp ?? hf.block; | |
if ((hf.forkHash === null || hf.forkHash === void 0) && blockOrTime !== null && blockOrTime !== void 0) { | |
hf.forkHash = this.forkHash(hf.name, genesisHash); | |
} | |
} | |
} | |
/** | |
* Returns the Genesis parameters of the current chain | |
* @returns Genesis dictionary | |
*/ | |
genesis() { | |
return this._chainParams.genesis; | |
} | |
/** | |
* Returns the hardforks for current chain | |
* @returns {Array} Array with arrays of hardforks | |
*/ | |
hardforks() { | |
const hfs = this._chainParams.hardforks; | |
if (this._chainParams.customHardforks !== void 0) { | |
this._chainParams.customHardforks; | |
} | |
return hfs; | |
} | |
/** | |
* Returns bootstrap nodes for the current chain | |
* @returns {Dictionary} Dict with bootstrap nodes | |
*/ | |
bootstrapNodes() { | |
return this._chainParams.bootstrapNodes; | |
} | |
/** | |
* Returns DNS networks for the current chain | |
* @returns {String[]} Array of DNS ENR urls | |
*/ | |
dnsNetworks() { | |
return this._chainParams.dnsNetworks; | |
} | |
/** | |
* Returns the hardfork set | |
* @returns Hardfork name | |
*/ | |
hardfork() { | |
return this._hardfork; | |
} | |
/** | |
* Returns the Id of current chain | |
* @returns chain Id | |
*/ | |
chainId() { | |
return BigInt(this._chainParams.chainId); | |
} | |
/** | |
* Returns the name of current chain | |
* @returns chain name (lower case) | |
*/ | |
chainName() { | |
return this._chainParams.name; | |
} | |
/** | |
* Returns the additionally activated EIPs | |
* (by using the `eips` constructor option) | |
* @returns List of EIPs | |
*/ | |
eips() { | |
return this._eips; | |
} | |
/** | |
* Returns the consensus type of the network | |
* Possible values: "pow"|"poa"|"pos" | |
* | |
* Note: This value can update along a Hardfork. | |
*/ | |
consensusType() { | |
const hardfork = this.hardfork(); | |
let value; | |
for (const hfChanges of this.HARDFORK_CHANGES) { | |
if ("consensus" in hfChanges[1]) { | |
value = hfChanges[1]["consensus"]["type"]; | |
} | |
if (hfChanges[0] === hardfork) | |
break; | |
} | |
return value ?? this._chainParams["consensus"]["type"]; | |
} | |
/** | |
* Returns the concrete consensus implementation | |
* algorithm or protocol for the network | |
* e.g. "ethash" for "pow" consensus type, | |
* "clique" for "poa" consensus type or | |
* "casper" for "pos" consensus type. | |
* | |
* Note: This value can update along a Hardfork. | |
*/ | |
consensusAlgorithm() { | |
const hardfork = this.hardfork(); | |
let value; | |
for (const hfChanges of this.HARDFORK_CHANGES) { | |
if ("consensus" in hfChanges[1]) { | |
value = hfChanges[1]["consensus"]["algorithm"]; | |
} | |
if (hfChanges[0] === hardfork) | |
break; | |
} | |
return value ?? this._chainParams["consensus"]["algorithm"]; | |
} | |
/** | |
* Returns a dictionary with consensus configuration | |
* parameters based on the consensus algorithm | |
* | |
* Expected returns (parameters must be present in | |
* the respective chain JSON files): | |
* | |
* ethash: empty object | |
* clique: period, epoch | |
* casper: empty object | |
* | |
* Note: This value can update along a Hardfork. | |
*/ | |
consensusConfig() { | |
const hardfork = this.hardfork(); | |
let value; | |
for (const hfChanges of this.HARDFORK_CHANGES) { | |
if ("consensus" in hfChanges[1]) { | |
const config = hfChanges[1]; | |
const algorithm = config["consensus"]["algorithm"]; | |
value = config["consensus"][algorithm]; | |
} | |
if (hfChanges[0] === hardfork) | |
break; | |
} | |
return value ?? this._chainParams["consensus"][this.consensusAlgorithm()] ?? {}; | |
} | |
/** | |
* Returns a deep copy of this {@link Common} instance. | |
*/ | |
copy() { | |
const copy = Object.assign(Object.create(Object.getPrototypeOf(this)), this); | |
copy.events = new eventsExports.EventEmitter(); | |
return copy; | |
} | |
} | |
var browser = { exports: {} }; | |
var ms; | |
var hasRequiredMs; | |
function requireMs() { | |
if (hasRequiredMs) return ms; | |
hasRequiredMs = 1; | |
var s = 1e3; | |
var m = s * 60; | |
var h = m * 60; | |
var d = h * 24; | |
var w = d * 7; | |
var y = d * 365.25; | |
ms = function(val, options) { | |
options = options || {}; | |
var type = typeof val; | |
if (type === "string" && val.length > 0) { | |
return parse(val); | |
} else if (type === "number" && isFinite(val)) { | |
return options.long ? fmtLong(val) : fmtShort(val); | |
} | |
throw new Error( | |
"val is not a non-empty string or a valid number. val=" + JSON.stringify(val) | |
); | |
}; | |
function parse(str) { | |
str = String(str); | |
if (str.length > 100) { | |
return; | |
} | |
var match = /^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec( | |
str | |
); | |
if (!match) { | |
return; | |
} | |
var n = parseFloat(match[1]); | |
var type = (match[2] || "ms").toLowerCase(); | |
switch (type) { | |
case "years": | |
case "year": | |
case "yrs": | |
case "yr": | |
case "y": | |
return n * y; | |
case "weeks": | |
case "week": | |
case "w": | |
return n * w; | |
case "days": | |
case "day": | |
case "d": | |
return n * d; | |
case "hours": | |
case "hour": | |
case "hrs": | |
case "hr": | |
case "h": | |
return n * h; | |
case "minutes": | |
case "minute": | |
case "mins": | |
case "min": | |
case "m": | |
return n * m; | |
case "seconds": | |
case "second": | |
case "secs": | |
case "sec": | |
case "s": | |
return n * s; | |
case "milliseconds": | |
case "millisecond": | |
case "msecs": | |
case "msec": | |
case "ms": | |
return n; | |
default: | |
return void 0; | |
} | |
} | |
function fmtShort(ms2) { | |
var msAbs = Math.abs(ms2); | |
if (msAbs >= d) { | |
return Math.round(ms2 / d) + "d"; | |
} | |
if (msAbs >= h) { | |
return Math.round(ms2 / h) + "h"; | |
} | |
if (msAbs >= m) { | |
return Math.round(ms2 / m) + "m"; | |
} | |
if (msAbs >= s) { | |
return Math.round(ms2 / s) + "s"; | |
} | |
return ms2 + "ms"; | |
} | |
function fmtLong(ms2) { | |
var msAbs = Math.abs(ms2); | |
if (msAbs >= d) { | |
return plural(ms2, msAbs, d, "day"); | |
} | |
if (msAbs >= h) { | |
return plural(ms2, msAbs, h, "hour"); | |
} | |
if (msAbs >= m) { | |
return plural(ms2, msAbs, m, "minute"); | |
} | |
if (msAbs >= s) { | |
return plural(ms2, msAbs, s, "second"); | |
} | |
return ms2 + " ms"; | |
} | |
function plural(ms2, msAbs, n, name) { | |
var isPlural = msAbs >= n * 1.5; | |
return Math.round(ms2 / n) + " " + name + (isPlural ? "s" : ""); | |
} | |
return ms; | |
} | |
function setup(env) { | |
createDebug.debug = createDebug; | |
createDebug.default = createDebug; | |
createDebug.coerce = coerce; | |
createDebug.disable = disable; | |
createDebug.enable = enable; | |
createDebug.enabled = enabled; | |
createDebug.humanize = requireMs(); | |
createDebug.destroy = destroy; | |
Object.keys(env).forEach((key) => { | |
createDebug[key] = env[key]; | |
}); | |
createDebug.names = []; | |
createDebug.skips = []; | |
createDebug.formatters = {}; | |
function selectColor(namespace) { | |
let hash2 = 0; | |
for (let i = 0; i < namespace.length; i++) { | |
hash2 = (hash2 << 5) - hash2 + namespace.charCodeAt(i); | |
hash2 |= 0; | |
} | |
return createDebug.colors[Math.abs(hash2) % createDebug.colors.length]; | |
} | |
createDebug.selectColor = selectColor; | |
function createDebug(namespace) { | |
let prevTime; | |
let enableOverride = null; | |
let namespacesCache; | |
let enabledCache; | |
function debug2(...args) { | |
if (!debug2.enabled) { | |
return; | |
} | |
const self = debug2; | |
const curr = Number(/* @__PURE__ */ new Date()); | |
const ms2 = curr - (prevTime || curr); | |
self.diff = ms2; | |
self.prev = prevTime; | |
self.curr = curr; | |
prevTime = curr; | |
args[0] = createDebug.coerce(args[0]); | |
if (typeof args[0] !== "string") { | |
args.unshift("%O"); | |
} | |
let index = 0; | |
args[0] = args[0].replace(/%([a-zA-Z%])/g, (match, format) => { | |
if (match === "%%") { | |
return "%"; | |
} | |
index++; | |
const formatter = createDebug.formatters[format]; | |
if (typeof formatter === "function") { | |
const val = args[index]; | |
match = formatter.call(self, val); | |
args.splice(index, 1); | |
index--; | |
} | |
return match; | |
}); | |
createDebug.formatArgs.call(self, args); | |
const logFn2 = self.log || createDebug.log; | |
logFn2.apply(self, args); | |
} | |
debug2.namespace = namespace; | |
debug2.useColors = createDebug.useColors(); | |
debug2.color = createDebug.selectColor(namespace); | |
debug2.extend = extend; | |
debug2.destroy = createDebug.destroy; | |
Object.defineProperty(debug2, "enabled", { | |
enumerable: true, | |
configurable: false, | |
get: () => { | |
if (enableOverride !== null) { | |
return enableOverride; | |
} | |
if (namespacesCache !== createDebug.namespaces) { | |
namespacesCache = createDebug.namespaces; | |
enabledCache = createDebug.enabled(namespace); | |
} | |
return enabledCache; | |
}, | |
set: (v) => { | |
enableOverride = v; | |
} | |
}); | |
if (typeof createDebug.init === "function") { | |
createDebug.init(debug2); | |
} | |
return debug2; | |
} | |
function extend(namespace, delimiter) { | |
const newDebug = createDebug(this.namespace + (typeof delimiter === "undefined" ? ":" : delimiter) + namespace); | |
newDebug.log = this.log; | |
return newDebug; | |
} | |
function enable(namespaces) { | |
createDebug.save(namespaces); | |
createDebug.namespaces = namespaces; | |
createDebug.names = []; | |
createDebug.skips = []; | |
let i; | |
const split2 = (typeof namespaces === "string" ? namespaces : "").split(/[\s,]+/); | |
const len = split2.length; | |
for (i = 0; i < len; i++) { | |
if (!split2[i]) { | |
continue; | |
} | |
namespaces = split2[i].replace(/\*/g, ".*?"); | |
if (namespaces[0] === "-") { | |
createDebug.skips.push(new RegExp("^" + namespaces.slice(1) + "$")); | |
} else { | |
createDebug.names.push(new RegExp("^" + namespaces + "$")); | |
} | |
} | |
} | |
function disable() { | |
const namespaces = [ | |
...createDebug.names.map(toNamespace), | |
...createDebug.skips.map(toNamespace).map((namespace) => "-" + namespace) | |
].join(","); | |
createDebug.enable(""); | |
return namespaces; | |
} | |
function enabled(name) { | |
if (name[name.length - 1] === "*") { | |
return true; | |
} | |
let i; | |
let len; | |
for (i = 0, len = createDebug.skips.length; i < len; i++) { | |
if (createDebug.skips[i].test(name)) { | |
return false; | |
} | |
} | |
for (i = 0, len = createDebug.names.length; i < len; i++) { | |
if (createDebug.names[i].test(name)) { | |
return true; | |
} | |
} | |
return false; | |
} | |
function toNamespace(regexp) { | |
return regexp.toString().substring(2, regexp.toString().length - 2).replace(/\.\*\?$/, "*"); | |
} | |
function coerce(val) { | |
if (val instanceof Error) { | |
return val.stack || val.message; | |
} | |
return val; | |
} | |
function destroy() { | |
console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`."); | |
} | |
createDebug.enable(createDebug.load()); | |
return createDebug; | |
} | |
var common = setup; | |
browser.exports; | |
(function(module, exports) { | |
exports.formatArgs = formatArgs; | |
exports.save = save; | |
exports.load = load; | |
exports.useColors = useColors; | |
exports.storage = localstorage(); | |
exports.destroy = /* @__PURE__ */ (() => { | |
let warned = false; | |
return () => { | |
if (!warned) { | |
warned = true; | |
console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`."); | |
} | |
}; | |
})(); | |
exports.colors = [ | |
"#0000CC", | |
"#0000FF", | |
"#0033CC", | |
"#0033FF", | |
"#0066CC", | |
"#0066FF", | |
"#0099CC", | |
"#0099FF", | |
"#00CC00", | |
"#00CC33", | |
"#00CC66", | |
"#00CC99", | |
"#00CCCC", | |
"#00CCFF", | |
"#3300CC", | |
"#3300FF", | |
"#3333CC", | |
"#3333FF", | |
"#3366CC", | |
"#3366FF", | |
"#3399CC", | |
"#3399FF", | |
"#33CC00", | |
"#33CC33", | |
"#33CC66", | |
"#33CC99", | |
"#33CCCC", | |
"#33CCFF", | |
"#6600CC", | |
"#6600FF", | |
"#6633CC", | |
"#6633FF", | |
"#66CC00", | |
"#66CC33", | |
"#9900CC", | |
"#9900FF", | |
"#9933CC", | |
"#9933FF", | |
"#99CC00", | |
"#99CC33", | |
"#CC0000", | |
"#CC0033", | |
"#CC0066", | |
"#CC0099", | |
"#CC00CC", | |
"#CC00FF", | |
"#CC3300", | |
"#CC3333", | |
"#CC3366", | |
"#CC3399", | |
"#CC33CC", | |
"#CC33FF", | |
"#CC6600", | |
"#CC6633", | |
"#CC9900", | |
"#CC9933", | |
"#CCCC00", | |
"#CCCC33", | |
"#FF0000", | |
"#FF0033", | |
"#FF0066", | |
"#FF0099", | |
"#FF00CC", | |
"#FF00FF", | |
"#FF3300", | |
"#FF3333", | |
"#FF3366", | |
"#FF3399", | |
"#FF33CC", | |
"#FF33FF", | |
"#FF6600", | |
"#FF6633", | |
"#FF9900", | |
"#FF9933", | |
"#FFCC00", | |
"#FFCC33" | |
]; | |
function useColors() { | |
if (typeof window !== "undefined" && window.process && (window.process.type === "renderer" || window.process.__nwjs)) { | |
return true; | |
} | |
if (typeof navigator !== "undefined" && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)) { | |
return false; | |
} | |
let m; | |
return typeof document !== "undefined" && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance || // Is firebug? http://stackoverflow.com/a/398120/376773 | |
typeof window !== "undefined" && window.console && (window.console.firebug || window.console.exception && window.console.table) || // Is firefox >= v31? | |
// https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages | |
typeof navigator !== "undefined" && navigator.userAgent && (m = navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)) && parseInt(m[1], 10) >= 31 || // Double check webkit in userAgent just in case we are in a worker | |
typeof navigator !== "undefined" && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/); | |
} | |
function formatArgs(args) { | |
args[0] = (this.useColors ? "%c" : "") + this.namespace + (this.useColors ? " %c" : " ") + args[0] + (this.useColors ? "%c " : " ") + "+" + module.exports.humanize(this.diff); | |
if (!this.useColors) { | |
return; | |
} | |
const c = "color: " + this.color; | |
args.splice(1, 0, c, "color: inherit"); | |
let index = 0; | |
let lastC = 0; | |
args[0].replace(/%[a-zA-Z%]/g, (match) => { | |
if (match === "%%") { | |
return; | |
} | |
index++; | |
if (match === "%c") { | |
lastC = index; | |
} | |
}); | |
args.splice(lastC, 0, c); | |
} | |
exports.log = console.debug || console.log || (() => { | |
}); | |
function save(namespaces) { | |
try { | |
if (namespaces) { | |
exports.storage.setItem("debug", namespaces); | |
} else { | |
exports.storage.removeItem("debug"); | |
} | |
} catch (error) { | |
} | |
} | |
function load() { | |
let r; | |
try { | |
r = exports.storage.getItem("debug"); | |
} catch (error) { | |
} | |
if (!r && typeof process !== "undefined" && "env" in process) { | |
r = process.env.DEBUG; | |
} | |
return r; | |
} | |
function localstorage() { | |
try { | |
return localStorage; | |
} catch (error) { | |
} | |
} | |
module.exports = common(exports); | |
const { formatters } = module.exports; | |
formatters.j = function(v) { | |
try { | |
return JSON.stringify(v); | |
} catch (error) { | |
return "[UnexpectedJSONParseError]: " + error.message; | |
} | |
}; | |
})(browser, browser.exports); | |
var browserExports = browser.exports; | |
const debugDefault = /* @__PURE__ */ getDefaultExportFromCjs(browserExports); | |
const EOFBYTES = new Uint8Array([FORMAT, MAGIC]); | |
const EOFHASH = keccak256(EOFBYTES); | |
function isEOF(code) { | |
const check = code.subarray(0, EOFBYTES.length); | |
return equalsBytes(EOFBYTES, check); | |
} | |
var ERROR = /* @__PURE__ */ ((ERROR2) => { | |
ERROR2["OUT_OF_GAS"] = "out of gas"; | |
ERROR2["CODESTORE_OUT_OF_GAS"] = "code store out of gas"; | |
ERROR2["CODESIZE_EXCEEDS_MAXIMUM"] = "code size to deposit exceeds maximum code size"; | |
ERROR2["STACK_UNDERFLOW"] = "stack underflow"; | |
ERROR2["STACK_OVERFLOW"] = "stack overflow"; | |
ERROR2["INVALID_JUMP"] = "invalid JUMP"; | |
ERROR2["INVALID_OPCODE"] = "invalid opcode"; | |
ERROR2["OUT_OF_RANGE"] = "value out of range"; | |
ERROR2["REVERT"] = "revert"; | |
ERROR2["STATIC_STATE_CHANGE"] = "static state change"; | |
ERROR2["INTERNAL_ERROR"] = "internal error"; | |
ERROR2["CREATE_COLLISION"] = "create collision"; | |
ERROR2["STOP"] = "stop"; | |
ERROR2["REFUND_EXHAUSTED"] = "refund exhausted"; | |
ERROR2["VALUE_OVERFLOW"] = "value overflow"; | |
ERROR2["INSUFFICIENT_BALANCE"] = "insufficient balance"; | |
ERROR2["INVALID_BEGINSUB"] = "invalid BEGINSUB"; | |
ERROR2["INVALID_RETURNSUB"] = "invalid RETURNSUB"; | |
ERROR2["INVALID_JUMPSUB"] = "invalid JUMPSUB"; | |
ERROR2["INVALID_BYTECODE_RESULT"] = "invalid bytecode deployed"; | |
ERROR2["INITCODE_SIZE_VIOLATION"] = "initcode exceeds max initcode size"; | |
ERROR2["INVALID_INPUT_LENGTH"] = "invalid input length"; | |
ERROR2["INVALID_EOF_FORMAT"] = "invalid EOF format"; | |
ERROR2["BLS_12_381_INVALID_INPUT_LENGTH"] = "invalid input length"; | |
ERROR2["BLS_12_381_POINT_NOT_ON_CURVE"] = "point not on curve"; | |
ERROR2["BLS_12_381_INPUT_EMPTY"] = "input is empty"; | |
ERROR2["BLS_12_381_FP_NOT_IN_FIELD"] = "fp point not in field"; | |
ERROR2["BN254_FP_NOT_IN_FIELD"] = "fp point not in field"; | |
ERROR2["INVALID_COMMITMENT"] = "kzg commitment does not match versioned hash"; | |
ERROR2["INVALID_INPUTS"] = "kzg inputs invalid"; | |
ERROR2["INVALID_PROOF"] = "kzg proof invalid"; | |
return ERROR2; | |
})(ERROR || {}); | |
class EvmError { | |
constructor(error) { | |
this.error = error; | |
this.errorType = "EvmError"; | |
} | |
} | |
function setupEOF(runState, eofMode = EOFContainerMode.Default) { | |
runState.env.eof = { | |
container: new EOFContainer(runState.code, eofMode), | |
eofRunState: { | |
returnStack: [] | |
// Return stack for RETF/CALLF/JUMPF | |
} | |
}; | |
if (runState.env.eof.container.body.txCallData !== void 0) { | |
runState.env.callData = runState.env.eof.container.body.txCallData; | |
} | |
const pc = runState.env.eof.container.header.getCodePosition(0); | |
runState.programCounter = pc; | |
} | |
const ceil = (value, ceiling) => { | |
const r = value % ceiling; | |
if (r === 0) { | |
return value; | |
} else { | |
return value + ceiling - r; | |
} | |
}; | |
const CONTAINER_SIZE = 8192; | |
class Memory { | |
constructor() { | |
this._store = new Uint8Array(CONTAINER_SIZE); | |
} | |
/** | |
* Extends the memory given an offset and size. Rounds extended | |
* memory to word-size. | |
*/ | |
extend(offset, size) { | |
if (size === 0) { | |
return; | |
} | |
const newSize = ceil(offset + size, 32); | |
const sizeDiff = newSize - this._store.length; | |
if (sizeDiff > 0) { | |
const expandBy = Math.ceil(sizeDiff / CONTAINER_SIZE) * CONTAINER_SIZE; | |
this._store = concatBytes$1(this._store, new Uint8Array(expandBy)); | |
} | |
} | |
/** | |
* Writes a byte array with length `size` to memory, starting from `offset`. | |
* @param offset - Starting position | |
* @param size - How many bytes to write | |
* @param value - Value | |
*/ | |
write(offset, size, value) { | |
if (size === 0) { | |
return; | |
} | |
this.extend(offset, size); | |
if (value.length !== size) throw new Error("Invalid value size"); | |
if (offset + size > this._store.length) throw new Error("Value exceeds memory capacity"); | |
this._store.set(value, offset); | |
} | |
/** | |
* Reads a slice of memory from `offset` till `offset + size` as a `Uint8Array`. | |
* It fills up the difference between memory's length and `offset + size` with zeros. | |
* @param offset - Starting position | |
* @param size - How many bytes to read | |
* @param avoidCopy - Avoid memory copy if possible for performance reasons (optional) | |
*/ | |
read(offset, size, avoidCopy) { | |
this.extend(offset, size); | |
const loaded = this._store.subarray(offset, offset + size); | |
if (avoidCopy === true) { | |
return loaded; | |
} | |
const returnBytes = new Uint8Array(size); | |
returnBytes.set(loaded); | |
return returnBytes; | |
} | |
} | |
const defaults = { | |
value: BIGINT_0, | |
caller: createZeroAddress(), | |
data: new Uint8Array(0), | |
depth: 0, | |
isStatic: false, | |
isCompiled: false, | |
delegatecall: false, | |
gasRefund: BIGINT_0 | |
}; | |
class Message { | |
constructor(opts) { | |
this.to = opts.to; | |
this.value = opts.value ?? defaults.value; | |
this.caller = opts.caller ?? defaults.caller; | |
this.gasLimit = opts.gasLimit; | |
this.data = opts.data ?? defaults.data; | |
this.eofCallData = opts.eofCallData; | |
this.depth = opts.depth ?? defaults.depth; | |
this.code = opts.code; | |
this._codeAddress = opts.codeAddress; | |
this.isStatic = opts.isStatic ?? defaults.isStatic; | |
this.isCompiled = opts.isCompiled ?? defaults.isCompiled; | |
this.salt = opts.salt; | |
this.selfdestruct = opts.selfdestruct; | |
this.createdAddresses = opts.createdAddresses; | |
this.delegatecall = opts.delegatecall ?? defaults.delegatecall; | |
this.gasRefund = opts.gasRefund ?? defaults.gasRefund; | |
this.blobVersionedHashes = opts.blobVersionedHashes; | |
this.accessWitness = opts.accessWitness; | |
if (this.value < 0) { | |
throw new Error(`value field cannot be negative, received ${this.value}`); | |
} | |
} | |
/** | |
* Note: should only be called in instances where `_codeAddress` or `to` is defined. | |
*/ | |
get codeAddress() { | |
const codeAddress = this._codeAddress ?? this.to; | |
if (!codeAddress) { | |
throw new Error("Missing codeAddress"); | |
} | |
return codeAddress; | |
} | |
} | |
class EVMMockBlockchain { | |
async getBlock() { | |
return { | |
hash() { | |
return zeros(32); | |
} | |
}; | |
} | |
async putBlock() { | |
} | |
shallowCopy() { | |
return this; | |
} | |
} | |
const DELEGATION_7702_FLAG = new Uint8Array([239, 1, 0]); | |
const MASK_160 = (BIGINT_1 << BIGINT_160) - BIGINT_1; | |
function createAddressFromStackBigInt(value) { | |
const maskedValue = value & MASK_160; | |
return createAddressFromBigInt(maskedValue); | |
} | |
function setLengthLeftStorage(value) { | |
if (equalsBytes(value, new Uint8Array(value.length))) { | |
return new Uint8Array(0); | |
} else { | |
return setLengthLeft(value, 32); | |
} | |
} | |
function trap(err) { | |
throw new EvmError(err); | |
} | |
function describeLocation(runState) { | |
const keccakFunction = runState.interpreter._evm.common.customCrypto.keccak256 ?? keccak256; | |
const hash2 = bytesToHex$1(keccakFunction(runState.interpreter.getCode())); | |
const address = runState.interpreter.getAddress().toString(); | |
const pc = runState.programCounter - 1; | |
return `${hash2}/${address}:${pc}`; | |
} | |
function divCeil(a, b) { | |
const div = a / b; | |
const modulus = mod(a, b); | |
if (modulus === BIGINT_0) return div; | |
return div < BIGINT_0 ? div - BIGINT_1 : div + BIGINT_1; | |
} | |
function getDataSlice(data, offset, length) { | |
const len = BigInt(data.length); | |
if (offset > len) { | |
offset = len; | |
} | |
let end = offset + length; | |
if (end > len) { | |
end = len; | |
} | |
data = data.subarray(Number(offset), Number(end)); | |
data = setLengthRight(data, Number(length)); | |
return data; | |
} | |
function getFullname(code, name) { | |
switch (name) { | |
case "LOG": | |
name += code - 160; | |
break; | |
case "PUSH": | |
name += code - 95; | |
break; | |
case "DUP": | |
name += code - 127; | |
break; | |
case "SWAP": | |
name += code - 143; | |
break; | |
} | |
return name; | |
} | |
function jumpIsValid(runState, dest) { | |
return runState.validJumps[dest] === 1; | |
} | |
function maxCallGas(gasLimit, gasLeft, runState, common2) { | |
if (common2.gteHardfork(Hardfork.TangerineWhistle)) { | |
const gasAllowed = gasLeft - gasLeft / BIGINT_64; | |
return gasLimit > gasAllowed ? gasAllowed : gasLimit; | |
} else { | |
return gasLimit; | |
} | |
} | |
function subMemUsage(runState, offset, length, common2) { | |
if (length === BIGINT_0) return BIGINT_0; | |
const newMemoryWordCount = divCeil(offset + length, BIGINT_32); | |
if (newMemoryWordCount <= runState.memoryWordCount) return BIGINT_0; | |
const words = newMemoryWordCount; | |
const fee = common2.param("memoryGas"); | |
const quadCoefficient = common2.param("quadCoefficientDivGas"); | |
let cost = words * fee + words * words / quadCoefficient; | |
if (cost > runState.highestMemCost) { | |
const currentHighestMemCost = runState.highestMemCost; | |
runState.highestMemCost = cost; | |
cost -= currentHighestMemCost; | |
} | |
runState.memoryWordCount = newMemoryWordCount; | |
return cost; | |
} | |
function writeCallOutput(runState, outOffset, outLength) { | |
const returnData = runState.interpreter.getReturnData(); | |
if (returnData.length > 0) { | |
const memOffset = Number(outOffset); | |
let dataLength = Number(outLength); | |
if (BigInt(returnData.length) < dataLength) { | |
dataLength = returnData.length; | |
} | |
const data = getDataSlice(returnData, BIGINT_0, BigInt(dataLength)); | |
runState.memory.extend(memOffset, dataLength); | |
runState.memory.write(memOffset, dataLength, data); | |
} | |
} | |
function updateSstoreGas(runState, currentStorage, value, common2) { | |
if (value.length === 0 && currentStorage.length === 0 || value.length > 0 && currentStorage.length > 0) { | |
const gas = common2.param("sstoreResetGas"); | |
return gas; | |
} else if (value.length === 0 && currentStorage.length > 0) { | |
const gas = common2.param("sstoreResetGas"); | |
runState.interpreter.refundGas(common2.param("sstoreRefundGas"), "updateSstoreGas"); | |
return gas; | |
} else { | |
return common2.param("sstoreSetGas"); | |
} | |
} | |
function mod(a, b) { | |
let r = a % b; | |
if (r < BIGINT_0) { | |
r = b + r; | |
} | |
return r; | |
} | |
function fromTwos(a) { | |
return BigInt.asIntN(256, a); | |
} | |
function toTwos(a) { | |
return BigInt.asUintN(256, a); | |
} | |
const N = BigInt(1157920892373162e62); | |
function exponentiation(bas, exp) { | |
let t = BIGINT_1; | |
while (exp > BIGINT_0) { | |
if (exp % BIGINT_2 !== BIGINT_0) { | |
t = t * bas % N; | |
} | |
bas = bas * bas % N; | |
exp = exp / BIGINT_2; | |
} | |
return t; | |
} | |
function getEIP7702DelegatedAddress(code) { | |
if (equalsBytes(code.slice(0, 3), DELEGATION_7702_FLAG)) { | |
return new Address(code.slice(3, 24)); | |
} | |
} | |
async function eip7702CodeCheck(runState, code) { | |
const address = getEIP7702DelegatedAddress(code); | |
if (address !== void 0) { | |
return runState.stateManager.getCode(address); | |
} | |
return code; | |
} | |
const handlers = /* @__PURE__ */ new Map([ | |
// 0x00: STOP | |
[ | |
0, | |
function() { | |
trap(ERROR.STOP); | |
} | |
], | |
// 0x01: ADD | |
[ | |
1, | |
function(runState) { | |
const [a, b] = runState.stack.popN(2); | |
const r = mod(a + b, TWO_POW256); | |
runState.stack.push(r); | |
} | |
], | |
// 0x02: MUL | |
[ | |
2, | |
function(runState) { | |
const [a, b] = runState.stack.popN(2); | |
const r = mod(a * b, TWO_POW256); | |
runState.stack.push(r); | |
} | |
], | |
// 0x03: SUB | |
[ | |
3, | |
function(runState) { | |
const [a, b] = runState.stack.popN(2); | |
const r = mod(a - b, TWO_POW256); | |
runState.stack.push(r); | |
} | |
], | |
// 0x04: DIV | |
[ | |
4, | |
function(runState) { | |
const [a, b] = runState.stack.popN(2); | |
let r; | |
if (b === BIGINT_0) { | |
r = BIGINT_0; | |
} else { | |
r = mod(a / b, TWO_POW256); | |
} | |
runState.stack.push(r); | |
} | |
], | |
// 0x05: SDIV | |
[ | |
5, | |
function(runState) { | |
const [a, b] = runState.stack.popN(2); | |
let r; | |
if (b === BIGINT_0) { | |
r = BIGINT_0; | |
} else { | |
r = toTwos(fromTwos(a) / fromTwos(b)); | |
} | |
runState.stack.push(r); | |
} | |
], | |
// 0x06: MOD | |
[ | |
6, | |
function(runState) { | |
const [a, b] = runState.stack.popN(2); | |
let r; | |
if (b === BIGINT_0) { | |
r = b; | |
} else { | |
r = mod(a, b); | |
} | |
runState.stack.push(r); | |
} | |
], | |
// 0x07: SMOD | |
[ | |
7, | |
function(runState) { | |
const [a, b] = runState.stack.popN(2); | |
let r; | |
if (b === BIGINT_0) { | |
r = b; | |
} else { | |
r = fromTwos(a) % fromTwos(b); | |
} | |
runState.stack.push(toTwos(r)); | |
} | |
], | |
// 0x08: ADDMOD | |
[ | |
8, | |
function(runState) { | |
const [a, b, c] = runState.stack.popN(3); | |
let r; | |
if (c === BIGINT_0) { | |
r = BIGINT_0; | |
} else { | |
r = mod(a + b, c); | |
} | |
runState.stack.push(r); | |
} | |
], | |
// 0x09: MULMOD | |
[ | |
9, | |
function(runState) { | |
const [a, b, c] = runState.stack.popN(3); | |
let r; | |
if (c === BIGINT_0) { | |
r = BIGINT_0; | |
} else { | |
r = mod(a * b, c); | |
} | |
runState.stack.push(r); | |
} | |
], | |
// 0x0a: EXP | |
[ | |
10, | |
function(runState) { | |
const [base, exponent] = runState.stack.popN(2); | |
if (base === BIGINT_2) { | |
switch (exponent) { | |
case BIGINT_96: | |
runState.stack.push(BIGINT_2EXP96); | |
return; | |
case BIGINT_160: | |
runState.stack.push(BIGINT_2EXP160); | |
return; | |
case BIGINT_224: | |
runState.stack.push(BIGINT_2EXP224); | |
return; | |
} | |
} | |
if (exponent === BIGINT_0) { | |
runState.stack.push(BIGINT_1); | |
return; | |
} | |
if (base === BIGINT_0) { | |
runState.stack.push(base); | |
return; | |
} | |
const r = exponentiation(base, exponent); | |
runState.stack.push(r); | |
} | |
], | |
// 0x0b: SIGNEXTEND | |
[ | |
11, | |
function(runState) { | |
let [k, val] = runState.stack.popN(2); | |
if (k < BIGINT_31) { | |
const signBit = k * BIGINT_8 + BIGINT_7; | |
const mask = (BIGINT_1 << signBit) - BIGINT_1; | |
if (val >> signBit & BIGINT_1) { | |
val = val | BigInt.asUintN(256, ~mask); | |
} else { | |
val = val & mask; | |
} | |
} | |
runState.stack.push(val); | |
} | |
], | |
// 0x10 range - bit ops | |
// 0x10: LT | |
[ | |
16, | |
function(runState) { | |
const [a, b] = runState.stack.popN(2); | |
const r = a < b ? BIGINT_1 : BIGINT_0; | |
runState.stack.push(r); | |
} | |
], | |
// 0x11: GT | |
[ | |
17, | |
function(runState) { | |
const [a, b] = runState.stack.popN(2); | |
const r = a > b ? BIGINT_1 : BIGINT_0; | |
runState.stack.push(r); | |
} | |
], | |
// 0x12: SLT | |
[ | |
18, | |
function(runState) { | |
const [a, b] = runState.stack.popN(2); | |
const r = fromTwos(a) < fromTwos(b) ? BIGINT_1 : BIGINT_0; | |
runState.stack.push(r); | |
} | |
], | |
// 0x13: SGT | |
[ | |
19, | |
function(runState) { | |
const [a, b] = runState.stack.popN(2); | |
const r = fromTwos(a) > fromTwos(b) ? BIGINT_1 : BIGINT_0; | |
runState.stack.push(r); | |
} | |
], | |
// 0x14: EQ | |
[ | |
20, | |
function(runState) { | |
const [a, b] = runState.stack.popN(2); | |
const r = a === b ? BIGINT_1 : BIGINT_0; | |
runState.stack.push(r); | |
} | |
], | |
// 0x15: ISZERO | |
[ | |
21, | |
function(runState) { | |
const a = runState.stack.pop(); | |
const r = a === BIGINT_0 ? BIGINT_1 : BIGINT_0; | |
runState.stack.push(r); | |
} | |
], | |
// 0x16: AND | |
[ | |
22, | |
function(runState) { | |
const [a, b] = runState.stack.popN(2); | |
const r = a & b; | |
runState.stack.push(r); | |
} | |
], | |
// 0x17: OR | |
[ | |
23, | |
function(runState) { | |
const [a, b] = runState.stack.popN(2); | |
const r = a | b; | |
runState.stack.push(r); | |
} | |
], | |
// 0x18: XOR | |
[ | |
24, | |
function(runState) { | |
const [a, b] = runState.stack.popN(2); | |
const r = a ^ b; | |
runState.stack.push(r); | |
} | |
], | |
// 0x19: NOT | |
[ | |
25, | |
function(runState) { | |
const a = runState.stack.pop(); | |
const r = BigInt.asUintN(256, ~a); | |
runState.stack.push(r); | |
} | |
], | |
// 0x1a: BYTE | |
[ | |
26, | |
function(runState) { | |
const [pos, word] = runState.stack.popN(2); | |
if (pos > BIGINT_32) { | |
runState.stack.push(BIGINT_0); | |
return; | |
} | |
const r = word >> (BIGINT_31 - pos) * BIGINT_8 & BIGINT_255; | |
runState.stack.push(r); | |
} | |
], | |
// 0x1b: SHL | |
[ | |
27, | |
function(runState) { | |
const [a, b] = runState.stack.popN(2); | |
if (a > BIGINT_256) { | |
runState.stack.push(BIGINT_0); | |
return; | |
} | |
const r = b << a & MAX_INTEGER_BIGINT; | |
runState.stack.push(r); | |
} | |
], | |
// 0x1c: SHR | |
[ | |
28, | |
function(runState) { | |
const [a, b] = runState.stack.popN(2); | |
if (a > 256) { | |
runState.stack.push(BIGINT_0); | |
return; | |
} | |
const r = b >> a; | |
runState.stack.push(r); | |
} | |
], | |
// 0x1d: SAR | |
[ | |
29, | |
function(runState) { | |
const [a, b] = runState.stack.popN(2); | |
let r; | |
const bComp = BigInt.asIntN(256, b); | |
const isSigned = bComp < 0; | |
if (a > 256) { | |
if (isSigned) { | |
r = MAX_INTEGER_BIGINT; | |
} else { | |
r = BIGINT_0; | |
} | |
runState.stack.push(r); | |
return; | |
} | |
const c = b >> a; | |
if (isSigned) { | |
const shiftedOutWidth = BIGINT_255 - a; | |
const mask = MAX_INTEGER_BIGINT >> shiftedOutWidth << shiftedOutWidth; | |
r = c | mask; | |
} else { | |
r = c; | |
} | |
runState.stack.push(r); | |
} | |
], | |
// 0x20 range - crypto | |
// 0x20: KECCAK256 | |
[ | |
32, | |
function(runState, common2) { | |
const [offset, length] = runState.stack.popN(2); | |
let data = new Uint8Array(0); | |
if (length !== BIGINT_0) { | |
data = runState.memory.read(Number(offset), Number(length)); | |
} | |
const r = BigInt(bytesToHex$1((common2.customCrypto.keccak256 ?? keccak256)(data))); | |
runState.stack.push(r); | |
} | |
], | |
// 0x30 range - closure state | |
// 0x30: ADDRESS | |
[ | |
48, | |
function(runState) { | |
const address = bytesToBigInt(runState.interpreter.getAddress().bytes); | |
runState.stack.push(address); | |
} | |
], | |
// 0x31: BALANCE | |
[ | |
49, | |
async function(runState) { | |
const addressBigInt = runState.stack.pop(); | |
const address = createAddressFromStackBigInt(addressBigInt); | |
const balance = await runState.interpreter.getExternalBalance(address); | |
runState.stack.push(balance); | |
} | |
], | |
// 0x32: ORIGIN | |
[ | |
50, | |
function(runState) { | |
runState.stack.push(runState.interpreter.getTxOrigin()); | |
} | |
], | |
// 0x33: CALLER | |
[ | |
51, | |
function(runState) { | |
runState.stack.push(runState.interpreter.getCaller()); | |
} | |
], | |
// 0x34: CALLVALUE | |
[ | |
52, | |
function(runState) { | |
runState.stack.push(runState.interpreter.getCallValue()); | |
} | |
], | |
// 0x35: CALLDATALOAD | |
[ | |
53, | |
function(runState) { | |
const pos = runState.stack.pop(); | |
if (pos > runState.interpreter.getCallDataSize()) { | |
runState.stack.push(BIGINT_0); | |
return; | |
} | |
const i = Number(pos); | |
let loaded = runState.interpreter.getCallData().subarray(i, i + 32); | |
loaded = loaded.length ? loaded : Uint8Array.from([0]); | |
let r = bytesToBigInt(loaded); | |
if (loaded.length < 32) { | |
r = r << BIGINT_8 * BigInt(32 - loaded.length); | |
} | |
runState.stack.push(r); | |
} | |
], | |
// 0x36: CALLDATASIZE | |
[ | |
54, | |
function(runState) { | |
const r = runState.interpreter.getCallDataSize(); | |
runState.stack.push(r); | |
} | |
], | |
// 0x37: CALLDATACOPY | |
[ | |
55, | |
function(runState) { | |
const [memOffset, dataOffset, dataLength] = runState.stack.popN(3); | |
if (dataLength !== BIGINT_0) { | |
const data = getDataSlice(runState.interpreter.getCallData(), dataOffset, dataLength); | |
const memOffsetNum = Number(memOffset); | |
const dataLengthNum = Number(dataLength); | |
runState.memory.write(memOffsetNum, dataLengthNum, data); | |
} | |
} | |
], | |
// 0x38: CODESIZE | |
[ | |
56, | |
function(runState) { | |
runState.stack.push(runState.interpreter.getCodeSize()); | |
} | |
], | |
// 0x39: CODECOPY | |
[ | |
57, | |
function(runState) { | |
const [memOffset, codeOffset, dataLength] = runState.stack.popN(3); | |
if (dataLength !== BIGINT_0) { | |
const data = getDataSlice(runState.interpreter.getCode(), codeOffset, dataLength); | |
const memOffsetNum = Number(memOffset); | |
const lengthNum = Number(dataLength); | |
runState.memory.write(memOffsetNum, lengthNum, data); | |
} | |
} | |
], | |
// 0x3b: EXTCODESIZE | |
[ | |
59, | |
async function(runState, common2) { | |
const addressBigInt = runState.stack.pop(); | |
const address = createAddressFromStackBigInt(addressBigInt); | |
let code = await runState.stateManager.getCode(address); | |
if (isEOF(code)) { | |
runState.stack.push(BigInt(EOFBYTES.length)); | |
return; | |
} else if (common2.isActivatedEIP(7702)) { | |
code = await eip7702CodeCheck(runState, code); | |
} | |
const size = BigInt(code.length); | |
runState.stack.push(size); | |
} | |
], | |
// 0x3c: EXTCODECOPY | |
[ | |
60, | |
async function(runState, common2) { | |
const [addressBigInt, memOffset, codeOffset, dataLength] = runState.stack.popN(4); | |
if (dataLength !== BIGINT_0) { | |
const address = createAddressFromStackBigInt(addressBigInt); | |
let code = await runState.stateManager.getCode(address); | |
if (isEOF(code)) { | |
code = EOFBYTES; | |
} else if (common2.isActivatedEIP(7702)) { | |
code = await eip7702CodeCheck(runState, code); | |
} | |
const data = getDataSlice(code, codeOffset, dataLength); | |
const memOffsetNum = Number(memOffset); | |
const lengthNum = Number(dataLength); | |
runState.memory.write(memOffsetNum, lengthNum, data); | |
} | |
} | |
], | |
// 0x3f: EXTCODEHASH | |
[ | |
63, | |
async function(runState, common2) { | |
const addressBigInt = runState.stack.pop(); | |
const address = createAddressFromStackBigInt(addressBigInt); | |
const code = await runState.stateManager.getCode(address); | |
if (isEOF(code)) { | |
runState.stack.push(bytesToBigInt(EOFHASH)); | |
return; | |
} else if (common2.isActivatedEIP(7702)) { | |
const possibleDelegatedAddress = getEIP7702DelegatedAddress(code); | |
if (possibleDelegatedAddress !== void 0) { | |
const account2 = await runState.stateManager.getAccount(possibleDelegatedAddress); | |
if (!account2 || account2.isEmpty()) { | |
runState.stack.push(BIGINT_0); | |
return; | |
} | |
runState.stack.push(BigInt(bytesToHex$1(account2.codeHash))); | |
return; | |
} else { | |
runState.stack.push(bytesToBigInt(keccak256(code))); | |
return; | |
} | |
} | |
const account = await runState.stateManager.getAccount(address); | |
if (!account || account.isEmpty()) { | |
runState.stack.push(BIGINT_0); | |
return; | |
} | |
runState.stack.push(BigInt(bytesToHex$1(account.codeHash))); | |
} | |
], | |
// 0x3d: RETURNDATASIZE | |
[ | |
61, | |
function(runState) { | |
runState.stack.push(runState.interpreter.getReturnDataSize()); | |
} | |
], | |
// 0x3e: RETURNDATACOPY | |
[ | |
62, | |
function(runState) { | |
const [memOffset, returnDataOffset, dataLength] = runState.stack.popN(3); | |
if (dataLength !== BIGINT_0) { | |
const data = getDataSlice( | |
runState.interpreter.getReturnData(), | |
returnDataOffset, | |
dataLength | |
); | |
const memOffsetNum = Number(memOffset); | |
const lengthNum = Number(dataLength); | |
runState.memory.write(memOffsetNum, lengthNum, data); | |
} | |
} | |
], | |
// 0x3a: GASPRICE | |
[ | |
58, | |
function(runState) { | |
runState.stack.push(runState.interpreter.getTxGasPrice()); | |
} | |
], | |
// '0x40' range - block operations | |
// 0x40: BLOCKHASH | |
[ | |
64, | |
async function(runState, common2) { | |
const number2 = runState.stack.pop(); | |
if (common2.isActivatedEIP(7709)) { | |
if (number2 >= runState.interpreter.getBlockNumber()) { | |
runState.stack.push(BIGINT_0); | |
return; | |
} | |
const diff = runState.interpreter.getBlockNumber() - number2; | |
if (diff > BIGINT_256 || diff <= BIGINT_0) { | |
runState.stack.push(BIGINT_0); | |
return; | |
} | |
const historyAddress = new Address( | |
bigIntToAddressBytes(common2.param("historyStorageAddress")) | |
); | |
const historyServeWindow = common2.param("historyServeWindow"); | |
const key = setLengthLeft(bigIntToBytes(number2 % historyServeWindow), 32); | |
if (common2.isActivatedEIP(6800)) { | |
const { treeIndex, subIndex } = getVerkleTreeIndicesForStorageSlot(number2); | |
const statelessGas = runState.env.accessWitness.touchAddressOnReadAndComputeGas( | |
historyAddress, | |
treeIndex, | |
subIndex | |
); | |
runState.interpreter.useGas(statelessGas, `BLOCKHASH`); | |
} | |
const storage = await runState.stateManager.getStorage(historyAddress, key); | |
runState.stack.push(bytesToBigInt(storage)); | |
} else { | |
const diff = runState.interpreter.getBlockNumber() - number2; | |
if (diff > BIGINT_256 || diff <= BIGINT_0) { | |
runState.stack.push(BIGINT_0); | |
return; | |
} | |
const block = await runState.blockchain.getBlock(Number(number2)); | |
runState.stack.push(bytesToBigInt(block.hash())); | |
} | |
} | |
], | |
// 0x41: COINBASE | |
[ | |
65, | |
function(runState) { | |
runState.stack.push(runState.interpreter.getBlockCoinbase()); | |
} | |
], | |
// 0x42: TIMESTAMP | |
[ | |
66, | |
function(runState) { | |
runState.stack.push(runState.interpreter.getBlockTimestamp()); | |
} | |
], | |
// 0x43: NUMBER | |
[ | |
67, | |
function(runState) { | |
runState.stack.push(runState.interpreter.getBlockNumber()); | |
} | |
], | |
// 0x44: DIFFICULTY (EIP-4399: supplanted as PREVRANDAO) | |
[ | |
68, | |
function(runState, common2) { | |
if (common2.isActivatedEIP(4399)) { | |
runState.stack.push(runState.interpreter.getBlockPrevRandao()); | |
} else { | |
runState.stack.push(runState.interpreter.getBlockDifficulty()); | |
} | |
} | |
], | |
// 0x45: GASLIMIT | |
[ | |
69, | |
function(runState) { | |
runState.stack.push(runState.interpreter.getBlockGasLimit()); | |
} | |
], | |
// 0x46: CHAINID | |
[ | |
70, | |
function(runState) { | |
runState.stack.push(runState.interpreter.getChainId()); | |
} | |
], | |
// 0x47: SELFBALANCE | |
[ | |
71, | |
function(runState) { | |
runState.stack.push(runState.interpreter.getSelfBalance()); | |
} | |
], | |
// 0x48: BASEFEE | |
[ | |
72, | |
function(runState) { | |
runState.stack.push(runState.interpreter.getBlockBaseFee()); | |
} | |
], | |
// 0x49: BLOBHASH | |
[ | |
73, | |
function(runState) { | |
const index = runState.stack.pop(); | |
if (runState.env.blobVersionedHashes.length > Number(index)) { | |
runState.stack.push(bytesToBigInt(runState.env.blobVersionedHashes[Number(index)])); | |
} else { | |
runState.stack.push(BIGINT_0); | |
} | |
} | |
], | |
// 0x4a: BLOBBASEFEE | |
[ | |
74, | |
function(runState) { | |
runState.stack.push(runState.interpreter.getBlobBaseFee()); | |
} | |
], | |
// 0x50 range - 'storage' and execution | |
// 0x50: POP | |
[ | |
80, | |
function(runState) { | |
runState.stack.pop(); | |
} | |
], | |
// 0x51: MLOAD | |
[ | |
81, | |
function(runState) { | |
const pos = runState.stack.pop(); | |
const word = runState.memory.read(Number(pos), 32, true); | |
runState.stack.push(bytesToBigInt(word)); | |
} | |
], | |
// 0x52: MSTORE | |
[ | |
82, | |
function(runState) { | |
const [offset, word] = runState.stack.popN(2); | |
const buf = setLengthLeft(bigIntToBytes(word), 32); | |
const offsetNum = Number(offset); | |
runState.memory.write(offsetNum, 32, buf); | |
} | |
], | |
// 0x53: MSTORE8 | |
[ | |
83, | |
function(runState) { | |
const [offset, byte] = runState.stack.popN(2); | |
const buf = bigIntToBytes(byte & BIGINT_255); | |
const offsetNum = Number(offset); | |
runState.memory.write(offsetNum, 1, buf); | |
} | |
], | |
// 0x54: SLOAD | |
[ | |
84, | |
async function(runState) { | |
const key = runState.stack.pop(); | |
const keyBuf = setLengthLeft(bigIntToBytes(key), 32); | |
const value = await runState.interpreter.storageLoad(keyBuf); | |
const valueBigInt = value.length ? bytesToBigInt(value) : BIGINT_0; | |
runState.stack.push(valueBigInt); | |
} | |
], | |
// 0x55: SSTORE | |
[ | |
85, | |
async function(runState) { | |
const [key, val] = runState.stack.popN(2); | |
const keyBuf = setLengthLeft(bigIntToBytes(key), 32); | |
let value; | |
if (val === BIGINT_0) { | |
value = Uint8Array.from([]); | |
} else { | |
value = bigIntToBytes(val); | |
} | |
await runState.interpreter.storageStore(keyBuf, value); | |
} | |
], | |
// 0x56: JUMP | |
[ | |
86, | |
function(runState) { | |
const dest = runState.stack.pop(); | |
if (dest > runState.interpreter.getCodeSize()) { | |
trap(ERROR.INVALID_JUMP + " at " + describeLocation(runState)); | |
} | |
const destNum = Number(dest); | |
if (!jumpIsValid(runState, destNum)) { | |
trap(ERROR.INVALID_JUMP + " at " + describeLocation(runState)); | |
} | |
runState.programCounter = destNum; | |
} | |
], | |
// 0x57: JUMPI | |
[ | |
87, | |
function(runState) { | |
const [dest, cond] = runState.stack.popN(2); | |
if (cond !== BIGINT_0) { | |
if (dest > runState.interpreter.getCodeSize()) { | |
trap(ERROR.INVALID_JUMP + " at " + describeLocation(runState)); | |
} | |
const destNum = Number(dest); | |
if (!jumpIsValid(runState, destNum)) { | |
trap(ERROR.INVALID_JUMP + " at " + describeLocation(runState)); | |
} | |
runState.programCounter = destNum; | |
} | |
} | |
], | |
// 0x58: PC | |
[ | |
88, | |
function(runState) { | |
runState.stack.push(BigInt(runState.programCounter - 1)); | |
} | |
], | |
// 0x59: MSIZE | |
[ | |
89, | |
function(runState) { | |
runState.stack.push(runState.memoryWordCount * BIGINT_32); | |
} | |
], | |
// 0x5a: GAS | |
[ | |
90, | |
function(runState) { | |
runState.stack.push(runState.interpreter.getGasLeft()); | |
} | |
], | |
// 0x5b: JUMPDEST | |
[91, function() { | |
}], | |
// 0x5c: TLOAD (EIP 1153) | |
[ | |
92, | |
function(runState) { | |
const key = runState.stack.pop(); | |
const keyBuf = setLengthLeft(bigIntToBytes(key), 32); | |
const value = runState.interpreter.transientStorageLoad(keyBuf); | |
const valueBN = value.length ? bytesToBigInt(value) : BIGINT_0; | |
runState.stack.push(valueBN); | |
} | |
], | |
// 0x5d: TSTORE (EIP 1153) | |
[ | |
93, | |
function(runState) { | |
if (runState.interpreter.isStatic()) { | |
trap(ERROR.STATIC_STATE_CHANGE); | |
} | |
const [key, val] = runState.stack.popN(2); | |
const keyBuf = setLengthLeft(bigIntToBytes(key), 32); | |
let value; | |
if (val === BIGINT_0) { | |
value = Uint8Array.from([]); | |
} else { | |
value = bigIntToBytes(val); | |
} | |
runState.interpreter.transientStorageStore(keyBuf, value); | |
} | |
], | |
// 0x5e: MCOPY (5656) | |
[ | |
94, | |
function(runState) { | |
const [dst, src, length] = runState.stack.popN(3); | |
const data = runState.memory.read(Number(src), Number(length), true); | |
runState.memory.write(Number(dst), Number(length), data); | |
} | |
], | |
// 0x5f: PUSH0 | |
[ | |
95, | |
function(runState) { | |
runState.stack.push(BIGINT_0); | |
} | |
], | |
// 0x60: PUSH | |
[ | |
96, | |
function(runState, common2) { | |
const numToPush = runState.opCode - 95; | |
if (runState.programCounter + numToPush > runState.code.length && common2.isActivatedEIP(3540)) { | |
trap(ERROR.OUT_OF_RANGE); | |
} | |
if (common2.isActivatedEIP(6800) && runState.env.chargeCodeAccesses === true) { | |
const contract = runState.interpreter.getAddress(); | |
const startOffset = Math.min(runState.code.length, runState.programCounter + 1); | |
const endOffset = Math.min(runState.code.length, startOffset + numToPush - 1); | |
const statelessGas = runState.env.accessWitness.touchCodeChunksRangeOnReadAndChargeGas( | |
contract, | |
startOffset, | |
endOffset | |
); | |
runState.interpreter.useGas(statelessGas, `PUSH`); | |
} | |
if (!runState.shouldDoJumpAnalysis) { | |
runState.stack.push(runState.cachedPushes[runState.programCounter]); | |
runState.programCounter += numToPush; | |
} else { | |
const loaded = bytesToBigInt( | |
runState.code.subarray(runState.programCounter, runState.programCounter + numToPush) | |
); | |
runState.programCounter += numToPush; | |
runState.stack.push(loaded); | |
} | |
} | |
], | |
// 0x80: DUP | |
[ | |
128, | |
function(runState) { | |
const stackPos = runState.opCode - 127; | |
runState.stack.dup(stackPos); | |
} | |
], | |
// 0x90: SWAP | |
[ | |
144, | |
function(runState) { | |
const stackPos = runState.opCode - 143; | |
runState.stack.swap(stackPos); | |
} | |
], | |
// 0xa0: LOG | |
[ | |
160, | |
function(runState) { | |
const [memOffset, memLength] = runState.stack.popN(2); | |
const topicsCount = runState.opCode - 160; | |
const topics = runState.stack.popN(topicsCount); | |
const topicsBuf = topics.map(function(a) { | |
return setLengthLeft(bigIntToBytes(a), 32); | |
}); | |
let mem = new Uint8Array(0); | |
if (memLength !== BIGINT_0) { | |
mem = runState.memory.read(Number(memOffset), Number(memLength)); | |
} | |
runState.interpreter.log(mem, topicsCount, topicsBuf); | |
} | |
], | |
// 0xd0: DATALOAD | |
[ | |
208, | |
function(runState, _common) { | |
if (runState.env.eof === void 0) { | |
trap(ERROR.INVALID_OPCODE); | |
} | |
const pos = runState.stack.pop(); | |
if (pos > runState.env.eof.container.body.dataSection.length) { | |
runState.stack.push(BIGINT_0); | |
return; | |
} | |
const i = Number(pos); | |
let loaded = runState.env.eof.container.body.dataSection.subarray(i, i + 32); | |
loaded = loaded.length ? loaded : Uint8Array.from([0]); | |
let r = bytesToBigInt(loaded); | |
if (loaded.length < 32) { | |
r = r << BIGINT_8 * BigInt(32 - loaded.length); | |
} | |
runState.stack.push(r); | |
} | |
], | |
// 0xd1: DATALOADN | |
[ | |
209, | |
function(runState, _common) { | |
if (runState.env.eof === void 0) { | |
trap(ERROR.INVALID_OPCODE); | |
} | |
const toLoad = Number( | |
bytesToBigInt(runState.code.subarray(runState.programCounter, runState.programCounter + 2)) | |
); | |
const data = bytesToBigInt( | |
runState.env.eof.container.body.dataSection.subarray(toLoad, toLoad + 32) | |
); | |
runState.stack.push(data); | |
runState.programCounter += 2; | |
} | |
], | |
// 0xd2: DATASIZE | |
[ | |
210, | |
function(runState, _common) { | |
if (runState.env.eof === void 0) { | |
trap(ERROR.INVALID_OPCODE); | |
} | |
runState.stack.push(BigInt(runState.env.eof.container.body.dataSection.length)); | |
} | |
], | |
// 0xd3: DATACOPY | |
[ | |
211, | |
function(runState, _common) { | |
if (runState.env.eof === void 0) { | |
trap(ERROR.INVALID_OPCODE); | |
} | |
const [memOffset, offset, size] = runState.stack.popN(3); | |
if (size !== BIGINT_0) { | |
const data = getDataSlice(runState.env.eof.container.body.dataSection, offset, size); | |
const memOffsetNum = Number(memOffset); | |
const dataLengthNum = Number(size); | |
runState.memory.write(memOffsetNum, dataLengthNum, data); | |
} | |
} | |
], | |
// 0xe0: RJUMP | |
[ | |
224, | |
function(runState, _common) { | |
if (runState.env.eof === void 0) { | |
trap(ERROR.INVALID_OPCODE); | |
} else { | |
const code = runState.env.code; | |
const rjumpDest = new DataView(code.buffer).getInt16(runState.programCounter); | |
runState.programCounter += 2 + rjumpDest; | |
} | |
} | |
], | |
// 0xe1: RJUMPI | |
[ | |
225, | |
function(runState, _common) { | |
if (runState.env.eof === void 0) { | |
trap(ERROR.INVALID_OPCODE); | |
} else { | |
const cond = runState.stack.pop(); | |
if (cond > 0) { | |
const code = runState.env.code; | |
const rjumpDest = new DataView(code.buffer).getInt16(runState.programCounter); | |
runState.programCounter += rjumpDest; | |
} | |
runState.programCounter += 2; | |
} | |
} | |
], | |
// 0xe2: RJUMPV | |
[ | |
226, | |
function(runState, _common) { | |
if (runState.env.eof === void 0) { | |
trap(ERROR.INVALID_OPCODE); | |
} else { | |
const code = runState.env.code; | |
const jumptableEntries = code[runState.programCounter]; | |
const jumptableSize = (jumptableEntries + 1) * 2; | |
runState.programCounter += 1; | |
const jumptableCase = runState.stack.pop(); | |
if (jumptableCase <= jumptableEntries) { | |
const rjumpDest = new DataView(code.buffer).getInt16( | |
runState.programCounter + Number(jumptableCase) * 2 | |
); | |
runState.programCounter += jumptableSize + rjumpDest; | |
} else { | |
runState.programCounter += jumptableSize; | |
} | |
} | |
} | |
], | |
// 0xe3: CALLF | |
[ | |
227, | |
function(runState, _common) { | |
var _a; | |
if (runState.env.eof === void 0) { | |
trap(ERROR.INVALID_OPCODE); | |
} | |
const sectionTarget = bytesToInt( | |
runState.code.slice(runState.programCounter, runState.programCounter + 2) | |
); | |
const stackItems = runState.stack.length; | |
const typeSection = runState.env.eof.container.body.typeSections[sectionTarget]; | |
if (1024 < stackItems + (typeSection == null ? void 0 : typeSection.inputs) - (typeSection == null ? void 0 : typeSection.maxStackHeight)) { | |
trap(EOFError.StackOverflow); | |
} | |
if (runState.env.eof.eofRunState.returnStack.length >= 1024) { | |
trap(EOFError.ReturnStackOverflow); | |
} | |
(_a = runState.env.eof) == null ? void 0 : _a.eofRunState.returnStack.push(runState.programCounter + 2); | |
runState.programCounter = runState.env.eof.container.header.getCodePosition(sectionTarget); | |
} | |
], | |
// 0xe4: RETF | |
[ | |
228, | |
function(runState, _common) { | |
if (runState.env.eof === void 0) { | |
trap(ERROR.INVALID_OPCODE); | |
} | |
const newPc = runState.env.eof.eofRunState.returnStack.pop(); | |
if (newPc === void 0) { | |
trap(EOFError.RetfNoReturn); | |
} | |
runState.programCounter = newPc; | |
} | |
], | |
// 0xe5: JUMPF | |
[ | |
229, | |
function(runState, _common) { | |
if (runState.env.eof === void 0) { | |
trap(ERROR.INVALID_OPCODE); | |
} | |
const sectionTarget = bytesToInt( | |
runState.code.slice(runState.programCounter, runState.programCounter + 2) | |
); | |
const stackItems = runState.stack.length; | |
const typeSection = runState.env.eof.container.body.typeSections[sectionTarget]; | |
if (1024 < stackItems + (typeSection == null ? void 0 : typeSection.inputs) - (typeSection == null ? void 0 : typeSection.maxStackHeight)) { | |
trap(EOFError.StackOverflow); | |
} | |
runState.programCounter = runState.env.eof.container.header.getCodePosition(sectionTarget); | |
} | |
], | |
// 0xe6: DUPN | |
[ | |
230, | |
function(runState, _common) { | |
if (runState.env.eof === void 0) { | |
trap(ERROR.INVALID_OPCODE); | |
} | |
const toDup = Number( | |
bytesToBigInt( | |
runState.code.subarray(runState.programCounter, runState.programCounter + 1) | |
) | |
) + 1; | |
runState.stack.dup(toDup); | |
runState.programCounter++; | |
} | |
], | |
// 0xe7: SWAPN | |
[ | |
231, | |
function(runState, _common) { | |
if (runState.env.eof === void 0) { | |
trap(ERROR.INVALID_OPCODE); | |
} | |
const toSwap = Number( | |
bytesToBigInt( | |
runState.code.subarray(runState.programCounter, runState.programCounter + 1) | |
) | |
) + 1; | |
runState.stack.swap(toSwap); | |
runState.programCounter++; | |
} | |
], | |
// 0xe8: EXCHANGE | |
[ | |
232, | |
function(runState, _common) { | |
if (runState.env.eof === void 0) { | |
trap(ERROR.INVALID_OPCODE); | |
} | |
const toExchange = Number( | |
bytesToBigInt(runState.code.subarray(runState.programCounter, runState.programCounter + 1)) | |
); | |
const n = (toExchange >> 4) + 1; | |
const m = (toExchange & 15) + 1; | |
runState.stack.exchange(n, n + m); | |
runState.programCounter++; | |
} | |
], | |
// 0xec: EOFCREATE | |
[ | |
236, | |
async function(runState, _common) { | |
if (runState.env.eof === void 0) { | |
trap(ERROR.INVALID_OPCODE); | |
} else { | |
const containerIndex = runState.env.code[runState.programCounter]; | |
const containerCode = runState.env.eof.container.body.containerSections[containerIndex]; | |
const [value, salt, inputOffset, inputSize] = runState.stack.popN(4); | |
const gasLimit = runState.messageGasLimit; | |
runState.messageGasLimit = void 0; | |
let data = new Uint8Array(0); | |
if (inputSize !== BIGINT_0) { | |
data = runState.memory.read(Number(inputOffset), Number(inputSize), true); | |
} | |
runState.programCounter++; | |
const ret = await runState.interpreter.eofcreate( | |
gasLimit, | |
value, | |
containerCode, | |
setLengthLeft(bigIntToBytes(salt), 32), | |
data | |
); | |
runState.stack.push(ret); | |
} | |
} | |
], | |
// 0xee: RETURNCONTRACT | |
[ | |
238, | |
async function(runState, _common) { | |
if (runState.env.eof === void 0) { | |
trap(ERROR.INVALID_OPCODE); | |
} else { | |
const containerIndex = runState.env.code[runState.programCounter]; | |
const containerCode = runState.env.eof.container.body.containerSections[containerIndex]; | |
const deployContainer = new EOFContainer(containerCode, EOFContainerMode.Initmode); | |
const [auxDataOffset, auxDataSize] = runState.stack.popN(2); | |
let auxData = new Uint8Array(0); | |
if (auxDataSize !== BIGINT_0) { | |
auxData = runState.memory.read(Number(auxDataOffset), Number(auxDataSize)); | |
} | |
const originalDataSize = deployContainer.header.dataSize; | |
const preDeployDataSectionSize = deployContainer.body.dataSection.length; | |
const actualSectionSize = preDeployDataSectionSize + Number(auxDataSize); | |
if (actualSectionSize < originalDataSize) { | |
trap(EOFError.InvalidReturnContractDataSize); | |
} | |
if (actualSectionSize > 65535) { | |
trap(ERROR.OUT_OF_GAS); | |
} | |
const newSize = setLengthLeft(bigIntToBytes(BigInt(actualSectionSize)), 2); | |
const dataSizePtr = deployContainer.header.dataSizePtr; | |
containerCode[dataSizePtr] = newSize[0]; | |
containerCode[dataSizePtr + 1] = newSize[1]; | |
const returnContainer = concatBytes$1(containerCode, auxData); | |
runState.interpreter.finish(returnContainer); | |
} | |
} | |
], | |
// '0xf0' range - closures | |
// 0xf0: CREATE | |
[ | |
240, | |
async function(runState, common2) { | |
const [value, offset, length] = runState.stack.popN(3); | |
if (common2.isActivatedEIP(3860) && length > Number(common2.param("maxInitCodeSize")) && !runState.interpreter._evm.allowUnlimitedInitCodeSize) { | |
trap(ERROR.INITCODE_SIZE_VIOLATION); | |
} | |
const gasLimit = runState.messageGasLimit; | |
runState.messageGasLimit = void 0; | |
let data = new Uint8Array(0); | |
if (length !== BIGINT_0) { | |
data = runState.memory.read(Number(offset), Number(length), true); | |
} | |
if (isEOF(data)) { | |
runState.stack.push(BIGINT_0); | |
return; | |
} | |
const ret = await runState.interpreter.create(gasLimit, value, data); | |
runState.stack.push(ret); | |
} | |
], | |
// 0xf5: CREATE2 | |
[ | |
245, | |
async function(runState, common2) { | |
if (runState.interpreter.isStatic()) { | |
trap(ERROR.STATIC_STATE_CHANGE); | |
} | |
const [value, offset, length, salt] = runState.stack.popN(4); | |
if (common2.isActivatedEIP(3860) && length > Number(common2.param("maxInitCodeSize")) && !runState.interpreter._evm.allowUnlimitedInitCodeSize) { | |
trap(ERROR.INITCODE_SIZE_VIOLATION); | |
} | |
const gasLimit = runState.messageGasLimit; | |
runState.messageGasLimit = void 0; | |
let data = new Uint8Array(0); | |
if (length !== BIGINT_0) { | |
data = runState.memory.read(Number(offset), Number(length), true); | |
} | |
if (isEOF(data)) { | |
runState.stack.push(BIGINT_0); | |
return; | |
} | |
const ret = await runState.interpreter.create2( | |
gasLimit, | |
value, | |
data, | |
setLengthLeft(bigIntToBytes(salt), 32) | |
); | |
runState.stack.push(ret); | |
} | |
], | |
// 0xf1: CALL | |
[ | |
241, | |
async function(runState, common2) { | |
const [_currentGasLimit, toAddr, value, inOffset, inLength, outOffset, outLength] = runState.stack.popN(7); | |
const toAddress = createAddressFromStackBigInt(toAddr); | |
let data = new Uint8Array(0); | |
if (inLength !== BIGINT_0) { | |
data = runState.memory.read(Number(inOffset), Number(inLength), true); | |
} | |
let gasLimit = runState.messageGasLimit; | |
if (value !== BIGINT_0) { | |
const callStipend = common2.param("callStipendGas"); | |
runState.interpreter.addStipend(callStipend); | |
gasLimit += callStipend; | |
} | |
runState.messageGasLimit = void 0; | |
const ret = await runState.interpreter.call(gasLimit, toAddress, value, data); | |
writeCallOutput(runState, outOffset, outLength); | |
runState.stack.push(ret); | |
} | |
], | |
// 0xf2: CALLCODE | |
[ | |
242, | |
async function(runState, common2) { | |
const [_currentGasLimit, toAddr, value, inOffset, inLength, outOffset, outLength] = runState.stack.popN(7); | |
const toAddress = createAddressFromStackBigInt(toAddr); | |
let gasLimit = runState.messageGasLimit; | |
if (value !== BIGINT_0) { | |
const callStipend = common2.param("callStipendGas"); | |
runState.interpreter.addStipend(callStipend); | |
gasLimit += callStipend; | |
} | |
runState.messageGasLimit = void 0; | |
let data = new Uint8Array(0); | |
if (inLength !== BIGINT_0) { | |
data = runState.memory.read(Number(inOffset), Number(inLength), true); | |
} | |
const ret = await runState.interpreter.callCode(gasLimit, toAddress, value, data); | |
writeCallOutput(runState, outOffset, outLength); | |
runState.stack.push(ret); | |
} | |
], | |
// 0xf4: DELEGATECALL | |
[ | |
244, | |
async function(runState) { | |
const value = runState.interpreter.getCallValue(); | |
const [_currentGasLimit, toAddr, inOffset, inLength, outOffset, outLength] = runState.stack.popN(6); | |
const toAddress = createAddressFromStackBigInt(toAddr); | |
let data = new Uint8Array(0); | |
if (inLength !== BIGINT_0) { | |
data = runState.memory.read(Number(inOffset), Number(inLength), true); | |
} | |
const gasLimit = runState.messageGasLimit; | |
runState.messageGasLimit = void 0; | |
const ret = await runState.interpreter.callDelegate(gasLimit, toAddress, value, data); | |
writeCallOutput(runState, outOffset, outLength); | |
runState.stack.push(ret); | |
} | |
], | |
// 0xf8: EXTCALL | |
[ | |
248, | |
async function(runState, _common) { | |
if (runState.env.eof === void 0) { | |
trap(ERROR.INVALID_OPCODE); | |
} else { | |
const [toAddr, inOffset, inLength, value] = runState.stack.popN(4); | |
const gasLimit = runState.messageGasLimit; | |
runState.messageGasLimit = void 0; | |
if (gasLimit === -BIGINT_1) { | |
runState.stack.push(BIGINT_1); | |
runState.returnBytes = new Uint8Array(0); | |
return; | |
} | |
const toAddress = createAddressFromStackBigInt(toAddr); | |
let data = new Uint8Array(0); | |
if (inLength !== BIGINT_0) { | |
data = runState.memory.read(Number(inOffset), Number(inLength), true); | |
} | |
const ret = await runState.interpreter.call(gasLimit, toAddress, value, data); | |
runState.stack.push(ret); | |
} | |
} | |
], | |
// 0xf9: EXTDELEGATECALL | |
[ | |
249, | |
async function(runState, _common) { | |
if (runState.env.eof === void 0) { | |
trap(ERROR.INVALID_OPCODE); | |
} else { | |
const value = runState.interpreter.getCallValue(); | |
const [toAddr, inOffset, inLength] = runState.stack.popN(3); | |
const gasLimit = runState.messageGasLimit; | |
runState.messageGasLimit = void 0; | |
if (gasLimit === -BIGINT_1) { | |
runState.stack.push(BIGINT_1); | |
runState.returnBytes = new Uint8Array(0); | |
return; | |
} | |
const toAddress = createAddressFromStackBigInt(toAddr); | |
const code = await runState.stateManager.getCode(toAddress); | |
if (!isEOF(code)) { | |
runState.stack.push(BIGINT_1); | |
return; | |
} | |
let data = new Uint8Array(0); | |
if (inLength !== BIGINT_0) { | |
data = runState.memory.read(Number(inOffset), Number(inLength), true); | |
} | |
const ret = await runState.interpreter.callDelegate(gasLimit, toAddress, value, data); | |
runState.stack.push(ret); | |
} | |
} | |
], | |
// 0xfa: STATICCALL | |
[ | |
250, | |
async function(runState) { | |
const value = BIGINT_0; | |
const [_currentGasLimit, toAddr, inOffset, inLength, outOffset, outLength] = runState.stack.popN(6); | |
const toAddress = createAddressFromStackBigInt(toAddr); | |
const gasLimit = runState.messageGasLimit; | |
runState.messageGasLimit = void 0; | |
let data = new Uint8Array(0); | |
if (inLength !== BIGINT_0) { | |
data = runState.memory.read(Number(inOffset), Number(inLength), true); | |
} | |
const ret = await runState.interpreter.callStatic(gasLimit, toAddress, value, data); | |
writeCallOutput(runState, outOffset, outLength); | |
runState.stack.push(ret); | |
} | |
], | |
// 0xfb: EXTSTATICCALL | |
[ | |
251, | |
async function(runState, _common) { | |
if (runState.env.eof === void 0) { | |
trap(ERROR.INVALID_OPCODE); | |
} else { | |
const value = BIGINT_0; | |
const [toAddr, inOffset, inLength] = runState.stack.popN(3); | |
const gasLimit = runState.messageGasLimit; | |
runState.messageGasLimit = void 0; | |
if (gasLimit === -BIGINT_1) { | |
runState.stack.push(BIGINT_1); | |
runState.returnBytes = new Uint8Array(0); | |
return; | |
} | |
const toAddress = createAddressFromStackBigInt(toAddr); | |
let data = new Uint8Array(0); | |
if (inLength !== BIGINT_0) { | |
data = runState.memory.read(Number(inOffset), Number(inLength), true); | |
} | |
const ret = await runState.interpreter.callStatic(gasLimit, toAddress, value, data); | |
runState.stack.push(ret); | |
} | |
} | |
], | |
// 0xf3: RETURN | |
[ | |
243, | |
function(runState) { | |
const [offset, length] = runState.stack.popN(2); | |
let returnData = new Uint8Array(0); | |
if (length !== BIGINT_0) { | |
returnData = runState.memory.read(Number(offset), Number(length)); | |
} | |
runState.interpreter.finish(returnData); | |
} | |
], | |
// 0xfd: REVERT | |
[ | |
253, | |
function(runState) { | |
const [offset, length] = runState.stack.popN(2); | |
let returnData = new Uint8Array(0); | |
if (length !== BIGINT_0) { | |
returnData = runState.memory.read(Number(offset), Number(length)); | |
} | |
runState.interpreter.revert(returnData); | |
} | |
], | |
// '0x70', range - other | |
// 0xff: SELFDESTRUCT | |
[ | |
255, | |
async function(runState) { | |
const selfdestructToAddressBigInt = runState.stack.pop(); | |
const selfdestructToAddress = createAddressFromStackBigInt(selfdestructToAddressBigInt); | |
return runState.interpreter.selfDestruct(selfdestructToAddress); | |
} | |
] | |
]); | |
const pushFn = handlers.get(96); | |
for (let i = 97; i <= 127; i++) { | |
handlers.set(i, pushFn); | |
} | |
const dupFn = handlers.get(128); | |
for (let i = 129; i <= 143; i++) { | |
handlers.set(i, dupFn); | |
} | |
const swapFn = handlers.get(144); | |
for (let i = 145; i <= 159; i++) { | |
handlers.set(i, swapFn); | |
} | |
const logFn = handlers.get(160); | |
for (let i = 161; i <= 164; i++) { | |
handlers.set(i, logFn); | |
} | |
function updateSstoreGasEIP1283(runState, currentStorage, originalStorage, value, common2) { | |
if (equalsBytes(currentStorage, value)) { | |
return common2.param("netSstoreNoopGas"); | |
} | |
if (equalsBytes(originalStorage, currentStorage)) { | |
if (originalStorage.length === 0) { | |
return common2.param("netSstoreInitGas"); | |
} | |
if (value.length === 0) { | |
runState.interpreter.refundGas( | |
common2.param("netSstoreClearRefundGas"), | |
"EIP-1283 -> netSstoreClearRefund" | |
); | |
} | |
return common2.param("netSstoreCleanGas"); | |
} | |
if (originalStorage.length !== 0) { | |
if (currentStorage.length === 0) { | |
runState.interpreter.subRefund( | |
common2.param("netSstoreClearRefundGas"), | |
"EIP-1283 -> netSstoreClearRefund" | |
); | |
} else if (value.length === 0) { | |
runState.interpreter.refundGas( | |
common2.param("netSstoreClearRefundGas"), | |
"EIP-1283 -> netSstoreClearRefund" | |
); | |
} | |
} | |
if (equalsBytes(originalStorage, value)) { | |
if (originalStorage.length === 0) { | |
runState.interpreter.refundGas( | |
common2.param("netSstoreResetClearRefundGas"), | |
"EIP-1283 -> netSstoreResetClearRefund" | |
); | |
} else { | |
runState.interpreter.refundGas( | |
common2.param("netSstoreResetRefundGas"), | |
"EIP-1283 -> netSstoreResetRefund" | |
); | |
} | |
} | |
return common2.param("netSstoreDirtyGas"); | |
} | |
function accessAddressEIP2929(runState, address, common2, chargeGas = true, isSelfdestruct = false) { | |
if (!common2.isActivatedEIP(2929)) return BIGINT_0; | |
if (!runState.interpreter.journal.isWarmedAddress(address)) { | |
runState.interpreter.journal.addWarmedAddress(address); | |
if (chargeGas && !common2.isActivatedEIP(6800)) { | |
return common2.param("coldaccountaccessGas"); | |
} | |
} else if (chargeGas && !isSelfdestruct) { | |
return common2.param("warmstoragereadGas"); | |
} | |
return BIGINT_0; | |
} | |
function accessStorageEIP2929(runState, key, isSstore, common2, chargeGas = true) { | |
if (!common2.isActivatedEIP(2929)) return BIGINT_0; | |
const address = runState.interpreter.getAddress().bytes; | |
const slotIsCold = !runState.interpreter.journal.isWarmedStorage(address, key); | |
if (slotIsCold) { | |
runState.interpreter.journal.addWarmedStorage(address, key); | |
if (chargeGas && !common2.isActivatedEIP(6800)) { | |
return common2.param("coldsloadGas"); | |
} | |
} else if (chargeGas && (!isSstore || common2.isActivatedEIP(6800))) { | |
return common2.param("warmstoragereadGas"); | |
} | |
return BIGINT_0; | |
} | |
function adjustSstoreGasEIP2929(runState, key, defaultCost, costName, common2) { | |
if (!common2.isActivatedEIP(2929)) return defaultCost; | |
const address = runState.interpreter.getAddress().bytes; | |
const warmRead = common2.param("warmstoragereadGas"); | |
const coldSload = common2.param("coldsloadGas"); | |
if (runState.interpreter.journal.isWarmedStorage(address, key)) { | |
switch (costName) { | |
case "noop": | |
return warmRead; | |
case "initRefund": | |
return common2.param("sstoreInitEIP2200Gas") - warmRead; | |
case "cleanRefund": | |
return common2.param("sstoreResetGas") - coldSload - warmRead; | |
} | |
} | |
return defaultCost; | |
} | |
function updateSstoreGasEIP2200(runState, currentStorage, originalStorage, value, key, common2) { | |
if (runState.interpreter.getGasLeft() <= common2.param("sstoreSentryEIP2200Gas")) { | |
trap(ERROR.OUT_OF_GAS); | |
} | |
if (equalsBytes(currentStorage, value)) { | |
const sstoreNoopCost = common2.param("sstoreNoopEIP2200Gas"); | |
return adjustSstoreGasEIP2929(runState, key, sstoreNoopCost, "noop", common2); | |
} | |
if (equalsBytes(originalStorage, currentStorage)) { | |
if (originalStorage.length === 0) { | |
return common2.param("sstoreInitEIP2200Gas"); | |
} | |
if (value.length === 0) { | |
runState.interpreter.refundGas( | |
common2.param("sstoreClearRefundEIP2200Gas"), | |
"EIP-2200 -> sstoreClearRefundEIP2200" | |
); | |
} | |
return common2.param("sstoreCleanEIP2200Gas"); | |
} | |
if (originalStorage.length > 0) { | |
if (currentStorage.length === 0) { | |
runState.interpreter.subRefund( | |
common2.param("sstoreClearRefundEIP2200Gas"), | |
"EIP-2200 -> sstoreClearRefundEIP2200" | |
); | |
} else if (value.length === 0) { | |
runState.interpreter.refundGas( | |
common2.param("sstoreClearRefundEIP2200Gas"), | |
"EIP-2200 -> sstoreClearRefundEIP2200" | |
); | |
} | |
} | |
if (equalsBytes(originalStorage, value)) { | |
if (originalStorage.length === 0) { | |
const sstoreInitRefund = common2.param("sstoreInitRefundEIP2200Gas"); | |
runState.interpreter.refundGas( | |
adjustSstoreGasEIP2929(runState, key, sstoreInitRefund, "initRefund", common2), | |
"EIP-2200 -> initRefund" | |
); | |
} else { | |
const sstoreCleanRefund = common2.param("sstoreCleanRefundEIP2200Gas"); | |
runState.interpreter.refundGas( | |
BigInt(adjustSstoreGasEIP2929(runState, key, sstoreCleanRefund, "cleanRefund", common2)), | |
"EIP-2200 -> cleanRefund" | |
); | |
} | |
} | |
return common2.param("sstoreDirtyEIP2200Gas"); | |
} | |
const EXTCALL_TARGET_MAX = BigInt(2) ** BigInt(8 * 20) - BigInt(1); | |
async function eip7702GasCost(runState, common2, address, charge2929Gas) { | |
const code = await runState.stateManager.getCode(address); | |
if (equalsBytes(code.slice(0, 3), DELEGATION_7702_FLAG)) { | |
return accessAddressEIP2929(runState, code.slice(3, 24), common2, charge2929Gas); | |
} | |
return BIGINT_0; | |
} | |
const dynamicGasHandlers = /* @__PURE__ */ new Map([ | |
[ | |
/* EXP */ | |
10, | |
async function(runState, gas, common2) { | |
const [_base, exponent] = runState.stack.peek(2); | |
if (exponent === BIGINT_0) { | |
return gas; | |
} | |
let byteLength = exponent.toString(2).length / 8; | |
if (byteLength > Math.trunc(byteLength)) { | |
byteLength = Math.trunc(byteLength) + 1; | |
} | |
if (byteLength < 1 || byteLength > 32) { | |
trap(ERROR.OUT_OF_RANGE); | |
} | |
const expPricePerByte = common2.param("expByteGas"); | |
gas += BigInt(byteLength) * expPricePerByte; | |
return gas; | |
} | |
], | |
[ | |
/* KECCAK256 */ | |
32, | |
async function(runState, gas, common2) { | |
const [offset, length] = runState.stack.peek(2); | |
gas += subMemUsage(runState, offset, length, common2); | |
gas += common2.param("keccak256WordGas") * divCeil(length, BIGINT_32); | |
return gas; | |
} | |
], | |
[ | |
/* BALANCE */ | |
49, | |
async function(runState, gas, common2) { | |
const address = createAddressFromStackBigInt(runState.stack.peek()[0]); | |
let charge2929Gas = true; | |
if (common2.isActivatedEIP(6800)) { | |
const coldAccessGas = runState.env.accessWitness.touchAddressOnReadAndComputeGas( | |
address, | |
0, | |
VERKLE_BASIC_DATA_LEAF_KEY | |
); | |
gas += coldAccessGas; | |
charge2929Gas = coldAccessGas === BIGINT_0; | |
} | |
if (common2.isActivatedEIP(2929)) { | |
gas += accessAddressEIP2929(runState, address.bytes, common2, charge2929Gas); | |
} | |
return gas; | |
} | |
], | |
[ | |
/* CALLDATACOPY */ | |
55, | |
async function(runState, gas, common2) { | |
const [memOffset, _dataOffset, dataLength] = runState.stack.peek(3); | |
gas += subMemUsage(runState, memOffset, dataLength, common2); | |
if (dataLength !== BIGINT_0) { | |
gas += common2.param("copyGas") * divCeil(dataLength, BIGINT_32); | |
} | |
return gas; | |
} | |
], | |
[ | |
/* CODECOPY */ | |
57, | |
async function(runState, gas, common2) { | |
const [memOffset, _codeOffset, dataLength] = runState.stack.peek(3); | |
gas += subMemUsage(runState, memOffset, dataLength, common2); | |
if (dataLength !== BIGINT_0) { | |
gas += common2.param("copyGas") * divCeil(dataLength, BIGINT_32); | |
if (common2.isActivatedEIP(6800) && runState.env.chargeCodeAccesses === true) { | |
const contract = runState.interpreter.getAddress(); | |
let codeEnd = _codeOffset + dataLength; | |
const codeSize = runState.interpreter.getCodeSize(); | |
if (codeEnd > codeSize) { | |
codeEnd = codeSize; | |
} | |
gas += runState.env.accessWitness.touchCodeChunksRangeOnReadAndChargeGas( | |
contract, | |
Number(_codeOffset), | |
Number(codeEnd) | |
); | |
} | |
} | |
return gas; | |
} | |
], | |
[ | |
/* EXTCODESIZE */ | |
59, | |
async function(runState, gas, common2) { | |
const address = createAddressFromStackBigInt(runState.stack.peek()[0]); | |
let charge2929Gas = true; | |
if (common2.isActivatedEIP(6800) && runState.interpreter._evm.getPrecompile(address) === void 0) { | |
let coldAccessGas = BIGINT_0; | |
coldAccessGas += runState.env.accessWitness.touchAddressOnReadAndComputeGas( | |
address, | |
0, | |
VERKLE_BASIC_DATA_LEAF_KEY | |
); | |
gas += coldAccessGas; | |
charge2929Gas = coldAccessGas === BIGINT_0; | |
} | |
if (common2.isActivatedEIP(2929)) { | |
gas += accessAddressEIP2929(runState, address.bytes, common2, charge2929Gas); | |
} | |
if (common2.isActivatedEIP(7702)) { | |
gas += await eip7702GasCost(runState, common2, address, charge2929Gas); | |
} | |
return gas; | |
} | |
], | |
[ | |
/* EXTCODECOPY */ | |
60, | |
async function(runState, gas, common2) { | |
const [addressBigInt, memOffset, _codeOffset, dataLength] = runState.stack.peek(4); | |
const address = createAddressFromStackBigInt(addressBigInt); | |
gas += subMemUsage(runState, memOffset, dataLength, common2); | |
let charge2929Gas = true; | |
if (common2.isActivatedEIP(6800) && runState.interpreter._evm.getPrecompile(address) === void 0) { | |
let coldAccessGas = BIGINT_0; | |
coldAccessGas += runState.env.accessWitness.touchAddressOnReadAndComputeGas( | |
address, | |
0, | |
VERKLE_BASIC_DATA_LEAF_KEY | |
); | |
gas += coldAccessGas; | |
charge2929Gas = coldAccessGas === BIGINT_0; | |
} | |
if (common2.isActivatedEIP(2929)) { | |
gas += accessAddressEIP2929(runState, address.bytes, common2, charge2929Gas); | |
} | |
if (common2.isActivatedEIP(7702)) { | |
gas += await eip7702GasCost(runState, common2, address, charge2929Gas); | |
} | |
if (dataLength !== BIGINT_0) { | |
gas += common2.param("copyGas") * divCeil(dataLength, BIGINT_32); | |
if (common2.isActivatedEIP(6800)) { | |
let codeEnd = _codeOffset + dataLength; | |
const codeSize = BigInt((await runState.stateManager.getCode(address)).length); | |
if (codeEnd > codeSize) { | |
codeEnd = codeSize; | |
} | |
gas += runState.env.accessWitness.touchCodeChunksRangeOnReadAndChargeGas( | |
address, | |
Number(_codeOffset), | |
Number(codeEnd) | |
); | |
} | |
} | |
return gas; | |
} | |
], | |
[ | |
/* RETURNDATACOPY */ | |
62, | |
async function(runState, gas, common2) { | |
const [memOffset, returnDataOffset, dataLength] = runState.stack.peek(3); | |
if (returnDataOffset + dataLength > runState.interpreter.getReturnDataSize()) { | |
if (runState.env.eof === void 0) { | |
trap(ERROR.OUT_OF_GAS); | |
} | |
} | |
gas += subMemUsage(runState, memOffset, dataLength, common2); | |
if (dataLength !== BIGINT_0) { | |
gas += common2.param("copyGas") * divCeil(dataLength, BIGINT_32); | |
} | |
return gas; | |
} | |
], | |
[ | |
/* EXTCODEHASH */ | |
63, | |
async function(runState, gas, common2) { | |
const address = createAddressFromStackBigInt(runState.stack.peek()[0]); | |
let charge2929Gas = true; | |
if (common2.isActivatedEIP(6800)) { | |
let coldAccessGas = BIGINT_0; | |
coldAccessGas += runState.env.accessWitness.touchAddressOnReadAndComputeGas( | |
address, | |
0, | |
VERKLE_CODE_HASH_LEAF_KEY | |
); | |
gas += coldAccessGas; | |
charge2929Gas = coldAccessGas === BIGINT_0; | |
} | |
if (common2.isActivatedEIP(2929)) { | |
gas += accessAddressEIP2929(runState, address.bytes, common2, charge2929Gas); | |
} | |
if (common2.isActivatedEIP(7702)) { | |
gas += await eip7702GasCost(runState, common2, address, charge2929Gas); | |
} | |
return gas; | |
} | |
], | |
[ | |
/* MLOAD */ | |
81, | |
async function(runState, gas, common2) { | |
const pos = runState.stack.peek()[0]; | |
gas += subMemUsage(runState, pos, BIGINT_32, common2); | |
return gas; | |
} | |
], | |
[ | |
/* MSTORE */ | |
82, | |
async function(runState, gas, common2) { | |
const offset = runState.stack.peek()[0]; | |
gas += subMemUsage(runState, offset, BIGINT_32, common2); | |
return gas; | |
} | |
], | |
[ | |
/* MSTORE8 */ | |
83, | |
async function(runState, gas, common2) { | |
const offset = runState.stack.peek()[0]; | |
gas += subMemUsage(runState, offset, BIGINT_1, common2); | |
return gas; | |
} | |
], | |
[ | |
/* SLOAD */ | |
84, | |
async function(runState, gas, common2) { | |
const key = runState.stack.peek()[0]; | |
const keyBuf = setLengthLeft(bigIntToBytes(key), 32); | |
let charge2929Gas = true; | |
if (common2.isActivatedEIP(6800)) { | |
const address = runState.interpreter.getAddress(); | |
const { treeIndex, subIndex } = getVerkleTreeIndicesForStorageSlot(key); | |
const coldAccessGas = runState.env.accessWitness.touchAddressOnReadAndComputeGas( | |
address, | |
treeIndex, | |
subIndex | |
); | |
gas += coldAccessGas; | |
charge2929Gas = coldAccessGas === BIGINT_0; | |
} | |
if (common2.isActivatedEIP(2929)) { | |
gas += accessStorageEIP2929(runState, keyBuf, false, common2, charge2929Gas); | |
} | |
return gas; | |
} | |
], | |
[ | |
/* SSTORE */ | |
85, | |
async function(runState, gas, common2) { | |
if (runState.interpreter.isStatic()) { | |
trap(ERROR.STATIC_STATE_CHANGE); | |
} | |
const [key, val] = runState.stack.peek(2); | |
const keyBytes = setLengthLeft(bigIntToBytes(key), 32); | |
let value; | |
if (val === BIGINT_0) { | |
value = Uint8Array.from([]); | |
} else { | |
value = bigIntToBytes(val); | |
} | |
const currentStorage = setLengthLeftStorage( | |
await runState.interpreter.storageLoad(keyBytes) | |
); | |
const originalStorage = setLengthLeftStorage( | |
await runState.interpreter.storageLoad(keyBytes, true) | |
); | |
if (common2.hardfork() === Hardfork.Constantinople) { | |
gas += updateSstoreGasEIP1283( | |
runState, | |
currentStorage, | |
originalStorage, | |
setLengthLeftStorage(value), | |
common2 | |
); | |
} else if (common2.gteHardfork(Hardfork.Istanbul)) { | |
if (!common2.isActivatedEIP(6800)) { | |
gas += updateSstoreGasEIP2200( | |
runState, | |
currentStorage, | |
originalStorage, | |
setLengthLeftStorage(value), | |
keyBytes, | |
common2 | |
); | |
} | |
} else { | |
gas += updateSstoreGas(runState, currentStorage, setLengthLeftStorage(value), common2); | |
} | |
let charge2929Gas = true; | |
if (common2.isActivatedEIP(6800)) { | |
const contract = runState.interpreter.getAddress(); | |
const { treeIndex, subIndex } = getVerkleTreeIndicesForStorageSlot(key); | |
const coldAccessGas = runState.env.accessWitness.touchAddressOnWriteAndComputeGas( | |
contract, | |
treeIndex, | |
subIndex | |
); | |
gas += coldAccessGas; | |
charge2929Gas = coldAccessGas === BIGINT_0; | |
} | |
if (common2.isActivatedEIP(2929)) { | |
gas += accessStorageEIP2929(runState, keyBytes, true, common2, charge2929Gas); | |
} | |
return gas; | |
} | |
], | |
[ | |
/* MCOPY */ | |
94, | |
async function(runState, gas, common2) { | |
const [dst, src, length] = runState.stack.peek(3); | |
const wordsCopied = (length + BIGINT_31) / BIGINT_32; | |
gas += BIGINT_3 * wordsCopied; | |
gas += subMemUsage(runState, src, length, common2); | |
gas += subMemUsage(runState, dst, length, common2); | |
return gas; | |
} | |
], | |
[ | |
/* LOG */ | |
160, | |
async function(runState, gas, common2) { | |
if (runState.interpreter.isStatic()) { | |
trap(ERROR.STATIC_STATE_CHANGE); | |
} | |
const [memOffset, memLength] = runState.stack.peek(2); | |
const topicsCount = runState.opCode - 160; | |
if (topicsCount < 0 || topicsCount > 4) { | |
trap(ERROR.OUT_OF_RANGE); | |
} | |
gas += subMemUsage(runState, memOffset, memLength, common2); | |
gas += common2.param("logTopicGas") * BigInt(topicsCount) + memLength * common2.param("logDataGas"); | |
return gas; | |
} | |
], | |
/* DATACOPY */ | |
[ | |
211, | |
async function(runState, gas, common2) { | |
const [memOffset, _dataOffset, dataLength] = runState.stack.peek(3); | |
gas += subMemUsage(runState, memOffset, dataLength, common2); | |
if (dataLength !== BIGINT_0) { | |
gas += common2.param("copyGas") * divCeil(dataLength, BIGINT_32); | |
} | |
return gas; | |
} | |
], | |
/* EOFCREATE */ | |
[ | |
236, | |
async function(runState, gas, common2) { | |
const containerIndex = runState.env.code[runState.programCounter + 1]; | |
const [_value, _salt, inputOffset, inputSize] = runState.stack.peek(4); | |
gas += subMemUsage(runState, inputOffset, inputSize, common2); | |
const container = runState.env.eof.container.body.containerSections[containerIndex]; | |
gas += common2.param("keccak256WordGas") * divCeil(BigInt(container.length), BIGINT_32); | |
const gasLeft = runState.interpreter.getGasLeft() - gas; | |
runState.messageGasLimit = maxCallGas(gasLeft, gasLeft, runState, common2); | |
return gas; | |
} | |
], | |
/* RETURNCONTRACT */ | |
[ | |
238, | |
async function(runState, gas, common2) { | |
const [auxDataOffset, auxDataSize] = runState.stack.peek(2); | |
gas += subMemUsage(runState, auxDataOffset, auxDataSize, common2); | |
return gas; | |
} | |
], | |
[ | |
/* CREATE */ | |
240, | |
async function(runState, gas, common2) { | |
if (runState.interpreter.isStatic()) { | |
trap(ERROR.STATIC_STATE_CHANGE); | |
} | |
const [_value, offset, length] = runState.stack.peek(3); | |
if (common2.isActivatedEIP(2929)) { | |
gas += accessAddressEIP2929( | |
runState, | |
runState.interpreter.getAddress().bytes, | |
common2, | |
false | |
); | |
} | |
if (common2.isActivatedEIP(3860)) { | |
gas += (length + BIGINT_31) / BIGINT_32 * common2.param("initCodeWordGas"); | |
} | |
gas += subMemUsage(runState, offset, length, common2); | |
let gasLimit = BigInt(runState.interpreter.getGasLeft()) - gas; | |
gasLimit = maxCallGas(gasLimit, gasLimit, runState, common2); | |
runState.messageGasLimit = gasLimit; | |
return gas; | |
} | |
], | |
[ | |
/* CALL */ | |
241, | |
async function(runState, gas, common2) { | |
const [currentGasLimit, toAddr, value, inOffset, inLength, outOffset, outLength] = runState.stack.peek(7); | |
const toAddress = createAddressFromStackBigInt(toAddr); | |
if (runState.interpreter.isStatic() && value !== BIGINT_0) { | |
trap(ERROR.STATIC_STATE_CHANGE); | |
} | |
gas += subMemUsage(runState, inOffset, inLength, common2); | |
gas += subMemUsage(runState, outOffset, outLength, common2); | |
let charge2929Gas = true; | |
if (common2.isActivatedEIP(6800) && runState.interpreter._evm.getPrecompile(toAddress) === void 0) { | |
const coldAccessGas = runState.env.accessWitness.touchAndChargeMessageCall(toAddress); | |
if (value !== BIGINT_0) { | |
const contractAddress = runState.interpreter.getAddress(); | |
gas += runState.env.accessWitness.touchAndChargeValueTransfer( | |
contractAddress, | |
toAddress | |
); | |
} | |
gas += coldAccessGas; | |
charge2929Gas = coldAccessGas === BIGINT_0; | |
} | |
if (common2.isActivatedEIP(2929)) { | |
gas += accessAddressEIP2929(runState, toAddress.bytes, common2, charge2929Gas); | |
} | |
if (common2.isActivatedEIP(7702)) { | |
gas += await eip7702GasCost(runState, common2, toAddress, charge2929Gas); | |
} | |
if (value !== BIGINT_0 && !common2.isActivatedEIP(6800)) { | |
gas += common2.param("callValueTransferGas"); | |
} | |
if (common2.gteHardfork(Hardfork.SpuriousDragon)) { | |
const account = await runState.stateManager.getAccount(toAddress); | |
let deadAccount = false; | |
if (account === void 0 || account.isEmpty()) { | |
deadAccount = true; | |
} | |
if (deadAccount && !(value === BIGINT_0)) { | |
gas += common2.param("callNewAccountGas"); | |
} | |
} else if (await runState.stateManager.getAccount(toAddress) === void 0) { | |
gas += common2.param("callNewAccountGas"); | |
} | |
const gasLimit = maxCallGas( | |
currentGasLimit, | |
runState.interpreter.getGasLeft() - gas, | |
runState, | |
common2 | |
); | |
if (gasLimit > runState.interpreter.getGasLeft() - gas) { | |
trap(ERROR.OUT_OF_GAS); | |
} | |
if (gas > runState.interpreter.getGasLeft()) { | |
trap(ERROR.OUT_OF_GAS); | |
} | |
runState.messageGasLimit = gasLimit; | |
return gas; | |
} | |
], | |
[ | |
/* CALLCODE */ | |
242, | |
async function(runState, gas, common2) { | |
const [currentGasLimit, toAddr, value, inOffset, inLength, outOffset, outLength] = runState.stack.peek(7); | |
const toAddress = createAddressFromStackBigInt(toAddr); | |
gas += subMemUsage(runState, inOffset, inLength, common2); | |
gas += subMemUsage(runState, outOffset, outLength, common2); | |
let charge2929Gas = true; | |
if (common2.isActivatedEIP(6800) && runState.interpreter._evm.getPrecompile(toAddress) === void 0) { | |
const coldAccessGas = runState.env.accessWitness.touchAndChargeMessageCall(toAddress); | |
gas += coldAccessGas; | |
charge2929Gas = coldAccessGas === BIGINT_0; | |
} | |
if (common2.isActivatedEIP(2929)) { | |
gas += accessAddressEIP2929( | |
runState, | |
createAddressFromStackBigInt(toAddr).bytes, | |
common2, | |
charge2929Gas | |
); | |
} | |
if (common2.isActivatedEIP(7702)) { | |
gas += await eip7702GasCost(runState, common2, toAddress, charge2929Gas); | |
} | |
if (value !== BIGINT_0) { | |
gas += common2.param("callValueTransferGas"); | |
} | |
const gasLimit = maxCallGas( | |
currentGasLimit, | |
runState.interpreter.getGasLeft() - gas, | |
runState, | |
common2 | |
); | |
if (gasLimit > runState.interpreter.getGasLeft() - gas) { | |
trap(ERROR.OUT_OF_GAS); | |
} | |
runState.messageGasLimit = gasLimit; | |
return gas; | |
} | |
], | |
[ | |
/* RETURN */ | |
243, | |
async function(runState, gas, common2) { | |
const [offset, length] = runState.stack.peek(2); | |
gas += subMemUsage(runState, offset, length, common2); | |
return gas; | |
} | |
], | |
[ | |
/* DELEGATECALL */ | |
244, | |
async function(runState, gas, common2) { | |
const [currentGasLimit, toAddr, inOffset, inLength, outOffset, outLength] = runState.stack.peek(6); | |
const toAddress = createAddressFromStackBigInt(toAddr); | |
gas += subMemUsage(runState, inOffset, inLength, common2); | |
gas += subMemUsage(runState, outOffset, outLength, common2); | |
let charge2929Gas = true; | |
if (common2.isActivatedEIP(6800) && runState.interpreter._evm.getPrecompile(toAddress) === void 0) { | |
const coldAccessGas = runState.env.accessWitness.touchAndChargeMessageCall(toAddress); | |
gas += coldAccessGas; | |
charge2929Gas = coldAccessGas === BIGINT_0; | |
} | |
if (common2.isActivatedEIP(2929)) { | |
gas += accessAddressEIP2929( | |
runState, | |
createAddressFromStackBigInt(toAddr).bytes, | |
common2, | |
charge2929Gas | |
); | |
} | |
if (common2.isActivatedEIP(7702)) { | |
gas += await eip7702GasCost(runState, common2, toAddress, charge2929Gas); | |
} | |
const gasLimit = maxCallGas( | |
currentGasLimit, | |
runState.interpreter.getGasLeft() - gas, | |
runState, | |
common2 | |
); | |
if (gasLimit > runState.interpreter.getGasLeft() - gas) { | |
trap(ERROR.OUT_OF_GAS); | |
} | |
runState.messageGasLimit = gasLimit; | |
return gas; | |
} | |
], | |
[ | |
/* CREATE2 */ | |
245, | |
async function(runState, gas, common2) { | |
if (runState.interpreter.isStatic()) { | |
trap(ERROR.STATIC_STATE_CHANGE); | |
} | |
const [_value, offset, length, _salt] = runState.stack.peek(4); | |
gas += subMemUsage(runState, offset, length, common2); | |
if (common2.isActivatedEIP(2929)) { | |
gas += accessAddressEIP2929( | |
runState, | |
runState.interpreter.getAddress().bytes, | |
common2, | |
false | |
); | |
} | |
if (common2.isActivatedEIP(3860)) { | |
gas += (length + BIGINT_31) / BIGINT_32 * common2.param("initCodeWordGas"); | |
} | |
gas += common2.param("keccak256WordGas") * divCeil(length, BIGINT_32); | |
let gasLimit = runState.interpreter.getGasLeft() - gas; | |
gasLimit = maxCallGas(gasLimit, gasLimit, runState, common2); | |
runState.messageGasLimit = gasLimit; | |
return gas; | |
} | |
], | |
/* EXTCALL */ | |
[ | |
248, | |
async function(runState, gas, common2) { | |
const [toAddr, inOffset, inLength, value] = runState.stack.peek(4); | |
if (runState.interpreter.isStatic() && value !== BIGINT_0) { | |
trap(ERROR.STATIC_STATE_CHANGE); | |
} | |
if (value > BIGINT_0) { | |
gas += common2.param("callValueTransferGas"); | |
} | |
if (toAddr > EXTCALL_TARGET_MAX) { | |
trap(EOFError.InvalidExtcallTarget); | |
} | |
gas += subMemUsage(runState, inOffset, inLength, common2); | |
const toAddress = createAddressFromStackBigInt(toAddr); | |
gas += accessAddressEIP2929(runState, toAddress.bytes, common2); | |
if (value > BIGINT_0) { | |
const account = await runState.stateManager.getAccount(toAddress); | |
const deadAccount = account === void 0 || account.isEmpty(); | |
if (deadAccount) { | |
gas += common2.param("callNewAccountGas"); | |
} | |
} | |
const minRetainedGas = common2.param("minRetainedGas"); | |
const minCalleeGas = common2.param("minCalleeGas"); | |
const currentGasAvailable = runState.interpreter.getGasLeft() - gas; | |
const reducedGas = currentGasAvailable / BIGINT_64; | |
let gasLimit; | |
if (reducedGas < minRetainedGas) { | |
gasLimit = currentGasAvailable - minRetainedGas; | |
} else { | |
gasLimit = currentGasAvailable - reducedGas; | |
} | |
if (runState.env.depth >= Number(common2.param("stackLimit")) || runState.env.contract.balance < value || gasLimit < minCalleeGas) { | |
gasLimit = -BIGINT_1; | |
} | |
runState.messageGasLimit = gasLimit; | |
return gas; | |
} | |
], | |
/* EXTDELEGATECALL */ | |
[ | |
249, | |
async function(runState, gas, common2) { | |
const [toAddr, inOffset, inLength] = runState.stack.peek(3); | |
if (toAddr > EXTCALL_TARGET_MAX) { | |
trap(EOFError.InvalidExtcallTarget); | |
} | |
gas += subMemUsage(runState, inOffset, inLength, common2); | |
const toAddress = createAddressFromStackBigInt(toAddr); | |
gas += accessAddressEIP2929(runState, toAddress.bytes, common2); | |
const minRetainedGas = common2.param("minRetainedGas"); | |
const minCalleeGas = common2.param("minCalleeGas"); | |
const currentGasAvailable = runState.interpreter.getGasLeft() - gas; | |
const reducedGas = currentGasAvailable / BIGINT_64; | |
let gasLimit; | |
if (reducedGas < minRetainedGas) { | |
gasLimit = currentGasAvailable - minRetainedGas; | |
} else { | |
gasLimit = currentGasAvailable - reducedGas; | |
} | |
if (runState.env.depth >= Number(common2.param("stackLimit")) || gasLimit < minCalleeGas) { | |
gasLimit = -BIGINT_1; | |
} | |
runState.messageGasLimit = gasLimit; | |
return gas; | |
} | |
], | |
[ | |
/* STATICCALL */ | |
250, | |
async function(runState, gas, common2) { | |
const [currentGasLimit, toAddr, inOffset, inLength, outOffset, outLength] = runState.stack.peek(6); | |
gas += subMemUsage(runState, inOffset, inLength, common2); | |
gas += subMemUsage(runState, outOffset, outLength, common2); | |
let charge2929Gas = true; | |
if (common2.isActivatedEIP(6800)) { | |
const toAddress = createAddressFromStackBigInt(toAddr); | |
const coldAccessGas = runState.env.accessWitness.touchAndChargeMessageCall(toAddress); | |
gas += coldAccessGas; | |
charge2929Gas = coldAccessGas === BIGINT_0; | |
} | |
if (common2.isActivatedEIP(2929)) { | |
gas += accessAddressEIP2929( | |
runState, | |
createAddressFromStackBigInt(toAddr).bytes, | |
common2, | |
charge2929Gas | |
); | |
} | |
if (common2.isActivatedEIP(7702)) { | |
gas += await eip7702GasCost( | |
runState, | |
common2, | |
createAddressFromStackBigInt(toAddr), | |
charge2929Gas | |
); | |
} | |
const gasLimit = maxCallGas( | |
currentGasLimit, | |
runState.interpreter.getGasLeft() - gas, | |
runState, | |
common2 | |
); | |
runState.messageGasLimit = gasLimit; | |
return gas; | |
} | |
], | |
/* EXTSTATICCALL */ | |
[ | |
251, | |
async function(runState, gas, common2) { | |
const [toAddr, inOffset, inLength] = runState.stack.peek(3); | |
if (toAddr > EXTCALL_TARGET_MAX) { | |
trap(EOFError.InvalidExtcallTarget); | |
} | |
gas += subMemUsage(runState, inOffset, inLength, common2); | |
const toAddress = createAddressFromStackBigInt(toAddr); | |
gas += accessAddressEIP2929(runState, toAddress.bytes, common2); | |
const minRetainedGas = common2.param("minRetainedGas"); | |
const minCalleeGas = common2.param("minCalleeGas"); | |
const currentGasAvailable = runState.interpreter.getGasLeft() - gas; | |
const reducedGas = currentGasAvailable / BIGINT_64; | |
let gasLimit; | |
if (reducedGas < minRetainedGas) { | |
gasLimit = currentGasAvailable - minRetainedGas; | |
} else { | |
gasLimit = currentGasAvailable - reducedGas; | |
} | |
if (runState.env.depth >= Number(common2.param("stackLimit")) || gasLimit < minCalleeGas) { | |
gasLimit = -BIGINT_1; | |
} | |
runState.messageGasLimit = gasLimit; | |
return gas; | |
} | |
], | |
[ | |
/* REVERT */ | |
253, | |
async function(runState, gas, common2) { | |
const [offset, length] = runState.stack.peek(2); | |
gas += subMemUsage(runState, offset, length, common2); | |
return gas; | |
} | |
], | |
[ | |
/* SELFDESTRUCT */ | |
255, | |
async function(runState, gas, common2) { | |
if (runState.interpreter.isStatic()) { | |
trap(ERROR.STATIC_STATE_CHANGE); | |
} | |
const selfdestructToaddressBigInt = runState.stack.peek()[0]; | |
const selfdestructToAddress = createAddressFromStackBigInt(selfdestructToaddressBigInt); | |
const contractAddress = runState.interpreter.getAddress(); | |
let deductGas = false; | |
const balance = await runState.interpreter.getExternalBalance(contractAddress); | |
if (common2.gteHardfork(Hardfork.SpuriousDragon)) { | |
if (balance > BIGINT_0) { | |
const account = await runState.stateManager.getAccount(selfdestructToAddress); | |
if (account === void 0 || account.isEmpty()) { | |
deductGas = true; | |
} | |
} | |
} else if (common2.gteHardfork(Hardfork.TangerineWhistle)) { | |
const exists2 = await runState.stateManager.getAccount(selfdestructToAddress) !== void 0; | |
if (!exists2) { | |
deductGas = true; | |
} | |
} | |
if (deductGas) { | |
gas += common2.param("callNewAccountGas"); | |
} | |
let selfDestructToCharge2929Gas = true; | |
if (common2.isActivatedEIP(6800)) { | |
gas += runState.env.accessWitness.touchAddressOnReadAndComputeGas( | |
contractAddress, | |
0, | |
VERKLE_BASIC_DATA_LEAF_KEY | |
); | |
if (balance > BIGINT_0) { | |
gas += runState.env.accessWitness.touchAddressOnWriteAndComputeGas( | |
contractAddress, | |
0, | |
VERKLE_BASIC_DATA_LEAF_KEY | |
); | |
} | |
let selfDestructToColdAccessGas = runState.env.accessWitness.touchAddressOnReadAndComputeGas( | |
selfdestructToAddress, | |
0, | |
VERKLE_BASIC_DATA_LEAF_KEY | |
); | |
if (balance > BIGINT_0) { | |
selfDestructToColdAccessGas += runState.env.accessWitness.touchAddressOnWriteAndComputeGas( | |
selfdestructToAddress, | |
0, | |
VERKLE_BASIC_DATA_LEAF_KEY | |
); | |
} | |
gas += selfDestructToColdAccessGas; | |
selfDestructToCharge2929Gas = selfDestructToColdAccessGas === BIGINT_0; | |
} | |
if (common2.isActivatedEIP(2929)) { | |
gas += accessAddressEIP2929( | |
runState, | |
selfdestructToAddress.bytes, | |
common2, | |
selfDestructToCharge2929Gas, | |
true | |
); | |
} | |
return gas; | |
} | |
] | |
]); | |
const logDynamicFunc = dynamicGasHandlers.get(160); | |
for (let i = 161; i <= 164; i++) { | |
dynamicGasHandlers.set(i, logDynamicFunc); | |
} | |
class Opcode { | |
constructor({ | |
code, | |
name, | |
fullName, | |
fee, | |
isAsync, | |
dynamicGas | |
}) { | |
this.code = code; | |
this.name = name; | |
this.fullName = fullName; | |
this.fee = fee; | |
this.feeBigInt = BigInt(fee); | |
this.isAsync = isAsync; | |
this.dynamicGas = dynamicGas; | |
this.isInvalid = this.name === "INVALID"; | |
Object.freeze(this); | |
} | |
} | |
const defaultOp = (name) => { | |
return { name, isAsync: false, dynamicGas: false }; | |
}; | |
const dynamicGasOp = (name) => { | |
return { name, isAsync: false, dynamicGas: true }; | |
}; | |
const asyncOp = (name) => { | |
return { name, isAsync: true, dynamicGas: false }; | |
}; | |
const asyncAndDynamicGasOp = (name) => { | |
return { name, isAsync: true, dynamicGas: true }; | |
}; | |
const opcodes = { | |
// 0x0 range - arithmetic ops | |
// name, async | |
0: defaultOp("STOP"), | |
1: defaultOp("ADD"), | |
2: defaultOp("MUL"), | |
3: defaultOp("SUB"), | |
4: defaultOp("DIV"), | |
5: defaultOp("SDIV"), | |
6: defaultOp("MOD"), | |
7: defaultOp("SMOD"), | |
8: defaultOp("ADDMOD"), | |
9: defaultOp("MULMOD"), | |
10: dynamicGasOp("EXP"), | |
11: defaultOp("SIGNEXTEND"), | |
// 0x10 range - bit ops | |
16: defaultOp("LT"), | |
17: defaultOp("GT"), | |
18: defaultOp("SLT"), | |
19: defaultOp("SGT"), | |
20: defaultOp("EQ"), | |
21: defaultOp("ISZERO"), | |
22: defaultOp("AND"), | |
23: defaultOp("OR"), | |
24: defaultOp("XOR"), | |
25: defaultOp("NOT"), | |
26: defaultOp("BYTE"), | |
// 0x20 range - crypto | |
32: dynamicGasOp("KECCAK256"), | |
// 0x30 range - closure state | |
48: asyncOp("ADDRESS"), | |
49: asyncAndDynamicGasOp("BALANCE"), | |
50: asyncOp("ORIGIN"), | |
51: asyncOp("CALLER"), | |
52: asyncOp("CALLVALUE"), | |
53: asyncOp("CALLDATALOAD"), | |
54: asyncOp("CALLDATASIZE"), | |
55: asyncAndDynamicGasOp("CALLDATACOPY"), | |
56: defaultOp("CODESIZE"), | |
57: dynamicGasOp("CODECOPY"), | |
58: defaultOp("GASPRICE"), | |
59: asyncAndDynamicGasOp("EXTCODESIZE"), | |
60: asyncAndDynamicGasOp("EXTCODECOPY"), | |
// '0x40' range - block operations | |
64: asyncOp("BLOCKHASH"), | |
65: asyncOp("COINBASE"), | |
66: asyncOp("TIMESTAMP"), | |
67: asyncOp("NUMBER"), | |
68: asyncOp("DIFFICULTY"), | |
69: asyncOp("GASLIMIT"), | |
// 0x50 range - 'storage' and execution | |
80: defaultOp("POP"), | |
81: dynamicGasOp("MLOAD"), | |
82: dynamicGasOp("MSTORE"), | |
83: dynamicGasOp("MSTORE8"), | |
84: asyncAndDynamicGasOp("SLOAD"), | |
85: asyncAndDynamicGasOp("SSTORE"), | |
86: defaultOp("JUMP"), | |
87: defaultOp("JUMPI"), | |
88: defaultOp("PC"), | |
89: defaultOp("MSIZE"), | |
90: defaultOp("GAS"), | |
91: defaultOp("JUMPDEST"), | |
// 0x60, range | |
96: defaultOp("PUSH"), | |
97: defaultOp("PUSH"), | |
98: defaultOp("PUSH"), | |
99: defaultOp("PUSH"), | |
100: defaultOp("PUSH"), | |
101: defaultOp("PUSH"), | |
102: defaultOp("PUSH"), | |
103: defaultOp("PUSH"), | |
104: defaultOp("PUSH"), | |
105: defaultOp("PUSH"), | |
106: defaultOp("PUSH"), | |
107: defaultOp("PUSH"), | |
108: defaultOp("PUSH"), | |
109: defaultOp("PUSH"), | |
110: defaultOp("PUSH"), | |
111: defaultOp("PUSH"), | |
112: defaultOp("PUSH"), | |
113: defaultOp("PUSH"), | |
114: defaultOp("PUSH"), | |
115: defaultOp("PUSH"), | |
116: defaultOp("PUSH"), | |
117: defaultOp("PUSH"), | |
118: defaultOp("PUSH"), | |
119: defaultOp("PUSH"), | |
120: defaultOp("PUSH"), | |
121: defaultOp("PUSH"), | |
122: defaultOp("PUSH"), | |
123: defaultOp("PUSH"), | |
124: defaultOp("PUSH"), | |
125: defaultOp("PUSH"), | |
126: defaultOp("PUSH"), | |
127: defaultOp("PUSH"), | |
128: defaultOp("DUP"), | |
129: defaultOp("DUP"), | |
130: defaultOp("DUP"), | |
131: defaultOp("DUP"), | |
132: defaultOp("DUP"), | |
133: defaultOp("DUP"), | |
134: defaultOp("DUP"), | |
135: defaultOp("DUP"), | |
136: defaultOp("DUP"), | |
137: defaultOp("DUP"), | |
138: defaultOp("DUP"), | |
139: defaultOp("DUP"), | |
140: defaultOp("DUP"), | |
141: defaultOp("DUP"), | |
142: defaultOp("DUP"), | |
143: defaultOp("DUP"), | |
144: defaultOp("SWAP"), | |
145: defaultOp("SWAP"), | |
146: defaultOp("SWAP"), | |
147: defaultOp("SWAP"), | |
148: defaultOp("SWAP"), | |
149: defaultOp("SWAP"), | |
150: defaultOp("SWAP"), | |
151: defaultOp("SWAP"), | |
152: defaultOp("SWAP"), | |
153: defaultOp("SWAP"), | |
154: defaultOp("SWAP"), | |
155: defaultOp("SWAP"), | |
156: defaultOp("SWAP"), | |
157: defaultOp("SWAP"), | |
158: defaultOp("SWAP"), | |
159: defaultOp("SWAP"), | |
160: dynamicGasOp("LOG"), | |
161: dynamicGasOp("LOG"), | |
162: dynamicGasOp("LOG"), | |
163: dynamicGasOp("LOG"), | |
164: dynamicGasOp("LOG"), | |
// '0xf0' range - closures | |
240: asyncAndDynamicGasOp("CREATE"), | |
241: asyncAndDynamicGasOp("CALL"), | |
242: asyncAndDynamicGasOp("CALLCODE"), | |
243: dynamicGasOp("RETURN"), | |
// '0x70', range - other | |
254: defaultOp("INVALID"), | |
255: asyncAndDynamicGasOp("SELFDESTRUCT") | |
}; | |
const hardforkOpcodes = [ | |
{ | |
hardfork: Hardfork.Homestead, | |
opcodes: { | |
244: asyncAndDynamicGasOp("DELEGATECALL") | |
// EIP-7 | |
} | |
}, | |
{ | |
hardfork: Hardfork.TangerineWhistle, | |
opcodes: { | |
84: asyncAndDynamicGasOp("SLOAD"), | |
241: asyncAndDynamicGasOp("CALL"), | |
242: asyncAndDynamicGasOp("CALLCODE"), | |
59: asyncAndDynamicGasOp("EXTCODESIZE"), | |
60: asyncAndDynamicGasOp("EXTCODECOPY"), | |
244: asyncAndDynamicGasOp("DELEGATECALL"), | |
// EIP-7 | |
255: asyncAndDynamicGasOp("SELFDESTRUCT"), | |
49: asyncAndDynamicGasOp("BALANCE") | |
} | |
}, | |
{ | |
hardfork: Hardfork.Byzantium, | |
opcodes: { | |
253: dynamicGasOp("REVERT"), | |
// EIP-140 | |
250: asyncAndDynamicGasOp("STATICCALL"), | |
// EIP-214 | |
61: asyncOp("RETURNDATASIZE"), | |
// EIP-211 | |
62: asyncAndDynamicGasOp("RETURNDATACOPY") | |
// EIP-211 | |
} | |
}, | |
{ | |
hardfork: Hardfork.Constantinople, | |
opcodes: { | |
27: defaultOp("SHL"), | |
// EIP-145 | |
28: defaultOp("SHR"), | |
// EIP-145 | |
29: defaultOp("SAR"), | |
// EIP-145 | |
63: asyncAndDynamicGasOp("EXTCODEHASH"), | |
// EIP-1052 | |
245: asyncAndDynamicGasOp("CREATE2") | |
// EIP-1014 | |
} | |
}, | |
{ | |
hardfork: Hardfork.Istanbul, | |
opcodes: { | |
70: defaultOp("CHAINID"), | |
// EIP-1344 | |
71: defaultOp("SELFBALANCE") | |
// EIP-1884 | |
} | |
}, | |
{ | |
hardfork: Hardfork.Paris, | |
opcodes: { | |
68: asyncOp("PREVRANDAO") | |
// EIP-4399 | |
} | |
} | |
]; | |
const eipOpcodes = [ | |
{ | |
eip: 663, | |
opcodes: { | |
230: defaultOp("DUPN"), | |
231: defaultOp("SWAPN"), | |
232: defaultOp("EXCHANGE") | |
} | |
}, | |
{ | |
eip: 1153, | |
opcodes: { | |
92: defaultOp("TLOAD"), | |
93: defaultOp("TSTORE") | |
} | |
}, | |
{ | |
eip: 3198, | |
opcodes: { | |
72: defaultOp("BASEFEE") | |
} | |
}, | |
{ | |
eip: 3855, | |
opcodes: { | |
95: defaultOp("PUSH0") | |
} | |
}, | |
{ | |
eip: 4200, | |
opcodes: { | |
224: defaultOp("RJUMP"), | |
225: defaultOp("RJUMPI"), | |
226: defaultOp("RJUMPV") | |
} | |
}, | |
{ | |
eip: 4750, | |
opcodes: { | |
227: defaultOp("CALLF"), | |
228: defaultOp("RETF") | |
} | |
}, | |
{ | |
eip: 4844, | |
opcodes: { | |
73: defaultOp("BLOBHASH") | |
} | |
}, | |
{ | |
eip: 5656, | |
opcodes: { | |
94: dynamicGasOp("MCOPY") | |
} | |
}, | |
{ | |
eip: 6206, | |
opcodes: { | |
229: defaultOp("JUMPF") | |
} | |
}, | |
{ | |
eip: 7069, | |
opcodes: { | |
247: defaultOp("RETURNDATALOAD"), | |
248: asyncAndDynamicGasOp("EXTCALL"), | |
249: asyncAndDynamicGasOp("EXTDELEGATECALL"), | |
251: asyncAndDynamicGasOp("EXTSTATICCALL") | |
} | |
}, | |
{ | |
eip: 7480, | |
opcodes: { | |
208: defaultOp("DATALOAD"), | |
209: defaultOp("DATALOADN"), | |
210: defaultOp("DATASIZE"), | |
211: dynamicGasOp("DATACOPY") | |
} | |
}, | |
{ | |
eip: 7516, | |
opcodes: { | |
74: defaultOp("BLOBBASEFEE") | |
} | |
}, | |
{ | |
eip: 7620, | |
opcodes: { | |
236: asyncAndDynamicGasOp("EOFCREATE"), | |
238: asyncAndDynamicGasOp("RETURNCONTRACT") | |
} | |
} | |
]; | |
function createOpcodes(opcodes2) { | |
const result = /* @__PURE__ */ new Map(); | |
for (const [key, value] of Object.entries(opcodes2)) { | |
const code = parseInt(key, 10); | |
if (isNaN(value.fee)) value.fee = 0; | |
result.set( | |
code, | |
new Opcode({ | |
code, | |
fullName: getFullname(code, value.name), | |
...value | |
}) | |
); | |
} | |
return result; | |
} | |
function getOpcodesForHF(common2, customOpcodes) { | |
let opcodeBuilder = { ...opcodes }; | |
const handlersCopy = new Map(handlers); | |
const dynamicGasHandlersCopy = new Map(dynamicGasHandlers); | |
for (let fork = 0; fork < hardforkOpcodes.length; fork++) { | |
if (common2.gteHardfork(hardforkOpcodes[fork].hardfork)) { | |
opcodeBuilder = { ...opcodeBuilder, ...hardforkOpcodes[fork].opcodes }; | |
} | |
} | |
for (const eipOps of eipOpcodes) { | |
if (common2.isActivatedEIP(eipOps.eip)) { | |
opcodeBuilder = { ...opcodeBuilder, ...eipOps.opcodes }; | |
} | |
} | |
for (const key in opcodeBuilder) { | |
const baseFee = Number(common2.param(`${opcodeBuilder[key].name.toLowerCase()}Gas`)); | |
if (baseFee === void 0) { | |
throw new Error(`base fee not defined for: ${opcodeBuilder[key].name}`); | |
} | |
opcodeBuilder[key].fee = baseFee; | |
} | |
if (customOpcodes) { | |
for (const _code of customOpcodes) { | |
const code = _code; | |
if (code.logicFunction === void 0) { | |
delete opcodeBuilder[code.opcode]; | |
continue; | |
} | |
if (code.opcodeName === void 0 || code.baseFee === void 0) { | |
throw new Error( | |
`Custom opcode ${code.opcode} does not have the required values: opcodeName and baseFee are required` | |
); | |
} | |
const entry = { | |
[code.opcode]: { | |
name: code.opcodeName, | |
isAsync: true, | |
dynamicGas: code.gasFunction !== void 0, | |
fee: code.baseFee, | |
feeBigInt: BigInt(code.baseFee) | |
} | |
}; | |
opcodeBuilder = { ...opcodeBuilder, ...entry }; | |
if (code.gasFunction !== void 0) { | |
dynamicGasHandlersCopy.set(code.opcode, code.gasFunction); | |
} | |
handlersCopy.set(code.opcode, code.logicFunction); | |
} | |
} | |
const ops = createOpcodes(opcodeBuilder); | |
const opcodeMap = []; | |
for (const [opNumber, op] of ops) { | |
const dynamicGas = dynamicGasHandlersCopy.get(opNumber); | |
const handler = handlersCopy.get(opNumber); | |
opcodeMap[opNumber] = { | |
opcodeInfo: op, | |
opHandler: handler, | |
gasHandler: dynamicGas | |
}; | |
} | |
const INVALID = opcodeMap[254]; | |
for (let i = 0; i <= 255; i++) { | |
if (opcodeMap[i] === void 0) { | |
opcodeMap[i] = INVALID; | |
} | |
} | |
return { | |
dynamicGasHandlers: dynamicGasHandlersCopy, | |
handlers: handlersCopy, | |
opcodes: ops, | |
opcodeMap | |
}; | |
} | |
class Stack { | |
constructor(maxHeight) { | |
this._len = 0; | |
this._store = []; | |
this._maxHeight = maxHeight ?? 1024; | |
} | |
get length() { | |
return this._len; | |
} | |
push(value) { | |
if (this._len >= this._maxHeight) { | |
throw new EvmError(ERROR.STACK_OVERFLOW); | |
} | |
this._store[this._len++] = value; | |
} | |
pop() { | |
if (this._len < 1) { | |
throw new EvmError(ERROR.STACK_UNDERFLOW); | |
} | |
return this._store[--this._len]; | |
} | |
/** | |
* Pop multiple items from stack. Top of stack is first item | |
* in returned array. | |
* @param num - Number of items to pop | |
*/ | |
popN(num = 1) { | |
if (this._len < num) { | |
throw new EvmError(ERROR.STACK_UNDERFLOW); | |
} | |
if (num === 0) { | |
return []; | |
} | |
const arr = Array(num); | |
const cache = this._store; | |
for (let pop = 0; pop < num; pop++) { | |
arr[pop] = cache[--this._len]; | |
} | |
return arr; | |
} | |
/** | |
* Return items from the stack | |
* @param num Number of items to return | |
* @throws {@link ERROR.STACK_UNDERFLOW} | |
*/ | |
peek(num = 1) { | |
const peekArray = Array(num); | |
let start = this._len; | |
for (let peek = 0; peek < num; peek++) { | |
const index = --start; | |
if (index < 0) { | |
throw new EvmError(ERROR.STACK_UNDERFLOW); | |
} | |
peekArray[peek] = this._store[index]; | |
} | |
return peekArray; | |
} | |
/** | |
* Swap top of stack with an item in the stack. | |
* @param position - Index of item from top of the stack (0-indexed) | |
*/ | |
swap(position) { | |
if (this._len <= position) { | |
throw new EvmError(ERROR.STACK_UNDERFLOW); | |
} | |
const head = this._len - 1; | |
const i = head - position; | |
const storageCached = this._store; | |
const tmp = storageCached[head]; | |
storageCached[head] = storageCached[i]; | |
storageCached[i] = tmp; | |
} | |
/** | |
* Pushes a copy of an item in the stack. | |
* @param position - Index of item to be copied (1-indexed) | |
*/ | |
// I would say that we do not need this method any more | |
// since you can't copy a primitive data type | |
// Nevertheless not sure if we "loose" something here? | |
// Will keep commented out for now | |
dup(position) { | |
const len = this._len; | |
if (len < position) { | |
throw new EvmError(ERROR.STACK_UNDERFLOW); | |
} | |
if (len >= this._maxHeight) { | |
throw new EvmError(ERROR.STACK_OVERFLOW); | |
} | |
const i = len - position; | |
this._store[this._len++] = this._store[i]; | |
} | |
/** | |
* Swap number 1 with number 2 on the stack | |
* @param swap1 | |
* @param swap2 | |
*/ | |
exchange(swap1, swap2) { | |
const headIndex = this._len - 1; | |
const exchangeIndex1 = headIndex - swap1; | |
const exchangeIndex2 = headIndex - swap2; | |
if (exchangeIndex1 < 0 || exchangeIndex2 < 0) { | |
throw new EvmError(ERROR.STACK_UNDERFLOW); | |
} | |
const cache = this._store[exchangeIndex2]; | |
this._store[exchangeIndex2] = this._store[exchangeIndex1]; | |
this._store[exchangeIndex1] = cache; | |
} | |
/** | |
* Returns a copy of the current stack. This represents the actual state of the stack | |
* (not the internal state of the stack, which might have unreachable elements in it) | |
*/ | |
getStack() { | |
return this._store.slice(0, this._len); | |
} | |
} | |
const debugGas$1 = debugDefault("evm:gas"); | |
class Interpreter { | |
// TODO remove gasLeft as constructor argument | |
constructor(evm, stateManager, blockchain, env, gasLeft, journal, performanceLogs, profilerOpts) { | |
this.opDebuggers = {}; | |
this._evm = evm; | |
this._stateManager = stateManager; | |
this.common = this._evm.common; | |
if (this.common.consensusType() === "poa" && this._evm["_optsCached"].cliqueSigner === void 0) | |
throw new Error( | |
"Must include cliqueSigner function if clique/poa is being used for consensus type" | |
); | |
this._runState = { | |
programCounter: 0, | |
opCode: 254, | |
// INVALID opcode | |
memory: new Memory(), | |
memoryWordCount: BIGINT_0, | |
highestMemCost: BIGINT_0, | |
stack: new Stack(), | |
code: new Uint8Array(0), | |
validJumps: Uint8Array.from([]), | |
cachedPushes: {}, | |
stateManager: this._stateManager, | |
blockchain, | |
env, | |
shouldDoJumpAnalysis: true, | |
interpreter: this, | |
gasRefund: env.gasRefund, | |
gasLeft, | |
returnBytes: new Uint8Array(0) | |
}; | |
this.journal = journal; | |
this._env = env; | |
this._result = { | |
logs: [], | |
returnValue: void 0, | |
selfdestruct: /* @__PURE__ */ new Set() | |
}; | |
this.profilerOpts = profilerOpts; | |
this.performanceLogger = performanceLogs; | |
} | |
async run(code, opts = {}) { | |
var _a; | |
if (!this.common.isActivatedEIP(3540) || code[0] !== FORMAT) { | |
this._runState.code = code; | |
} else if (this.common.isActivatedEIP(3540)) { | |
if (code[1] !== MAGIC) { | |
return { | |
runState: this._runState, | |
exceptionError: new EvmError(ERROR.INVALID_BYTECODE_RESULT) | |
}; | |
} | |
if (code[2] !== VERSION) { | |
return { | |
runState: this._runState, | |
exceptionError: new EvmError(ERROR.INVALID_EOF_FORMAT) | |
}; | |
} | |
this._runState.code = code; | |
const isTxCreate = this._env.isCreate && this._env.depth === 0; | |
const eofMode = isTxCreate ? EOFContainerMode.TxInitmode : EOFContainerMode.Default; | |
try { | |
setupEOF(this._runState, eofMode); | |
} catch (e) { | |
return { | |
runState: this._runState, | |
exceptionError: new EvmError(ERROR.INVALID_EOF_FORMAT) | |
// TODO: verify if all gas should be consumed | |
}; | |
} | |
if (isTxCreate) { | |
try { | |
validateEOF( | |
this._runState.code, | |
this._evm, | |
ContainerSectionType.InitCode, | |
EOFContainerMode.TxInitmode | |
); | |
} catch (e) { | |
return { | |
runState: this._runState, | |
exceptionError: new EvmError(ERROR.INVALID_EOF_FORMAT) | |
// TODO: verify if all gas should be consumed | |
}; | |
} | |
} | |
} | |
this._runState.programCounter = opts.pc ?? this._runState.programCounter; | |
const pc = this._runState.programCounter; | |
if (pc !== 0 && (pc < 0 || pc >= this._runState.code.length)) { | |
throw new Error("Internal error: program counter not in range"); | |
} | |
let err; | |
let cachedOpcodes; | |
let doJumpAnalysis = true; | |
let timer; | |
let overheadTimer; | |
if (((_a = this.profilerOpts) == null ? void 0 : _a.enabled) === true && this.performanceLogger.hasTimer()) { | |
timer = this.performanceLogger.pauseTimer(); | |
overheadTimer = this.performanceLogger.startTimer("Overhead"); | |
} | |
while (this._runState.programCounter < this._runState.code.length) { | |
const programCounter = this._runState.programCounter; | |
let opCode; | |
let opCodeObj; | |
if (doJumpAnalysis) { | |
opCode = this._runState.code[programCounter]; | |
if (opCode === 86 || opCode === 87 || opCode === 94) { | |
const { jumps, pushes, opcodesCached } = this._getValidJumpDestinations( | |
this._runState.code | |
); | |
this._runState.validJumps = jumps; | |
this._runState.cachedPushes = pushes; | |
this._runState.shouldDoJumpAnalysis = false; | |
cachedOpcodes = opcodesCached; | |
doJumpAnalysis = false; | |
} | |
} else { | |
opCodeObj = cachedOpcodes[programCounter]; | |
opCode = opCodeObj.opcodeInfo.code; | |
} | |
if (opCode === 254 && this.common.isActivatedEIP(6800) && // is this a code loaded from state using witnesses | |
this._runState.env.chargeCodeAccesses === true) { | |
const contract = this._runState.interpreter.getAddress(); | |
if (!await this._runState.stateManager.checkChunkWitnessPresent(contract, programCounter)) { | |
throw Error(`Invalid witness with missing codeChunk for pc=${programCounter}`); | |
} | |
} | |
this._runState.opCode = opCode; | |
try { | |
if (overheadTimer !== void 0) { | |
this.performanceLogger.pauseTimer(); | |
} | |
await this.runStep(opCodeObj); | |
if (overheadTimer !== void 0) { | |
this.performanceLogger.unpauseTimer(overheadTimer); | |
} | |
} catch (e) { | |
if (overheadTimer !== void 0) { | |
this.performanceLogger.unpauseTimer(overheadTimer); | |
} | |
if (!("errorType" in e && e.errorType === "EvmError")) { | |
throw e; | |
} | |
if (e.error !== ERROR.STOP) { | |
err = e; | |
} | |
break; | |
} | |
} | |
if (timer !== void 0) { | |
this.performanceLogger.stopTimer(overheadTimer, 0); | |
this.performanceLogger.unpauseTimer(timer); | |
} | |
return { | |
runState: this._runState, | |
exceptionError: err | |
}; | |
} | |
/** | |
* Executes the opcode to which the program counter is pointing, | |
* reducing its base gas cost, and increments the program counter. | |
*/ | |
async runStep(opcodeObj) { | |
var _a, _b; | |
const opEntry = opcodeObj ?? this.lookupOpInfo(this._runState.opCode); | |
const opInfo = opEntry.opcodeInfo; | |
let timer; | |
if (((_a = this.profilerOpts) == null ? void 0 : _a.enabled) === true) { | |
timer = this.performanceLogger.startTimer(opInfo.name); | |
} | |
let gas = opInfo.feeBigInt; | |
try { | |
if (opInfo.dynamicGas) { | |
gas = await opEntry.gasHandler(this._runState, gas, this.common); | |
} | |
if (this.common.isActivatedEIP(6800) && this._env.chargeCodeAccesses === true) { | |
const contract = this._runState.interpreter.getAddress(); | |
const statelessGas = this._runState.env.accessWitness.touchCodeChunksRangeOnReadAndChargeGas( | |
contract, | |
this._runState.programCounter, | |
this._runState.programCounter | |
); | |
gas += statelessGas; | |
debugGas$1(`codechunk accessed statelessGas=${statelessGas} (-> ${gas})`); | |
} | |
if (this._evm.events.listenerCount("step") > 0 || this._evm.DEBUG) { | |
await this._runStepHook(gas, this.getGasLeft()); | |
} | |
if (opInfo.isInvalid) { | |
throw new EvmError(ERROR.INVALID_OPCODE); | |
} | |
this.useGas(gas, opInfo); | |
this._runState.programCounter++; | |
const opFn = opEntry.opHandler; | |
if (opInfo.isAsync) { | |
await opFn.apply(null, [this._runState, this.common]); | |
} else { | |
opFn.apply(null, [this._runState, this.common]); | |
} | |
} finally { | |
if (((_b = this.profilerOpts) == null ? void 0 : _b.enabled) === true) { | |
this.performanceLogger.stopTimer( | |
timer, | |
Number(gas), | |
"opcodes", | |
opInfo.fee, | |
Number(gas) - opInfo.fee | |
); | |
} | |
} | |
} | |
/** | |
* Get info for an opcode from EVM's list of opcodes. | |
*/ | |
lookupOpInfo(op) { | |
return this._evm._opcodeMap[op]; | |
} | |
async _runStepHook(dynamicFee, gasLeft) { | |
const opcodeInfo = this.lookupOpInfo(this._runState.opCode); | |
const opcode = opcodeInfo.opcodeInfo; | |
const eventObj = { | |
pc: this._runState.programCounter, | |
gasLeft, | |
gasRefund: this._runState.gasRefund, | |
opcode: { | |
name: opcode.fullName, | |
fee: opcode.fee, | |
dynamicFee, | |
isAsync: opcode.isAsync | |
}, | |
stack: this._runState.stack.getStack(), | |
depth: this._env.depth, | |
address: this._env.address, | |
account: this._env.contract, | |
memory: this._runState.memory._store.subarray(0, Number(this._runState.memoryWordCount) * 32), | |
memoryWordCount: this._runState.memoryWordCount, | |
codeAddress: this._env.codeAddress, | |
stateManager: this._runState.stateManager | |
}; | |
if (this._evm.DEBUG) { | |
let hexStack = []; | |
hexStack = eventObj.stack.map((item) => { | |
return bigIntToHex(BigInt(item)); | |
}); | |
const name = eventObj.opcode.name; | |
const opTrace = { | |
pc: eventObj.pc, | |
op: name, | |
gas: bigIntToHex(eventObj.gasLeft), | |
gasCost: bigIntToHex(dynamicFee), | |
stack: hexStack, | |
depth: eventObj.depth | |
}; | |
if (!(name in this.opDebuggers)) { | |
this.opDebuggers[name] = debugDefault(`evm:ops:${name}`); | |
} | |
this.opDebuggers[name](JSON.stringify(opTrace)); | |
} | |
await this._evm._emit("step", eventObj); | |
} | |
// Returns all valid jump and jumpsub destinations. | |
_getValidJumpDestinations(code) { | |
const jumps = new Uint8Array(code.length).fill(0); | |
const pushes = {}; | |
const opcodesCached = Array(code.length); | |
for (let i = 0; i < code.length; i++) { | |
const opcode = code[i]; | |
opcodesCached[i] = this.lookupOpInfo(opcode); | |
if (opcode <= 127) { | |
if (opcode >= 96) { | |
const extraSteps = opcode - 95; | |
const push = bytesToBigInt(code.slice(i + 1, i + opcode - 94)); | |
pushes[i + 1] = push; | |
i += extraSteps; | |
} else if (opcode === 91) { | |
jumps[i] = 1; | |
} | |
} | |
} | |
return { jumps, pushes, opcodesCached }; | |
} | |
/** | |
* Subtracts an amount from the gas counter. | |
* @param amount - Amount of gas to consume | |
* @param context - Usage context for debugging | |
* @throws if out of gas | |
*/ | |
useGas(amount, context) { | |
this._runState.gasLeft -= amount; | |
if (this._evm.DEBUG) { | |
let tempString = ""; | |
if (typeof context === "string") { | |
tempString = context + ": "; | |
} else if (context !== void 0) { | |
tempString = `${context.name} fee: `; | |
} | |
debugGas$1(`${tempString}used ${amount} gas (-> ${this._runState.gasLeft})`); | |
} | |
if (this._runState.gasLeft < BIGINT_0) { | |
this._runState.gasLeft = BIGINT_0; | |
trap(ERROR.OUT_OF_GAS); | |
} | |
} | |
/** | |
* Adds a positive amount to the gas counter. | |
* @param amount - Amount of gas refunded | |
* @param context - Usage context for debugging | |
*/ | |
refundGas(amount, context) { | |
if (this._evm.DEBUG) { | |
debugGas$1( | |
`${typeof context === "string" ? context + ": " : ""}refund ${amount} gas (-> ${this._runState.gasRefund})` | |
); | |
} | |
this._runState.gasRefund += amount; | |
} | |
/** | |
* Reduces amount of gas to be refunded by a positive value. | |
* @param amount - Amount to subtract from gas refunds | |
* @param context - Usage context for debugging | |
*/ | |
subRefund(amount, context) { | |
if (this._evm.DEBUG) { | |
debugGas$1( | |
`${typeof context === "string" ? context + ": " : ""}sub gas refund ${amount} (-> ${this._runState.gasRefund})` | |
); | |
} | |
this._runState.gasRefund -= amount; | |
if (this._runState.gasRefund < BIGINT_0) { | |
this._runState.gasRefund = BIGINT_0; | |
trap(ERROR.REFUND_EXHAUSTED); | |
} | |
} | |
/** | |
* Increments the internal gasLeft counter. Used for adding callStipend. | |
* @param amount - Amount to add | |
*/ | |
addStipend(amount) { | |
if (this._evm.DEBUG) { | |
debugGas$1(`add stipend ${amount} (-> ${this._runState.gasLeft})`); | |
} | |
this._runState.gasLeft += amount; | |
} | |
/** | |
* Returns balance of the given account. | |
* @param address - Address of account | |
*/ | |
async getExternalBalance(address) { | |
if (address.equals(this._env.address)) { | |
return this._env.contract.balance; | |
} | |
let account = await this._stateManager.getAccount(address); | |
if (!account) { | |
account = new Account(); | |
} | |
return account.balance; | |
} | |
/** | |
* Store 256-bit a value in memory to persistent storage. | |
*/ | |
async storageStore(key, value) { | |
await this._stateManager.putStorage(this._env.address, key, value); | |
const account = await this._stateManager.getAccount(this._env.address); | |
if (!account) { | |
throw new Error("could not read account while persisting memory"); | |
} | |
this._env.contract = account; | |
} | |
/** | |
* Loads a 256-bit value to memory from persistent storage. | |
* @param key - Storage key | |
* @param original - If true, return the original storage value (default: false) | |
*/ | |
async storageLoad(key, original = false) { | |
if (original) { | |
return this._stateManager.originalStorageCache.get(this._env.address, key); | |
} else { | |
return this._stateManager.getStorage(this._env.address, key); | |
} | |
} | |
/** | |
* Store 256-bit a value in memory to transient storage. | |
* @param address Address to use | |
* @param key Storage key | |
* @param value Storage value | |
*/ | |
transientStorageStore(key, value) { | |
return this._evm.transientStorage.put(this._env.address, key, value); | |
} | |
/** | |
* Loads a 256-bit value to memory from transient storage. | |
* @param address Address to use | |
* @param key Storage key | |
*/ | |
transientStorageLoad(key) { | |
return this._evm.transientStorage.get(this._env.address, key); | |
} | |
/** | |
* Set the returning output data for the execution. | |
* @param returnData - Output data to return | |
*/ | |
finish(returnData) { | |
this._result.returnValue = returnData; | |
trap(ERROR.STOP); | |
} | |
/** | |
* Set the returning output data for the execution. This will halt the | |
* execution immediately and set the execution result to "reverted". | |
* @param returnData - Output data to return | |
*/ | |
revert(returnData) { | |
this._result.returnValue = returnData; | |
trap(ERROR.REVERT); | |
} | |
/** | |
* Returns address of currently executing account. | |
*/ | |
getAddress() { | |
return this._env.address; | |
} | |
/** | |
* Returns balance of self. | |
*/ | |
getSelfBalance() { | |
return this._env.contract.balance; | |
} | |
/** | |
* Returns the deposited value by the instruction/transaction | |
* responsible for this execution. | |
*/ | |
getCallValue() { | |
return this._env.callValue; | |
} | |
/** | |
* Returns input data in current environment. This pertains to the input | |
* data passed with the message call instruction or transaction. | |
*/ | |
getCallData() { | |
return this._env.callData; | |
} | |
/** | |
* Returns size of input data in current environment. This pertains to the | |
* input data passed with the message call instruction or transaction. | |
*/ | |
getCallDataSize() { | |
return BigInt(this._env.callData.length); | |
} | |
/** | |
* Returns caller address. This is the address of the account | |
* that is directly responsible for this execution. | |
*/ | |
getCaller() { | |
return bytesToBigInt(this._env.caller.bytes); | |
} | |
/** | |
* Returns the size of code running in current environment. | |
*/ | |
getCodeSize() { | |
return BigInt(this._env.code.length); | |
} | |
/** | |
* Returns the code running in current environment. | |
*/ | |
getCode() { | |
return this._env.code; | |
} | |
/** | |
* Returns the current gasCounter. | |
*/ | |
getGasLeft() { | |
return this._runState.gasLeft; | |
} | |
/** | |
* Returns size of current return data buffer. This contains the return data | |
* from the last executed call, callCode, callDelegate, callStatic or create. | |
* Note: create only fills the return data buffer in case of a failure. | |
*/ | |
getReturnDataSize() { | |
return BigInt(this._runState.returnBytes.length); | |
} | |
/** | |
* Returns the current return data buffer. This contains the return data | |
* from last executed call, callCode, callDelegate, callStatic or create. | |
* Note: create only fills the return data buffer in case of a failure. | |
*/ | |
getReturnData() { | |
return this._runState.returnBytes; | |
} | |
/** | |
* Returns true if the current call must be executed statically. | |
*/ | |
isStatic() { | |
return this._env.isStatic; | |
} | |
/** | |
* Returns price of gas in current environment. | |
*/ | |
getTxGasPrice() { | |
return this._env.gasPrice; | |
} | |
/** | |
* Returns the execution's origination address. This is the | |
* sender of original transaction; it is never an account with | |
* non-empty associated code. | |
*/ | |
getTxOrigin() { | |
return bytesToBigInt(this._env.origin.bytes); | |
} | |
/** | |
* Returns the block’s number. | |
*/ | |
getBlockNumber() { | |
return this._env.block.header.number; | |
} | |
/** | |
* Returns the block's beneficiary address. | |
*/ | |
getBlockCoinbase() { | |
let coinbase; | |
if (this.common.consensusAlgorithm() === ConsensusAlgorithm.Clique) { | |
coinbase = this._evm["_optsCached"].cliqueSigner(this._env.block.header); | |
} else { | |
coinbase = this._env.block.header.coinbase; | |
} | |
return bytesToBigInt(coinbase.toBytes()); | |
} | |
/** | |
* Returns the block's timestamp. | |
*/ | |
getBlockTimestamp() { | |
return this._env.block.header.timestamp; | |
} | |
/** | |
* Returns the block's difficulty. | |
*/ | |
getBlockDifficulty() { | |
return this._env.block.header.difficulty; | |
} | |
/** | |
* Returns the block's prevRandao field. | |
*/ | |
getBlockPrevRandao() { | |
return bytesToBigInt(this._env.block.header.prevRandao); | |
} | |
/** | |
* Returns the block's gas limit. | |
*/ | |
getBlockGasLimit() { | |
return this._env.block.header.gasLimit; | |
} | |
/** | |
* Returns the Base Fee of the block as proposed in [EIP-3198](https://eips.ethereum.org/EIPS/eip-3198) | |
*/ | |
getBlockBaseFee() { | |
const baseFee = this._env.block.header.baseFeePerGas; | |
if (baseFee === void 0) { | |
throw new Error("Block has no Base Fee"); | |
} | |
return baseFee; | |
} | |
/** | |
* Returns the Blob Base Fee of the block as proposed in [EIP-7516](https://eips.ethereum.org/EIPS/eip-7516) | |
*/ | |
getBlobBaseFee() { | |
const blobBaseFee = this._env.block.header.getBlobGasPrice(); | |
if (blobBaseFee === void 0) { | |
throw new Error("Block has no Blob Base Fee"); | |
} | |
return blobBaseFee; | |
} | |
/** | |
* Returns the chain ID for current chain. Introduced for the | |
* CHAINID opcode proposed in [EIP-1344](https://eips.ethereum.org/EIPS/eip-1344). | |
*/ | |
getChainId() { | |
return this.common.chainId(); | |
} | |
/** | |
* Sends a message with arbitrary data to a given address path. | |
*/ | |
async call(gasLimit, address, value, data) { | |
const msg = new Message({ | |
caller: this._env.address, | |
gasLimit, | |
to: address, | |
value, | |
data, | |
isStatic: this._env.isStatic, | |
depth: this._env.depth + 1, | |
blobVersionedHashes: this._env.blobVersionedHashes, | |
accessWitness: this._env.accessWitness | |
}); | |
return this._baseCall(msg); | |
} | |
/** | |
* Message-call into this account with an alternative account's code. | |
*/ | |
async callCode(gasLimit, address, value, data) { | |
const msg = new Message({ | |
caller: this._env.address, | |
gasLimit, | |
to: this._env.address, | |
codeAddress: address, | |
value, | |
data, | |
isStatic: this._env.isStatic, | |
depth: this._env.depth + 1, | |
blobVersionedHashes: this._env.blobVersionedHashes, | |
accessWitness: this._env.accessWitness | |
}); | |
return this._baseCall(msg); | |
} | |
/** | |
* Sends a message with arbitrary data to a given address path, but disallow | |
* state modifications. This includes log, create, selfdestruct and call with | |
* a non-zero value. | |
*/ | |
async callStatic(gasLimit, address, value, data) { | |
const msg = new Message({ | |
caller: this._env.address, | |
gasLimit, | |
to: address, | |
value, | |
data, | |
isStatic: true, | |
depth: this._env.depth + 1, | |
blobVersionedHashes: this._env.blobVersionedHashes, | |
accessWitness: this._env.accessWitness | |
}); | |
return this._baseCall(msg); | |
} | |
/** | |
* Message-call into this account with an alternative account’s code, but | |
* persisting the current values for sender and value. | |
*/ | |
async callDelegate(gasLimit, address, value, data) { | |
const msg = new Message({ | |
caller: this._env.caller, | |
gasLimit, | |
to: this._env.address, | |
codeAddress: address, | |
value, | |
data, | |
isStatic: this._env.isStatic, | |
delegatecall: true, | |
depth: this._env.depth + 1, | |
blobVersionedHashes: this._env.blobVersionedHashes, | |
accessWitness: this._env.accessWitness | |
}); | |
return this._baseCall(msg); | |
} | |
async _baseCall(msg) { | |
const selfdestruct = new Set(this._result.selfdestruct); | |
msg.selfdestruct = selfdestruct; | |
msg.gasRefund = this._runState.gasRefund; | |
this._runState.returnBytes = new Uint8Array(0); | |
let createdAddresses; | |
if (this.common.isActivatedEIP(6780)) { | |
createdAddresses = new Set(this._result.createdAddresses); | |
msg.createdAddresses = createdAddresses; | |
} | |
this._runState.returnBytes = new Uint8Array(0); | |
if (this._env.depth >= Number(this.common.param("stackLimit")) || msg.delegatecall !== true && this._env.contract.balance < msg.value) { | |
return BIGINT_0; | |
} | |
const results = await this._evm.runCall({ message: msg }); | |
if (results.execResult.logs) { | |
this._result.logs = this._result.logs.concat(results.execResult.logs); | |
} | |
this.useGas(results.execResult.executionGasUsed, "CALL, STATICCALL, DELEGATECALL, CALLCODE"); | |
if (results.execResult.returnValue !== void 0 && (!results.execResult.exceptionError || results.execResult.exceptionError.error === ERROR.REVERT)) { | |
this._runState.returnBytes = results.execResult.returnValue; | |
} | |
if (!results.execResult.exceptionError) { | |
for (const addressToSelfdestructHex of selfdestruct) { | |
this._result.selfdestruct.add(addressToSelfdestructHex); | |
} | |
if (this.common.isActivatedEIP(6780)) { | |
for (const item of createdAddresses) { | |
this._result.createdAddresses.add(item); | |
} | |
} | |
const account = await this._stateManager.getAccount(this._env.address); | |
if (!account) { | |
throw new Error("could not read contract account"); | |
} | |
this._env.contract = account; | |
this._runState.gasRefund = results.execResult.gasRefund ?? BIGINT_0; | |
} | |
return this._getReturnCode(results); | |
} | |
/** | |
* Creates a new contract with a given value. | |
*/ | |
async create(gasLimit, value, codeToRun, salt, eofCallData) { | |
const selfdestruct = new Set(this._result.selfdestruct); | |
const caller = this._env.address; | |
const depth = this._env.depth + 1; | |
this._runState.returnBytes = new Uint8Array(0); | |
if (this._env.depth >= Number(this.common.param("stackLimit")) || this._env.contract.balance < value) { | |
return BIGINT_0; | |
} | |
if (this._env.contract.nonce >= MAX_UINT64) { | |
return BIGINT_0; | |
} | |
this._env.contract.nonce += BIGINT_1; | |
await this.journal.putAccount(this._env.address, this._env.contract); | |
if (this.common.isActivatedEIP(3860)) { | |
if (codeToRun.length > Number(this.common.param("maxInitCodeSize")) && this._evm.allowUnlimitedInitCodeSize === false) { | |
return BIGINT_0; | |
} | |
} | |
const message = new Message({ | |
caller, | |
gasLimit, | |
value, | |
data: codeToRun, | |
eofCallData, | |
salt, | |
depth, | |
selfdestruct, | |
gasRefund: this._runState.gasRefund, | |
blobVersionedHashes: this._env.blobVersionedHashes, | |
accessWitness: this._env.accessWitness | |
}); | |
let createdAddresses; | |
if (this.common.isActivatedEIP(6780)) { | |
createdAddresses = new Set(this._result.createdAddresses); | |
message.createdAddresses = createdAddresses; | |
} | |
const results = await this._evm.runCall({ message }); | |
if (results.execResult.logs) { | |
this._result.logs = this._result.logs.concat(results.execResult.logs); | |
} | |
this.useGas(results.execResult.executionGasUsed, "CREATE"); | |
if (results.execResult.exceptionError && results.execResult.exceptionError.error === ERROR.REVERT) { | |
this._runState.returnBytes = results.execResult.returnValue; | |
} | |
if (!results.execResult.exceptionError || results.execResult.exceptionError.error === ERROR.CODESTORE_OUT_OF_GAS) { | |
for (const addressToSelfdestructHex of selfdestruct) { | |
this._result.selfdestruct.add(addressToSelfdestructHex); | |
} | |
if (this.common.isActivatedEIP(6780)) { | |
for (const item of createdAddresses) { | |
this._result.createdAddresses.add(item); | |
} | |
} | |
const account = await this._stateManager.getAccount(this._env.address); | |
if (!account) { | |
throw new Error("could not read contract account"); | |
} | |
this._env.contract = account; | |
this._runState.gasRefund = results.execResult.gasRefund ?? BIGINT_0; | |
if (results.createdAddress) { | |
return bytesToBigInt(results.createdAddress.bytes); | |
} | |
} | |
return this._getReturnCode(results, true); | |
} | |
/** | |
* Creates a new contract with a given value. Generates | |
* a deterministic address via CREATE2 rules. | |
*/ | |
async create2(gasLimit, value, data, salt) { | |
return this.create(gasLimit, value, data, salt); | |
} | |
/** | |
* Creates a new contract with a given value. Generates | |
* a deterministic address via EOFCREATE rules. | |
*/ | |
async eofcreate(gasLimit, value, containerData, salt, callData) { | |
return this.create(gasLimit, value, containerData, salt, callData); | |
} | |
/** | |
* Mark account for later deletion and give the remaining balance to the | |
* specified beneficiary address. This will cause a trap and the | |
* execution will be aborted immediately. | |
* @param toAddress - Beneficiary address | |
*/ | |
async selfDestruct(toAddress) { | |
return this._selfDestruct(toAddress); | |
} | |
async _selfDestruct(toAddress) { | |
if (!this._result.selfdestruct.has(bytesToHex$1(this._env.address.bytes))) { | |
this.refundGas(this.common.param("selfdestructRefundGas")); | |
} | |
this._result.selfdestruct.add(bytesToHex$1(this._env.address.bytes)); | |
const toSelf = equalsBytes(toAddress.bytes, this._env.address.bytes); | |
if (!toSelf) { | |
let toAccount = await this._stateManager.getAccount(toAddress); | |
if (!toAccount) { | |
toAccount = new Account(); | |
} | |
toAccount.balance += this._env.contract.balance; | |
await this.journal.putAccount(toAddress, toAccount); | |
} | |
let doModify = !this.common.isActivatedEIP(6780); | |
if (!doModify) { | |
doModify = this._env.createdAddresses.has(this._env.address.toString()); | |
if (!doModify) { | |
doModify = !toSelf; | |
} | |
} | |
if (doModify) { | |
await this._stateManager.modifyAccountFields(this._env.address, { | |
balance: BIGINT_0 | |
}); | |
} | |
trap(ERROR.STOP); | |
} | |
/** | |
* Creates a new log in the current environment. | |
*/ | |
log(data, numberOfTopics, topics) { | |
if (numberOfTopics < 0 || numberOfTopics > 4) { | |
trap(ERROR.OUT_OF_RANGE); | |
} | |
if (topics.length !== numberOfTopics) { | |
trap(ERROR.INTERNAL_ERROR); | |
} | |
const log = [this._env.address.bytes, topics, data]; | |
this._result.logs.push(log); | |
} | |
_getReturnCode(results, isEOFCreate = false) { | |
if (this._runState.env.eof === void 0 || isEOFCreate) { | |
if (results.execResult.exceptionError) { | |
return BIGINT_0; | |
} else { | |
return BIGINT_1; | |
} | |
} else { | |
if (results.execResult.exceptionError !== void 0) { | |
if (results.execResult.exceptionError.error === ERROR.REVERT) { | |
return BIGINT_1; | |
} else { | |
return BIGINT_2; | |
} | |
} | |
return BIGINT_0; | |
} | |
} | |
} | |
class Journal { | |
constructor(stateManager, common2) { | |
var _a, _b; | |
this.DEBUG = typeof window === "undefined" ? ((_b = (_a = process == null ? void 0 : process.env) == null ? void 0 : _a.DEBUG) == null ? void 0 : _b.includes("ethjs")) ?? false : false; | |
this._debug = debugDefault("evm:journal"); | |
this.cleanJournal(); | |
this.journalHeight = 0; | |
this.stateManager = stateManager; | |
this.common = common2; | |
} | |
/** | |
* Clears the internal `accessList` map, and mark this journal to start reporting | |
* which addresses and storages have been accessed | |
*/ | |
startReportingAccessList() { | |
this.accessList = /* @__PURE__ */ new Map(); | |
} | |
/** | |
* Clears the internal `preimages` map, and marks this journal to start reporting | |
* the images (hashed addresses) of the accounts that have been accessed | |
*/ | |
startReportingPreimages() { | |
this.preimages = /* @__PURE__ */ new Map(); | |
} | |
async putAccount(address, account) { | |
this.touchAddress(address); | |
return this.stateManager.putAccount(address, account); | |
} | |
async deleteAccount(address) { | |
this.touchAddress(address); | |
await this.stateManager.deleteAccount(address); | |
} | |
touchAddress(address) { | |
const str = address.toString().slice(2); | |
this.touchAccount(str); | |
} | |
touchAccount(address) { | |
if (this.preimages !== void 0) { | |
const bytesAddress = unprefixedHexToBytes(address); | |
if (this.stateManager.getAppliedKey === void 0) { | |
throw new Error( | |
"touchAccount: stateManager.getAppliedKey can not be undefined if preimage storing is enabled" | |
); | |
} | |
const hashedKey = this.stateManager.getAppliedKey(bytesAddress); | |
this.preimages.set(bytesToHex$1(hashedKey), bytesAddress); | |
} | |
if (!this.touched.has(address)) { | |
this.touched.add(address); | |
const diffArr = this.journalDiff[this.journalDiff.length - 1][1]; | |
diffArr[2].add(address); | |
} | |
} | |
async commit() { | |
this.journalHeight--; | |
this.journalDiff.push([this.journalHeight, [/* @__PURE__ */ new Set(), /* @__PURE__ */ new Map(), /* @__PURE__ */ new Set()]]); | |
await this.stateManager.commit(); | |
} | |
async checkpoint() { | |
this.journalHeight++; | |
this.journalDiff.push([this.journalHeight, [/* @__PURE__ */ new Set(), /* @__PURE__ */ new Map(), /* @__PURE__ */ new Set()]]); | |
await this.stateManager.checkpoint(); | |
} | |
async revert() { | |
let finalI; | |
for (let i = this.journalDiff.length - 1; i >= 0; i--) { | |
finalI = i; | |
const [height, diff] = this.journalDiff[i]; | |
if (height < this.journalHeight) { | |
break; | |
} | |
const addressSet = diff[0]; | |
const slotsMap = diff[1]; | |
const touchedSet = diff[2]; | |
for (const address of addressSet) { | |
if (this.journal.has(address)) { | |
this.journal.delete(address); | |
} | |
} | |
for (const [address, delSlots] of slotsMap) { | |
if (this.journal.has(address)) { | |
const slots = this.journal.get(address); | |
for (const delSlot of delSlots) { | |
slots.delete(delSlot); | |
} | |
} | |
} | |
for (const address of touchedSet) { | |
if (address !== RIPEMD160_ADDRESS_STRING) { | |
this.touched.delete(address); | |
} | |
} | |
} | |
this.journalDiff = this.journalDiff.slice(0, finalI + 1); | |
this.journalHeight--; | |
await this.stateManager.revert(); | |
} | |
cleanJournal() { | |
this.journalHeight = 0; | |
this.journal = /* @__PURE__ */ new Map(); | |
this.alwaysWarmJournal = /* @__PURE__ */ new Map(); | |
this.touched = /* @__PURE__ */ new Set(); | |
this.journalDiff = [[0, [/* @__PURE__ */ new Set(), /* @__PURE__ */ new Map(), /* @__PURE__ */ new Set()]]]; | |
} | |
/** | |
* Removes accounts from the state trie that have been touched, | |
* as defined in EIP-161 (https://eips.ethereum.org/EIPS/eip-161). | |
* Also cleanups any other internal fields | |
*/ | |
async cleanup() { | |
if (this.common.gteHardfork(Hardfork.SpuriousDragon)) { | |
for (const addressHex of this.touched) { | |
const address = new Address(hexToBytes$2(`0x${addressHex}`)); | |
const account = await this.stateManager.getAccount(address); | |
if (account === void 0 || account.isEmpty()) { | |
await this.deleteAccount(address); | |
if (this.DEBUG) { | |
this._debug(`Cleanup touched account address=${address} (>= SpuriousDragon)`); | |
} | |
} | |
} | |
} | |
this.cleanJournal(); | |
delete this.accessList; | |
delete this.preimages; | |
} | |
addAlwaysWarmAddress(addressStr, addToAccessList = false) { | |
const address = stripHexPrefix$1(addressStr); | |
if (!this.alwaysWarmJournal.has(address)) { | |
this.alwaysWarmJournal.set(address, /* @__PURE__ */ new Set()); | |
} | |
if (addToAccessList && this.accessList !== void 0) { | |
if (!this.accessList.has(address)) { | |
this.accessList.set(address, /* @__PURE__ */ new Set()); | |
} | |
} | |
} | |
addAlwaysWarmSlot(addressStr, slotStr, addToAccessList = false) { | |
const address = stripHexPrefix$1(addressStr); | |
this.addAlwaysWarmAddress(address, addToAccessList); | |
const slotsSet = this.alwaysWarmJournal.get(address); | |
const slot = stripHexPrefix$1(slotStr); | |
slotsSet.add(slot); | |
if (addToAccessList && this.accessList !== void 0) { | |
this.accessList.get(address).add(slot); | |
} | |
} | |
/** | |
* Returns true if the address is warm in the current context | |
* @param address - The address (as a Uint8Array) to check | |
*/ | |
isWarmedAddress(address) { | |
const addressHex = bytesToUnprefixedHex(address); | |
const warm = this.journal.has(addressHex) || this.alwaysWarmJournal.has(addressHex); | |
return warm; | |
} | |
/** | |
* Add a warm address in the current context | |
* @param addressArr - The address (as a Uint8Array) to check | |
*/ | |
addWarmedAddress(addressArr) { | |
const address = bytesToUnprefixedHex(addressArr); | |
if (!this.journal.has(address)) { | |
this.journal.set(address, /* @__PURE__ */ new Set()); | |
const diffArr = this.journalDiff[this.journalDiff.length - 1][1]; | |
diffArr[0].add(address); | |
} | |
if (this.accessList !== void 0) { | |
if (!this.accessList.has(address)) { | |
this.accessList.set(address, /* @__PURE__ */ new Set()); | |
} | |
} | |
} | |
/** | |
* Returns true if the slot of the address is warm | |
* @param address - The address (as a Uint8Array) to check | |
* @param slot - The slot (as a Uint8Array) to check | |
*/ | |
isWarmedStorage(address, slot) { | |
const addressHex = bytesToUnprefixedHex(address); | |
const slots = this.journal.get(addressHex); | |
if (slots === void 0) { | |
if (this.alwaysWarmJournal.has(addressHex)) { | |
return this.alwaysWarmJournal.get(addressHex).has(bytesToUnprefixedHex(slot)); | |
} | |
return false; | |
} | |
if (slots.has(bytesToUnprefixedHex(slot))) { | |
return true; | |
} else if (this.alwaysWarmJournal.has(addressHex)) { | |
return this.alwaysWarmJournal.get(addressHex).has(bytesToUnprefixedHex(slot)); | |
} | |
return false; | |
} | |
/** | |
* Mark the storage slot in the address as warm in the current context | |
* @param address - The address (as a Uint8Array) to check | |
* @param slot - The slot (as a Uint8Array) to check | |
*/ | |
addWarmedStorage(address, slot) { | |
const addressHex = bytesToUnprefixedHex(address); | |
let slots = this.journal.get(addressHex); | |
if (slots === void 0) { | |
this.addWarmedAddress(address); | |
slots = this.journal.get(addressHex); | |
} | |
const slotStr = bytesToUnprefixedHex(slot); | |
if (!slots.has(slotStr)) { | |
slots.add(slotStr); | |
const diff = this.journalDiff[this.journalDiff.length - 1][1]; | |
const addressSlotMap = diff[1]; | |
if (!addressSlotMap.has(addressHex)) { | |
addressSlotMap.set(addressHex, /* @__PURE__ */ new Set()); | |
} | |
const slotsSet = addressSlotMap.get(addressHex); | |
slotsSet.add(slotStr); | |
} | |
if (this.accessList !== void 0) { | |
const addrSet = this.accessList.get(addressHex); | |
addrSet.add(slotStr); | |
} | |
} | |
} | |
const blockGasLimit = 3e7; | |
const slotTime = 12; | |
const bpsNormalizer = blockGasLimit / slotTime; | |
class Timer { | |
constructor(tag) { | |
this.runTime = 0; | |
this.tag = tag; | |
this.startTime = performance.now(); | |
} | |
pause() { | |
this.runTime = this.runTime + performance.now() - this.startTime; | |
} | |
unpause() { | |
this.startTime = performance.now(); | |
} | |
time() { | |
return (performance.now() - this.startTime + this.runTime) / 1e3; | |
} | |
} | |
class EVMPerformanceLogger { | |
constructor() { | |
this.clear(); | |
} | |
clear() { | |
this.opcodes = {}; | |
this.precompiles = {}; | |
} | |
getLogs() { | |
function getLogsFor(obj) { | |
const output2 = []; | |
for (const key in obj) { | |
const field = obj[key]; | |
const gasPerSecond = field.gasUsed / field.time; | |
const entry = { | |
calls: field.calls, | |
totalTime: Math.round(field.time * 1e6) / 1e3, | |
avgTimePerCall: Math.round(field.time / field.calls * 1e6) / 1e3, | |
gasUsed: field.gasUsed, | |
millionGasPerSecond: Math.round(gasPerSecond / 1e3) / 1e3, | |
blocksPerSlot: Math.round(gasPerSecond / bpsNormalizer * 1e3) / 1e3, | |
tag: key | |
}; | |
if (field.dynamicGasUsed !== void 0) { | |
entry.staticGas = field.staticGas; | |
entry.staticGasUsed = field.staticGas * field.calls; | |
entry.dynamicGasUsed = field.dynamicGasUsed; | |
} | |
output2.push(entry); | |
} | |
output2.sort((a, b) => { | |
return b.millionGasPerSecond - a.millionGasPerSecond; | |
}); | |
return output2; | |
} | |
return { | |
opcodes: getLogsFor(this.opcodes), | |
precompiles: getLogsFor(this.precompiles) | |
}; | |
} | |
hasTimer() { | |
return this.currentTimer !== void 0; | |
} | |
// Start a new timer | |
// Only one timer can be timing at the same time | |
startTimer(tag) { | |
if (this.currentTimer !== void 0) { | |
throw new Error("Cannot have two timers running at the same time"); | |
} | |
this.currentTimer = new Timer(tag); | |
return this.currentTimer; | |
} | |
// Pauses current timer and returns that timer | |
pauseTimer() { | |
const timer = this.currentTimer; | |
if (timer === void 0) { | |
throw new Error("No timer to pause"); | |
} | |
timer.pause(); | |
this.currentTimer = void 0; | |
return timer; | |
} | |
// Unpauses current timer and returns that timer | |
unpauseTimer(timer) { | |
if (this.currentTimer !== void 0) { | |
throw new Error("Cannot unpause timer: another timer is already running"); | |
} | |
timer.unpause(); | |
this.currentTimer = timer; | |
} | |
// Stops a timer from running | |
stopTimer(timer, gasUsed, targetTimer = "opcodes", staticGas, dynamicGas) { | |
if (this.currentTimer === void 0 || this.currentTimer !== timer) { | |
throw new Error("Cannot stop timer: another timer is already running"); | |
} | |
const time = timer.time(); | |
const tag = timer.tag; | |
this.currentTimer = void 0; | |
const target = this[targetTimer]; | |
if (target[tag] === void 0) { | |
target[tag] = { | |
calls: 0, | |
time: 0, | |
gasUsed: 0 | |
}; | |
} | |
const obj = target[tag]; | |
obj.calls++; | |
obj.time += time; | |
obj.gasUsed += gasUsed; | |
if (targetTimer === "opcodes") { | |
obj.staticGas = staticGas; | |
obj.dynamicGasUsed = (obj.dynamicGasUsed ?? 0) + dynamicGas; | |
} | |
} | |
} | |
const paramsEVM = { | |
/** | |
* Frontier/Chainstart | |
*/ | |
1: { | |
// gasConfig | |
maxRefundQuotient: 2, | |
// Maximum refund quotient; max tx refund is min(tx.gasUsed/maxRefundQuotient, tx.gasRefund) | |
// gasPrices | |
basefeeGas: 2, | |
// Gas base cost, used e.g. for ChainID opcode (Istanbul) | |
expGas: 10, | |
// Base fee of the EXP opcode | |
expByteGas: 10, | |
// Times ceil(log256(exponent)) for the EXP instruction | |
keccak256Gas: 30, | |
// Base fee of the SHA3 opcode | |
keccak256WordGas: 6, | |
// Once per word of the SHA3 operation's data | |
sloadGas: 50, | |
// Base fee of the SLOAD opcode | |
sstoreSetGas: 2e4, | |
// Once per SSTORE operation if the zeroness changes from zero | |
sstoreResetGas: 5e3, | |
// Once per SSTORE operation if the zeroness does not change from zero | |
sstoreRefundGas: 15e3, | |
// Once per SSTORE operation if the zeroness changes to zero | |
jumpdestGas: 1, | |
// Base fee of the JUMPDEST opcode | |
logGas: 375, | |
// Base fee of the LOG opcode | |
logDataGas: 8, | |
// Per byte in a LOG* operation's data | |
logTopicGas: 375, | |
// Multiplied by the * of the LOG*, per LOG transaction. e.g. LOG0 incurs 0 * c_txLogTopicGas, LOG4 incurs 4 * c_txLogTopicGas | |
createGas: 32e3, | |
// Base fee of the CREATE opcode | |
callGas: 40, | |
// Base fee of the CALL opcode | |
callStipendGas: 2300, | |
// Free gas given at beginning of call | |
callValueTransferGas: 9e3, | |
// Paid for CALL when the value transfer is non-zero | |
callNewAccountGas: 25e3, | |
// Paid for CALL when the destination address didn't exist prior | |
selfdestructRefundGas: 24e3, | |
// Refunded following a selfdestruct operation | |
memoryGas: 3, | |
// Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL | |
quadCoefficientDivGas: 512, | |
// Divisor for the quadratic particle of the memory cost equation | |
createDataGas: 200, | |
// | |
copyGas: 3, | |
// Multiplied by the number of 32-byte words that are copied (round up) for any *COPY operation and added | |
ecRecoverGas: 3e3, | |
sha256Gas: 60, | |
sha256WordGas: 12, | |
ripemd160Gas: 600, | |
ripemd160WordGas: 120, | |
identityGas: 15, | |
identityWordGas: 3, | |
stopGas: 0, | |
// Base fee of the STOP opcode | |
addGas: 3, | |
// Base fee of the ADD opcode | |
mulGas: 5, | |
// Base fee of the MUL opcode | |
subGas: 3, | |
// Base fee of the SUB opcode | |
divGas: 5, | |
// Base fee of the DIV opcode | |
sdivGas: 5, | |
// Base fee of the SDIV opcode | |
modGas: 5, | |
// Base fee of the MOD opcode | |
smodGas: 5, | |
// Base fee of the SMOD opcode | |
addmodGas: 8, | |
// Base fee of the ADDMOD opcode | |
mulmodGas: 8, | |
// Base fee of the MULMOD opcode | |
signextendGas: 5, | |
// Base fee of the SIGNEXTEND opcode | |
ltGas: 3, | |
// Base fee of the LT opcode | |
gtGas: 3, | |
// Base fee of the GT opcode | |
sltGas: 3, | |
// Base fee of the SLT opcode | |
sgtGas: 3, | |
// Base fee of the SGT opcode | |
eqGas: 3, | |
// Base fee of the EQ opcode | |
iszeroGas: 3, | |
// Base fee of the ISZERO opcode | |
andGas: 3, | |
// Base fee of the AND opcode | |
orGas: 3, | |
// Base fee of the OR opcode | |
xorGas: 3, | |
// Base fee of the XOR opcode | |
notGas: 3, | |
// Base fee of the NOT opcode | |
byteGas: 3, | |
// Base fee of the BYTE opcode | |
addressGas: 2, | |
// Base fee of the ADDRESS opcode | |
balanceGas: 20, | |
// Base fee of the BALANCE opcode | |
originGas: 2, | |
// Base fee of the ORIGIN opcode | |
callerGas: 2, | |
// Base fee of the CALLER opcode | |
callvalueGas: 2, | |
// Base fee of the CALLVALUE opcode | |
calldataloadGas: 3, | |
// Base fee of the CALLDATALOAD opcode | |
calldatasizeGas: 2, | |
// Base fee of the CALLDATASIZE opcode | |
calldatacopyGas: 3, | |
// Base fee of the CALLDATACOPY opcode | |
codesizeGas: 2, | |
// Base fee of the CODESIZE opcode | |
codecopyGas: 3, | |
// Base fee of the CODECOPY opcode | |
gaspriceGas: 2, | |
// Base fee of the GASPRICE opcode | |
extcodesizeGas: 20, | |
// Base fee of the EXTCODESIZE opcode | |
extcodecopyGas: 20, | |
// Base fee of the EXTCODECOPY opcode | |
blockhashGas: 20, | |
// Base fee of the BLOCKHASH opcode | |
coinbaseGas: 2, | |
// Base fee of the COINBASE opcode | |
timestampGas: 2, | |
// Base fee of the TIMESTAMP opcode | |
numberGas: 2, | |
// Base fee of the NUMBER opcode | |
difficultyGas: 2, | |
// Base fee of the DIFFICULTY opcode | |
gaslimitGas: 2, | |
// Base fee of the GASLIMIT opcode | |
popGas: 2, | |
// Base fee of the POP opcode | |
mloadGas: 3, | |
// Base fee of the MLOAD opcode | |
mstoreGas: 3, | |
// Base fee of the MSTORE opcode | |
mstore8Gas: 3, | |
// Base fee of the MSTORE8 opcode | |
sstoreGas: 0, | |
// Base fee of the SSTORE opcode | |
jumpGas: 8, | |
// Base fee of the JUMP opcode | |
jumpiGas: 10, | |
// Base fee of the JUMPI opcode | |
pcGas: 2, | |
// Base fee of the PC opcode | |
msizeGas: 2, | |
// Base fee of the MSIZE opcode | |
gasGas: 2, | |
// Base fee of the GAS opcode | |
pushGas: 3, | |
// Base fee of the PUSH opcode | |
dupGas: 3, | |
// Base fee of the DUP opcode | |
swapGas: 3, | |
// Base fee of the SWAP opcode | |
callcodeGas: 40, | |
// Base fee of the CALLCODE opcode | |
returnGas: 0, | |
// Base fee of the RETURN opcode | |
invalidGas: 0, | |
// Base fee of the INVALID opcode | |
selfdestructGas: 0, | |
// Base fee of the SELFDESTRUCT opcode | |
prevrandaoGas: 0, | |
// TODO: these below 0-gas additions might also point to non-clean implementations in the code base | |
// evm | |
stackLimit: 1024, | |
// Maximum size of VM stack allowed | |
callCreateDepth: 1024 | |
// Maximum depth of call/create stack | |
}, | |
/** | |
. * Homestead HF Meta EIP | |
. */ | |
606: { | |
// gasPrices | |
delegatecallGas: 40 | |
// Base fee of the DELEGATECALL opcode | |
}, | |
/** | |
. * TangerineWhistle HF Meta EIP | |
. */ | |
608: { | |
// gasPrices | |
sloadGas: 200, | |
// Once per SLOAD operation | |
callGas: 700, | |
// Once per CALL operation & message call transaction | |
extcodesizeGas: 700, | |
// Base fee of the EXTCODESIZE opcode | |
extcodecopyGas: 700, | |
// Base fee of the EXTCODECOPY opcode | |
balanceGas: 400, | |
// Base fee of the BALANCE opcode | |
delegatecallGas: 700, | |
// Base fee of the DELEGATECALL opcode | |
callcodeGas: 700, | |
// Base fee of the CALLCODE opcode | |
selfdestructGas: 5e3 | |
// Base fee of the SELFDESTRUCT opcode | |
}, | |
/** | |
. * Spurious Dragon HF Meta EIP | |
. */ | |
607: { | |
// gasPrices | |
expByteGas: 50, | |
// Times ceil(log256(exponent)) for the EXP instruction | |
// evm | |
maxCodeSize: 24576 | |
// Maximum length of contract code | |
}, | |
/** | |
. * Byzantium HF Meta EIP | |
. */ | |
609: { | |
// gasPrices | |
modexpGquaddivisorGas: 20, | |
// Gquaddivisor from modexp precompile for gas calculation | |
bn254AddGas: 500, | |
// Gas costs for curve addition precompile | |
bn254MulGas: 4e4, | |
// Gas costs for curve multiplication precompile | |
bn254PairingGas: 1e5, | |
// Base gas costs for curve pairing precompile | |
bn254PairingWordGas: 8e4, | |
// Gas costs regarding curve pairing precompile input length | |
revertGas: 0, | |
// Base fee of the REVERT opcode | |
staticcallGas: 700, | |
// Base fee of the STATICCALL opcode | |
returndatasizeGas: 2, | |
// Base fee of the RETURNDATASIZE opcode | |
returndatacopyGas: 3 | |
// Base fee of the RETURNDATACOPY opcode | |
}, | |
/** | |
. * Constantinople HF Meta EIP | |
. */ | |
1013: { | |
// gasPrices | |
netSstoreNoopGas: 200, | |
// Once per SSTORE operation if the value doesn't change | |
netSstoreInitGas: 2e4, | |
// Once per SSTORE operation from clean zero | |
netSstoreCleanGas: 5e3, | |
// Once per SSTORE operation from clean non-zero | |
netSstoreDirtyGas: 200, | |
// Once per SSTORE operation from dirty | |
netSstoreClearRefundGas: 15e3, | |
// Once per SSTORE operation for clearing an originally existing storage slot | |
netSstoreResetRefundGas: 4800, | |
// Once per SSTORE operation for resetting to the original non-zero value | |
netSstoreResetClearRefundGas: 19800, | |
// Once per SSTORE operation for resetting to the original zero value | |
shlGas: 3, | |
// Base fee of the SHL opcode | |
shrGas: 3, | |
// Base fee of the SHR opcode | |
sarGas: 3, | |
// Base fee of the SAR opcode | |
extcodehashGas: 400, | |
// Base fee of the EXTCODEHASH opcode | |
create2Gas: 32e3 | |
// Base fee of the CREATE2 opcode | |
}, | |
/** | |
. * Petersburg HF Meta EIP | |
. */ | |
1716: { | |
// gasPrices | |
netSstoreNoopGas: null, | |
// Removed along EIP-1283 | |
netSstoreInitGas: null, | |
// Removed along EIP-1283 | |
netSstoreCleanGas: null, | |
// Removed along EIP-1283 | |
netSstoreDirtyGas: null, | |
// Removed along EIP-1283 | |
netSstoreClearRefundGas: null, | |
// Removed along EIP-1283 | |
netSstoreResetRefundGas: null, | |
// Removed along EIP-1283 | |
netSstoreResetClearRefundGas: null | |
// Removed along EIP-1283 | |
}, | |
/** | |
. * Istanbul HF Meta EIP | |
. */ | |
1679: { | |
// gasPrices | |
blake2RoundGas: 1, | |
// Gas cost per round for the Blake2 F precompile | |
bn254AddGas: 150, | |
// Gas costs for curve addition precompile | |
bn254MulGas: 6e3, | |
// Gas costs for curve multiplication precompile | |
bn254PairingGas: 45e3, | |
// Base gas costs for curve pairing precompile | |
bn254PairingWordGas: 34e3, | |
// Gas costs regarding curve pairing precompile input length | |
sstoreSentryEIP2200Gas: 2300, | |
// Minimum gas required to be present for an SSTORE call, not consumed | |
sstoreNoopEIP2200Gas: 800, | |
// Once per SSTORE operation if the value doesn't change | |
sstoreDirtyEIP2200Gas: 800, | |
// Once per SSTORE operation if a dirty value is changed | |
sstoreInitEIP2200Gas: 2e4, | |
// Once per SSTORE operation from clean zero to non-zero | |
sstoreInitRefundEIP2200Gas: 19200, | |
// Once per SSTORE operation for resetting to the original zero value | |
sstoreCleanEIP2200Gas: 5e3, | |
// Once per SSTORE operation from clean non-zero to something else | |
sstoreCleanRefundEIP2200Gas: 4200, | |
// Once per SSTORE operation for resetting to the original non-zero value | |
sstoreClearRefundEIP2200Gas: 15e3, | |
// Once per SSTORE operation for clearing an originally existing storage slot | |
balanceGas: 700, | |
// Base fee of the BALANCE opcode | |
extcodehashGas: 700, | |
// Base fee of the EXTCODEHASH opcode | |
chainidGas: 2, | |
// Base fee of the CHAINID opcode | |
selfbalanceGas: 5, | |
// Base fee of the SELFBALANCE opcode | |
sloadGas: 800 | |
// Base fee of the SLOAD opcode | |
}, | |
/** | |
. * SWAPN, DUPN and EXCHANGE instructions | |
. */ | |
663: { | |
// gasPrices | |
dupnGas: 3, | |
// Base fee of the DUPN opcode | |
swapnGas: 3, | |
// Base fee of the SWAPN opcode | |
exchangeGas: 3 | |
// Base fee of the EXCHANGE opcode | |
}, | |
/** | |
. * Transient storage opcodes | |
. */ | |
1153: { | |
// gasPrices | |
tstoreGas: 100, | |
// Base fee of the TSTORE opcode | |
tloadGas: 100 | |
// Base fee of the TLOAD opcode | |
}, | |
1559: { | |
elasticityMultiplier: 2 | |
// Maximum block gas target elasticity | |
}, | |
/** | |
. * ModExp gas cost | |
. */ | |
2565: { | |
// gasPrices | |
modexpGquaddivisorGas: 3 | |
// Gquaddivisor from modexp precompile for gas calculation | |
}, | |
/** | |
* BLS12-381 precompiles | |
*/ | |
2537: { | |
// gasPrices | |
bls12381G1AddGas: 500, | |
// Gas cost of a single BLS12-381 G1 addition precompile-call | |
bls12381G1MulGas: 12e3, | |
// Gas cost of a single BLS12-381 G1 multiplication precompile-call | |
bls12381G2AddGas: 800, | |
// Gas cost of a single BLS12-381 G2 addition precompile-call | |
bls12381G2MulGas: 45e3, | |
// Gas cost of a single BLS12-381 G2 multiplication precompile-call | |
bls12381PairingBaseGas: 65e3, | |
// Base gas cost of BLS12-381 pairing check | |
bls12381PairingPerPairGas: 43e3, | |
// Per-pair gas cost of BLS12-381 pairing check | |
bls12381MapG1Gas: 5500, | |
// Gas cost of BLS12-381 map field element to G1 | |
bls12381MapG2Gas: 75e3 | |
// Gas cost of BLS12-381 map field element to G2 | |
}, | |
/** | |
. * Gas cost increases for state access opcodes | |
. */ | |
2929: { | |
// gasPrices | |
coldsloadGas: 2100, | |
// Gas cost of the first read of storage from a given location (per transaction) | |
coldaccountaccessGas: 2600, | |
// Gas cost of the first read of a given address (per transaction) | |
warmstoragereadGas: 100, | |
// Gas cost of reading storage locations which have already loaded 'cold' | |
sstoreCleanEIP2200Gas: 2900, | |
// Once per SSTORE operation from clean non-zero to something else | |
sstoreNoopEIP2200Gas: 100, | |
// Once per SSTORE operation if the value doesn't change | |
sstoreDirtyEIP2200Gas: 100, | |
// Once per SSTORE operation if a dirty value is changed | |
sstoreInitRefundEIP2200Gas: 19900, | |
// Once per SSTORE operation for resetting to the original zero value | |
sstoreCleanRefundEIP2200Gas: 4900, | |
// Once per SSTORE operation for resetting to the original non-zero value | |
callGas: 0, | |
// Base fee of the CALL opcode | |
callcodeGas: 0, | |
// Base fee of the CALLCODE opcode | |
delegatecallGas: 0, | |
// Base fee of the DELEGATECALL opcode | |
staticcallGas: 0, | |
// Base fee of the STATICCALL opcode | |
balanceGas: 0, | |
// Base fee of the BALANCE opcode | |
extcodesizeGas: 0, | |
// Base fee of the EXTCODESIZE opcode | |
extcodecopyGas: 0, | |
// Base fee of the EXTCODECOPY opcode | |
extcodehashGas: 0, | |
// Base fee of the EXTCODEHASH opcode | |
sloadGas: 0, | |
// Base fee of the SLOAD opcode | |
sstoreGas: 0 | |
// Base fee of the SSTORE opcode | |
}, | |
/** | |
* Save historical block hashes in state (Verkle related usage, UNSTABLE) | |
*/ | |
2935: { | |
// evm | |
historyStorageAddress: "0x0aae40965e6800cd9b1f4b05ff21581047e3f91e", | |
// The address where the historical blockhashes are stored | |
historyServeWindow: 8192 | |
// The amount of blocks to be served by the historical blockhash contract | |
}, | |
/** | |
. * BASEFEE opcode | |
. */ | |
3198: { | |
// gasPrices | |
basefeeGas: 2 | |
// Gas cost of the BASEFEE opcode | |
}, | |
/** | |
. * Reduction in refunds | |
. */ | |
3529: { | |
// gasConfig | |
maxRefundQuotient: 5, | |
// Maximum refund quotient; max tx refund is min(tx.gasUsed/maxRefundQuotient, tx.gasRefund) | |
// gasPrices | |
selfdestructRefundGas: 0, | |
// Refunded following a selfdestruct operation | |
sstoreClearRefundEIP2200Gas: 4800 | |
// Once per SSTORE operation for clearing an originally existing storage slot | |
}, | |
/** | |
. * PUSH0 instruction | |
. */ | |
3855: { | |
// gasPrices | |
push0Gas: 2 | |
// Base fee of the PUSH0 opcode | |
}, | |
/** | |
. * Limit and meter initcode | |
. */ | |
3860: { | |
// gasPrices | |
initCodeWordGas: 2, | |
// Gas to pay for each word (32 bytes) of initcode when creating a contract | |
// vm | |
maxInitCodeSize: 49152 | |
// Maximum length of initialization code when creating a contract | |
}, | |
/** | |
* EOF - Static relative jumps | |
*/ | |
4200: { | |
// gasPrices | |
rjumpGas: 2, | |
// Base fee of the RJUMP opcode | |
rjumpiGas: 4, | |
// Base fee of the RJUMPI opcode | |
rjumpvGas: 4 | |
// Base fee of the RJUMPV opcode | |
}, | |
/** | |
. * Supplant DIFFICULTY opcode with PREVRANDAO | |
. */ | |
4399: { | |
// gasPrices | |
prevrandaoGas: 2 | |
// Base fee of the PREVRANDAO opcode (previously DIFFICULTY) | |
}, | |
/** | |
* EOF - Functions | |
*/ | |
4750: { | |
// gasPrices | |
callfGas: 5, | |
// Base fee of the CALLF opcode | |
retfGas: 3 | |
// Base fee of the RETF opcode | |
}, | |
/** | |
. * Shard Blob Transactions | |
. */ | |
4844: { | |
kzgPointEvaluationPrecompileGas: 5e4, | |
// The fee associated with the point evaluation precompile | |
blobhashGas: 3, | |
// Base fee of the BLOBHASH opcode | |
// sharding | |
blobCommitmentVersionKzg: 1, | |
// The number indicated a versioned hash is a KZG commitment | |
fieldElementsPerBlob: 4096 | |
// The number of field elements allowed per blob | |
}, | |
/** | |
* MCOPY - Memory copying instruction | |
*/ | |
5656: { | |
// gasPrices | |
mcopyGas: 3 | |
// Base fee of the MCOPY opcode | |
}, | |
/** | |
* EOF - JUMPF and non-returning functions | |
*/ | |
6206: { | |
// gasPrices | |
jumpfGas: 5 | |
// Base fee of the JUMPF opcode | |
}, | |
/** | |
* Ethereum state using a unified verkle tree (experimental) | |
*/ | |
6800: { | |
// gasPrices | |
createGas: 1e3, | |
// Base fee of the CREATE opcode | |
coldsloadGas: 0 | |
// Gas cost of the first read of storage from a given location (per transaction) | |
}, | |
/** | |
. * Revamped CALL instructions | |
. */ | |
7069: { | |
/* Note: per EIP these are the additionally required EIPs: | |
EIP 150 - This is the entire Tangerine Whistle hardfork | |
EIP 211 - (RETURNDATASIZE / RETURNDATACOPY) - Included in Byzantium | |
EIP 214 - (STATICCALL) - Included in Byzantium | |
*/ | |
// gasPrices | |
extcallGas: 0, | |
// Base fee of the EXTCALL opcode | |
extdelegatecallGas: 0, | |
// Base fee of the EXTDELEGATECALL opcode | |
extstaticcallGas: 0, | |
// Base fee of the EXTSTATICCALL opcode | |
returndataloadGas: 3, | |
// Base fee of the RETURNDATALOAD opcode | |
minRetainedGas: 5e3, | |
// Minimum gas retained prior to executing an EXT*CALL opcode (this is the minimum gas available after performing the EXT*CALL) | |
minCalleeGas: 2300 | |
//Minimum gas available to the the address called by an EXT*CALL opcode | |
}, | |
/** | |
* EOF - Data section access instructions | |
*/ | |
7480: { | |
// gasPrices | |
dataloadGas: 4, | |
// Base fee of the DATALOAD opcode | |
dataloadnGas: 3, | |
// Base fee of the DATALOADN opcode | |
datasizeGas: 2, | |
// Base fee of the DATASIZE opcode | |
datacopyGas: 3 | |
// Base fee of the DATACOPY opcode | |
}, | |
/** | |
. * BLOBBASEFEE opcode | |
. */ | |
7516: { | |
// gasPrices | |
blobbasefeeGas: 2 | |
// Gas cost of the BLOBBASEFEE opcode | |
}, | |
/** | |
. * EOF Contract Creation | |
. */ | |
7620: { | |
/* Note: per EIP these are the additionally required EIPs: | |
EIP 170 - (Max contract size) - Included in Spurious Dragon | |
*/ | |
// gasPrices | |
eofcreateGas: 32e3, | |
// Base fee of the EOFCREATE opcode (Same as CREATE/CREATE2) | |
returncontractGas: 0 | |
// Base fee of the RETURNCONTRACT opcode | |
} | |
}; | |
const gasLimitCheck = (opts, gasUsed, pName) => { | |
if (opts._debug !== void 0) { | |
opts._debug( | |
`Run ${pName} precompile data=${short(opts.data)} length=${opts.data.length} gasLimit=${opts.gasLimit} gasUsed=${gasUsed}` | |
); | |
} | |
if (opts.gasLimit < gasUsed) { | |
if (opts._debug !== void 0) { | |
opts._debug(`${pName} failed: OOG`); | |
} | |
return false; | |
} | |
return true; | |
}; | |
const equalityLengthCheck = (opts, length, pName) => { | |
if (opts.data.length !== length) { | |
if (opts._debug !== void 0) { | |
opts._debug( | |
`${pName} failed: Invalid input length length=${opts.data.length} (expected: ${length})` | |
); | |
} | |
return false; | |
} | |
return true; | |
}; | |
const moduloLengthCheck = (opts, length, pName) => { | |
if (opts.data.length % length !== 0) { | |
if (opts._debug !== void 0) { | |
opts._debug( | |
`${pName} failed: Invalid input length length=${opts.data.length} (expected: ${length}*k bytes)` | |
); | |
} | |
return false; | |
} | |
return true; | |
}; | |
function precompile01(opts) { | |
const pName = getPrecompileName("01"); | |
const ecrecoverFunction = opts.common.customCrypto.ecrecover ?? ecrecover; | |
const gasUsed = opts.common.param("ecRecoverGas"); | |
if (!gasLimitCheck(opts, gasUsed, pName)) { | |
return OOGResult(opts.gasLimit); | |
} | |
const data = setLengthRight(opts.data, 128); | |
const msgHash = data.subarray(0, 32); | |
const v = data.subarray(32, 64); | |
const vBigInt = bytesToBigInt(v); | |
if (vBigInt !== BIGINT_27 && vBigInt !== BIGINT_28) { | |
if (opts._debug !== void 0) { | |
opts._debug(`${pName} failed: v neither 27 nor 28`); | |
} | |
return { | |
executionGasUsed: gasUsed, | |
returnValue: new Uint8Array() | |
}; | |
} | |
const r = data.subarray(64, 96); | |
const s = data.subarray(96, 128); | |
let publicKey; | |
try { | |
if (opts._debug !== void 0) { | |
opts._debug( | |
`${pName}: PK recovery with msgHash=${bytesToHex$1(msgHash)} v=${bytesToHex$1( | |
v | |
)} r=${bytesToHex$1(r)}s=${bytesToHex$1(s)}}` | |
); | |
} | |
publicKey = ecrecoverFunction(msgHash, bytesToBigInt(v), r, s); | |
} catch (e) { | |
if (opts._debug !== void 0) { | |
opts._debug(`${pName} failed: PK recovery failed`); | |
} | |
return { | |
executionGasUsed: gasUsed, | |
returnValue: new Uint8Array(0) | |
}; | |
} | |
const address = setLengthLeft(publicToAddress(publicKey), 32); | |
if (opts._debug !== void 0) { | |
opts._debug(`${pName} return address=${bytesToHex$1(address)}`); | |
} | |
return { | |
executionGasUsed: gasUsed, | |
returnValue: address | |
}; | |
} | |
function precompile02(opts) { | |
const pName = getPrecompileName("02"); | |
const data = opts.data; | |
const sha256Function = opts.common.customCrypto.sha256 ?? sha256; | |
let gasUsed = opts.common.param("sha256Gas"); | |
gasUsed += opts.common.param("sha256WordGas") * BigInt(Math.ceil(data.length / 32)); | |
if (!gasLimitCheck(opts, gasUsed, pName)) { | |
return OOGResult(opts.gasLimit); | |
} | |
const hash2 = sha256Function(data); | |
if (opts._debug !== void 0) { | |
opts._debug(`${pName} return hash=${bytesToHex$1(hash2)}`); | |
} | |
return { | |
executionGasUsed: gasUsed, | |
returnValue: hash2 | |
}; | |
} | |
const Rho = /* @__PURE__ */ new Uint8Array([7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8]); | |
const Id = /* @__PURE__ */ new Uint8Array(new Array(16).fill(0).map((_, i) => i)); | |
const Pi = /* @__PURE__ */ Id.map((i) => (9 * i + 5) % 16); | |
let idxL = [Id]; | |
let idxR = [Pi]; | |
for (let i = 0; i < 4; i++) | |
for (let j of [idxL, idxR]) | |
j.push(j[i].map((k) => Rho[k])); | |
const shifts = /* @__PURE__ */ [ | |
[11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8], | |
[12, 13, 11, 15, 6, 9, 9, 7, 12, 15, 11, 13, 7, 8, 7, 7], | |
[13, 15, 14, 11, 7, 7, 6, 8, 13, 14, 13, 12, 5, 5, 6, 9], | |
[14, 11, 12, 14, 8, 6, 5, 5, 15, 12, 15, 14, 9, 9, 8, 6], | |
[15, 12, 13, 13, 9, 5, 8, 6, 14, 11, 12, 11, 8, 6, 5, 5] | |
].map((i) => new Uint8Array(i)); | |
const shiftsL = /* @__PURE__ */ idxL.map((idx, i) => idx.map((j) => shifts[i][j])); | |
const shiftsR = /* @__PURE__ */ idxR.map((idx, i) => idx.map((j) => shifts[i][j])); | |
const Kl = /* @__PURE__ */ new Uint32Array([ | |
0, | |
1518500249, | |
1859775393, | |
2400959708, | |
2840853838 | |
]); | |
const Kr = /* @__PURE__ */ new Uint32Array([ | |
1352829926, | |
1548603684, | |
1836072691, | |
2053994217, | |
0 | |
]); | |
function f(group, x, y, z) { | |
if (group === 0) | |
return x ^ y ^ z; | |
else if (group === 1) | |
return x & y | ~x & z; | |
else if (group === 2) | |
return (x | ~y) ^ z; | |
else if (group === 3) | |
return x & z | y & ~z; | |
else | |
return x ^ (y | ~z); | |
} | |
const R_BUF = /* @__PURE__ */ new Uint32Array(16); | |
class RIPEMD160 extends HashMD { | |
constructor() { | |
super(64, 20, 8, true); | |
this.h0 = 1732584193 | 0; | |
this.h1 = 4023233417 | 0; | |
this.h2 = 2562383102 | 0; | |
this.h3 = 271733878 | 0; | |
this.h4 = 3285377520 | 0; | |
} | |
get() { | |
const { h0, h1, h2, h3, h4 } = this; | |
return [h0, h1, h2, h3, h4]; | |
} | |
set(h0, h1, h2, h3, h4) { | |
this.h0 = h0 | 0; | |
this.h1 = h1 | 0; | |
this.h2 = h2 | 0; | |
this.h3 = h3 | 0; | |
this.h4 = h4 | 0; | |
} | |
process(view, offset) { | |
for (let i = 0; i < 16; i++, offset += 4) | |
R_BUF[i] = view.getUint32(offset, true); | |
let al = this.h0 | 0, ar = al, bl = this.h1 | 0, br = bl, cl = this.h2 | 0, cr = cl, dl = this.h3 | 0, dr = dl, el = this.h4 | 0, er = el; | |
for (let group = 0; group < 5; group++) { | |
const rGroup = 4 - group; | |
const hbl = Kl[group], hbr = Kr[group]; | |
const rl = idxL[group], rr = idxR[group]; | |
const sl = shiftsL[group], sr = shiftsR[group]; | |
for (let i = 0; i < 16; i++) { | |
const tl = rotl(al + f(group, bl, cl, dl) + R_BUF[rl[i]] + hbl, sl[i]) + el | 0; | |
al = el, el = dl, dl = rotl(cl, 10) | 0, cl = bl, bl = tl; | |
} | |
for (let i = 0; i < 16; i++) { | |
const tr = rotl(ar + f(rGroup, br, cr, dr) + R_BUF[rr[i]] + hbr, sr[i]) + er | 0; | |
ar = er, er = dr, dr = rotl(cr, 10) | 0, cr = br, br = tr; | |
} | |
} | |
this.set(this.h1 + cl + dr | 0, this.h2 + dl + er | 0, this.h3 + el + ar | 0, this.h4 + al + br | 0, this.h0 + bl + cr | 0); | |
} | |
roundClean() { | |
R_BUF.fill(0); | |
} | |
destroy() { | |
this.destroyed = true; | |
this.buffer.fill(0); | |
this.set(0, 0, 0, 0, 0); | |
} | |
} | |
const ripemd160$1 = /* @__PURE__ */ wrapConstructor(() => new RIPEMD160()); | |
const ripemd160 = wrapHash(ripemd160$1); | |
function precompile03(opts) { | |
const pName = getPrecompileName("03"); | |
const data = opts.data; | |
let gasUsed = opts.common.param("ripemd160Gas"); | |
gasUsed += opts.common.param("ripemd160WordGas") * BigInt(Math.ceil(data.length / 32)); | |
if (!gasLimitCheck(opts, gasUsed, pName)) { | |
return OOGResult(opts.gasLimit); | |
} | |
const hash2 = setLengthLeft(ripemd160(data), 32); | |
if (opts._debug !== void 0) { | |
opts._debug(`${pName} return hash=${bytesToHex$1(hash2)}`); | |
} | |
return { | |
executionGasUsed: gasUsed, | |
returnValue: setLengthLeft(ripemd160(data), 32) | |
}; | |
} | |
function precompile04(opts) { | |
const pName = getPrecompileName("04"); | |
const data = opts.data; | |
let gasUsed = opts.common.param("identityGas"); | |
gasUsed += opts.common.param("identityWordGas") * BigInt(Math.ceil(data.length / 32)); | |
if (!gasLimitCheck(opts, gasUsed, pName)) { | |
return OOGResult(opts.gasLimit); | |
} | |
if (opts._debug !== void 0) { | |
opts._debug(`${pName} return data=${short(opts.data)}`); | |
} | |
return { | |
executionGasUsed: gasUsed, | |
returnValue: Uint8Array.from(data) | |
// Copy the memory (`Uint8Array.from()`) | |
}; | |
} | |
const BIGINT_4 = BigInt(4); | |
const BIGINT_16 = BigInt(16); | |
const BIGINT_200 = BigInt(200); | |
const BIGINT_480 = BigInt(480); | |
const BIGINT_1024 = BigInt(1024); | |
const BIGINT_3072 = BigInt(3072); | |
const BIGINT_199680 = BigInt(199680); | |
const maxInt = BigInt(Number.MAX_SAFE_INTEGER); | |
const maxSize = BigInt(2147483647); | |
function multiplicationComplexity(x) { | |
let fac1; | |
let fac2; | |
if (x <= BIGINT_64) { | |
return x ** BIGINT_2; | |
} else if (x <= BIGINT_1024) { | |
fac1 = x ** BIGINT_2 / BIGINT_4; | |
fac2 = x * BIGINT_96; | |
return fac1 + fac2 - BIGINT_3072; | |
} else { | |
fac1 = x ** BIGINT_2 / BIGINT_16; | |
fac2 = x * BIGINT_480; | |
return fac1 + fac2 - BIGINT_199680; | |
} | |
} | |
function multiplicationComplexityEIP2565(x) { | |
const words = (x + BIGINT_7) / BIGINT_8; | |
return words * words; | |
} | |
function getAdjustedExponentLength(data) { | |
let expBytesStart; | |
try { | |
const baseLen = bytesToBigInt(data.subarray(0, 32)); | |
expBytesStart = 96 + Number(baseLen); | |
} catch (e) { | |
expBytesStart = Number.MAX_SAFE_INTEGER - 32; | |
} | |
const expLen = bytesToBigInt(data.subarray(32, 64)); | |
let firstExpBytes = data.subarray(expBytesStart, expBytesStart + 32); | |
firstExpBytes = setLengthRight(firstExpBytes, 32); | |
let firstExpBigInt = bytesToBigInt(firstExpBytes); | |
let max32expLen = 0; | |
if (expLen < BIGINT_32) { | |
max32expLen = 32 - Number(expLen); | |
} | |
firstExpBigInt = firstExpBigInt >> BIGINT_8 * BigInt(Math.max(max32expLen, 0)); | |
let bitLen2 = -1; | |
while (firstExpBigInt > BIGINT_0) { | |
bitLen2 = bitLen2 + 1; | |
firstExpBigInt = firstExpBigInt >> BIGINT_1; | |
} | |
let expLenMinus32OrZero = expLen - BIGINT_32; | |
if (expLenMinus32OrZero < BIGINT_0) { | |
expLenMinus32OrZero = BIGINT_0; | |
} | |
const eightTimesExpLenMinus32OrZero = expLenMinus32OrZero * BIGINT_8; | |
let adjustedExpLen = eightTimesExpLenMinus32OrZero; | |
if (bitLen2 > 0) { | |
adjustedExpLen += BigInt(bitLen2); | |
} | |
return adjustedExpLen; | |
} | |
function expMod(a, power, modulo) { | |
if (power === BIGINT_0) { | |
return BIGINT_1 % modulo; | |
} | |
let res = BIGINT_1; | |
while (power > BIGINT_0) { | |
if (power & BIGINT_1) res = res * a % modulo; | |
a = a * a % modulo; | |
power >>= BIGINT_1; | |
} | |
return res; | |
} | |
function precompile05(opts) { | |
const pName = getPrecompileName("05"); | |
const data = opts.data.length < 96 ? setLengthRight(opts.data, 96) : opts.data; | |
let adjustedELen = getAdjustedExponentLength(data); | |
if (adjustedELen < BIGINT_1) { | |
adjustedELen = BIGINT_1; | |
} | |
const bLen = bytesToBigInt(data.subarray(0, 32)); | |
const eLen = bytesToBigInt(data.subarray(32, 64)); | |
const mLen = bytesToBigInt(data.subarray(64, 96)); | |
let maxLen = bLen; | |
if (maxLen < mLen) { | |
maxLen = mLen; | |
} | |
const Gquaddivisor = opts.common.param("modexpGquaddivisorGas"); | |
let gasUsed; | |
const bStart = BIGINT_96; | |
const bEnd = bStart + bLen; | |
const eStart = bEnd; | |
const eEnd = eStart + eLen; | |
const mStart = eEnd; | |
const mEnd = mStart + mLen; | |
if (!opts.common.isActivatedEIP(2565)) { | |
gasUsed = adjustedELen * multiplicationComplexity(maxLen) / Gquaddivisor; | |
} else { | |
gasUsed = adjustedELen * multiplicationComplexityEIP2565(maxLen) / Gquaddivisor; | |
if (gasUsed < BIGINT_200) { | |
gasUsed = BIGINT_200; | |
} | |
} | |
if (!gasLimitCheck(opts, gasUsed, pName)) { | |
return OOGResult(opts.gasLimit); | |
} | |
if (bLen === BIGINT_0 && mLen === BIGINT_0) { | |
return { | |
executionGasUsed: gasUsed, | |
returnValue: new Uint8Array() | |
}; | |
} | |
if (bLen > maxSize || eLen > maxSize || mLen > maxSize) { | |
if (opts._debug !== void 0) { | |
opts._debug(`${pName} failed: OOG`); | |
} | |
return OOGResult(opts.gasLimit); | |
} | |
if (mEnd > maxInt) { | |
if (opts._debug !== void 0) { | |
opts._debug(`${pName} failed: OOG`); | |
} | |
return OOGResult(opts.gasLimit); | |
} | |
const B = bytesToBigInt(setLengthRight(data.subarray(Number(bStart), Number(bEnd)), Number(bLen))); | |
const E = bytesToBigInt(setLengthRight(data.subarray(Number(eStart), Number(eEnd)), Number(eLen))); | |
const M = bytesToBigInt(setLengthRight(data.subarray(Number(mStart), Number(mEnd)), Number(mLen))); | |
let R2; | |
if (M === BIGINT_0) { | |
R2 = new Uint8Array(); | |
} else { | |
R2 = expMod(B, E, M); | |
if (R2 === BIGINT_0) { | |
R2 = new Uint8Array(); | |
} else { | |
R2 = bigIntToBytes(R2); | |
} | |
} | |
const res = setLengthLeft(R2, Number(mLen)); | |
if (opts._debug !== void 0) { | |
opts._debug(`${pName} return value=${bytesToHex$1(res)}`); | |
} | |
return { | |
executionGasUsed: gasUsed, | |
returnValue: res | |
}; | |
} | |
function precompile06(opts) { | |
const pName = getPrecompileName("06"); | |
const gasUsed = opts.common.param("bn254AddGas"); | |
if (!gasLimitCheck(opts, gasUsed, pName)) { | |
return OOGResult(opts.gasLimit); | |
} | |
const input = setLengthRight(opts.data.subarray(0, 128), 128); | |
let returnData; | |
try { | |
returnData = opts._EVM["_bn254"].add(input); | |
} catch (e) { | |
if (opts._debug !== void 0) { | |
opts._debug(`${pName} failed: ${e.message}`); | |
} | |
return EvmErrorResult(e, opts.gasLimit); | |
} | |
if (returnData.length !== 64) { | |
if (opts._debug !== void 0) { | |
opts._debug(`${pName} failed: OOG`); | |
} | |
return OOGResult(opts.gasLimit); | |
} | |
if (opts._debug !== void 0) { | |
opts._debug(`${pName} return value=${bytesToHex$1(returnData)}`); | |
} | |
return { | |
executionGasUsed: gasUsed, | |
returnValue: returnData | |
}; | |
} | |
function precompile07(opts) { | |
const pName = getPrecompileName("07"); | |
const gasUsed = opts.common.param("bn254MulGas"); | |
if (!gasLimitCheck(opts, gasUsed, pName)) { | |
return OOGResult(opts.gasLimit); | |
} | |
const input = setLengthRight(opts.data.subarray(0, 128), 128); | |
let returnData; | |
try { | |
returnData = opts._EVM["_bn254"].mul(input); | |
} catch (e) { | |
if (opts._debug !== void 0) { | |
opts._debug(`${pName} failed: ${e.message}`); | |
} | |
return EvmErrorResult(e, opts.gasLimit); | |
} | |
if (returnData.length !== 64) { | |
if (opts._debug !== void 0) { | |
opts._debug(`${pName} failed: OOG`); | |
} | |
return OOGResult(opts.gasLimit); | |
} | |
if (opts._debug !== void 0) { | |
opts._debug(`${pName} return value=${bytesToHex$1(returnData)}`); | |
} | |
return { | |
executionGasUsed: gasUsed, | |
returnValue: returnData | |
}; | |
} | |
function precompile08(opts) { | |
const pName = getPrecompileName("08"); | |
if (!moduloLengthCheck(opts, 192, pName)) { | |
return EvmErrorResult(new EvmError(ERROR.INVALID_INPUT_LENGTH), opts.gasLimit); | |
} | |
const inputDataSize = BigInt(Math.floor(opts.data.length / 192)); | |
const gasUsed = opts.common.param("bn254PairingGas") + inputDataSize * opts.common.param("bn254PairingWordGas"); | |
if (!gasLimitCheck(opts, gasUsed, pName)) { | |
return OOGResult(opts.gasLimit); | |
} | |
let returnData; | |
try { | |
returnData = opts._EVM["_bn254"].pairing(opts.data); | |
} catch (e) { | |
if (opts._debug !== void 0) { | |
opts._debug(`${pName} failed: ${e.message}`); | |
} | |
return EvmErrorResult(e, opts.gasLimit); | |
} | |
if (returnData.length !== 32) { | |
if (opts._debug !== void 0) { | |
opts._debug(`${pName} failed: OOG`); | |
} | |
return OOGResult(opts.gasLimit); | |
} | |
if (opts._debug !== void 0) { | |
opts._debug(`${pName} return value=${bytesToHex$1(returnData)}`); | |
} | |
return { | |
executionGasUsed: gasUsed, | |
returnValue: returnData | |
}; | |
} | |
function ADD64AA(v, a, b) { | |
const o0 = v[a] + v[b]; | |
let o1 = v[a + 1] + v[b + 1]; | |
if (o0 >= 4294967296) { | |
o1++; | |
} | |
v[a] = o0; | |
v[a + 1] = o1; | |
} | |
function ADD64AC(v, a, b0, b1) { | |
let o0 = v[a] + b0; | |
if (b0 < 0) { | |
o0 += 4294967296; | |
} | |
let o1 = v[a + 1] + b1; | |
if (o0 >= 4294967296) { | |
o1++; | |
} | |
v[a] = o0; | |
v[a + 1] = o1; | |
} | |
function B2B_G(v, mw, a, b, c, d, ix, iy) { | |
const x0 = mw[ix]; | |
const x1 = mw[ix + 1]; | |
const y0 = mw[iy]; | |
const y1 = mw[iy + 1]; | |
ADD64AA(v, a, b); | |
ADD64AC(v, a, x0, x1); | |
let xor0 = v[d] ^ v[a]; | |
let xor1 = v[d + 1] ^ v[a + 1]; | |
v[d] = xor1; | |
v[d + 1] = xor0; | |
ADD64AA(v, c, d); | |
xor0 = v[b] ^ v[c]; | |
xor1 = v[b + 1] ^ v[c + 1]; | |
v[b] = xor0 >>> 24 ^ xor1 << 8; | |
v[b + 1] = xor1 >>> 24 ^ xor0 << 8; | |
ADD64AA(v, a, b); | |
ADD64AC(v, a, y0, y1); | |
xor0 = v[d] ^ v[a]; | |
xor1 = v[d + 1] ^ v[a + 1]; | |
v[d] = xor0 >>> 16 ^ xor1 << 16; | |
v[d + 1] = xor1 >>> 16 ^ xor0 << 16; | |
ADD64AA(v, c, d); | |
xor0 = v[b] ^ v[c]; | |
xor1 = v[b + 1] ^ v[c + 1]; | |
v[b] = xor1 >>> 31 ^ xor0 << 1; | |
v[b + 1] = xor0 >>> 31 ^ xor1 << 1; | |
} | |
const BLAKE2B_IV32 = new Uint32Array([4089235720, 1779033703, 2227873595, 3144134277, 4271175723, 1013904242, 1595750129, 2773480762, 2917565137, 1359893119, 725511199, 2600822924, 4215389547, 528734635, 327033209, 1541459225]); | |
const SIGMA8 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3, 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4, 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8, 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13, 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9, 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11, 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10, 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5, 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3]; | |
const SIGMA82 = new Uint8Array( | |
SIGMA8.map(function(x) { | |
return x * 2; | |
}) | |
); | |
function F(h, m, t, f2, rounds) { | |
const v = new Uint32Array(32); | |
let i = 0; | |
for (i = 0; i < 16; i++) { | |
v[i] = h[i]; | |
v[i + 16] = BLAKE2B_IV32[i]; | |
} | |
v[24] = v[24] ^ t[0]; | |
v[25] = v[25] ^ t[1]; | |
v[26] = v[26] ^ t[2]; | |
v[27] = v[27] ^ t[3]; | |
if (f2) { | |
v[28] = ~v[28]; | |
v[29] = ~v[29]; | |
} | |
for (i = 0; i < rounds; i++) { | |
const ri = i % 10 * 16; | |
B2B_G(v, m, 0, 8, 16, 24, SIGMA82[ri + 0], SIGMA82[ri + 1]); | |
B2B_G(v, m, 2, 10, 18, 26, SIGMA82[ri + 2], SIGMA82[ri + 3]); | |
B2B_G(v, m, 4, 12, 20, 28, SIGMA82[ri + 4], SIGMA82[ri + 5]); | |
B2B_G(v, m, 6, 14, 22, 30, SIGMA82[ri + 6], SIGMA82[ri + 7]); | |
B2B_G(v, m, 0, 10, 20, 30, SIGMA82[ri + 8], SIGMA82[ri + 9]); | |
B2B_G(v, m, 2, 12, 22, 24, SIGMA82[ri + 10], SIGMA82[ri + 11]); | |
B2B_G(v, m, 4, 14, 16, 26, SIGMA82[ri + 12], SIGMA82[ri + 13]); | |
B2B_G(v, m, 6, 8, 18, 28, SIGMA82[ri + 14], SIGMA82[ri + 15]); | |
} | |
for (i = 0; i < 16; i++) { | |
h[i] = h[i] ^ v[i] ^ v[i + 16]; | |
} | |
} | |
function precompile09(opts) { | |
const pName = getPrecompileName("09"); | |
const data = opts.data; | |
if (data.length !== 213) { | |
if (opts._debug !== void 0) { | |
opts._debug(`${pName} failed: OUT_OF_RANGE dataLength=${data.length}`); | |
} | |
return { | |
returnValue: new Uint8Array(0), | |
executionGasUsed: opts.gasLimit, | |
exceptionError: new EvmError(ERROR.OUT_OF_RANGE) | |
}; | |
} | |
const lastByte = data.subarray(212, 213)[0]; | |
if (lastByte !== 1 && lastByte !== 0) { | |
if (opts._debug !== void 0) { | |
opts._debug(`${pName} failed: OUT_OF_RANGE lastByte=${lastByte}`); | |
} | |
return { | |
returnValue: new Uint8Array(0), | |
executionGasUsed: opts.gasLimit, | |
exceptionError: new EvmError(ERROR.OUT_OF_RANGE) | |
}; | |
} | |
const rounds = new DataView(data.buffer, data.byteOffset).getUint32(0); | |
const hRaw = new DataView(data.buffer, data.byteOffset + 4, 64); | |
const mRaw = new DataView(data.buffer, data.byteOffset + 68, 128); | |
const tRaw = new DataView(data.buffer, data.byteOffset + 196, 16); | |
const f2 = lastByte === 1; | |
let gasUsed = opts.common.param("blake2RoundGas"); | |
gasUsed *= BigInt(rounds); | |
if (!gasLimitCheck(opts, gasUsed, pName)) { | |
return OOGResult(opts.gasLimit); | |
} | |
const h = new Uint32Array(16); | |
for (let i = 0; i < 16; i++) { | |
h[i] = hRaw.getUint32(i * 4, true); | |
} | |
const m = new Uint32Array(32); | |
for (let i = 0; i < 32; i++) { | |
m[i] = mRaw.getUint32(i * 4, true); | |
} | |
const t = new Uint32Array(4); | |
for (let i = 0; i < 4; i++) { | |
t[i] = tRaw.getUint32(i * 4, true); | |
} | |
F(h, m, t, f2, rounds); | |
const output2 = new Uint8Array(64); | |
const outputView = new DataView(output2.buffer); | |
for (let i = 0; i < 16; i++) { | |
outputView.setUint32(i * 4, h[i], true); | |
} | |
if (opts._debug !== void 0) { | |
opts._debug(`${pName} return hash=${bytesToHex$1(output2)}`); | |
} | |
return { | |
executionGasUsed: gasUsed, | |
returnValue: output2 | |
}; | |
} | |
const BLS_MODULUS = BigInt( | |
"52435875175126190479447740508185965837690552500527637822603658699938581184513" | |
); | |
const modulusBuffer = setLengthLeft(bigIntToBytes(BLS_MODULUS), 32); | |
async function precompile0a(opts) { | |
var _a, _b, _c; | |
const pName = getPrecompileName("0a"); | |
if (((_a = opts.common.customCrypto) == null ? void 0 : _a.kzg) === void 0) { | |
throw new Error("kzg not initialized"); | |
} | |
const gasUsed = opts.common.param("kzgPointEvaluationPrecompileGas"); | |
if (!gasLimitCheck(opts, gasUsed, pName)) { | |
return OOGResult(opts.gasLimit); | |
} | |
if (opts.data.length !== 192) { | |
return EvmErrorResult(new EvmError(ERROR.INVALID_INPUT_LENGTH), opts.gasLimit); | |
} | |
const version = Number(opts.common.param("blobCommitmentVersionKzg")); | |
const fieldElementsPerBlob = opts.common.param("fieldElementsPerBlob"); | |
const versionedHash = opts.data.subarray(0, 32); | |
const z = opts.data.subarray(32, 64); | |
const y = opts.data.subarray(64, 96); | |
const commitment = opts.data.subarray(96, 144); | |
const kzgProof = opts.data.subarray(144, 192); | |
if (bytesToHex$1(computeVersionedHash(commitment, version)) !== bytesToHex$1(versionedHash)) { | |
if (opts._debug !== void 0) { | |
opts._debug(`${pName} failed: INVALID_COMMITMENT`); | |
} | |
return EvmErrorResult(new EvmError(ERROR.INVALID_COMMITMENT), opts.gasLimit); | |
} | |
if (opts._debug !== void 0) { | |
opts._debug( | |
`${pName}: proof verification with commitment=${bytesToHex$1( | |
commitment | |
)} z=${bytesToHex$1(z)} y=${bytesToHex$1(y)} kzgProof=${bytesToHex$1(kzgProof)}` | |
); | |
} | |
try { | |
const res = (_c = (_b = opts.common.customCrypto) == null ? void 0 : _b.kzg) == null ? void 0 : _c.verifyKzgProof(commitment, z, y, kzgProof); | |
if (res === false) { | |
return EvmErrorResult(new EvmError(ERROR.INVALID_PROOF), opts.gasLimit); | |
} | |
} catch (err) { | |
if (err.message.includes("C_KZG_BADARGS") === true) { | |
if (opts._debug !== void 0) { | |
opts._debug(`${pName} failed: INVALID_INPUTS`); | |
} | |
return EvmErrorResult(new EvmError(ERROR.INVALID_INPUTS), opts.gasLimit); | |
} | |
if (opts._debug !== void 0) { | |
opts._debug(`${pName} failed: Unknown error - ${err.message}`); | |
} | |
return EvmErrorResult(new EvmError(ERROR.REVERT), opts.gasLimit); | |
} | |
const fieldElementsBuffer = setLengthLeft(bigIntToBytes(fieldElementsPerBlob), 32); | |
if (opts._debug !== void 0) { | |
opts._debug( | |
`${pName} return fieldElements=${bytesToHex$1( | |
fieldElementsBuffer | |
)} modulus=${bytesToHex$1(modulusBuffer)}` | |
); | |
} | |
return { | |
executionGasUsed: gasUsed, | |
returnValue: concatBytes$1(fieldElementsBuffer, modulusBuffer) | |
}; | |
} | |
const BLS_FIELD_MODULUS = BigInt( | |
"0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab" | |
); | |
const BLS_G1_POINT_BYTE_LENGTH = 128; | |
const BLS_G2_POINT_BYTE_LENGTH = 256; | |
const BLS_G1_INFINITY_POINT_BYTES = new Uint8Array(BLS_G1_POINT_BYTE_LENGTH); | |
const BLS_G2_INFINITY_POINT_BYTES = new Uint8Array(BLS_G2_POINT_BYTE_LENGTH); | |
const BLS_ZERO_BUFFER = new Uint8Array(32); | |
const BLS_ONE_BUFFER = concatBytes$1(new Uint8Array(31), hexToBytes$1("0x01")); | |
const BLS_GAS_DISCOUNT_PAIRS = [ | |
[1, 1200], | |
[2, 888], | |
[3, 764], | |
[4, 641], | |
[5, 594], | |
[6, 547], | |
[7, 500], | |
[8, 453], | |
[9, 438], | |
[10, 423], | |
[11, 408], | |
[12, 394], | |
[13, 379], | |
[14, 364], | |
[15, 349], | |
[16, 334], | |
[17, 330], | |
[18, 326], | |
[19, 322], | |
[20, 318], | |
[21, 314], | |
[22, 310], | |
[23, 306], | |
[24, 302], | |
[25, 298], | |
[26, 294], | |
[27, 289], | |
[28, 285], | |
[29, 281], | |
[30, 277], | |
[31, 273], | |
[32, 269], | |
[33, 268], | |
[34, 266], | |
[35, 265], | |
[36, 263], | |
[37, 262], | |
[38, 260], | |
[39, 259], | |
[40, 257], | |
[41, 256], | |
[42, 254], | |
[43, 253], | |
[44, 251], | |
[45, 250], | |
[46, 248], | |
[47, 247], | |
[48, 245], | |
[49, 244], | |
[50, 242], | |
[51, 241], | |
[52, 239], | |
[53, 238], | |
[54, 236], | |
[55, 235], | |
[56, 233], | |
[57, 232], | |
[58, 231], | |
[59, 229], | |
[60, 228], | |
[61, 226], | |
[62, 225], | |
[63, 223], | |
[64, 222], | |
[65, 221], | |
[66, 220], | |
[67, 219], | |
[68, 219], | |
[69, 218], | |
[70, 217], | |
[71, 216], | |
[72, 216], | |
[73, 215], | |
[74, 214], | |
[75, 213], | |
[76, 213], | |
[77, 212], | |
[78, 211], | |
[79, 211], | |
[80, 210], | |
[81, 209], | |
[82, 208], | |
[83, 208], | |
[84, 207], | |
[85, 206], | |
[86, 205], | |
[87, 205], | |
[88, 204], | |
[89, 203], | |
[90, 202], | |
[91, 202], | |
[92, 201], | |
[93, 200], | |
[94, 199], | |
[95, 199], | |
[96, 198], | |
[97, 197], | |
[98, 196], | |
[99, 196], | |
[100, 195], | |
[101, 194], | |
[102, 193], | |
[103, 193], | |
[104, 192], | |
[105, 191], | |
[106, 191], | |
[107, 190], | |
[108, 189], | |
[109, 188], | |
[110, 188], | |
[111, 187], | |
[112, 186], | |
[113, 185], | |
[114, 185], | |
[115, 184], | |
[116, 183], | |
[117, 182], | |
[118, 182], | |
[119, 181], | |
[120, 180], | |
[121, 179], | |
[122, 179], | |
[123, 178], | |
[124, 177], | |
[125, 176], | |
[126, 176], | |
[127, 175], | |
[128, 174] | |
]; | |
function BLS12_381_ToG1Point$1(input, mcl, verifyOrder = true) { | |
if (equalsBytes(input, BLS_G1_INFINITY_POINT_BYTES)) { | |
return new mcl.G1(); | |
} | |
const p_x = bytesToUnprefixedHex(input.subarray(16, BLS_G1_POINT_BYTE_LENGTH / 2)); | |
const p_y = bytesToUnprefixedHex(input.subarray(80, BLS_G1_POINT_BYTE_LENGTH)); | |
const G1 = new mcl.G1(); | |
const Fp_X = new mcl.Fp(); | |
const Fp_Y = new mcl.Fp(); | |
const One = new mcl.Fp(); | |
Fp_X.setStr(p_x, 16); | |
Fp_Y.setStr(p_y, 16); | |
One.setStr("1", 16); | |
G1.setX(Fp_X); | |
G1.setY(Fp_Y); | |
G1.setZ(One); | |
mcl.verifyOrderG1(verifyOrder); | |
if (verifyOrder && G1.isValidOrder() === false) { | |
throw new EvmError(ERROR.BLS_12_381_POINT_NOT_ON_CURVE); | |
} | |
if (G1.isValid() === false) { | |
throw new EvmError(ERROR.BLS_12_381_POINT_NOT_ON_CURVE); | |
} | |
return G1; | |
} | |
function BLS12_381_FromG1Point$1(input) { | |
const decodeStr = input.getStr(16); | |
const decoded = decodeStr.match(/"?[0-9a-f]+"?/g); | |
if (decodeStr === "0") { | |
return new Uint8Array(BLS_G1_POINT_BYTE_LENGTH); | |
} | |
const xBytes = setLengthLeft(unprefixedHexToBytes(decoded[1]), 64); | |
const yBytes = setLengthLeft(unprefixedHexToBytes(decoded[2]), 64); | |
return concatBytes$1(xBytes, yBytes); | |
} | |
function BLS12_381_ToG2Point$1(input, mcl, verifyOrder = true) { | |
if (equalsBytes(input, BLS_G2_INFINITY_POINT_BYTES)) { | |
return new mcl.G2(); | |
} | |
const p_x_1 = input.subarray(0, 64); | |
const p_x_2 = input.subarray(64, BLS_G2_POINT_BYTE_LENGTH / 2); | |
const p_y_1 = input.subarray(128, 192); | |
const p_y_2 = input.subarray(192, BLS_G2_POINT_BYTE_LENGTH); | |
const Fp2X = BLS12_381_ToFp2Point$1(p_x_1, p_x_2, mcl); | |
const Fp2Y = BLS12_381_ToFp2Point$1(p_y_1, p_y_2, mcl); | |
const FpOne = new mcl.Fp(); | |
FpOne.setStr("1", 16); | |
const FpZero = new mcl.Fp(); | |
FpZero.setStr("0", 16); | |
const Fp2One = new mcl.Fp2(); | |
Fp2One.set_a(FpOne); | |
Fp2One.set_b(FpZero); | |
const p = new mcl.G2(); | |
p.setX(Fp2X); | |
p.setY(Fp2Y); | |
p.setZ(Fp2One); | |
mcl.verifyOrderG2(verifyOrder); | |
if (verifyOrder && p.isValidOrder() === false) { | |
throw new EvmError(ERROR.BLS_12_381_POINT_NOT_ON_CURVE); | |
} | |
if (p.isValid() === false) { | |
throw new EvmError(ERROR.BLS_12_381_POINT_NOT_ON_CURVE); | |
} | |
return p; | |
} | |
function BLS12_381_FromG2Point$1(input) { | |
const decodeStr = input.getStr(16); | |
if (decodeStr === "0") { | |
return new Uint8Array(BLS_G2_POINT_BYTE_LENGTH); | |
} | |
const decoded = decodeStr.match(/"?[0-9a-f]+"?/g); | |
const xBytes1 = setLengthLeft(unprefixedHexToBytes(decoded[1]), 64); | |
const xBytes2 = setLengthLeft(unprefixedHexToBytes(decoded[2]), 64); | |
const yBytes1 = setLengthLeft(unprefixedHexToBytes(decoded[3]), 64); | |
const yBytes2 = setLengthLeft(unprefixedHexToBytes(decoded[4]), 64); | |
return concatBytes$1(xBytes1, xBytes2, yBytes1, yBytes2); | |
} | |
function BLS12_381_ToFrPoint$1(input, mcl) { | |
const mclHex = mcl.fromHexStr(bytesToUnprefixedHex(input)); | |
const Fr2 = new mcl.Fr(); | |
Fr2.setBigEndianMod(mclHex); | |
return Fr2; | |
} | |
function BLS12_381_ToFpPoint$1(fpCoordinate, mcl) { | |
if (bytesToBigInt(fpCoordinate) >= BLS_FIELD_MODULUS) { | |
throw new EvmError(ERROR.BLS_12_381_FP_NOT_IN_FIELD); | |
} | |
const fp = new mcl.Fp(); | |
fp.setBigEndianMod(mcl.fromHexStr(bytesToUnprefixedHex(fpCoordinate))); | |
return fp; | |
} | |
function BLS12_381_ToFp2Point$1(fpXCoordinate, fpYCoordinate, mcl) { | |
if (bytesToBigInt(fpXCoordinate) >= BLS_FIELD_MODULUS) { | |
throw new EvmError(ERROR.BLS_12_381_FP_NOT_IN_FIELD); | |
} | |
if (bytesToBigInt(fpYCoordinate) >= BLS_FIELD_MODULUS) { | |
throw new EvmError(ERROR.BLS_12_381_FP_NOT_IN_FIELD); | |
} | |
const fp_x = new mcl.Fp(); | |
const fp_y = new mcl.Fp(); | |
const fp2 = new mcl.Fp2(); | |
fp_x.setStr(bytesToUnprefixedHex(fpXCoordinate.subarray(16)), 16); | |
fp_y.setStr(bytesToUnprefixedHex(fpYCoordinate.subarray(16)), 16); | |
fp2.set_a(fp_x); | |
fp2.set_b(fp_y); | |
return fp2; | |
} | |
class MCLBLS { | |
constructor(mcl) { | |
this._mcl = mcl; | |
} | |
init() { | |
this._mcl.setMapToMode(this._mcl.IRTF); | |
this._mcl.verifyOrderG1(true); | |
this._mcl.verifyOrderG2(true); | |
} | |
addG1(input) { | |
const p1 = BLS12_381_ToG1Point$1(input.subarray(0, BLS_G1_POINT_BYTE_LENGTH), this._mcl, false); | |
const p2 = BLS12_381_ToG1Point$1( | |
input.subarray(BLS_G1_POINT_BYTE_LENGTH, BLS_G1_POINT_BYTE_LENGTH * 2), | |
this._mcl, | |
false | |
); | |
const result = this._mcl.add(p1, p2); | |
return BLS12_381_FromG1Point$1(result); | |
} | |
mulG1(input) { | |
const p = BLS12_381_ToG1Point$1(input.subarray(0, BLS_G1_POINT_BYTE_LENGTH), this._mcl); | |
const frPoint = BLS12_381_ToFrPoint$1(input.subarray(BLS_G1_POINT_BYTE_LENGTH, 160), this._mcl); | |
const result = this._mcl.mul(p, frPoint); | |
return BLS12_381_FromG1Point$1(result); | |
} | |
addG2(input) { | |
const p1 = BLS12_381_ToG2Point$1(input.subarray(0, BLS_G2_POINT_BYTE_LENGTH), this._mcl, false); | |
const p2 = BLS12_381_ToG2Point$1( | |
input.subarray(BLS_G2_POINT_BYTE_LENGTH, BLS_G2_POINT_BYTE_LENGTH * 2), | |
this._mcl, | |
false | |
); | |
const result = this._mcl.add(p1, p2); | |
return BLS12_381_FromG2Point$1(result); | |
} | |
mulG2(input) { | |
const p = BLS12_381_ToG2Point$1(input.subarray(0, BLS_G2_POINT_BYTE_LENGTH), this._mcl); | |
const frPoint = BLS12_381_ToFrPoint$1(input.subarray(BLS_G2_POINT_BYTE_LENGTH, 288), this._mcl); | |
const result = this._mcl.mul(p, frPoint); | |
return BLS12_381_FromG2Point$1(result); | |
} | |
mapFPtoG1(input) { | |
const Fp1Point = BLS12_381_ToFpPoint$1(input.subarray(0, 64), this._mcl); | |
const result = Fp1Point.mapToG1(); | |
return BLS12_381_FromG1Point$1(result); | |
} | |
mapFP2toG2(input) { | |
const Fp2Point = BLS12_381_ToFp2Point$1(input.subarray(0, 64), input.subarray(64, 128), this._mcl); | |
const result = Fp2Point.mapToG2(); | |
return BLS12_381_FromG2Point$1(result); | |
} | |
msmG1(input) { | |
const pairLength = 160; | |
const numPairs = input.length / pairLength; | |
const G1Array = []; | |
const FrArray = []; | |
for (let k = 0; k < numPairs; k++) { | |
const pairStart = pairLength * k; | |
const G1 = BLS12_381_ToG1Point$1( | |
input.subarray(pairStart, pairStart + BLS_G1_POINT_BYTE_LENGTH), | |
this._mcl | |
); | |
const Fr2 = BLS12_381_ToFrPoint$1( | |
input.subarray(pairStart + BLS_G1_POINT_BYTE_LENGTH, pairStart + pairLength), | |
this._mcl | |
); | |
G1Array.push(G1); | |
FrArray.push(Fr2); | |
} | |
const result = this._mcl.mulVec(G1Array, FrArray); | |
return BLS12_381_FromG1Point$1(result); | |
} | |
msmG2(input) { | |
const pairLength = 288; | |
const numPairs = input.length / pairLength; | |
const G2Array = []; | |
const FrArray = []; | |
for (let k = 0; k < numPairs; k++) { | |
const pairStart = pairLength * k; | |
const G2 = BLS12_381_ToG2Point$1( | |
input.subarray(pairStart, pairStart + BLS_G2_POINT_BYTE_LENGTH), | |
this._mcl | |
); | |
const Fr2 = BLS12_381_ToFrPoint$1( | |
input.subarray(pairStart + BLS_G2_POINT_BYTE_LENGTH, pairStart + pairLength), | |
this._mcl | |
); | |
G2Array.push(G2); | |
FrArray.push(Fr2); | |
} | |
const result = this._mcl.mulVec(G2Array, FrArray); | |
return BLS12_381_FromG2Point$1(result); | |
} | |
pairingCheck(input) { | |
const pairLength = 384; | |
const pairs = []; | |
for (let k = 0; k < input.length / pairLength; k++) { | |
const pairStart = pairLength * k; | |
const G1 = BLS12_381_ToG1Point$1( | |
input.subarray(pairStart, pairStart + BLS_G1_POINT_BYTE_LENGTH), | |
this._mcl | |
); | |
const g2start = pairStart + BLS_G1_POINT_BYTE_LENGTH; | |
const G2 = BLS12_381_ToG2Point$1( | |
input.subarray(g2start, g2start + BLS_G2_POINT_BYTE_LENGTH), | |
this._mcl | |
); | |
pairs.push([G1, G2]); | |
} | |
let GT; | |
for (let index = 0; index < pairs.length; index++) { | |
const pair = pairs[index]; | |
const G1 = pair[0]; | |
const G2 = pair[1]; | |
if (index === 0) { | |
GT = this._mcl.millerLoop(G1, G2); | |
} else { | |
GT = this._mcl.mul(GT, this._mcl.millerLoop(G1, G2)); | |
} | |
} | |
GT = this._mcl.finalExp(GT); | |
if (GT.isOne() === true) { | |
return BLS_ONE_BUFFER; | |
} else { | |
return BLS_ZERO_BUFFER; | |
} | |
} | |
} | |
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | |
const _0n$2 = BigInt(0), _1n$3 = BigInt(1), _2n$3 = BigInt(2), _3n$3 = BigInt(3); | |
function NAfDecomposition(a) { | |
const res = []; | |
for (; a > _1n$3; a >>= _1n$3) { | |
if ((a & _1n$3) === _0n$2) | |
res.unshift(0); | |
else if ((a & _3n$3) === _3n$3) { | |
res.unshift(-1); | |
a += _1n$3; | |
} else | |
res.unshift(1); | |
} | |
return res; | |
} | |
function bls(CURVE) { | |
const { Fp: Fp3, Fr: Fr2, Fp2: Fp22, Fp6: Fp62, Fp12: Fp122 } = CURVE.fields; | |
const BLS_X_IS_NEGATIVE = CURVE.params.xNegative; | |
const TWIST = CURVE.params.twistType; | |
const G1_ = weierstrassPoints({ n: Fr2.ORDER, ...CURVE.G1 }); | |
const G1 = Object.assign(G1_, createHasher(G1_.ProjectivePoint, CURVE.G1.mapToCurve, { | |
...CURVE.htfDefaults, | |
...CURVE.G1.htfDefaults | |
})); | |
const G2_ = weierstrassPoints({ n: Fr2.ORDER, ...CURVE.G2 }); | |
const G2 = Object.assign(G2_, createHasher(G2_.ProjectivePoint, CURVE.G2.mapToCurve, { | |
...CURVE.htfDefaults, | |
...CURVE.G2.htfDefaults | |
})); | |
let lineFunction; | |
if (TWIST === "multiplicative") { | |
lineFunction = (c0, c1, c2, f2, Px, Py) => Fp122.mul014(f2, c0, Fp22.mul(c1, Px), Fp22.mul(c2, Py)); | |
} else if (TWIST === "divisive") { | |
lineFunction = (c0, c1, c2, f2, Px, Py) => Fp122.mul034(f2, Fp22.mul(c2, Py), Fp22.mul(c1, Px), c0); | |
} else | |
throw new Error("bls: unknown twist type"); | |
const Fp2div2 = Fp22.div(Fp22.ONE, Fp22.mul(Fp22.ONE, _2n$3)); | |
function pointDouble(ell, Rx, Ry, Rz) { | |
const t0 = Fp22.sqr(Ry); | |
const t1 = Fp22.sqr(Rz); | |
const t2 = Fp22.mulByB(Fp22.mul(t1, _3n$3)); | |
const t3 = Fp22.mul(t2, _3n$3); | |
const t4 = Fp22.sub(Fp22.sub(Fp22.sqr(Fp22.add(Ry, Rz)), t1), t0); | |
const c0 = Fp22.sub(t2, t0); | |
const c1 = Fp22.mul(Fp22.sqr(Rx), _3n$3); | |
const c2 = Fp22.neg(t4); | |
ell.push([c0, c1, c2]); | |
Rx = Fp22.mul(Fp22.mul(Fp22.mul(Fp22.sub(t0, t3), Rx), Ry), Fp2div2); | |
Ry = Fp22.sub(Fp22.sqr(Fp22.mul(Fp22.add(t0, t3), Fp2div2)), Fp22.mul(Fp22.sqr(t2), _3n$3)); | |
Rz = Fp22.mul(t0, t4); | |
return { Rx, Ry, Rz }; | |
} | |
function pointAdd(ell, Rx, Ry, Rz, Qx, Qy) { | |
const t0 = Fp22.sub(Ry, Fp22.mul(Qy, Rz)); | |
const t1 = Fp22.sub(Rx, Fp22.mul(Qx, Rz)); | |
const c0 = Fp22.sub(Fp22.mul(t0, Qx), Fp22.mul(t1, Qy)); | |
const c1 = Fp22.neg(t0); | |
const c2 = t1; | |
ell.push([c0, c1, c2]); | |
const t2 = Fp22.sqr(t1); | |
const t3 = Fp22.mul(t2, t1); | |
const t4 = Fp22.mul(t2, Rx); | |
const t5 = Fp22.add(Fp22.sub(t3, Fp22.mul(t4, _2n$3)), Fp22.mul(Fp22.sqr(t0), Rz)); | |
Rx = Fp22.mul(t1, t5); | |
Ry = Fp22.sub(Fp22.mul(Fp22.sub(t4, t5), t0), Fp22.mul(t3, Ry)); | |
Rz = Fp22.mul(Rz, t3); | |
return { Rx, Ry, Rz }; | |
} | |
const ATE_NAF = NAfDecomposition(CURVE.params.ateLoopSize); | |
const calcPairingPrecomputes = memoized((point) => { | |
const p = point; | |
const { x, y } = p.toAffine(); | |
const Qx = x, Qy = y, negQy = Fp22.neg(y); | |
let Rx = Qx, Ry = Qy, Rz = Fp22.ONE; | |
const ell = []; | |
for (const bit of ATE_NAF) { | |
const cur = []; | |
({ Rx, Ry, Rz } = pointDouble(cur, Rx, Ry, Rz)); | |
if (bit) | |
({ Rx, Ry, Rz } = pointAdd(cur, Rx, Ry, Rz, Qx, bit === -1 ? negQy : Qy)); | |
ell.push(cur); | |
} | |
if (CURVE.postPrecompute) { | |
const last = ell[ell.length - 1]; | |
CURVE.postPrecompute(Rx, Ry, Rz, Qx, Qy, pointAdd.bind(null, last)); | |
} | |
return ell; | |
}); | |
function millerLoopBatch(pairs, withFinalExponent = false) { | |
let f12 = Fp122.ONE; | |
if (pairs.length) { | |
const ellLen = pairs[0][0].length; | |
for (let i = 0; i < ellLen; i++) { | |
f12 = Fp122.sqr(f12); | |
for (const [ell, Px, Py] of pairs) { | |
for (const [c0, c1, c2] of ell[i]) | |
f12 = lineFunction(c0, c1, c2, f12, Px, Py); | |
} | |
} | |
} | |
if (BLS_X_IS_NEGATIVE) | |
f12 = Fp122.conjugate(f12); | |
return withFinalExponent ? Fp122.finalExponentiate(f12) : f12; | |
} | |
function pairingBatch(pairs, withFinalExponent = true) { | |
const res = []; | |
G1.ProjectivePoint.normalizeZ(pairs.map(({ g1 }) => g1)); | |
G2.ProjectivePoint.normalizeZ(pairs.map(({ g2 }) => g2)); | |
for (const { g1, g2 } of pairs) { | |
if (g1.equals(G1.ProjectivePoint.ZERO) || g2.equals(G2.ProjectivePoint.ZERO)) | |
throw new Error("pairing is not available for ZERO point"); | |
g1.assertValidity(); | |
g2.assertValidity(); | |
const Qa = g1.toAffine(); | |
res.push([calcPairingPrecomputes(g2), Qa.x, Qa.y]); | |
} | |
return millerLoopBatch(res, withFinalExponent); | |
} | |
function pairing(Q, P, withFinalExponent = true) { | |
return pairingBatch([{ g1: Q, g2: P }], withFinalExponent); | |
} | |
const utils = { | |
randomPrivateKey: () => { | |
const length = getMinHashLength(Fr2.ORDER); | |
return mapHashToField(CURVE.randomBytes(length), Fr2.ORDER); | |
}, | |
calcPairingPrecomputes | |
}; | |
const { ShortSignature } = CURVE.G1; | |
const { Signature } = CURVE.G2; | |
function normP1(point) { | |
return point instanceof G1.ProjectivePoint ? point : G1.ProjectivePoint.fromHex(point); | |
} | |
function normP1Hash(point, htfOpts) { | |
return point instanceof G1.ProjectivePoint ? point : G1.hashToCurve(ensureBytes("point", point), htfOpts); | |
} | |
function normP2(point) { | |
return point instanceof G2.ProjectivePoint ? point : Signature.fromHex(point); | |
} | |
function normP2Hash(point, htfOpts) { | |
return point instanceof G2.ProjectivePoint ? point : G2.hashToCurve(ensureBytes("point", point), htfOpts); | |
} | |
function getPublicKey(privateKey) { | |
return G1.ProjectivePoint.fromPrivateKey(privateKey).toRawBytes(true); | |
} | |
function getPublicKeyForShortSignatures(privateKey) { | |
return G2.ProjectivePoint.fromPrivateKey(privateKey).toRawBytes(true); | |
} | |
function sign(message, privateKey, htfOpts) { | |
const msgPoint = normP2Hash(message, htfOpts); | |
msgPoint.assertValidity(); | |
const sigPoint = msgPoint.multiply(G1.normPrivateKeyToScalar(privateKey)); | |
if (message instanceof G2.ProjectivePoint) | |
return sigPoint; | |
return Signature.toRawBytes(sigPoint); | |
} | |
function signShortSignature(message, privateKey, htfOpts) { | |
const msgPoint = normP1Hash(message, htfOpts); | |
msgPoint.assertValidity(); | |
const sigPoint = msgPoint.multiply(G1.normPrivateKeyToScalar(privateKey)); | |
if (message instanceof G1.ProjectivePoint) | |
return sigPoint; | |
return ShortSignature.toRawBytes(sigPoint); | |
} | |
function verify(signature, message, publicKey, htfOpts) { | |
const P = normP1(publicKey); | |
const Hm = normP2Hash(message, htfOpts); | |
const G = G1.ProjectivePoint.BASE; | |
const S = normP2(signature); | |
const exp = pairingBatch([ | |
{ g1: P.negate(), g2: Hm }, | |
// ePHM = pairing(P.negate(), Hm, false); | |
{ g1: G, g2: S } | |
// eGS = pairing(G, S, false); | |
]); | |
return Fp122.eql(exp, Fp122.ONE); | |
} | |
function verifyShortSignature(signature, message, publicKey, htfOpts) { | |
const P = normP2(publicKey); | |
const Hm = normP1Hash(message, htfOpts); | |
const G = G2.ProjectivePoint.BASE; | |
const S = normP1(signature); | |
const exp = pairingBatch([ | |
{ g1: Hm, g2: P }, | |
// eHmP = pairing(Hm, P, false); | |
{ g1: S, g2: G.negate() } | |
// eSG = pairing(S, G.negate(), false); | |
]); | |
return Fp122.eql(exp, Fp122.ONE); | |
} | |
function aggregatePublicKeys(publicKeys) { | |
if (!publicKeys.length) | |
throw new Error("Expected non-empty array"); | |
const agg = publicKeys.map(normP1).reduce((sum, p) => sum.add(p), G1.ProjectivePoint.ZERO); | |
const aggAffine = agg; | |
if (publicKeys[0] instanceof G1.ProjectivePoint) { | |
aggAffine.assertValidity(); | |
return aggAffine; | |
} | |
return aggAffine.toRawBytes(true); | |
} | |
function aggregateSignatures(signatures) { | |
if (!signatures.length) | |
throw new Error("Expected non-empty array"); | |
const agg = signatures.map(normP2).reduce((sum, s) => sum.add(s), G2.ProjectivePoint.ZERO); | |
const aggAffine = agg; | |
if (signatures[0] instanceof G2.ProjectivePoint) { | |
aggAffine.assertValidity(); | |
return aggAffine; | |
} | |
return Signature.toRawBytes(aggAffine); | |
} | |
function aggregateShortSignatures(signatures) { | |
if (!signatures.length) | |
throw new Error("Expected non-empty array"); | |
const agg = signatures.map(normP1).reduce((sum, s) => sum.add(s), G1.ProjectivePoint.ZERO); | |
const aggAffine = agg; | |
if (signatures[0] instanceof G1.ProjectivePoint) { | |
aggAffine.assertValidity(); | |
return aggAffine; | |
} | |
return ShortSignature.toRawBytes(aggAffine); | |
} | |
function verifyBatch(signature, messages, publicKeys, htfOpts) { | |
if (!messages.length) | |
throw new Error("Expected non-empty messages array"); | |
if (publicKeys.length !== messages.length) | |
throw new Error("Pubkey count should equal msg count"); | |
const sig = normP2(signature); | |
const nMessages = messages.map((i) => normP2Hash(i, htfOpts)); | |
const nPublicKeys = publicKeys.map(normP1); | |
const messagePubKeyMap = /* @__PURE__ */ new Map(); | |
for (let i = 0; i < nPublicKeys.length; i++) { | |
const pub = nPublicKeys[i]; | |
const msg = nMessages[i]; | |
let keys = messagePubKeyMap.get(msg); | |
if (keys === void 0) { | |
keys = []; | |
messagePubKeyMap.set(msg, keys); | |
} | |
keys.push(pub); | |
} | |
const paired = []; | |
try { | |
for (const [msg, keys] of messagePubKeyMap) { | |
const groupPublicKey = keys.reduce((acc, msg2) => acc.add(msg2)); | |
paired.push({ g1: groupPublicKey, g2: msg }); | |
} | |
paired.push({ g1: G1.ProjectivePoint.BASE.negate(), g2: sig }); | |
return Fp122.eql(pairingBatch(paired), Fp122.ONE); | |
} catch { | |
return false; | |
} | |
} | |
G1.ProjectivePoint.BASE._setWindowSize(4); | |
return { | |
getPublicKey, | |
getPublicKeyForShortSignatures, | |
sign, | |
signShortSignature, | |
verify, | |
verifyBatch, | |
verifyShortSignature, | |
aggregatePublicKeys, | |
aggregateSignatures, | |
aggregateShortSignatures, | |
millerLoopBatch, | |
pairing, | |
pairingBatch, | |
G1, | |
G2, | |
Signature, | |
ShortSignature, | |
fields: { | |
Fr: Fr2, | |
Fp: Fp3, | |
Fp2: Fp22, | |
Fp6: Fp62, | |
Fp12: Fp122 | |
}, | |
params: { | |
ateLoopSize: CURVE.params.ateLoopSize, | |
r: CURVE.params.r, | |
G1b: CURVE.G1.b, | |
G2b: CURVE.G2.b | |
}, | |
utils | |
}; | |
} | |
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | |
const _0n$1 = BigInt(0), _1n$2 = BigInt(1), _2n$2 = BigInt(2), _3n$2 = BigInt(3); | |
function calcFrobeniusCoefficients(Fp3, nonResidue, modulus, degree, num = 1, divisor) { | |
const _divisor = BigInt(divisor === void 0 ? degree : divisor); | |
const towerModulus = modulus ** BigInt(degree); | |
const res = []; | |
for (let i = 0; i < num; i++) { | |
const a = BigInt(i + 1); | |
const powers = []; | |
for (let j = 0, qPower = _1n$2; j < degree; j++) { | |
const power = (a * qPower - a) / _divisor % towerModulus; | |
powers.push(Fp3.pow(nonResidue, power)); | |
qPower *= modulus; | |
} | |
res.push(powers); | |
} | |
return res; | |
} | |
function psiFrobenius(Fp3, Fp22, base) { | |
const PSI_X = Fp22.pow(base, (Fp3.ORDER - _1n$2) / _3n$2); | |
const PSI_Y = Fp22.pow(base, (Fp3.ORDER - _1n$2) / _2n$2); | |
function psi2(x, y) { | |
const x2 = Fp22.mul(Fp22.frobeniusMap(x, 1), PSI_X); | |
const y2 = Fp22.mul(Fp22.frobeniusMap(y, 1), PSI_Y); | |
return [x2, y2]; | |
} | |
const PSI2_X = Fp22.pow(base, (Fp3.ORDER ** _2n$2 - _1n$2) / _3n$2); | |
const PSI2_Y = Fp22.pow(base, (Fp3.ORDER ** _2n$2 - _1n$2) / _2n$2); | |
if (!Fp22.eql(PSI2_Y, Fp22.neg(Fp22.ONE))) | |
throw new Error("psiFrobenius: PSI2_Y!==-1"); | |
function psi22(x, y) { | |
return [Fp22.mul(x, PSI2_X), Fp22.neg(y)]; | |
} | |
const mapAffine = (fn) => (c, P) => { | |
const affine = P.toAffine(); | |
const p = fn(affine.x, affine.y); | |
return c.fromAffine({ x: p[0], y: p[1] }); | |
}; | |
const G2psi3 = mapAffine(psi2); | |
const G2psi22 = mapAffine(psi22); | |
return { psi: psi2, psi2: psi22, G2psi: G2psi3, G2psi2: G2psi22, PSI_X, PSI_Y, PSI2_X, PSI2_Y }; | |
} | |
function tower12(opts) { | |
const { ORDER } = opts; | |
const Fp3 = Field(ORDER); | |
const FpNONRESIDUE = Fp3.create(opts.NONRESIDUE || BigInt(-1)); | |
const FpLegendre$1 = FpLegendre(ORDER); | |
const Fpdiv2 = Fp3.div(Fp3.ONE, _2n$2); | |
const FP2_FROBENIUS_COEFFICIENTS = calcFrobeniusCoefficients(Fp3, FpNONRESIDUE, Fp3.ORDER, 2)[0]; | |
const Fp2Add = ({ c0, c1 }, { c0: r0, c1: r1 }) => ({ | |
c0: Fp3.add(c0, r0), | |
c1: Fp3.add(c1, r1) | |
}); | |
const Fp2Subtract = ({ c0, c1 }, { c0: r0, c1: r1 }) => ({ | |
c0: Fp3.sub(c0, r0), | |
c1: Fp3.sub(c1, r1) | |
}); | |
const Fp2Multiply = ({ c0, c1 }, rhs) => { | |
if (typeof rhs === "bigint") | |
return { c0: Fp3.mul(c0, rhs), c1: Fp3.mul(c1, rhs) }; | |
const { c0: r0, c1: r1 } = rhs; | |
let t1 = Fp3.mul(c0, r0); | |
let t2 = Fp3.mul(c1, r1); | |
const o0 = Fp3.sub(t1, t2); | |
const o1 = Fp3.sub(Fp3.mul(Fp3.add(c0, c1), Fp3.add(r0, r1)), Fp3.add(t1, t2)); | |
return { c0: o0, c1: o1 }; | |
}; | |
const Fp2Square = ({ c0, c1 }) => { | |
const a = Fp3.add(c0, c1); | |
const b = Fp3.sub(c0, c1); | |
const c = Fp3.add(c0, c0); | |
return { c0: Fp3.mul(a, b), c1: Fp3.mul(c, c1) }; | |
}; | |
const Fp2fromBigTuple = (tuple) => { | |
if (tuple.length !== 2) | |
throw new Error("Invalid tuple"); | |
const fps = tuple.map((n) => Fp3.create(n)); | |
return { c0: fps[0], c1: fps[1] }; | |
}; | |
const FP2_ORDER = ORDER * ORDER; | |
const Fp2Nonresidue = Fp2fromBigTuple(opts.FP2_NONRESIDUE); | |
const Fp22 = { | |
ORDER: FP2_ORDER, | |
NONRESIDUE: Fp2Nonresidue, | |
BITS: bitLen(FP2_ORDER), | |
BYTES: Math.ceil(bitLen(FP2_ORDER) / 8), | |
MASK: bitMask(bitLen(FP2_ORDER)), | |
ZERO: { c0: Fp3.ZERO, c1: Fp3.ZERO }, | |
ONE: { c0: Fp3.ONE, c1: Fp3.ZERO }, | |
create: (num) => num, | |
isValid: ({ c0, c1 }) => typeof c0 === "bigint" && typeof c1 === "bigint", | |
is0: ({ c0, c1 }) => Fp3.is0(c0) && Fp3.is0(c1), | |
eql: ({ c0, c1 }, { c0: r0, c1: r1 }) => Fp3.eql(c0, r0) && Fp3.eql(c1, r1), | |
neg: ({ c0, c1 }) => ({ c0: Fp3.neg(c0), c1: Fp3.neg(c1) }), | |
pow: (num, power) => FpPow(Fp22, num, power), | |
invertBatch: (nums) => FpInvertBatch(Fp22, nums), | |
// Normalized | |
add: Fp2Add, | |
sub: Fp2Subtract, | |
mul: Fp2Multiply, | |
sqr: Fp2Square, | |
// NonNormalized stuff | |
addN: Fp2Add, | |
subN: Fp2Subtract, | |
mulN: Fp2Multiply, | |
sqrN: Fp2Square, | |
// Why inversion for bigint inside Fp instead of Fp2? it is even used in that context? | |
div: (lhs, rhs) => Fp22.mul(lhs, typeof rhs === "bigint" ? Fp3.inv(Fp3.create(rhs)) : Fp22.inv(rhs)), | |
inv: ({ c0: a, c1: b }) => { | |
const factor = Fp3.inv(Fp3.create(a * a + b * b)); | |
return { c0: Fp3.mul(factor, Fp3.create(a)), c1: Fp3.mul(factor, Fp3.create(-b)) }; | |
}, | |
sqrt: (num) => { | |
if (opts.Fp2sqrt) | |
return opts.Fp2sqrt(num); | |
const { c0, c1 } = num; | |
if (Fp3.is0(c1)) { | |
if (Fp3.eql(FpLegendre$1(Fp3, c0), Fp3.ONE)) | |
return Fp22.create({ c0: Fp3.sqrt(c0), c1: Fp3.ZERO }); | |
else | |
return Fp22.create({ c0: Fp3.ZERO, c1: Fp3.sqrt(Fp3.div(c0, FpNONRESIDUE)) }); | |
} | |
const a = Fp3.sqrt(Fp3.sub(Fp3.sqr(c0), Fp3.mul(Fp3.sqr(c1), FpNONRESIDUE))); | |
let d = Fp3.mul(Fp3.add(a, c0), Fpdiv2); | |
const legendre = FpLegendre$1(Fp3, d); | |
if (!Fp3.is0(legendre) && !Fp3.eql(legendre, Fp3.ONE)) | |
d = Fp3.sub(d, a); | |
const a0 = Fp3.sqrt(d); | |
const candidateSqrt = Fp22.create({ c0: a0, c1: Fp3.div(Fp3.mul(c1, Fpdiv2), a0) }); | |
if (!Fp22.eql(Fp22.sqr(candidateSqrt), num)) | |
throw new Error("Cannot find square root"); | |
const x1 = candidateSqrt; | |
const x2 = Fp22.neg(x1); | |
const { re: re1, im: im1 } = Fp22.reim(x1); | |
const { re: re2, im: im2 } = Fp22.reim(x2); | |
if (im1 > im2 || im1 === im2 && re1 > re2) | |
return x1; | |
return x2; | |
}, | |
// Same as sgn0_m_eq_2 in RFC 9380 | |
isOdd: (x) => { | |
const { re: x0, im: x1 } = Fp22.reim(x); | |
const sign_0 = x0 % _2n$2; | |
const zero_0 = x0 === _0n$1; | |
const sign_1 = x1 % _2n$2; | |
return BigInt(sign_0 || zero_0 && sign_1) == _1n$2; | |
}, | |
// Bytes util | |
fromBytes(b) { | |
if (b.length !== Fp22.BYTES) | |
throw new Error(`fromBytes wrong length=${b.length}`); | |
return { c0: Fp3.fromBytes(b.subarray(0, Fp3.BYTES)), c1: Fp3.fromBytes(b.subarray(Fp3.BYTES)) }; | |
}, | |
toBytes: ({ c0, c1 }) => concatBytes$2(Fp3.toBytes(c0), Fp3.toBytes(c1)), | |
cmov: ({ c0, c1 }, { c0: r0, c1: r1 }, c) => ({ | |
c0: Fp3.cmov(c0, r0, c), | |
c1: Fp3.cmov(c1, r1, c) | |
}), | |
reim: ({ c0, c1 }) => ({ re: c0, im: c1 }), | |
// multiply by u + 1 | |
mulByNonresidue: ({ c0, c1 }) => Fp22.mul({ c0, c1 }, Fp2Nonresidue), | |
mulByB: opts.Fp2mulByB, | |
fromBigTuple: Fp2fromBigTuple, | |
frobeniusMap: ({ c0, c1 }, power) => ({ | |
c0, | |
c1: Fp3.mul(c1, FP2_FROBENIUS_COEFFICIENTS[power % 2]) | |
}) | |
}; | |
const Fp6Add = ({ c0, c1, c2 }, { c0: r0, c1: r1, c2: r2 }) => ({ | |
c0: Fp22.add(c0, r0), | |
c1: Fp22.add(c1, r1), | |
c2: Fp22.add(c2, r2) | |
}); | |
const Fp6Subtract = ({ c0, c1, c2 }, { c0: r0, c1: r1, c2: r2 }) => ({ | |
c0: Fp22.sub(c0, r0), | |
c1: Fp22.sub(c1, r1), | |
c2: Fp22.sub(c2, r2) | |
}); | |
const Fp6Multiply = ({ c0, c1, c2 }, rhs) => { | |
if (typeof rhs === "bigint") { | |
return { | |
c0: Fp22.mul(c0, rhs), | |
c1: Fp22.mul(c1, rhs), | |
c2: Fp22.mul(c2, rhs) | |
}; | |
} | |
const { c0: r0, c1: r1, c2: r2 } = rhs; | |
const t0 = Fp22.mul(c0, r0); | |
const t1 = Fp22.mul(c1, r1); | |
const t2 = Fp22.mul(c2, r2); | |
return { | |
// t0 + (c1 + c2) * (r1 * r2) - (T1 + T2) * (u + 1) | |
c0: Fp22.add(t0, Fp22.mulByNonresidue(Fp22.sub(Fp22.mul(Fp22.add(c1, c2), Fp22.add(r1, r2)), Fp22.add(t1, t2)))), | |
// (c0 + c1) * (r0 + r1) - (T0 + T1) + T2 * (u + 1) | |
c1: Fp22.add(Fp22.sub(Fp22.mul(Fp22.add(c0, c1), Fp22.add(r0, r1)), Fp22.add(t0, t1)), Fp22.mulByNonresidue(t2)), | |
// T1 + (c0 + c2) * (r0 + r2) - T0 + T2 | |
c2: Fp22.sub(Fp22.add(t1, Fp22.mul(Fp22.add(c0, c2), Fp22.add(r0, r2))), Fp22.add(t0, t2)) | |
}; | |
}; | |
const Fp6Square = ({ c0, c1, c2 }) => { | |
let t0 = Fp22.sqr(c0); | |
let t1 = Fp22.mul(Fp22.mul(c0, c1), _2n$2); | |
let t3 = Fp22.mul(Fp22.mul(c1, c2), _2n$2); | |
let t4 = Fp22.sqr(c2); | |
return { | |
c0: Fp22.add(Fp22.mulByNonresidue(t3), t0), | |
// T3 * (u + 1) + T0 | |
c1: Fp22.add(Fp22.mulByNonresidue(t4), t1), | |
// T4 * (u + 1) + T1 | |
// T1 + (c0 - c1 + c2)² + T3 - T0 - T4 | |
c2: Fp22.sub(Fp22.sub(Fp22.add(Fp22.add(t1, Fp22.sqr(Fp22.add(Fp22.sub(c0, c1), c2))), t3), t0), t4) | |
}; | |
}; | |
const [FP6_FROBENIUS_COEFFICIENTS_1, FP6_FROBENIUS_COEFFICIENTS_2] = calcFrobeniusCoefficients(Fp22, Fp2Nonresidue, Fp3.ORDER, 6, 2, 3); | |
const Fp62 = { | |
ORDER: Fp22.ORDER, | |
// TODO: unused, but need to verify | |
BITS: 3 * Fp22.BITS, | |
BYTES: 3 * Fp22.BYTES, | |
MASK: bitMask(3 * Fp22.BITS), | |
ZERO: { c0: Fp22.ZERO, c1: Fp22.ZERO, c2: Fp22.ZERO }, | |
ONE: { c0: Fp22.ONE, c1: Fp22.ZERO, c2: Fp22.ZERO }, | |
create: (num) => num, | |
isValid: ({ c0, c1, c2 }) => Fp22.isValid(c0) && Fp22.isValid(c1) && Fp22.isValid(c2), | |
is0: ({ c0, c1, c2 }) => Fp22.is0(c0) && Fp22.is0(c1) && Fp22.is0(c2), | |
neg: ({ c0, c1, c2 }) => ({ c0: Fp22.neg(c0), c1: Fp22.neg(c1), c2: Fp22.neg(c2) }), | |
eql: ({ c0, c1, c2 }, { c0: r0, c1: r1, c2: r2 }) => Fp22.eql(c0, r0) && Fp22.eql(c1, r1) && Fp22.eql(c2, r2), | |
sqrt: notImplemented, | |
// Do we need division by bigint at all? Should be done via order: | |
div: (lhs, rhs) => Fp62.mul(lhs, typeof rhs === "bigint" ? Fp3.inv(Fp3.create(rhs)) : Fp62.inv(rhs)), | |
pow: (num, power) => FpPow(Fp62, num, power), | |
invertBatch: (nums) => FpInvertBatch(Fp62, nums), | |
// Normalized | |
add: Fp6Add, | |
sub: Fp6Subtract, | |
mul: Fp6Multiply, | |
sqr: Fp6Square, | |
// NonNormalized stuff | |
addN: Fp6Add, | |
subN: Fp6Subtract, | |
mulN: Fp6Multiply, | |
sqrN: Fp6Square, | |
inv: ({ c0, c1, c2 }) => { | |
let t0 = Fp22.sub(Fp22.sqr(c0), Fp22.mulByNonresidue(Fp22.mul(c2, c1))); | |
let t1 = Fp22.sub(Fp22.mulByNonresidue(Fp22.sqr(c2)), Fp22.mul(c0, c1)); | |
let t2 = Fp22.sub(Fp22.sqr(c1), Fp22.mul(c0, c2)); | |
let t4 = Fp22.inv(Fp22.add(Fp22.mulByNonresidue(Fp22.add(Fp22.mul(c2, t1), Fp22.mul(c1, t2))), Fp22.mul(c0, t0))); | |
return { c0: Fp22.mul(t4, t0), c1: Fp22.mul(t4, t1), c2: Fp22.mul(t4, t2) }; | |
}, | |
// Bytes utils | |
fromBytes: (b) => { | |
if (b.length !== Fp62.BYTES) | |
throw new Error(`fromBytes wrong length=${b.length}`); | |
return { | |
c0: Fp22.fromBytes(b.subarray(0, Fp22.BYTES)), | |
c1: Fp22.fromBytes(b.subarray(Fp22.BYTES, 2 * Fp22.BYTES)), | |
c2: Fp22.fromBytes(b.subarray(2 * Fp22.BYTES)) | |
}; | |
}, | |
toBytes: ({ c0, c1, c2 }) => concatBytes$2(Fp22.toBytes(c0), Fp22.toBytes(c1), Fp22.toBytes(c2)), | |
cmov: ({ c0, c1, c2 }, { c0: r0, c1: r1, c2: r2 }, c) => ({ | |
c0: Fp22.cmov(c0, r0, c), | |
c1: Fp22.cmov(c1, r1, c), | |
c2: Fp22.cmov(c2, r2, c) | |
}), | |
fromBigSix: (t) => { | |
if (!Array.isArray(t) || t.length !== 6) | |
throw new Error("Invalid Fp6 usage"); | |
return { | |
c0: Fp22.fromBigTuple(t.slice(0, 2)), | |
c1: Fp22.fromBigTuple(t.slice(2, 4)), | |
c2: Fp22.fromBigTuple(t.slice(4, 6)) | |
}; | |
}, | |
frobeniusMap: ({ c0, c1, c2 }, power) => ({ | |
c0: Fp22.frobeniusMap(c0, power), | |
c1: Fp22.mul(Fp22.frobeniusMap(c1, power), FP6_FROBENIUS_COEFFICIENTS_1[power % 6]), | |
c2: Fp22.mul(Fp22.frobeniusMap(c2, power), FP6_FROBENIUS_COEFFICIENTS_2[power % 6]) | |
}), | |
mulByFp2: ({ c0, c1, c2 }, rhs) => ({ | |
c0: Fp22.mul(c0, rhs), | |
c1: Fp22.mul(c1, rhs), | |
c2: Fp22.mul(c2, rhs) | |
}), | |
mulByNonresidue: ({ c0, c1, c2 }) => ({ c0: Fp22.mulByNonresidue(c2), c1: c0, c2: c1 }), | |
// Sparse multiplication | |
mul1: ({ c0, c1, c2 }, b1) => ({ | |
c0: Fp22.mulByNonresidue(Fp22.mul(c2, b1)), | |
c1: Fp22.mul(c0, b1), | |
c2: Fp22.mul(c1, b1) | |
}), | |
// Sparse multiplication | |
mul01({ c0, c1, c2 }, b0, b1) { | |
let t0 = Fp22.mul(c0, b0); | |
let t1 = Fp22.mul(c1, b1); | |
return { | |
// ((c1 + c2) * b1 - T1) * (u + 1) + T0 | |
c0: Fp22.add(Fp22.mulByNonresidue(Fp22.sub(Fp22.mul(Fp22.add(c1, c2), b1), t1)), t0), | |
// (b0 + b1) * (c0 + c1) - T0 - T1 | |
c1: Fp22.sub(Fp22.sub(Fp22.mul(Fp22.add(b0, b1), Fp22.add(c0, c1)), t0), t1), | |
// (c0 + c2) * b0 - T0 + T1 | |
c2: Fp22.add(Fp22.sub(Fp22.mul(Fp22.add(c0, c2), b0), t0), t1) | |
}; | |
} | |
}; | |
const FP12_FROBENIUS_COEFFICIENTS = calcFrobeniusCoefficients(Fp22, Fp2Nonresidue, Fp3.ORDER, 12, 1, 6)[0]; | |
const Fp12Add = ({ c0, c1 }, { c0: r0, c1: r1 }) => ({ | |
c0: Fp62.add(c0, r0), | |
c1: Fp62.add(c1, r1) | |
}); | |
const Fp12Subtract = ({ c0, c1 }, { c0: r0, c1: r1 }) => ({ | |
c0: Fp62.sub(c0, r0), | |
c1: Fp62.sub(c1, r1) | |
}); | |
const Fp12Multiply = ({ c0, c1 }, rhs) => { | |
if (typeof rhs === "bigint") | |
return { c0: Fp62.mul(c0, rhs), c1: Fp62.mul(c1, rhs) }; | |
let { c0: r0, c1: r1 } = rhs; | |
let t1 = Fp62.mul(c0, r0); | |
let t2 = Fp62.mul(c1, r1); | |
return { | |
c0: Fp62.add(t1, Fp62.mulByNonresidue(t2)), | |
// T1 + T2 * v | |
// (c0 + c1) * (r0 + r1) - (T1 + T2) | |
c1: Fp62.sub(Fp62.mul(Fp62.add(c0, c1), Fp62.add(r0, r1)), Fp62.add(t1, t2)) | |
}; | |
}; | |
const Fp12Square = ({ c0, c1 }) => { | |
let ab = Fp62.mul(c0, c1); | |
return { | |
// (c1 * v + c0) * (c0 + c1) - AB - AB * v | |
c0: Fp62.sub(Fp62.sub(Fp62.mul(Fp62.add(Fp62.mulByNonresidue(c1), c0), Fp62.add(c0, c1)), ab), Fp62.mulByNonresidue(ab)), | |
c1: Fp62.add(ab, ab) | |
}; | |
}; | |
function Fp4Square2(a, b) { | |
const a2 = Fp22.sqr(a); | |
const b2 = Fp22.sqr(b); | |
return { | |
first: Fp22.add(Fp22.mulByNonresidue(b2), a2), | |
// b² * Nonresidue + a² | |
second: Fp22.sub(Fp22.sub(Fp22.sqr(Fp22.add(a, b)), a2), b2) | |
// (a + b)² - a² - b² | |
}; | |
} | |
const Fp122 = { | |
ORDER: Fp22.ORDER, | |
// TODO: unused, but need to verify | |
BITS: 2 * Fp22.BITS, | |
BYTES: 2 * Fp22.BYTES, | |
MASK: bitMask(2 * Fp22.BITS), | |
ZERO: { c0: Fp62.ZERO, c1: Fp62.ZERO }, | |
ONE: { c0: Fp62.ONE, c1: Fp62.ZERO }, | |
create: (num) => num, | |
isValid: ({ c0, c1 }) => Fp62.isValid(c0) && Fp62.isValid(c1), | |
is0: ({ c0, c1 }) => Fp62.is0(c0) && Fp62.is0(c1), | |
neg: ({ c0, c1 }) => ({ c0: Fp62.neg(c0), c1: Fp62.neg(c1) }), | |
eql: ({ c0, c1 }, { c0: r0, c1: r1 }) => Fp62.eql(c0, r0) && Fp62.eql(c1, r1), | |
sqrt: notImplemented, | |
inv: ({ c0, c1 }) => { | |
let t = Fp62.inv(Fp62.sub(Fp62.sqr(c0), Fp62.mulByNonresidue(Fp62.sqr(c1)))); | |
return { c0: Fp62.mul(c0, t), c1: Fp62.neg(Fp62.mul(c1, t)) }; | |
}, | |
div: (lhs, rhs) => Fp122.mul(lhs, typeof rhs === "bigint" ? Fp3.inv(Fp3.create(rhs)) : Fp122.inv(rhs)), | |
pow: (num, power) => FpPow(Fp122, num, power), | |
invertBatch: (nums) => FpInvertBatch(Fp122, nums), | |
// Normalized | |
add: Fp12Add, | |
sub: Fp12Subtract, | |
mul: Fp12Multiply, | |
sqr: Fp12Square, | |
// NonNormalized stuff | |
addN: Fp12Add, | |
subN: Fp12Subtract, | |
mulN: Fp12Multiply, | |
sqrN: Fp12Square, | |
// Bytes utils | |
fromBytes: (b) => { | |
if (b.length !== Fp122.BYTES) | |
throw new Error(`fromBytes wrong length=${b.length}`); | |
return { | |
c0: Fp62.fromBytes(b.subarray(0, Fp62.BYTES)), | |
c1: Fp62.fromBytes(b.subarray(Fp62.BYTES)) | |
}; | |
}, | |
toBytes: ({ c0, c1 }) => concatBytes$2(Fp62.toBytes(c0), Fp62.toBytes(c1)), | |
cmov: ({ c0, c1 }, { c0: r0, c1: r1 }, c) => ({ | |
c0: Fp62.cmov(c0, r0, c), | |
c1: Fp62.cmov(c1, r1, c) | |
}), | |
// Utils | |
// toString() { | |
// return `Fp12(${this.c0} + ${this.c1} * w)`; | |
// }, | |
// fromTuple(c: [Fp6, Fp6]) { | |
// return new Fp12(...c); | |
// } | |
fromBigTwelve: (t) => ({ | |
c0: Fp62.fromBigSix(t.slice(0, 6)), | |
c1: Fp62.fromBigSix(t.slice(6, 12)) | |
}), | |
// Raises to q**i -th power | |
frobeniusMap(lhs, power) { | |
const { c0, c1, c2 } = Fp62.frobeniusMap(lhs.c1, power); | |
const coeff = FP12_FROBENIUS_COEFFICIENTS[power % 12]; | |
return { | |
c0: Fp62.frobeniusMap(lhs.c0, power), | |
c1: Fp62.create({ | |
c0: Fp22.mul(c0, coeff), | |
c1: Fp22.mul(c1, coeff), | |
c2: Fp22.mul(c2, coeff) | |
}) | |
}; | |
}, | |
mulByFp2: ({ c0, c1 }, rhs) => ({ | |
c0: Fp62.mulByFp2(c0, rhs), | |
c1: Fp62.mulByFp2(c1, rhs) | |
}), | |
conjugate: ({ c0, c1 }) => ({ c0, c1: Fp62.neg(c1) }), | |
// Sparse multiplication | |
mul014: ({ c0, c1 }, o0, o1, o4) => { | |
let t0 = Fp62.mul01(c0, o0, o1); | |
let t1 = Fp62.mul1(c1, o4); | |
return { | |
c0: Fp62.add(Fp62.mulByNonresidue(t1), t0), | |
// T1 * v + T0 | |
// (c1 + c0) * [o0, o1+o4] - T0 - T1 | |
c1: Fp62.sub(Fp62.sub(Fp62.mul01(Fp62.add(c1, c0), o0, Fp22.add(o1, o4)), t0), t1) | |
}; | |
}, | |
mul034: ({ c0, c1 }, o0, o3, o4) => { | |
const a = Fp62.create({ | |
c0: Fp22.mul(c0.c0, o0), | |
c1: Fp22.mul(c0.c1, o0), | |
c2: Fp22.mul(c0.c2, o0) | |
}); | |
const b = Fp62.mul01(c1, o3, o4); | |
const e = Fp62.mul01(Fp62.add(c0, c1), Fp22.add(o0, o3), o4); | |
return { | |
c0: Fp62.add(Fp62.mulByNonresidue(b), a), | |
c1: Fp62.sub(e, Fp62.add(a, b)) | |
}; | |
}, | |
// A cyclotomic group is a subgroup of Fp^n defined by | |
// GΦₙ(p) = {α ∈ Fpⁿ : α^Φₙ(p) = 1} | |
// The result of any pairing is in a cyclotomic subgroup | |
// https://eprint.iacr.org/2009/565.pdf | |
_cyclotomicSquare: opts.Fp12cyclotomicSquare, | |
_cyclotomicExp: opts.Fp12cyclotomicExp, | |
// https://eprint.iacr.org/2010/354.pdf | |
// https://eprint.iacr.org/2009/565.pdf | |
finalExponentiate: opts.Fp12finalExponentiate | |
}; | |
return { Fp: Fp3, Fp2: Fp22, Fp6: Fp62, Fp4Square: Fp4Square2, Fp12: Fp122 }; | |
} | |
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | |
const _0n = BigInt(0), _1n$1 = BigInt(1), _2n$1 = BigInt(2), _3n$1 = BigInt(3), _4n = BigInt(4); | |
const BLS_X = BigInt("0xd201000000010000"); | |
const BLS_X_LEN = bitLen(BLS_X); | |
const { Fp: Fp$1, Fp2: Fp2$1, Fp6: Fp6$1, Fp4Square: Fp4Square$1, Fp12: Fp12$1 } = tower12({ | |
// Order of Fp | |
ORDER: BigInt("0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab"), | |
// Finite extension field over irreducible polynominal. | |
// Fp(u) / (u² - β) where β = -1 | |
FP2_NONRESIDUE: [_1n$1, _1n$1], | |
Fp2mulByB: ({ c0, c1 }) => { | |
const t0 = Fp$1.mul(c0, _4n); | |
const t1 = Fp$1.mul(c1, _4n); | |
return { c0: Fp$1.sub(t0, t1), c1: Fp$1.add(t0, t1) }; | |
}, | |
// Fp12 | |
// A cyclotomic group is a subgroup of Fp^n defined by | |
// GΦₙ(p) = {α ∈ Fpⁿ : α^Φₙ(p) = 1} | |
// The result of any pairing is in a cyclotomic subgroup | |
// https://eprint.iacr.org/2009/565.pdf | |
Fp12cyclotomicSquare: ({ c0, c1 }) => { | |
const { c0: c0c0, c1: c0c1, c2: c0c2 } = c0; | |
const { c0: c1c0, c1: c1c1, c2: c1c2 } = c1; | |
const { first: t3, second: t4 } = Fp4Square$1(c0c0, c1c1); | |
const { first: t5, second: t6 } = Fp4Square$1(c1c0, c0c2); | |
const { first: t7, second: t8 } = Fp4Square$1(c0c1, c1c2); | |
const t9 = Fp2$1.mulByNonresidue(t8); | |
return { | |
c0: Fp6$1.create({ | |
c0: Fp2$1.add(Fp2$1.mul(Fp2$1.sub(t3, c0c0), _2n$1), t3), | |
// 2 * (T3 - c0c0) + T3 | |
c1: Fp2$1.add(Fp2$1.mul(Fp2$1.sub(t5, c0c1), _2n$1), t5), | |
// 2 * (T5 - c0c1) + T5 | |
c2: Fp2$1.add(Fp2$1.mul(Fp2$1.sub(t7, c0c2), _2n$1), t7) | |
}), | |
// 2 * (T7 - c0c2) + T7 | |
c1: Fp6$1.create({ | |
c0: Fp2$1.add(Fp2$1.mul(Fp2$1.add(t9, c1c0), _2n$1), t9), | |
// 2 * (T9 + c1c0) + T9 | |
c1: Fp2$1.add(Fp2$1.mul(Fp2$1.add(t4, c1c1), _2n$1), t4), | |
// 2 * (T4 + c1c1) + T4 | |
c2: Fp2$1.add(Fp2$1.mul(Fp2$1.add(t6, c1c2), _2n$1), t6) | |
}) | |
}; | |
}, | |
Fp12cyclotomicExp(num, n) { | |
let z = Fp12$1.ONE; | |
for (let i = BLS_X_LEN - 1; i >= 0; i--) { | |
z = Fp12$1._cyclotomicSquare(z); | |
if (bitGet(n, i)) | |
z = Fp12$1.mul(z, num); | |
} | |
return z; | |
}, | |
// https://eprint.iacr.org/2010/354.pdf | |
// https://eprint.iacr.org/2009/565.pdf | |
Fp12finalExponentiate: (num) => { | |
const x = BLS_X; | |
const t0 = Fp12$1.div(Fp12$1.frobeniusMap(num, 6), num); | |
const t1 = Fp12$1.mul(Fp12$1.frobeniusMap(t0, 2), t0); | |
const t2 = Fp12$1.conjugate(Fp12$1._cyclotomicExp(t1, x)); | |
const t3 = Fp12$1.mul(Fp12$1.conjugate(Fp12$1._cyclotomicSquare(t1)), t2); | |
const t4 = Fp12$1.conjugate(Fp12$1._cyclotomicExp(t3, x)); | |
const t5 = Fp12$1.conjugate(Fp12$1._cyclotomicExp(t4, x)); | |
const t6 = Fp12$1.mul(Fp12$1.conjugate(Fp12$1._cyclotomicExp(t5, x)), Fp12$1._cyclotomicSquare(t2)); | |
const t7 = Fp12$1.conjugate(Fp12$1._cyclotomicExp(t6, x)); | |
const t2_t5_pow_q2 = Fp12$1.frobeniusMap(Fp12$1.mul(t2, t5), 2); | |
const t4_t1_pow_q3 = Fp12$1.frobeniusMap(Fp12$1.mul(t4, t1), 3); | |
const t6_t1c_pow_q1 = Fp12$1.frobeniusMap(Fp12$1.mul(t6, Fp12$1.conjugate(t1)), 1); | |
const t7_t3c_t1 = Fp12$1.mul(Fp12$1.mul(t7, Fp12$1.conjugate(t3)), t1); | |
return Fp12$1.mul(Fp12$1.mul(Fp12$1.mul(t2_t5_pow_q2, t4_t1_pow_q3), t6_t1c_pow_q1), t7_t3c_t1); | |
} | |
}); | |
const Fr$1 = Field(BigInt("0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001")); | |
const isogenyMapG2 = isogenyMap(Fp2$1, [ | |
// xNum | |
[ | |
[ | |
"0x5c759507e8e333ebb5b7a9a47d7ed8532c52d39fd3a042a88b58423c50ae15d5c2638e343d9c71c6238aaaaaaaa97d6", | |
"0x5c759507e8e333ebb5b7a9a47d7ed8532c52d39fd3a042a88b58423c50ae15d5c2638e343d9c71c6238aaaaaaaa97d6" | |
], | |
[ | |
"0x0", | |
"0x11560bf17baa99bc32126fced787c88f984f87adf7ae0c7f9a208c6b4f20a4181472aaa9cb8d555526a9ffffffffc71a" | |
], | |
[ | |
"0x11560bf17baa99bc32126fced787c88f984f87adf7ae0c7f9a208c6b4f20a4181472aaa9cb8d555526a9ffffffffc71e", | |
"0x8ab05f8bdd54cde190937e76bc3e447cc27c3d6fbd7063fcd104635a790520c0a395554e5c6aaaa9354ffffffffe38d" | |
], | |
[ | |
"0x171d6541fa38ccfaed6dea691f5fb614cb14b4e7f4e810aa22d6108f142b85757098e38d0f671c7188e2aaaaaaaa5ed1", | |
"0x0" | |
] | |
], | |
// xDen | |
[ | |
[ | |
"0x0", | |
"0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaa63" | |
], | |
[ | |
"0xc", | |
"0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaa9f" | |
], | |
["0x1", "0x0"] | |
// LAST 1 | |
], | |
// yNum | |
[ | |
[ | |
"0x1530477c7ab4113b59a4c18b076d11930f7da5d4a07f649bf54439d87d27e500fc8c25ebf8c92f6812cfc71c71c6d706", | |
"0x1530477c7ab4113b59a4c18b076d11930f7da5d4a07f649bf54439d87d27e500fc8c25ebf8c92f6812cfc71c71c6d706" | |
], | |
[ | |
"0x0", | |
"0x5c759507e8e333ebb5b7a9a47d7ed8532c52d39fd3a042a88b58423c50ae15d5c2638e343d9c71c6238aaaaaaaa97be" | |
], | |
[ | |
"0x11560bf17baa99bc32126fced787c88f984f87adf7ae0c7f9a208c6b4f20a4181472aaa9cb8d555526a9ffffffffc71c", | |
"0x8ab05f8bdd54cde190937e76bc3e447cc27c3d6fbd7063fcd104635a790520c0a395554e5c6aaaa9354ffffffffe38f" | |
], | |
[ | |
"0x124c9ad43b6cf79bfbf7043de3811ad0761b0f37a1e26286b0e977c69aa274524e79097a56dc4bd9e1b371c71c718b10", | |
"0x0" | |
] | |
], | |
// yDen | |
[ | |
[ | |
"0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffa8fb", | |
"0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffa8fb" | |
], | |
[ | |
"0x0", | |
"0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffa9d3" | |
], | |
[ | |
"0x12", | |
"0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaa99" | |
], | |
["0x1", "0x0"] | |
// LAST 1 | |
] | |
].map((i) => i.map((pair) => Fp2$1.fromBigTuple(pair.map(BigInt))))); | |
const isogenyMapG1 = isogenyMap(Fp$1, [ | |
// xNum | |
[ | |
"0x11a05f2b1e833340b809101dd99815856b303e88a2d7005ff2627b56cdb4e2c85610c2d5f2e62d6eaeac1662734649b7", | |
"0x17294ed3e943ab2f0588bab22147a81c7c17e75b2f6a8417f565e33c70d1e86b4838f2a6f318c356e834eef1b3cb83bb", | |
"0xd54005db97678ec1d1048c5d10a9a1bce032473295983e56878e501ec68e25c958c3e3d2a09729fe0179f9dac9edcb0", | |
"0x1778e7166fcc6db74e0609d307e55412d7f5e4656a8dbf25f1b33289f1b330835336e25ce3107193c5b388641d9b6861", | |
"0xe99726a3199f4436642b4b3e4118e5499db995a1257fb3f086eeb65982fac18985a286f301e77c451154ce9ac8895d9", | |
"0x1630c3250d7313ff01d1201bf7a74ab5db3cb17dd952799b9ed3ab9097e68f90a0870d2dcae73d19cd13c1c66f652983", | |
"0xd6ed6553fe44d296a3726c38ae652bfb11586264f0f8ce19008e218f9c86b2a8da25128c1052ecaddd7f225a139ed84", | |
"0x17b81e7701abdbe2e8743884d1117e53356de5ab275b4db1a682c62ef0f2753339b7c8f8c8f475af9ccb5618e3f0c88e", | |
"0x80d3cf1f9a78fc47b90b33563be990dc43b756ce79f5574a2c596c928c5d1de4fa295f296b74e956d71986a8497e317", | |
"0x169b1f8e1bcfa7c42e0c37515d138f22dd2ecb803a0c5c99676314baf4bb1b7fa3190b2edc0327797f241067be390c9e", | |
"0x10321da079ce07e272d8ec09d2565b0dfa7dccdde6787f96d50af36003b14866f69b771f8c285decca67df3f1605fb7b", | |
"0x6e08c248e260e70bd1e962381edee3d31d79d7e22c837bc23c0bf1bc24c6b68c24b1b80b64d391fa9c8ba2e8ba2d229" | |
], | |
// xDen | |
[ | |
"0x8ca8d548cff19ae18b2e62f4bd3fa6f01d5ef4ba35b48ba9c9588617fc8ac62b558d681be343df8993cf9fa40d21b1c", | |
"0x12561a5deb559c4348b4711298e536367041e8ca0cf0800c0126c2588c48bf5713daa8846cb026e9e5c8276ec82b3bff", | |
"0xb2962fe57a3225e8137e629bff2991f6f89416f5a718cd1fca64e00b11aceacd6a3d0967c94fedcfcc239ba5cb83e19", | |
"0x3425581a58ae2fec83aafef7c40eb545b08243f16b1655154cca8abc28d6fd04976d5243eecf5c4130de8938dc62cd8", | |
"0x13a8e162022914a80a6f1d5f43e7a07dffdfc759a12062bb8d6b44e833b306da9bd29ba81f35781d539d395b3532a21e", | |
"0xe7355f8e4e667b955390f7f0506c6e9395735e9ce9cad4d0a43bcef24b8982f7400d24bc4228f11c02df9a29f6304a5", | |
"0x772caacf16936190f3e0c63e0596721570f5799af53a1894e2e073062aede9cea73b3538f0de06cec2574496ee84a3a", | |
"0x14a7ac2a9d64a8b230b3f5b074cf01996e7f63c21bca68a81996e1cdf9822c580fa5b9489d11e2d311f7d99bbdcc5a5e", | |
"0xa10ecf6ada54f825e920b3dafc7a3cce07f8d1d7161366b74100da67f39883503826692abba43704776ec3a79a1d641", | |
"0x95fc13ab9e92ad4476d6e3eb3a56680f682b4ee96f7d03776df533978f31c1593174e4b4b7865002d6384d168ecdd0a", | |
"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001" | |
// LAST 1 | |
], | |
// yNum | |
[ | |
"0x90d97c81ba24ee0259d1f094980dcfa11ad138e48a869522b52af6c956543d3cd0c7aee9b3ba3c2be9845719707bb33", | |
"0x134996a104ee5811d51036d776fb46831223e96c254f383d0f906343eb67ad34d6c56711962fa8bfe097e75a2e41c696", | |
"0xcc786baa966e66f4a384c86a3b49942552e2d658a31ce2c344be4b91400da7d26d521628b00523b8dfe240c72de1f6", | |
"0x1f86376e8981c217898751ad8746757d42aa7b90eeb791c09e4a3ec03251cf9de405aba9ec61deca6355c77b0e5f4cb", | |
"0x8cc03fdefe0ff135caf4fe2a21529c4195536fbe3ce50b879833fd221351adc2ee7f8dc099040a841b6daecf2e8fedb", | |
"0x16603fca40634b6a2211e11db8f0a6a074a7d0d4afadb7bd76505c3d3ad5544e203f6326c95a807299b23ab13633a5f0", | |
"0x4ab0b9bcfac1bbcb2c977d027796b3ce75bb8ca2be184cb5231413c4d634f3747a87ac2460f415ec961f8855fe9d6f2", | |
"0x987c8d5333ab86fde9926bd2ca6c674170a05bfe3bdd81ffd038da6c26c842642f64550fedfe935a15e4ca31870fb29", | |
"0x9fc4018bd96684be88c9e221e4da1bb8f3abd16679dc26c1e8b6e6a1f20cabe69d65201c78607a360370e577bdba587", | |
"0xe1bba7a1186bdb5223abde7ada14a23c42a0ca7915af6fe06985e7ed1e4d43b9b3f7055dd4eba6f2bafaaebca731c30", | |
"0x19713e47937cd1be0dfd0b8f1d43fb93cd2fcbcb6caf493fd1183e416389e61031bf3a5cce3fbafce813711ad011c132", | |
"0x18b46a908f36f6deb918c143fed2edcc523559b8aaf0c2462e6bfe7f911f643249d9cdf41b44d606ce07c8a4d0074d8e", | |
"0xb182cac101b9399d155096004f53f447aa7b12a3426b08ec02710e807b4633f06c851c1919211f20d4c04f00b971ef8", | |
"0x245a394ad1eca9b72fc00ae7be315dc757b3b080d4c158013e6632d3c40659cc6cf90ad1c232a6442d9d3f5db980133", | |
"0x5c129645e44cf1102a159f748c4a3fc5e673d81d7e86568d9ab0f5d396a7ce46ba1049b6579afb7866b1e715475224b", | |
"0x15e6be4e990f03ce4ea50b3b42df2eb5cb181d8f84965a3957add4fa95af01b2b665027efec01c7704b456be69c8b604" | |
], | |
// yDen | |
[ | |
"0x16112c4c3a9c98b252181140fad0eae9601a6de578980be6eec3232b5be72e7a07f3688ef60c206d01479253b03663c1", | |
"0x1962d75c2381201e1a0cbd6c43c348b885c84ff731c4d59ca4a10356f453e01f78a4260763529e3532f6102c2e49a03d", | |
"0x58df3306640da276faaae7d6e8eb15778c4855551ae7f310c35a5dd279cd2eca6757cd636f96f891e2538b53dbf67f2", | |
"0x16b7d288798e5395f20d23bf89edb4d1d115c5dbddbcd30e123da489e726af41727364f2c28297ada8d26d98445f5416", | |
"0xbe0e079545f43e4b00cc912f8228ddcc6d19c9f0f69bbb0542eda0fc9dec916a20b15dc0fd2ededda39142311a5001d", | |
"0x8d9e5297186db2d9fb266eaac783182b70152c65550d881c5ecd87b6f0f5a6449f38db9dfa9cce202c6477faaf9b7ac", | |
"0x166007c08a99db2fc3ba8734ace9824b5eecfdfa8d0cf8ef5dd365bc400a0051d5fa9c01a58b1fb93d1a1399126a775c", | |
"0x16a3ef08be3ea7ea03bcddfabba6ff6ee5a4375efa1f4fd7feb34fd206357132b920f5b00801dee460ee415a15812ed9", | |
"0x1866c8ed336c61231a1be54fd1d74cc4f9fb0ce4c6af5920abc5750c4bf39b4852cfe2f7bb9248836b233d9d55535d4a", | |
"0x167a55cda70a6e1cea820597d94a84903216f763e13d87bb5308592e7ea7d4fbc7385ea3d529b35e346ef48bb8913f55", | |
"0x4d2f259eea405bd48f010a01ad2911d9c6dd039bb61a6290e591b36e636a5c871a5c29f4f83060400f8b49cba8f6aa8", | |
"0xaccbb67481d033ff5852c1e48c50c477f94ff8aefce42d28c0f9a88cea7913516f968986f7ebbea9684b529e2561092", | |
"0xad6b9514c767fe3c3613144b45f1496543346d98adf02267d5ceef9a00d9b8693000763e3b90ac11e99b138573345cc", | |
"0x2660400eb2e4f3b628bdd0d53cd76f2bf565b94e72927c1cb748df27942480e420517bd8714cc80d1fadc1326ed06f7", | |
"0xe0fa1d816ddc03e6b24255e0d7819c171c40f65e273b853324efcd6356caa205ca2f570f13497804415473a1d634b8f", | |
"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001" | |
// LAST 1 | |
] | |
].map((i) => i.map((j) => BigInt(j)))); | |
const G2_SWU = mapToCurveSimpleSWU(Fp2$1, { | |
A: Fp2$1.create({ c0: Fp$1.create(_0n), c1: Fp$1.create(BigInt(240)) }), | |
// A' = 240 * I | |
B: Fp2$1.create({ c0: Fp$1.create(BigInt(1012)), c1: Fp$1.create(BigInt(1012)) }), | |
// B' = 1012 * (1 + I) | |
Z: Fp2$1.create({ c0: Fp$1.create(BigInt(-2)), c1: Fp$1.create(BigInt(-1)) }) | |
// Z: -(2 + I) | |
}); | |
const G1_SWU = mapToCurveSimpleSWU(Fp$1, { | |
A: Fp$1.create(BigInt("0x144698a3b8e9433d693a02c96d4982b0ea985383ee66a8d8e8981aefd881ac98936f8da0e0f97f5cf428082d584c1d")), | |
B: Fp$1.create(BigInt("0x12e2908d11688030018b12e8753eee3b2016c1f0f24f4070a0b9c14fcef35ef55a23215a316ceaa5d1cc48e98e172be0")), | |
Z: Fp$1.create(BigInt(11)) | |
}); | |
const { G2psi: G2psi$1, G2psi2 } = psiFrobenius(Fp$1, Fp2$1, Fp2$1.div(Fp2$1.ONE, Fp2$1.NONRESIDUE)); | |
const htfDefaults$1 = Object.freeze({ | |
// DST: a domain separation tag | |
// defined in section 2.2.5 | |
// Use utils.getDSTLabel(), utils.setDSTLabel(value) | |
DST: "BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_", | |
encodeDST: "BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_", | |
// p: the characteristic of F | |
// where F is a finite field of characteristic p and order q = p^m | |
p: Fp$1.ORDER, | |
// m: the extension degree of F, m >= 1 | |
// where F is a finite field of characteristic p and order q = p^m | |
m: 2, | |
// k: the target security level for the suite in bits | |
// defined in section 5.1 | |
k: 128, | |
// option to use a message that has already been processed by | |
// expand_message_xmd | |
expand: "xmd", | |
// Hash functions for: expand_message_xmd is appropriate for use with a | |
// wide range of hash functions, including SHA-2, SHA-3, BLAKE2, and others. | |
// BBS+ uses blake2: https://github.com/hyperledger/aries-framework-go/issues/2247 | |
hash: sha256$1 | |
}); | |
const COMPRESSED_ZERO = setMask(Fp$1.toBytes(_0n), { infinity: true, compressed: true }); | |
function parseMask(bytes2) { | |
bytes2 = bytes2.slice(); | |
const mask = bytes2[0] & 224; | |
const compressed = !!(mask >> 7 & 1); | |
const infinity = !!(mask >> 6 & 1); | |
const sort = !!(mask >> 5 & 1); | |
bytes2[0] &= 31; | |
return { compressed, infinity, sort, value: bytes2 }; | |
} | |
function setMask(bytes2, mask) { | |
if (bytes2[0] & 224) | |
throw new Error("setMask: non-empty mask"); | |
if (mask.compressed) | |
bytes2[0] |= 128; | |
if (mask.infinity) | |
bytes2[0] |= 64; | |
if (mask.sort) | |
bytes2[0] |= 32; | |
return bytes2; | |
} | |
function signatureG1ToRawBytes(point) { | |
point.assertValidity(); | |
const isZero = point.equals(bls12_381.G1.ProjectivePoint.ZERO); | |
const { x, y } = point.toAffine(); | |
if (isZero) | |
return COMPRESSED_ZERO.slice(); | |
const P = Fp$1.ORDER; | |
const sort = Boolean(y * _2n$1 / P); | |
return setMask(numberToBytesBE(x, Fp$1.BYTES), { compressed: true, sort }); | |
} | |
function signatureG2ToRawBytes(point) { | |
point.assertValidity(); | |
const len = Fp$1.BYTES; | |
if (point.equals(bls12_381.G2.ProjectivePoint.ZERO)) | |
return concatBytes$2(COMPRESSED_ZERO, numberToBytesBE(_0n, len)); | |
const { x, y } = point.toAffine(); | |
const { re: x0, im: x1 } = Fp2$1.reim(x); | |
const { re: y0, im: y1 } = Fp2$1.reim(y); | |
const tmp = y1 > _0n ? y1 * _2n$1 : y0 * _2n$1; | |
const sort = Boolean(tmp / Fp$1.ORDER & _1n$1); | |
const z2 = x0; | |
return concatBytes$2(setMask(numberToBytesBE(x1, len), { sort, compressed: true }), numberToBytesBE(z2, len)); | |
} | |
const bls12_381 = bls({ | |
// Fields | |
fields: { | |
Fp: Fp$1, | |
Fp2: Fp2$1, | |
Fp6: Fp6$1, | |
Fp12: Fp12$1, | |
Fr: Fr$1 | |
}, | |
// G1 is the order-q subgroup of E1(Fp) : y² = x³ + 4, #E1(Fp) = h1q, where | |
// characteristic; z + (z⁴ - z² + 1)(z - 1)²/3 | |
G1: { | |
Fp: Fp$1, | |
// cofactor; (z - 1)²/3 | |
h: BigInt("0x396c8c005555e1568c00aaab0000aaab"), | |
// generator's coordinates | |
// x = 3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507 | |
// y = 1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569 | |
Gx: BigInt("0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb"), | |
Gy: BigInt("0x08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1"), | |
a: Fp$1.ZERO, | |
b: _4n, | |
htfDefaults: { ...htfDefaults$1, m: 1, DST: "BLS_SIG_BLS12381G1_XMD:SHA-256_SSWU_RO_NUL_" }, | |
wrapPrivateKey: true, | |
allowInfinityPoint: true, | |
// Checks is the point resides in prime-order subgroup. | |
// point.isTorsionFree() should return true for valid points | |
// It returns false for shitty points. | |
// https://eprint.iacr.org/2021/1130.pdf | |
isTorsionFree: (c, point) => { | |
const cubicRootOfUnityModP = BigInt("0x5f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe"); | |
const phi = new c(Fp$1.mul(point.px, cubicRootOfUnityModP), point.py, point.pz); | |
const xP = point.multiplyUnsafe(BLS_X).negate(); | |
const u2P = xP.multiplyUnsafe(BLS_X); | |
return u2P.equals(phi); | |
}, | |
// Clear cofactor of G1 | |
// https://eprint.iacr.org/2019/403 | |
clearCofactor: (_c, point) => { | |
return point.multiplyUnsafe(BLS_X).add(point); | |
}, | |
mapToCurve: (scalars) => { | |
const { x, y } = G1_SWU(Fp$1.create(scalars[0])); | |
return isogenyMapG1(x, y); | |
}, | |
fromBytes: (bytes2) => { | |
const { compressed, infinity, sort, value } = parseMask(bytes2); | |
if (value.length === 48 && compressed) { | |
const P = Fp$1.ORDER; | |
const compressedValue = bytesToNumberBE(value); | |
const x = Fp$1.create(compressedValue & Fp$1.MASK); | |
if (infinity) { | |
if (x !== _0n) | |
throw new Error("G1: non-empty compressed point at infinity"); | |
return { x: _0n, y: _0n }; | |
} | |
const right = Fp$1.add(Fp$1.pow(x, _3n$1), Fp$1.create(bls12_381.params.G1b)); | |
let y = Fp$1.sqrt(right); | |
if (!y) | |
throw new Error("Invalid compressed G1 point"); | |
if (y * _2n$1 / P !== BigInt(sort)) | |
y = Fp$1.neg(y); | |
return { x: Fp$1.create(x), y: Fp$1.create(y) }; | |
} else if (value.length === 96 && !compressed) { | |
const x = bytesToNumberBE(value.subarray(0, Fp$1.BYTES)); | |
const y = bytesToNumberBE(value.subarray(Fp$1.BYTES)); | |
if (infinity) { | |
if (x !== _0n || y !== _0n) | |
throw new Error("G1: non-empty point at infinity"); | |
return bls12_381.G1.ProjectivePoint.ZERO.toAffine(); | |
} | |
return { x: Fp$1.create(x), y: Fp$1.create(y) }; | |
} else { | |
throw new Error("Invalid point G1, expected 48/96 bytes"); | |
} | |
}, | |
toBytes: (c, point, isCompressed) => { | |
const isZero = point.equals(c.ZERO); | |
const { x, y } = point.toAffine(); | |
if (isCompressed) { | |
if (isZero) | |
return COMPRESSED_ZERO.slice(); | |
const P = Fp$1.ORDER; | |
const sort = Boolean(y * _2n$1 / P); | |
return setMask(numberToBytesBE(x, Fp$1.BYTES), { compressed: true, sort }); | |
} else { | |
if (isZero) { | |
const x2 = concatBytes$2(new Uint8Array([64]), new Uint8Array(2 * Fp$1.BYTES - 1)); | |
return x2; | |
} else { | |
return concatBytes$2(numberToBytesBE(x, Fp$1.BYTES), numberToBytesBE(y, Fp$1.BYTES)); | |
} | |
} | |
}, | |
ShortSignature: { | |
fromHex(hex) { | |
const { infinity, sort, value } = parseMask(ensureBytes("signatureHex", hex, 48)); | |
const P = Fp$1.ORDER; | |
const compressedValue = bytesToNumberBE(value); | |
if (infinity) | |
return bls12_381.G1.ProjectivePoint.ZERO; | |
const x = Fp$1.create(compressedValue & Fp$1.MASK); | |
const right = Fp$1.add(Fp$1.pow(x, _3n$1), Fp$1.create(bls12_381.params.G1b)); | |
let y = Fp$1.sqrt(right); | |
if (!y) | |
throw new Error("Invalid compressed G1 point"); | |
const aflag = BigInt(sort); | |
if (y * _2n$1 / P !== aflag) | |
y = Fp$1.neg(y); | |
const point = bls12_381.G1.ProjectivePoint.fromAffine({ x, y }); | |
point.assertValidity(); | |
return point; | |
}, | |
toRawBytes(point) { | |
return signatureG1ToRawBytes(point); | |
}, | |
toHex(point) { | |
return bytesToHex$2(signatureG1ToRawBytes(point)); | |
} | |
} | |
}, | |
// G2 is the order-q subgroup of E2(Fp²) : y² = x³+4(1+√−1), | |
// where Fp2 is Fp[√−1]/(x2+1). #E2(Fp2 ) = h2q, where | |
// G² - 1 | |
// h2q | |
G2: { | |
Fp: Fp2$1, | |
// cofactor | |
h: BigInt("0x5d543a95414e7f1091d50792876a202cd91de4547085abaa68a205b2e5a7ddfa628f1cb4d9e82ef21537e293a6691ae1616ec6e786f0c70cf1c38e31c7238e5"), | |
Gx: Fp2$1.fromBigTuple([ | |
BigInt("0x024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8"), | |
BigInt("0x13e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e") | |
]), | |
// y = | |
// 927553665492332455747201965776037880757740193453592970025027978793976877002675564980949289727957565575433344219582, | |
// 1985150602287291935568054521177171638300868978215655730859378665066344726373823718423869104263333984641494340347905 | |
Gy: Fp2$1.fromBigTuple([ | |
BigInt("0x0ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801"), | |
BigInt("0x0606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be") | |
]), | |
a: Fp2$1.ZERO, | |
b: Fp2$1.fromBigTuple([_4n, _4n]), | |
hEff: BigInt("0xbc69f08f2ee75b3584c6a0ea91b352888e2a8e9145ad7689986ff031508ffe1329c2f178731db956d82bf015d1212b02ec0ec69d7477c1ae954cbc06689f6a359894c0adebbf6b4e8020005aaa95551"), | |
htfDefaults: { ...htfDefaults$1 }, | |
wrapPrivateKey: true, | |
allowInfinityPoint: true, | |
mapToCurve: (scalars) => { | |
const { x, y } = G2_SWU(Fp2$1.fromBigTuple(scalars)); | |
return isogenyMapG2(x, y); | |
}, | |
// Checks is the point resides in prime-order subgroup. | |
// point.isTorsionFree() should return true for valid points | |
// It returns false for shitty points. | |
// https://eprint.iacr.org/2021/1130.pdf | |
isTorsionFree: (c, P) => { | |
return P.multiplyUnsafe(BLS_X).negate().equals(G2psi$1(c, P)); | |
}, | |
// Maps the point into the prime-order subgroup G2. | |
// clear_cofactor_bls12381_g2 from cfrg-hash-to-curve-11 | |
// https://eprint.iacr.org/2017/419.pdf | |
// prettier-ignore | |
clearCofactor: (c, P) => { | |
const x = BLS_X; | |
let t1 = P.multiplyUnsafe(x).negate(); | |
let t2 = G2psi$1(c, P); | |
let t3 = P.double(); | |
t3 = G2psi2(c, t3); | |
t3 = t3.subtract(t2); | |
t2 = t1.add(t2); | |
t2 = t2.multiplyUnsafe(x).negate(); | |
t3 = t3.add(t2); | |
t3 = t3.subtract(t1); | |
const Q = t3.subtract(P); | |
return Q; | |
}, | |
fromBytes: (bytes2) => { | |
const { compressed, infinity, sort, value } = parseMask(bytes2); | |
if (!compressed && !infinity && sort || // 00100000 | |
!compressed && infinity && sort || // 01100000 | |
sort && infinity && compressed) { | |
throw new Error("Invalid encoding flag: " + (bytes2[0] & 224)); | |
} | |
const L = Fp$1.BYTES; | |
const slc = (b, from, to) => bytesToNumberBE(b.slice(from, to)); | |
if (value.length === 96 && compressed) { | |
const b = bls12_381.params.G2b; | |
const P = Fp$1.ORDER; | |
if (infinity) { | |
if (value.reduce((p, c) => p !== 0 ? c + 1 : c, 0) > 0) { | |
throw new Error("Invalid compressed G2 point"); | |
} | |
return { x: Fp2$1.ZERO, y: Fp2$1.ZERO }; | |
} | |
const x_1 = slc(value, 0, L); | |
const x_0 = slc(value, L, 2 * L); | |
const x = Fp2$1.create({ c0: Fp$1.create(x_0), c1: Fp$1.create(x_1) }); | |
const right = Fp2$1.add(Fp2$1.pow(x, _3n$1), b); | |
let y = Fp2$1.sqrt(right); | |
const Y_bit = y.c1 === _0n ? y.c0 * _2n$1 / P : y.c1 * _2n$1 / P ? _1n$1 : _0n; | |
y = sort && Y_bit > 0 ? y : Fp2$1.neg(y); | |
return { x, y }; | |
} else if (value.length === 192 && !compressed) { | |
if (infinity) { | |
if (value.reduce((p, c) => p !== 0 ? c + 1 : c, 0) > 0) { | |
throw new Error("Invalid uncompressed G2 point"); | |
} | |
return { x: Fp2$1.ZERO, y: Fp2$1.ZERO }; | |
} | |
const x1 = slc(value, 0, L); | |
const x0 = slc(value, L, 2 * L); | |
const y1 = slc(value, 2 * L, 3 * L); | |
const y0 = slc(value, 3 * L, 4 * L); | |
return { x: Fp2$1.fromBigTuple([x0, x1]), y: Fp2$1.fromBigTuple([y0, y1]) }; | |
} else { | |
throw new Error("Invalid point G2, expected 96/192 bytes"); | |
} | |
}, | |
toBytes: (c, point, isCompressed) => { | |
const { BYTES: len, ORDER: P } = Fp$1; | |
const isZero = point.equals(c.ZERO); | |
const { x, y } = point.toAffine(); | |
if (isCompressed) { | |
if (isZero) | |
return concatBytes$2(COMPRESSED_ZERO, numberToBytesBE(_0n, len)); | |
const flag = Boolean(y.c1 === _0n ? y.c0 * _2n$1 / P : y.c1 * _2n$1 / P); | |
return concatBytes$2(setMask(numberToBytesBE(x.c1, len), { compressed: true, sort: flag }), numberToBytesBE(x.c0, len)); | |
} else { | |
if (isZero) | |
return concatBytes$2(new Uint8Array([64]), new Uint8Array(4 * len - 1)); | |
const { re: x0, im: x1 } = Fp2$1.reim(x); | |
const { re: y0, im: y1 } = Fp2$1.reim(y); | |
return concatBytes$2(numberToBytesBE(x1, len), numberToBytesBE(x0, len), numberToBytesBE(y1, len), numberToBytesBE(y0, len)); | |
} | |
}, | |
Signature: { | |
// TODO: Optimize, it's very slow because of sqrt. | |
fromHex(hex) { | |
const { infinity, sort, value } = parseMask(ensureBytes("signatureHex", hex)); | |
const P = Fp$1.ORDER; | |
const half = value.length / 2; | |
if (half !== 48 && half !== 96) | |
throw new Error("Invalid compressed signature length, must be 96 or 192"); | |
const z1 = bytesToNumberBE(value.slice(0, half)); | |
const z2 = bytesToNumberBE(value.slice(half)); | |
if (infinity) | |
return bls12_381.G2.ProjectivePoint.ZERO; | |
const x1 = Fp$1.create(z1 & Fp$1.MASK); | |
const x2 = Fp$1.create(z2); | |
const x = Fp2$1.create({ c0: x2, c1: x1 }); | |
const y2 = Fp2$1.add(Fp2$1.pow(x, _3n$1), bls12_381.params.G2b); | |
let y = Fp2$1.sqrt(y2); | |
if (!y) | |
throw new Error("Failed to find a square root"); | |
const { re: y0, im: y1 } = Fp2$1.reim(y); | |
const aflag1 = BigInt(sort); | |
const isGreater = y1 > _0n && y1 * _2n$1 / P !== aflag1; | |
const isZero = y1 === _0n && y0 * _2n$1 / P !== aflag1; | |
if (isGreater || isZero) | |
y = Fp2$1.neg(y); | |
const point = bls12_381.G2.ProjectivePoint.fromAffine({ x, y }); | |
point.assertValidity(); | |
return point; | |
}, | |
toRawBytes(point) { | |
return signatureG2ToRawBytes(point); | |
}, | |
toHex(point) { | |
return bytesToHex$2(signatureG2ToRawBytes(point)); | |
} | |
} | |
}, | |
params: { | |
ateLoopSize: BLS_X, | |
// The BLS parameter x for BLS12-381 | |
r: Fr$1.ORDER, | |
// order; z⁴ − z² + 1; CURVE.n from other curves | |
xNegative: true, | |
twistType: "multiplicative" | |
}, | |
htfDefaults: htfDefaults$1, | |
hash: sha256$1, | |
randomBytes | |
}); | |
const G1_ZERO = bls12_381.G1.ProjectivePoint.ZERO; | |
const G2_ZERO = bls12_381.G2.ProjectivePoint.ZERO; | |
function BLS12_381_ToG1Point(input, verifyOrder = true) { | |
if (equalsBytes(input, BLS_G1_INFINITY_POINT_BYTES)) { | |
return G1_ZERO; | |
} | |
const x = bytesToBigInt(input.subarray(16, BLS_G1_POINT_BYTE_LENGTH / 2)); | |
const y = bytesToBigInt(input.subarray(80, BLS_G1_POINT_BYTE_LENGTH)); | |
const G1 = bls12_381.G1.ProjectivePoint.fromAffine({ | |
x, | |
y | |
}); | |
try { | |
G1.assertValidity(); | |
} catch (e) { | |
if (verifyOrder || e.message !== "bad point: not in prime-order subgroup") | |
throw new EvmError(ERROR.BLS_12_381_POINT_NOT_ON_CURVE); | |
} | |
return G1; | |
} | |
function BLS12_381_FromG1Point(input) { | |
const xBytes = setLengthLeft(bigIntToBytes(input.x), 64); | |
const yBytes = setLengthLeft(bigIntToBytes(input.y), 64); | |
return concatBytes$1(xBytes, yBytes); | |
} | |
function BLS12_381_ToG2Point(input, verifyOrder = true) { | |
if (equalsBytes(input, BLS_G2_INFINITY_POINT_BYTES)) { | |
return G2_ZERO; | |
} | |
const p_x_1 = input.subarray(0, 64); | |
const p_x_2 = input.subarray(64, BLS_G2_POINT_BYTE_LENGTH / 2); | |
const p_y_1 = input.subarray(128, 192); | |
const p_y_2 = input.subarray(192, BLS_G2_POINT_BYTE_LENGTH); | |
const Fp2X = BLS12_381_ToFp2Point(p_x_1, p_x_2); | |
const Fp2Y = BLS12_381_ToFp2Point(p_y_1, p_y_2); | |
const pG2 = bls12_381.G2.ProjectivePoint.fromAffine({ | |
x: Fp2X, | |
y: Fp2Y | |
}); | |
try { | |
pG2.assertValidity(); | |
} catch (e) { | |
if (verifyOrder || e.message !== "bad point: not in prime-order subgroup") | |
throw new EvmError(ERROR.BLS_12_381_POINT_NOT_ON_CURVE); | |
} | |
return pG2; | |
} | |
function BLS12_381_FromG2Point(input) { | |
const xBytes1 = setLengthLeft(bigIntToBytes(input.x.c0), 64); | |
const xBytes2 = setLengthLeft(bigIntToBytes(input.x.c1), 64); | |
const yBytes1 = setLengthLeft(bigIntToBytes(input.y.c0), 64); | |
const yBytes2 = setLengthLeft(bigIntToBytes(input.y.c1), 64); | |
return concatBytes$1(xBytes1, xBytes2, yBytes1, yBytes2); | |
} | |
function BLS12_381_ToFrPoint(input) { | |
const Fr2 = bls12_381.fields.Fr.fromBytes(input); | |
return bls12_381.fields.Fr.create(Fr2); | |
} | |
function BLS12_381_ToFpPoint(fpCoordinate) { | |
if (bytesToBigInt(fpCoordinate) >= BLS_FIELD_MODULUS) { | |
throw new EvmError(ERROR.BLS_12_381_FP_NOT_IN_FIELD); | |
} | |
const FP = bls12_381.fields.Fp.fromBytes(fpCoordinate.slice(16)); | |
return FP; | |
} | |
function BLS12_381_ToFp2Point(fpXCoordinate, fpYCoordinate) { | |
if (bytesToBigInt(fpXCoordinate) >= BLS_FIELD_MODULUS) { | |
throw new EvmError(ERROR.BLS_12_381_FP_NOT_IN_FIELD); | |
} | |
if (bytesToBigInt(fpYCoordinate) >= BLS_FIELD_MODULUS) { | |
throw new EvmError(ERROR.BLS_12_381_FP_NOT_IN_FIELD); | |
} | |
const fpBytes = concatBytes$1(fpXCoordinate.subarray(16), fpYCoordinate.subarray(16)); | |
const FP = bls12_381.fields.Fp2.fromBytes(fpBytes); | |
return FP; | |
} | |
class NobleBLS { | |
addG1(input) { | |
const p1 = BLS12_381_ToG1Point(input.subarray(0, BLS_G1_POINT_BYTE_LENGTH), false); | |
const p2 = BLS12_381_ToG1Point( | |
input.subarray(BLS_G1_POINT_BYTE_LENGTH, BLS_G1_POINT_BYTE_LENGTH * 2), | |
false | |
); | |
const p = p1.add(p2); | |
const result = BLS12_381_FromG1Point(p); | |
return result; | |
} | |
mulG1(input) { | |
const p = BLS12_381_ToG1Point(input.subarray(0, BLS_G1_POINT_BYTE_LENGTH)); | |
const scalar = BLS12_381_ToFrPoint(input.subarray(BLS_G1_POINT_BYTE_LENGTH, 160)); | |
if (scalar === BIGINT_0) { | |
return BLS_G1_INFINITY_POINT_BYTES; | |
} | |
const result = p.multiplyUnsafe(scalar); | |
return BLS12_381_FromG1Point(result); | |
} | |
addG2(input) { | |
const p1 = BLS12_381_ToG2Point(input.subarray(0, BLS_G2_POINT_BYTE_LENGTH), false); | |
const p2 = BLS12_381_ToG2Point( | |
input.subarray(BLS_G2_POINT_BYTE_LENGTH, BLS_G2_POINT_BYTE_LENGTH * 2), | |
false | |
); | |
const p = p1.add(p2); | |
const result = BLS12_381_FromG2Point(p); | |
return result; | |
} | |
mulG2(input) { | |
const p = BLS12_381_ToG2Point(input.subarray(0, BLS_G2_POINT_BYTE_LENGTH)); | |
const scalar = BLS12_381_ToFrPoint(input.subarray(BLS_G2_POINT_BYTE_LENGTH, 288)); | |
if (scalar === BIGINT_0) { | |
return BLS_G2_INFINITY_POINT_BYTES; | |
} | |
const result = p.multiplyUnsafe(scalar); | |
return BLS12_381_FromG2Point(result); | |
} | |
mapFPtoG1(input) { | |
const FP = BLS12_381_ToFpPoint(input.subarray(0, 64)); | |
const result = bls12_381.G1.mapToCurve([FP]).toAffine(); | |
const resultBytes = BLS12_381_FromG1Point(result); | |
return resultBytes; | |
} | |
mapFP2toG2(input) { | |
const Fp2Point = BLS12_381_ToFp2Point(input.subarray(0, 64), input.subarray(64, 128)); | |
const result = bls12_381.G2.mapToCurve([Fp2Point.c0, Fp2Point.c1]).toAffine(); | |
const resultBytes = BLS12_381_FromG2Point(result); | |
return resultBytes; | |
} | |
msmG1(input) { | |
const pairLength = 160; | |
const numPairs = input.length / pairLength; | |
let pRes = G1_ZERO; | |
for (let k = 0; k < numPairs; k++) { | |
const pairStart = pairLength * k; | |
const G1 = BLS12_381_ToG1Point( | |
input.subarray(pairStart, pairStart + BLS_G1_POINT_BYTE_LENGTH) | |
); | |
const Fr2 = BLS12_381_ToFrPoint( | |
input.subarray(pairStart + BLS_G1_POINT_BYTE_LENGTH, pairStart + pairLength) | |
); | |
let pMul; | |
if (Fr2 === BIGINT_0) { | |
pMul = G1_ZERO; | |
} else { | |
pMul = G1.multiplyUnsafe(Fr2); | |
} | |
pRes = pRes.add(pMul); | |
} | |
return BLS12_381_FromG1Point(pRes); | |
} | |
msmG2(input) { | |
const pairLength = 288; | |
const numPairs = input.length / pairLength; | |
let pRes = G2_ZERO; | |
for (let k = 0; k < numPairs; k++) { | |
const pairStart = pairLength * k; | |
const G2 = BLS12_381_ToG2Point( | |
input.subarray(pairStart, pairStart + BLS_G2_POINT_BYTE_LENGTH) | |
); | |
const Fr2 = BLS12_381_ToFrPoint( | |
input.subarray(pairStart + BLS_G2_POINT_BYTE_LENGTH, pairStart + pairLength) | |
); | |
let pMul; | |
if (Fr2 === BIGINT_0) { | |
pMul = G2_ZERO; | |
} else { | |
pMul = G2.multiplyUnsafe(Fr2); | |
} | |
pRes = pRes.add(pMul); | |
} | |
return BLS12_381_FromG2Point(pRes); | |
} | |
pairingCheck(input) { | |
const pairLength = 384; | |
const pairs = []; | |
for (let k = 0; k < input.length / pairLength; k++) { | |
const pairStart = pairLength * k; | |
const G1 = BLS12_381_ToG1Point( | |
input.subarray(pairStart, pairStart + BLS_G1_POINT_BYTE_LENGTH) | |
); | |
const g2start = pairStart + BLS_G1_POINT_BYTE_LENGTH; | |
const G2 = BLS12_381_ToG2Point(input.subarray(g2start, g2start + BLS_G2_POINT_BYTE_LENGTH)); | |
pairs.push({ g1: G1, g2: G2 }); | |
} | |
for (const { g1, g2 } of pairs) { | |
const _g2 = g2; | |
if (g1.equals(G1_ZERO) || _g2.equals(G2_ZERO)) { | |
return BLS_ONE_BUFFER; | |
} | |
} | |
const FP12 = bls12_381.pairingBatch(pairs, true); | |
if (bls12_381.fields.Fp12.eql(FP12, bls12_381.fields.Fp12.ONE)) { | |
return BLS_ONE_BUFFER; | |
} else { | |
return BLS_ZERO_BUFFER; | |
} | |
} | |
} | |
const ZERO_BYTES_16 = new Uint8Array(16); | |
const msmGasUsed = (numPairs, gasUsedPerPair) => { | |
const gasDiscountMax = BLS_GAS_DISCOUNT_PAIRS[BLS_GAS_DISCOUNT_PAIRS.length - 1][1]; | |
let gasDiscountMultiplier; | |
if (numPairs <= BLS_GAS_DISCOUNT_PAIRS.length) { | |
if (numPairs === 0) { | |
gasDiscountMultiplier = 0; | |
} else { | |
gasDiscountMultiplier = BLS_GAS_DISCOUNT_PAIRS[numPairs - 1][1]; | |
} | |
} else { | |
gasDiscountMultiplier = gasDiscountMax; | |
} | |
return BigInt(numPairs) * gasUsedPerPair * BigInt(gasDiscountMultiplier) / BigInt(1e3); | |
}; | |
const leading16ZeroBytesCheck = (opts, zeroByteRanges, pName, pairStart = 0) => { | |
for (const index in zeroByteRanges) { | |
const slicedBuffer = opts.data.subarray( | |
zeroByteRanges[index][0] + pairStart, | |
zeroByteRanges[index][1] + pairStart | |
); | |
if (!(equalsBytes(slicedBuffer, ZERO_BYTES_16) === true)) { | |
if (opts._debug !== void 0) { | |
opts._debug(`${pName} failed: Point not on curve`); | |
} | |
return false; | |
} | |
} | |
return true; | |
}; | |
async function precompile0b(opts) { | |
const pName = getPrecompileName("0b"); | |
const bls2 = opts._EVM._bls; | |
const gasUsed = opts.common.paramByEIP("bls12381G1AddGas", 2537) ?? BigInt(0); | |
if (!gasLimitCheck(opts, gasUsed, pName)) { | |
return OOGResult(opts.gasLimit); | |
} | |
if (!equalityLengthCheck(opts, 256, pName)) { | |
return EvmErrorResult(new EvmError(ERROR.BLS_12_381_INVALID_INPUT_LENGTH), opts.gasLimit); | |
} | |
const zeroByteRanges = [ | |
[0, 16], | |
[64, 80], | |
[128, 144], | |
[192, 208] | |
]; | |
if (!leading16ZeroBytesCheck(opts, zeroByteRanges, pName)) { | |
return EvmErrorResult(new EvmError(ERROR.BLS_12_381_POINT_NOT_ON_CURVE), opts.gasLimit); | |
} | |
let returnValue; | |
try { | |
returnValue = bls2.addG1(opts.data); | |
} catch (e) { | |
if (opts._debug !== void 0) { | |
opts._debug(`${pName} failed: ${e.message}`); | |
} | |
return EvmErrorResult(e, opts.gasLimit); | |
} | |
if (opts._debug !== void 0) { | |
opts._debug(`${pName} return value=${bytesToHex$1(returnValue)}`); | |
} | |
return { | |
executionGasUsed: gasUsed, | |
returnValue | |
}; | |
} | |
async function precompile0c(opts) { | |
const pName = getPrecompileName("0c"); | |
const bls2 = opts._EVM._bls; | |
const gasUsed = opts.common.paramByEIP("bls12381G1MulGas", 2537) ?? BigInt(0); | |
if (!gasLimitCheck(opts, gasUsed, pName)) { | |
return OOGResult(opts.gasLimit); | |
} | |
if (!equalityLengthCheck(opts, 160, pName)) { | |
return EvmErrorResult(new EvmError(ERROR.BLS_12_381_INVALID_INPUT_LENGTH), opts.gasLimit); | |
} | |
const zeroByteRanges = [ | |
[0, 16], | |
[64, 80] | |
]; | |
if (!leading16ZeroBytesCheck(opts, zeroByteRanges, pName)) { | |
return EvmErrorResult(new EvmError(ERROR.BLS_12_381_POINT_NOT_ON_CURVE), opts.gasLimit); | |
} | |
let returnValue; | |
try { | |
returnValue = bls2.mulG1(opts.data); | |
} catch (e) { | |
if (opts._debug !== void 0) { | |
opts._debug(`${pName} failed: ${e.message}`); | |
} | |
return EvmErrorResult(e, opts.gasLimit); | |
} | |
if (opts._debug !== void 0) { | |
opts._debug(`${pName} return value=${bytesToHex$1(returnValue)}`); | |
} | |
return { | |
executionGasUsed: gasUsed, | |
returnValue | |
}; | |
} | |
async function precompile0d(opts) { | |
const pName = getPrecompileName("0d"); | |
const bls2 = opts._EVM._bls; | |
const inputData = opts.data; | |
if (inputData.length === 0) { | |
if (opts._debug !== void 0) { | |
opts._debug(`${pName} failed: Empty input`); | |
} | |
return EvmErrorResult(new EvmError(ERROR.BLS_12_381_INPUT_EMPTY), opts.gasLimit); | |
} | |
const numPairs = Math.floor(inputData.length / 160); | |
const gasUsedPerPair = opts.common.paramByEIP("bls12381G1MulGas", 2537) ?? BigInt(0); | |
const gasUsed = msmGasUsed(numPairs, gasUsedPerPair); | |
if (!gasLimitCheck(opts, gasUsed, pName)) { | |
return OOGResult(opts.gasLimit); | |
} | |
if (inputData.length % 160 !== 0) { | |
if (opts._debug !== void 0) { | |
opts._debug(`${pName} failed: Invalid input length length=${inputData.length}`); | |
} | |
return EvmErrorResult(new EvmError(ERROR.BLS_12_381_INVALID_INPUT_LENGTH), opts.gasLimit); | |
} | |
if (!moduloLengthCheck(opts, 160, pName)) { | |
return EvmErrorResult(new EvmError(ERROR.BLS_12_381_INVALID_INPUT_LENGTH), opts.gasLimit); | |
} | |
const zeroByteRanges = [ | |
[0, 16], | |
[64, 80] | |
]; | |
for (let k = 0; k < numPairs; k++) { | |
const pairStart = 160 * k; | |
if (!leading16ZeroBytesCheck(opts, zeroByteRanges, pName, pairStart)) { | |
return EvmErrorResult(new EvmError(ERROR.BLS_12_381_POINT_NOT_ON_CURVE), opts.gasLimit); | |
} | |
} | |
let returnValue; | |
try { | |
returnValue = bls2.msmG1(opts.data); | |
} catch (e) { | |
if (opts._debug !== void 0) { | |
opts._debug(`${pName} failed: ${e.message}`); | |
} | |
return EvmErrorResult(e, opts.gasLimit); | |
} | |
if (opts._debug !== void 0) { | |
opts._debug(`${pName} return value=${bytesToHex$1(returnValue)}`); | |
} | |
return { | |
executionGasUsed: gasUsed, | |
returnValue | |
}; | |
} | |
async function precompile0e(opts) { | |
const pName = getPrecompileName("0e"); | |
const bls2 = opts._EVM._bls; | |
const gasUsed = opts.common.paramByEIP("bls12381G2AddGas", 2537) ?? BigInt(0); | |
if (!gasLimitCheck(opts, gasUsed, pName)) { | |
return OOGResult(opts.gasLimit); | |
} | |
if (!equalityLengthCheck(opts, 512, pName)) { | |
return EvmErrorResult(new EvmError(ERROR.BLS_12_381_INVALID_INPUT_LENGTH), opts.gasLimit); | |
} | |
const zeroByteRanges = [ | |
[0, 16], | |
[64, 80], | |
[128, 144], | |
[192, 208], | |
[256, 272], | |
[320, 336], | |
[384, 400], | |
[448, 464] | |
]; | |
if (!leading16ZeroBytesCheck(opts, zeroByteRanges, pName)) { | |
return EvmErrorResult(new EvmError(ERROR.BLS_12_381_POINT_NOT_ON_CURVE), opts.gasLimit); | |
} | |
let returnValue; | |
try { | |
returnValue = bls2.addG2(opts.data); | |
} catch (e) { | |
return EvmErrorResult(e, opts.gasLimit); | |
} | |
if (opts._debug !== void 0) { | |
opts._debug(`${pName} return value=${bytesToHex$1(returnValue)}`); | |
} | |
return { | |
executionGasUsed: gasUsed, | |
returnValue | |
}; | |
} | |
async function precompile0f(opts) { | |
const pName = getPrecompileName("0f"); | |
const bls2 = opts._EVM._bls; | |
const gasUsed = opts.common.paramByEIP("bls12381G2MulGas", 2537) ?? BigInt(0); | |
if (!gasLimitCheck(opts, gasUsed, pName)) { | |
return OOGResult(opts.gasLimit); | |
} | |
if (!equalityLengthCheck(opts, 288, pName)) { | |
return EvmErrorResult(new EvmError(ERROR.BLS_12_381_INVALID_INPUT_LENGTH), opts.gasLimit); | |
} | |
const zeroByteRanges = [ | |
[0, 16], | |
[64, 80], | |
[128, 144], | |
[192, 208] | |
]; | |
if (!leading16ZeroBytesCheck(opts, zeroByteRanges, pName)) { | |
return EvmErrorResult(new EvmError(ERROR.BLS_12_381_POINT_NOT_ON_CURVE), opts.gasLimit); | |
} | |
let returnValue; | |
try { | |
returnValue = bls2.mulG2(opts.data); | |
} catch (e) { | |
if (opts._debug !== void 0) { | |
opts._debug(`${pName} failed: ${e.message}`); | |
} | |
return EvmErrorResult(e, opts.gasLimit); | |
} | |
if (opts._debug !== void 0) { | |
opts._debug(`${pName} return value=${bytesToHex$1(returnValue)}`); | |
} | |
return { | |
executionGasUsed: gasUsed, | |
returnValue | |
}; | |
} | |
async function precompile10(opts) { | |
const pName = getPrecompileName("10"); | |
const bls2 = opts._EVM._bls; | |
if (opts.data.length === 0) { | |
if (opts._debug !== void 0) { | |
opts._debug(`${pName} failed: Empty input`); | |
} | |
return EvmErrorResult(new EvmError(ERROR.BLS_12_381_INPUT_EMPTY), opts.gasLimit); | |
} | |
const numPairs = Math.floor(opts.data.length / 288); | |
const gasUsedPerPair = opts.common.paramByEIP("bls12381G2MulGas", 2537) ?? BigInt(0); | |
const gasUsed = msmGasUsed(numPairs, gasUsedPerPair); | |
if (!gasLimitCheck(opts, gasUsed, pName)) { | |
return OOGResult(opts.gasLimit); | |
} | |
if (!moduloLengthCheck(opts, 288, pName)) { | |
return EvmErrorResult(new EvmError(ERROR.BLS_12_381_INVALID_INPUT_LENGTH), opts.gasLimit); | |
} | |
const zeroByteRanges = [ | |
[0, 16], | |
[64, 80], | |
[128, 144], | |
[192, 208] | |
]; | |
for (let k = 0; k < numPairs; k++) { | |
const pairStart = 288 * k; | |
if (!leading16ZeroBytesCheck(opts, zeroByteRanges, pName, pairStart)) { | |
return EvmErrorResult(new EvmError(ERROR.BLS_12_381_POINT_NOT_ON_CURVE), opts.gasLimit); | |
} | |
} | |
let returnValue; | |
try { | |
returnValue = bls2.msmG2(opts.data); | |
} catch (e) { | |
if (opts._debug !== void 0) { | |
opts._debug(`${pName} failed: ${e.message}`); | |
} | |
return EvmErrorResult(e, opts.gasLimit); | |
} | |
if (opts._debug !== void 0) { | |
opts._debug(`${pName} return value=${bytesToHex$1(returnValue)}`); | |
} | |
return { | |
executionGasUsed: gasUsed, | |
returnValue | |
}; | |
} | |
async function precompile11(opts) { | |
const pName = getPrecompileName("11"); | |
const bls2 = opts._EVM._bls; | |
const baseGas = opts.common.paramByEIP("bls12381PairingBaseGas", 2537) ?? BigInt(0); | |
if (opts.data.length === 0) { | |
if (opts._debug !== void 0) { | |
opts._debug(`${pName} failed: Empty input`); | |
} | |
return EvmErrorResult(new EvmError(ERROR.BLS_12_381_INPUT_EMPTY), opts.gasLimit); | |
} | |
const gasUsedPerPair = opts.common.paramByEIP("bls12381PairingPerPairGas", 2537) ?? BigInt(0); | |
if (!moduloLengthCheck(opts, 384, pName)) { | |
return EvmErrorResult(new EvmError(ERROR.BLS_12_381_INVALID_INPUT_LENGTH), opts.gasLimit); | |
} | |
const gasUsed = baseGas + gasUsedPerPair * BigInt(Math.floor(opts.data.length / 384)); | |
if (!gasLimitCheck(opts, gasUsed, pName)) { | |
return OOGResult(opts.gasLimit); | |
} | |
const zeroByteRanges = [ | |
[0, 16], | |
[64, 80], | |
[128, 144], | |
[192, 208], | |
[256, 272], | |
[320, 336] | |
]; | |
for (let k = 0; k < opts.data.length / 384; k++) { | |
const pairStart = 384 * k; | |
if (!leading16ZeroBytesCheck(opts, zeroByteRanges, pName, pairStart)) { | |
return EvmErrorResult(new EvmError(ERROR.BLS_12_381_POINT_NOT_ON_CURVE), opts.gasLimit); | |
} | |
} | |
let returnValue; | |
try { | |
returnValue = bls2.pairingCheck(opts.data); | |
} catch (e) { | |
if (opts._debug !== void 0) { | |
opts._debug(`${pName} failed: ${e.message}`); | |
} | |
return EvmErrorResult(e, opts.gasLimit); | |
} | |
if (opts._debug !== void 0) { | |
opts._debug(`${pName} return value=${bytesToHex$1(returnValue)}`); | |
} | |
return { | |
executionGasUsed: gasUsed, | |
returnValue | |
}; | |
} | |
async function precompile12(opts) { | |
const pName = getPrecompileName("12"); | |
const bls2 = opts._EVM._bls; | |
const gasUsed = opts.common.paramByEIP("bls12381MapG1Gas", 2537) ?? BigInt(0); | |
if (!gasLimitCheck(opts, gasUsed, pName)) { | |
return OOGResult(opts.gasLimit); | |
} | |
if (!equalityLengthCheck(opts, 64, pName)) { | |
return EvmErrorResult(new EvmError(ERROR.BLS_12_381_INVALID_INPUT_LENGTH), opts.gasLimit); | |
} | |
const zeroByteRanges = [[0, 16]]; | |
if (!leading16ZeroBytesCheck(opts, zeroByteRanges, pName)) { | |
return EvmErrorResult(new EvmError(ERROR.BLS_12_381_POINT_NOT_ON_CURVE), opts.gasLimit); | |
} | |
let returnValue; | |
try { | |
returnValue = bls2.mapFPtoG1(opts.data); | |
} catch (e) { | |
if (opts._debug !== void 0) { | |
opts._debug(`${pName} failed: ${e.message}`); | |
} | |
return EvmErrorResult(e, opts.gasLimit); | |
} | |
if (opts._debug !== void 0) { | |
opts._debug(`${pName} return value=${bytesToHex$1(returnValue)}`); | |
} | |
return { | |
executionGasUsed: gasUsed, | |
returnValue | |
}; | |
} | |
async function precompile13(opts) { | |
const pName = getPrecompileName("13"); | |
const bls2 = opts._EVM._bls; | |
const gasUsed = opts.common.paramByEIP("bls12381MapG2Gas", 2537) ?? BigInt(0); | |
if (!gasLimitCheck(opts, gasUsed, pName)) { | |
return OOGResult(opts.gasLimit); | |
} | |
if (!equalityLengthCheck(opts, 128, pName)) { | |
return EvmErrorResult(new EvmError(ERROR.BLS_12_381_INVALID_INPUT_LENGTH), opts.gasLimit); | |
} | |
const zeroByteRanges = [ | |
[0, 16], | |
[64, 80] | |
]; | |
if (!leading16ZeroBytesCheck(opts, zeroByteRanges, pName)) { | |
return EvmErrorResult(new EvmError(ERROR.BLS_12_381_POINT_NOT_ON_CURVE), opts.gasLimit); | |
} | |
let returnValue; | |
try { | |
returnValue = bls2.mapFP2toG2(opts.data); | |
} catch (e) { | |
if (opts._debug !== void 0) { | |
opts._debug(`${pName} failed: ${e.message}`); | |
} | |
return EvmErrorResult(e, opts.gasLimit); | |
} | |
if (opts._debug !== void 0) { | |
opts._debug(`${pName} return value=${bytesToHex$1(returnValue)}`); | |
} | |
return { | |
executionGasUsed: gasUsed, | |
returnValue | |
}; | |
} | |
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | |
const _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3); | |
const _6n = BigInt(6); | |
const BN_X = BigInt("4965661367192848881"); | |
const BN_X_LEN = bitLen(BN_X); | |
const SIX_X_SQUARED = _6n * BN_X ** _2n; | |
const Fr = Field(BigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617")); | |
const Fp2B = { | |
c0: BigInt("19485874751759354771024239261021720505790618469301721065564631296452457478373"), | |
c1: BigInt("266929791119991161246907387137283842545076965332900288569378510910307636690") | |
}; | |
const { Fp, Fp2, Fp6, Fp4Square, Fp12 } = tower12({ | |
ORDER: BigInt("21888242871839275222246405745257275088696311157297823662689037894645226208583"), | |
FP2_NONRESIDUE: [BigInt(9), _1n], | |
Fp2mulByB: (num) => Fp2.mul(num, Fp2B), | |
// The result of any pairing is in a cyclotomic subgroup | |
// https://eprint.iacr.org/2009/565.pdf | |
Fp12cyclotomicSquare: ({ c0, c1 }) => { | |
const { c0: c0c0, c1: c0c1, c2: c0c2 } = c0; | |
const { c0: c1c0, c1: c1c1, c2: c1c2 } = c1; | |
const { first: t3, second: t4 } = Fp4Square(c0c0, c1c1); | |
const { first: t5, second: t6 } = Fp4Square(c1c0, c0c2); | |
const { first: t7, second: t8 } = Fp4Square(c0c1, c1c2); | |
let t9 = Fp2.mulByNonresidue(t8); | |
return { | |
c0: Fp6.create({ | |
c0: Fp2.add(Fp2.mul(Fp2.sub(t3, c0c0), _2n), t3), | |
// 2 * (T3 - c0c0) + T3 | |
c1: Fp2.add(Fp2.mul(Fp2.sub(t5, c0c1), _2n), t5), | |
// 2 * (T5 - c0c1) + T5 | |
c2: Fp2.add(Fp2.mul(Fp2.sub(t7, c0c2), _2n), t7) | |
}), | |
// 2 * (T7 - c0c2) + T7 | |
c1: Fp6.create({ | |
c0: Fp2.add(Fp2.mul(Fp2.add(t9, c1c0), _2n), t9), | |
// 2 * (T9 + c1c0) + T9 | |
c1: Fp2.add(Fp2.mul(Fp2.add(t4, c1c1), _2n), t4), | |
// 2 * (T4 + c1c1) + T4 | |
c2: Fp2.add(Fp2.mul(Fp2.add(t6, c1c2), _2n), t6) | |
}) | |
}; | |
}, | |
Fp12cyclotomicExp(num, n) { | |
let z = Fp12.ONE; | |
for (let i = BN_X_LEN - 1; i >= 0; i--) { | |
z = Fp12._cyclotomicSquare(z); | |
if (bitGet(n, i)) | |
z = Fp12.mul(z, num); | |
} | |
return z; | |
}, | |
// https://eprint.iacr.org/2010/354.pdf | |
// https://eprint.iacr.org/2009/565.pdf | |
Fp12finalExponentiate: (num) => { | |
const powMinusX = (num2) => Fp12.conjugate(Fp12._cyclotomicExp(num2, BN_X)); | |
const r0 = Fp12.mul(Fp12.conjugate(num), Fp12.inv(num)); | |
const r = Fp12.mul(Fp12.frobeniusMap(r0, 2), r0); | |
const y1 = Fp12._cyclotomicSquare(powMinusX(r)); | |
const y2 = Fp12.mul(Fp12._cyclotomicSquare(y1), y1); | |
const y4 = powMinusX(y2); | |
const y6 = powMinusX(Fp12._cyclotomicSquare(y4)); | |
const y8 = Fp12.mul(Fp12.mul(Fp12.conjugate(y6), y4), Fp12.conjugate(y2)); | |
const y9 = Fp12.mul(y8, y1); | |
return Fp12.mul(Fp12.frobeniusMap(Fp12.mul(Fp12.conjugate(r), y9), 3), Fp12.mul(Fp12.frobeniusMap(y8, 2), Fp12.mul(Fp12.frobeniusMap(y9, 1), Fp12.mul(Fp12.mul(y8, y4), r)))); | |
} | |
}); | |
const { G2psi, psi } = psiFrobenius(Fp, Fp2, Fp2.NONRESIDUE); | |
const htfDefaults = Object.freeze({ | |
// DST: a domain separation tag defined in section 2.2.5 | |
DST: "BN254G2_XMD:SHA-256_SVDW_RO_", | |
encodeDST: "BN254G2_XMD:SHA-256_SVDW_RO_", | |
p: Fp.ORDER, | |
m: 2, | |
k: 128, | |
expand: "xmd", | |
hash: sha256$1 | |
}); | |
const bn254 = bls({ | |
// Fields | |
fields: { Fp, Fp2, Fp6, Fp12, Fr }, | |
G1: { | |
Fp, | |
h: BigInt(1), | |
Gx: BigInt(1), | |
Gy: BigInt(2), | |
a: Fp.ZERO, | |
b: _3n, | |
htfDefaults: { ...htfDefaults, m: 1, DST: "BN254G2_XMD:SHA-256_SVDW_RO_" }, | |
wrapPrivateKey: true, | |
allowInfinityPoint: true, | |
mapToCurve: notImplemented, | |
fromBytes: notImplemented, | |
toBytes: notImplemented, | |
ShortSignature: { | |
fromHex: notImplemented, | |
toRawBytes: notImplemented, | |
toHex: notImplemented | |
} | |
}, | |
G2: { | |
Fp: Fp2, | |
// cofactor: (36 * X^4) + (36 * X^3) + (30 * X^2) + 6*X + 1 | |
h: BigInt("21888242871839275222246405745257275088844257914179612981679871602714643921549"), | |
Gx: Fp2.fromBigTuple([ | |
BigInt("10857046999023057135944570762232829481370756359578518086990519993285655852781"), | |
BigInt("11559732032986387107991004021392285783925812861821192530917403151452391805634") | |
]), | |
Gy: Fp2.fromBigTuple([ | |
BigInt("8495653923123431417604973247489272438418190587263600148770280649306958101930"), | |
BigInt("4082367875863433681332203403145435568316851327593401208105741076214120093531") | |
]), | |
a: Fp2.ZERO, | |
b: Fp2B, | |
hEff: BigInt("21888242871839275222246405745257275088844257914179612981679871602714643921549"), | |
htfDefaults: { ...htfDefaults }, | |
wrapPrivateKey: true, | |
allowInfinityPoint: true, | |
isTorsionFree: (c, P) => P.multiplyUnsafe(SIX_X_SQUARED).equals(G2psi(c, P)), | |
// [p]P = [6X^2]P | |
mapToCurve: notImplemented, | |
fromBytes: notImplemented, | |
toBytes: notImplemented, | |
Signature: { | |
fromHex: notImplemented, | |
toRawBytes: notImplemented, | |
toHex: notImplemented | |
} | |
}, | |
params: { | |
ateLoopSize: BN_X * _6n + _2n, | |
r: Fr.ORDER, | |
xNegative: false, | |
twistType: "divisive" | |
}, | |
htfDefaults, | |
hash: sha256$1, | |
randomBytes, | |
postPrecompute: (Rx, Ry, Rz, Qx, Qy, pointAdd) => { | |
const q = psi(Qx, Qy); | |
({ Rx, Ry, Rz } = pointAdd(Rx, Ry, Rz, q[0], q[1])); | |
const q2 = psi(q[0], q[1]); | |
pointAdd(Rx, Ry, Rz, q2[0], Fp2.neg(q2[1])); | |
} | |
}); | |
weierstrass({ | |
a: BigInt(0), | |
b: BigInt(3), | |
Fp, | |
n: BigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617"), | |
Gx: BigInt(1), | |
Gy: BigInt(2), | |
h: BigInt(1), | |
...getHash(sha256$1) | |
}); | |
const G1_INFINITY_POINT_BYTES = new Uint8Array(64); | |
const G2_INFINITY_POINT_BYTES = new Uint8Array(128); | |
const G1_POINT_BYTE_LENGTH = 64; | |
const G1_ELEMENT_BYTE_LENGTH = 32; | |
const G2_POINT_BYTE_LENGTH = 128; | |
const ZERO_BUFFER = new Uint8Array(32); | |
const ONE_BUFFER = concatBytes$1(new Uint8Array(31), hexToBytes$1("0x01")); | |
function toG1Point(input) { | |
if (equalsBytes(input, G1_INFINITY_POINT_BYTES)) { | |
return bn254.G1.ProjectivePoint.ZERO; | |
} | |
const x = bytesToBigInt(input.subarray(0, G1_ELEMENT_BYTE_LENGTH)); | |
const y = bytesToBigInt(input.subarray(G1_ELEMENT_BYTE_LENGTH, G1_POINT_BYTE_LENGTH)); | |
const G1 = bn254.G1.ProjectivePoint.fromAffine({ | |
x, | |
y | |
}); | |
G1.assertValidity(); | |
return G1; | |
} | |
function fromG1Point(input) { | |
const xBytes = setLengthLeft(bigIntToBytes(input.x), G1_ELEMENT_BYTE_LENGTH); | |
const yBytes = setLengthLeft(bigIntToBytes(input.y), G1_ELEMENT_BYTE_LENGTH); | |
return concatBytes$1(xBytes, yBytes); | |
} | |
function toFrPoint(input) { | |
const Fr2 = bn254.fields.Fr.fromBytes(input); | |
if (Fr2 >= bn254.fields.Fr.ORDER) { | |
return Fr2 % bn254.fields.Fr.ORDER; | |
} | |
return Fr2; | |
} | |
function toG2Point(input) { | |
if (equalsBytes(input, G2_INFINITY_POINT_BYTES)) { | |
return bn254.G2.ProjectivePoint.ZERO; | |
} | |
const p_x_2 = input.subarray(0, G1_ELEMENT_BYTE_LENGTH); | |
const p_x_1 = input.subarray(G1_ELEMENT_BYTE_LENGTH, G1_ELEMENT_BYTE_LENGTH * 2); | |
const start2 = G1_ELEMENT_BYTE_LENGTH * 2; | |
const p_y_2 = input.subarray(start2, start2 + G1_ELEMENT_BYTE_LENGTH); | |
const p_y_1 = input.subarray(start2 + G1_ELEMENT_BYTE_LENGTH, start2 + G1_ELEMENT_BYTE_LENGTH * 2); | |
for (const p of [p_x_1, p_x_2, p_y_1, p_y_2]) { | |
const pB = bytesToBigInt(p); | |
if (bn254.fields.Fp.create(pB) !== pB) { | |
throw new EvmError(ERROR.BN254_FP_NOT_IN_FIELD); | |
} | |
} | |
const Fp2X = toFp2Point(p_x_1, p_x_2); | |
const Fp2Y = toFp2Point(p_y_1, p_y_2); | |
const pG2 = bn254.G2.ProjectivePoint.fromAffine({ | |
x: Fp2X, | |
y: Fp2Y | |
}); | |
pG2.assertValidity(); | |
return pG2; | |
} | |
function toFp2Point(fpXCoordinate, fpYCoordinate) { | |
if (bytesToBigInt(fpXCoordinate) >= bn254.fields.Fp2.ORDER) { | |
throw new EvmError(ERROR.BN254_FP_NOT_IN_FIELD); | |
} | |
if (bytesToBigInt(fpYCoordinate) >= bn254.fields.Fp2.ORDER) { | |
throw new EvmError(ERROR.BN254_FP_NOT_IN_FIELD); | |
} | |
const fpBytes = concatBytes$1(fpXCoordinate, fpYCoordinate); | |
const FP = bn254.fields.Fp2.fromBytes(fpBytes); | |
return FP; | |
} | |
class NobleBN254 { | |
add(input) { | |
const p1 = toG1Point(input.slice(0, G1_POINT_BYTE_LENGTH)); | |
const p2 = toG1Point(input.slice(G1_POINT_BYTE_LENGTH, G1_POINT_BYTE_LENGTH * 2)); | |
const result = fromG1Point(p1.add(p2)); | |
return result; | |
} | |
mul(input) { | |
const p1 = toG1Point(input.slice(0, G1_POINT_BYTE_LENGTH)); | |
const scalar = toFrPoint(input.slice(G1_POINT_BYTE_LENGTH, 96)); | |
if (scalar === BIGINT_0) { | |
return G1_INFINITY_POINT_BYTES; | |
} | |
const result = fromG1Point(p1.multiply(scalar)); | |
return result; | |
} | |
pairing(input) { | |
const pairLength = 192; | |
const pairs = []; | |
for (let k = 0; k < input.length / pairLength; k++) { | |
const pairStart = pairLength * k; | |
const G1 = toG1Point(input.subarray(pairStart, pairStart + G1_POINT_BYTE_LENGTH)); | |
const g2start = pairStart + G1_POINT_BYTE_LENGTH; | |
const G2 = toG2Point(input.subarray(g2start, g2start + G2_POINT_BYTE_LENGTH)); | |
if (G1 === bn254.G1.ProjectivePoint.ZERO || G2 === bn254.G2.ProjectivePoint.ZERO) { | |
continue; | |
} | |
pairs.push({ g1: G1, g2: G2 }); | |
} | |
const res = bn254.pairingBatch(pairs); | |
if (bn254.fields.Fp12.eql(res, bn254.fields.Fp12.ONE) === true) { | |
return ONE_BUFFER; | |
} else { | |
return ZERO_BUFFER; | |
} | |
} | |
} | |
class RustBN254 { | |
constructor(rustbn) { | |
this._rustbn = rustbn; | |
} | |
add(input) { | |
const inputStr = bytesToUnprefixedHex(input); | |
return hexToBytes$1(this._rustbn.ec_add(inputStr)); | |
} | |
mul(input) { | |
const inputHex = bytesToUnprefixedHex(input); | |
return hexToBytes$1(this._rustbn.ec_mul(inputHex)); | |
} | |
pairing(input) { | |
const inputStr = bytesToUnprefixedHex(input); | |
return hexToBytes$1(this._rustbn.ec_pairing(inputStr)); | |
} | |
} | |
const BYTES_19 = "00000000000000000000000000000000000000"; | |
const precompileEntries = [ | |
{ | |
address: BYTES_19 + "01", | |
check: { | |
type: 1, | |
param: Hardfork.Chainstart | |
}, | |
precompile: precompile01, | |
name: "ECRECOVER (0x01)" | |
}, | |
{ | |
address: BYTES_19 + "02", | |
check: { | |
type: 1, | |
param: Hardfork.Chainstart | |
}, | |
precompile: precompile02, | |
name: "SHA256 (0x02)" | |
}, | |
{ | |
address: BYTES_19 + "03", | |
check: { | |
type: 1, | |
param: Hardfork.Chainstart | |
}, | |
precompile: precompile03, | |
name: "RIPEMD160 (0x03)" | |
}, | |
{ | |
address: BYTES_19 + "04", | |
check: { | |
type: 1, | |
param: Hardfork.Chainstart | |
}, | |
precompile: precompile04, | |
name: "IDENTITY (0x04)" | |
}, | |
{ | |
address: BYTES_19 + "05", | |
check: { | |
type: 1, | |
param: Hardfork.Byzantium | |
}, | |
precompile: precompile05, | |
name: "MODEXP (0x05)" | |
}, | |
{ | |
address: BYTES_19 + "06", | |
check: { | |
type: 1, | |
param: Hardfork.Byzantium | |
}, | |
precompile: precompile06, | |
name: "BN254_ADD (0x06)" | |
}, | |
{ | |
address: BYTES_19 + "07", | |
check: { | |
type: 1, | |
param: Hardfork.Byzantium | |
}, | |
precompile: precompile07, | |
name: "BN254_MUL (0x07)" | |
}, | |
{ | |
address: BYTES_19 + "08", | |
check: { | |
type: 1, | |
param: Hardfork.Byzantium | |
}, | |
precompile: precompile08, | |
name: "BN254_PAIRING (0x08)" | |
}, | |
{ | |
address: BYTES_19 + "09", | |
check: { | |
type: 1, | |
param: Hardfork.Istanbul | |
}, | |
precompile: precompile09, | |
name: "BLAKE2f (0x09)" | |
}, | |
{ | |
address: BYTES_19 + "0a", | |
check: { | |
type: 0, | |
param: 4844 | |
}, | |
precompile: precompile0a, | |
name: "KZG_POINT_EVALUATION (0x0a)" | |
}, | |
{ | |
address: BYTES_19 + "0b", | |
check: { | |
type: 0, | |
param: 2537 | |
}, | |
precompile: precompile0b, | |
name: "BLS12_G1ADD (0x0b)" | |
}, | |
{ | |
address: BYTES_19 + "0c", | |
check: { | |
type: 0, | |
param: 2537 | |
}, | |
precompile: precompile0c, | |
name: "BLS12_G1MUL (0x0c)" | |
}, | |
{ | |
address: BYTES_19 + "0d", | |
check: { | |
type: 0, | |
param: 2537 | |
}, | |
precompile: precompile0d, | |
name: "BLS12_G1MSM (0x0d)" | |
}, | |
{ | |
address: BYTES_19 + "0e", | |
check: { | |
type: 0, | |
param: 2537 | |
}, | |
precompile: precompile0e, | |
name: "BLS12_G2ADD (0x0e)" | |
}, | |
{ | |
address: BYTES_19 + "0f", | |
check: { | |
type: 0, | |
param: 2537 | |
}, | |
precompile: precompile0f, | |
name: "BLS12_G2MUL (0x0f)" | |
}, | |
{ | |
address: BYTES_19 + "10", | |
check: { | |
type: 0, | |
param: 2537 | |
}, | |
precompile: precompile10, | |
name: "BLS12_G2MSM (0x10)" | |
}, | |
{ | |
address: BYTES_19 + "11", | |
check: { | |
type: 0, | |
param: 2537 | |
}, | |
precompile: precompile11, | |
name: "BLS12_PAIRING (0x11)" | |
}, | |
{ | |
address: BYTES_19 + "12", | |
check: { | |
type: 0, | |
param: 2537 | |
}, | |
precompile: precompile12, | |
name: "BLS12_MAP_FP_TO_G1 (0x12)" | |
}, | |
{ | |
address: BYTES_19 + "13", | |
check: { | |
type: 0, | |
param: 2537 | |
}, | |
precompile: precompile13, | |
name: "BLS12_MAP_FP2_TO_G2 (0x13)" | |
} | |
]; | |
function getActivePrecompiles(common2, customPrecompiles) { | |
const precompileMap = /* @__PURE__ */ new Map(); | |
if (customPrecompiles) { | |
for (const precompile of customPrecompiles) { | |
precompileMap.set( | |
bytesToUnprefixedHex(precompile.address.bytes), | |
"function" in precompile ? precompile.function : void 0 | |
); | |
} | |
} | |
for (const entry of precompileEntries) { | |
if (precompileMap.has(entry.address)) { | |
continue; | |
} | |
const type = entry.check.type; | |
if (type === 1 && common2.gteHardfork(entry.check.param) || entry.check.type === 0 && common2.isActivatedEIP(entry.check.param)) { | |
precompileMap.set(entry.address, entry.precompile); | |
} | |
} | |
return precompileMap; | |
} | |
function getPrecompileName(addressUnprefixedStr) { | |
if (addressUnprefixedStr.length < 40) { | |
addressUnprefixedStr = addressUnprefixedStr.padStart(40, "0"); | |
} | |
for (const entry of precompileEntries) { | |
if (entry.address === addressUnprefixedStr) { | |
return entry.name; | |
} | |
} | |
return ""; | |
} | |
class TransientStorage { | |
constructor() { | |
this._storage = /* @__PURE__ */ new Map(); | |
this._changeJournal = []; | |
this._indices = [0]; | |
} | |
/** | |
* Get the value for the given address and key | |
* @param addr the address for which transient storage is accessed | |
* @param key the key of the address to get | |
*/ | |
get(addr, key) { | |
const map = this._storage.get(addr.toString()); | |
if (!map) { | |
return new Uint8Array(32); | |
} | |
const value = map.get(bytesToHex$1(key)); | |
if (!value) { | |
return new Uint8Array(32); | |
} | |
return value; | |
} | |
/** | |
* Put the given value for the address and key | |
* @param addr the address of the contract for which the key is being set | |
* @param key the slot to set for the address | |
* @param value the new value of the transient storage slot to set | |
*/ | |
put(addr, key, value) { | |
if (key.length !== 32) { | |
throw new Error("Transient storage key must be 32 bytes long"); | |
} | |
if (value.length > 32) { | |
throw new Error("Transient storage value cannot be longer than 32 bytes"); | |
} | |
const addrString = addr.toString(); | |
if (!this._storage.has(addrString)) { | |
this._storage.set(addrString, /* @__PURE__ */ new Map()); | |
} | |
const map = this._storage.get(addrString); | |
const keyStr = bytesToHex$1(key); | |
const prevValue = map.get(keyStr) ?? new Uint8Array(32); | |
this._changeJournal.push({ | |
addr: addrString, | |
key: keyStr, | |
prevValue | |
}); | |
map.set(keyStr, value); | |
} | |
/** | |
* Commit all the changes since the last checkpoint | |
*/ | |
commit() { | |
if (this._indices.length === 0) throw new Error("Nothing to commit"); | |
this._indices.pop(); | |
} | |
/** | |
* To be called whenever entering a new context. If revert is called after checkpoint, all changes after the latest checkpoint are reverted. | |
*/ | |
checkpoint() { | |
this._indices.push(this._changeJournal.length); | |
} | |
/** | |
* Revert transient storage to the last checkpoint | |
*/ | |
revert() { | |
const lastCheckpoint = this._indices.pop(); | |
if (typeof lastCheckpoint === "undefined") throw new Error("Nothing to revert"); | |
for (let i = this._changeJournal.length - 1; i >= lastCheckpoint; i--) { | |
const { key, prevValue, addr } = this._changeJournal[i]; | |
this._storage.get(addr).set(key, prevValue); | |
} | |
this._changeJournal.splice(lastCheckpoint, this._changeJournal.length - lastCheckpoint); | |
} | |
/** | |
* Create a JSON representation of the current transient storage state | |
*/ | |
toJSON() { | |
const result = {}; | |
for (const [address, map] of this._storage.entries()) { | |
result[address] = {}; | |
for (const [key, value] of map.entries()) { | |
result[address][key] = bytesToHex$1(value); | |
} | |
} | |
return result; | |
} | |
/** | |
* Clear transient storage state. | |
*/ | |
clear() { | |
this._storage = /* @__PURE__ */ new Map(); | |
this._changeJournal = []; | |
} | |
} | |
const debug = debugDefault("evm:evm"); | |
const debugGas = debugDefault("evm:gas"); | |
const debugPrecompiles = debugDefault("evm:precompiles"); | |
const _EVM = class _EVM { | |
/** | |
* | |
* Creates new EVM object | |
* | |
* @deprecated The direct usage of this constructor is replaced since | |
* non-finalized async initialization lead to side effects. Please | |
* use the async {@link createEVM} constructor instead (same API). | |
* | |
* @param opts The EVM options | |
* @param bn128 Initialized bn128 WASM object for precompile usage (internal) | |
*/ | |
constructor(opts) { | |
var _a, _b, _c, _d; | |
this.DEBUG = false; | |
this.common = opts.common; | |
this.blockchain = opts.blockchain; | |
this.stateManager = opts.stateManager; | |
if (this.common.isActivatedEIP(6800)) { | |
const mandatory = ["checkChunkWitnessPresent"]; | |
for (const m of mandatory) { | |
if (!(m in this.stateManager)) { | |
throw new Error( | |
`State manager used must implement ${m} if Verkle (EIP-6800) is activated` | |
); | |
} | |
} | |
} | |
this.events = new AsyncEventEmitter(); | |
this._optsCached = opts; | |
const supportedEIPs = [ | |
663, | |
1153, | |
1559, | |
2537, | |
2565, | |
2718, | |
2929, | |
2930, | |
2935, | |
3198, | |
3529, | |
3540, | |
3541, | |
3607, | |
3651, | |
3670, | |
3855, | |
3860, | |
4200, | |
4399, | |
4750, | |
4788, | |
4844, | |
4895, | |
5133, | |
5450, | |
5656, | |
6110, | |
6206, | |
6780, | |
6800, | |
7002, | |
7069, | |
7251, | |
7480, | |
7516, | |
7620, | |
7685, | |
7692, | |
7698, | |
7702, | |
7709 | |
]; | |
for (const eip of this.common.eips()) { | |
if (!supportedEIPs.includes(eip)) { | |
throw new Error(`EIP-${eip} is not supported by the EVM`); | |
} | |
} | |
if (!_EVM.supportedHardforks.includes(this.common.hardfork())) { | |
throw new Error( | |
`Hardfork ${this.common.hardfork()} not set as supported in supportedHardforks` | |
); | |
} | |
this.common.updateParams(opts.params ?? paramsEVM); | |
this.allowUnlimitedContractSize = opts.allowUnlimitedContractSize ?? false; | |
this.allowUnlimitedInitCodeSize = opts.allowUnlimitedInitCodeSize ?? false; | |
this._customOpcodes = opts.customOpcodes; | |
this._customPrecompiles = opts.customPrecompiles; | |
this.journal = new Journal(this.stateManager, this.common); | |
this.transientStorage = new TransientStorage(); | |
this.common.events.on("hardforkChanged", () => { | |
this.getActiveOpcodes(); | |
this._precompiles = getActivePrecompiles(this.common, this._customPrecompiles); | |
}); | |
this.getActiveOpcodes(); | |
this._precompiles = getActivePrecompiles(this.common, this._customPrecompiles); | |
if (this.common.isActivatedEIP(2537)) { | |
this._bls = opts.bls ?? new NobleBLS(); | |
(_b = (_a = this._bls).init) == null ? void 0 : _b.call(_a); | |
} | |
this._bn254 = opts.bn254; | |
this._emit = async (topic, data) => { | |
return new Promise((resolve) => this.events.emit(topic, data, resolve)); | |
}; | |
this.performanceLogger = new EVMPerformanceLogger(); | |
this.DEBUG = typeof window === "undefined" ? ((_d = (_c = process == null ? void 0 : process.env) == null ? void 0 : _c.DEBUG) == null ? void 0 : _d.includes("ethjs")) ?? false : false; | |
} | |
get precompiles() { | |
return this._precompiles; | |
} | |
get opcodes() { | |
return this._opcodes; | |
} | |
/** | |
* Returns a list with the currently activated opcodes | |
* available for EVM execution | |
*/ | |
getActiveOpcodes() { | |
const data = getOpcodesForHF(this.common, this._customOpcodes); | |
this._opcodes = data.opcodes; | |
this._dynamicGasHandlers = data.dynamicGasHandlers; | |
this._handlers = data.handlers; | |
this._opcodeMap = data.opcodeMap; | |
return data.opcodes; | |
} | |
async _executeCall(message) { | |
var _a, _b; | |
let gasLimit = message.gasLimit; | |
const fromAddress = message.caller; | |
if (this.common.isActivatedEIP(6800)) { | |
const sendsValue = message.value !== BIGINT_0; | |
if (message.depth === 0) { | |
const originAccessGas = message.accessWitness.touchTxOriginAndComputeGas(fromAddress); | |
debugGas(`originAccessGas=${originAccessGas} waived off for origin at depth=0`); | |
const destAccessGas = message.accessWitness.touchTxTargetAndComputeGas(message.to, { | |
sendsValue | |
}); | |
debugGas(`destAccessGas=${destAccessGas} waived off for target at depth=0`); | |
} | |
let callAccessGas = message.accessWitness.touchAndChargeMessageCall(message.to); | |
if (sendsValue) { | |
callAccessGas += message.accessWitness.touchAndChargeValueTransfer(fromAddress, message.to); | |
} | |
gasLimit -= callAccessGas; | |
if (gasLimit < BIGINT_0) { | |
if (this.DEBUG) { | |
debugGas(`callAccessGas charged(${callAccessGas}) caused OOG (-> ${gasLimit})`); | |
} | |
return { execResult: OOGResult(message.gasLimit) }; | |
} else { | |
if (this.DEBUG) { | |
debugGas(`callAccessGas used (${callAccessGas} gas (-> ${gasLimit}))`); | |
} | |
} | |
} | |
let account = await this.stateManager.getAccount(fromAddress); | |
if (!account) { | |
account = new Account(); | |
} | |
let errorMessage; | |
if (!message.delegatecall) { | |
try { | |
await this._reduceSenderBalance(account, message); | |
} catch (e) { | |
errorMessage = e; | |
} | |
} | |
let toAccount = await this.stateManager.getAccount(message.to); | |
if (!toAccount) { | |
if (this.common.isActivatedEIP(6800)) { | |
const absenceProofAccessGas = message.accessWitness.touchAndChargeProofOfAbsence( | |
message.to | |
); | |
gasLimit -= absenceProofAccessGas; | |
if (gasLimit < BIGINT_0) { | |
if (this.DEBUG) { | |
debugGas( | |
`Proof of absence access charged(${absenceProofAccessGas}) caused OOG (-> ${gasLimit})` | |
); | |
} | |
return { execResult: OOGResult(message.gasLimit) }; | |
} else { | |
if (this.DEBUG) { | |
debugGas(`Proof of absence access used (${absenceProofAccessGas} gas (-> ${gasLimit}))`); | |
} | |
} | |
} | |
toAccount = new Account(); | |
} | |
if (!message.delegatecall) { | |
try { | |
await this._addToBalance(toAccount, message); | |
} catch (e) { | |
errorMessage = e; | |
} | |
} | |
await this._loadCode(message); | |
let exit = false; | |
if (!message.code || typeof message.code !== "function" && message.code.length === 0) { | |
exit = true; | |
if (this.DEBUG) { | |
debug(`Exit early on no code (CALL)`); | |
} | |
} | |
if (errorMessage !== void 0) { | |
exit = true; | |
if (this.DEBUG) { | |
debug(`Exit early on value transfer overflowed (CALL)`); | |
} | |
} | |
if (exit) { | |
return { | |
execResult: { | |
gasRefund: message.gasRefund, | |
executionGasUsed: message.gasLimit - gasLimit, | |
exceptionError: errorMessage, | |
// Only defined if addToBalance failed | |
returnValue: new Uint8Array(0) | |
} | |
}; | |
} | |
let result; | |
if (message.isCompiled) { | |
let timer; | |
let callTimer; | |
let target; | |
if (((_a = this._optsCached.profiler) == null ? void 0 : _a.enabled) === true) { | |
target = bytesToUnprefixedHex(message.codeAddress.bytes); | |
target = getPrecompileName(target) ?? target.slice(20); | |
if (this.performanceLogger.hasTimer()) { | |
callTimer = this.performanceLogger.pauseTimer(); | |
} | |
timer = this.performanceLogger.startTimer(target); | |
} | |
result = await this.runPrecompile(message.code, message.data, gasLimit); | |
if (((_b = this._optsCached.profiler) == null ? void 0 : _b.enabled) === true) { | |
this.performanceLogger.stopTimer(timer, Number(result.executionGasUsed), "precompiles"); | |
if (callTimer !== void 0) { | |
this.performanceLogger.unpauseTimer(callTimer); | |
} | |
} | |
result.gasRefund = message.gasRefund; | |
} else { | |
if (this.DEBUG) { | |
debug(`Start bytecode processing...`); | |
} | |
result = await this.runInterpreter({ ...message, gasLimit }); | |
} | |
if (message.depth === 0) { | |
this.postMessageCleanup(); | |
} | |
result.executionGasUsed += message.gasLimit - gasLimit; | |
return { | |
execResult: result | |
}; | |
} | |
async _executeCreate(message) { | |
let gasLimit = message.gasLimit; | |
const fromAddress = message.caller; | |
if (this.common.isActivatedEIP(6800)) { | |
if (message.depth === 0) { | |
const originAccessGas = message.accessWitness.touchTxOriginAndComputeGas(fromAddress); | |
debugGas(`originAccessGas=${originAccessGas} waived off for origin at depth=0`); | |
} | |
} | |
let account = await this.stateManager.getAccount(message.caller); | |
if (!account) { | |
account = new Account(); | |
} | |
await this._reduceSenderBalance(account, message); | |
if (this.common.isActivatedEIP(3860)) { | |
if (message.data.length > Number(this.common.param("maxInitCodeSize")) && !this.allowUnlimitedInitCodeSize) { | |
return { | |
createdAddress: message.to, | |
execResult: { | |
returnValue: new Uint8Array(0), | |
exceptionError: new EvmError(ERROR.INITCODE_SIZE_VIOLATION), | |
executionGasUsed: message.gasLimit | |
} | |
}; | |
} | |
} | |
message.code = message.data; | |
message.data = message.eofCallData ?? new Uint8Array(); | |
message.to = await this._generateAddress(message); | |
if (this.common.isActivatedEIP(6780)) { | |
message.createdAddresses.add(message.to.toString()); | |
} | |
if (this.DEBUG) { | |
debug(`Generated CREATE contract address ${message.to}`); | |
} | |
let toAccount = await this.stateManager.getAccount(message.to); | |
if (!toAccount) { | |
toAccount = new Account(); | |
} | |
if (this.common.isActivatedEIP(6800)) { | |
const contractCreateAccessGas = message.accessWitness.touchAndChargeContractCreateInit( | |
message.to | |
); | |
gasLimit -= contractCreateAccessGas; | |
if (gasLimit < BIGINT_0) { | |
if (this.DEBUG) { | |
debugGas( | |
`ContractCreateInit charge(${contractCreateAccessGas}) caused OOG (-> ${gasLimit})` | |
); | |
} | |
return { execResult: OOGResult(message.gasLimit) }; | |
} else { | |
if (this.DEBUG) { | |
debugGas(`ContractCreateInit charged (${contractCreateAccessGas} gas (-> ${gasLimit}))`); | |
} | |
} | |
} | |
if (toAccount.nonce && toAccount.nonce > BIGINT_0 || !(equalsBytes(toAccount.codeHash, KECCAK256_NULL) === true) || // See EIP 7610 and the discussion `https://ethereum-magicians.org/t/eip-7610-revert-creation-in-case-of-non-empty-storage` | |
!(equalsBytes(toAccount.storageRoot, KECCAK256_RLP) === true)) { | |
if (this.DEBUG) { | |
debug(`Returning on address collision`); | |
} | |
return { | |
createdAddress: message.to, | |
execResult: { | |
returnValue: new Uint8Array(0), | |
exceptionError: new EvmError(ERROR.CREATE_COLLISION), | |
executionGasUsed: message.gasLimit | |
} | |
}; | |
} | |
await this.journal.putAccount(message.to, toAccount); | |
await this.stateManager.clearStorage(message.to); | |
const newContractEvent = { | |
address: message.to, | |
code: message.code | |
}; | |
await this._emit("newContract", newContractEvent); | |
toAccount = await this.stateManager.getAccount(message.to); | |
if (!toAccount) { | |
toAccount = new Account(); | |
} | |
if (this.common.gteHardfork(Hardfork.SpuriousDragon)) { | |
toAccount.nonce += BIGINT_1; | |
} | |
let errorMessage; | |
try { | |
await this._addToBalance(toAccount, message); | |
} catch (e) { | |
errorMessage = e; | |
} | |
let exit = false; | |
if (message.code === void 0 || typeof message.code !== "function" && message.code.length === 0) { | |
exit = true; | |
if (this.DEBUG) { | |
debug(`Exit early on no code (CREATE)`); | |
} | |
} | |
if (errorMessage !== void 0) { | |
exit = true; | |
if (this.DEBUG) { | |
debug(`Exit early on value transfer overflowed (CREATE)`); | |
} | |
} | |
if (exit) { | |
if (this.common.isActivatedEIP(6800)) { | |
const createCompleteAccessGas = message.accessWitness.touchAndChargeContractCreateCompleted(message.to); | |
gasLimit -= createCompleteAccessGas; | |
if (gasLimit < BIGINT_0) { | |
if (this.DEBUG) { | |
debug( | |
`ContractCreateComplete access gas (${createCompleteAccessGas}) caused OOG (-> ${gasLimit})` | |
); | |
} | |
return { execResult: OOGResult(message.gasLimit) }; | |
} else { | |
debug( | |
`ContractCreateComplete access used (${createCompleteAccessGas}) gas (-> ${gasLimit})` | |
); | |
} | |
} | |
return { | |
createdAddress: message.to, | |
execResult: { | |
executionGasUsed: message.gasLimit - gasLimit, | |
gasRefund: message.gasRefund, | |
exceptionError: errorMessage, | |
// only defined if addToBalance failed | |
returnValue: new Uint8Array(0) | |
} | |
}; | |
} | |
if (this.DEBUG) { | |
debug(`Start bytecode processing...`); | |
} | |
let result = await this.runInterpreter({ ...message, gasLimit, isCreate: true }); | |
result.executionGasUsed += message.gasLimit - gasLimit; | |
let totalGas = result.executionGasUsed; | |
let returnFee = BIGINT_0; | |
if (!result.exceptionError && !this.common.isActivatedEIP(6800)) { | |
returnFee = BigInt(result.returnValue.length) * BigInt(this.common.param("createDataGas")); | |
totalGas = totalGas + returnFee; | |
if (this.DEBUG) { | |
debugGas(`Add return value size fee (${returnFee} to gas used (-> ${totalGas}))`); | |
} | |
} | |
let allowedCodeSize = true; | |
if (!result.exceptionError && this.common.gteHardfork(Hardfork.SpuriousDragon) && result.returnValue.length > Number(this.common.param("maxCodeSize"))) { | |
allowedCodeSize = false; | |
} | |
let CodestoreOOG = false; | |
if (totalGas <= message.gasLimit && (this.allowUnlimitedContractSize || allowedCodeSize)) { | |
if (this.common.isActivatedEIP(3541) && result.returnValue[0] === FORMAT) { | |
if (!this.common.isActivatedEIP(3540)) { | |
result = { ...result, ...INVALID_BYTECODE_RESULT(message.gasLimit) }; | |
} else if ( | |
// TODO check if this is correct | |
// Also likely cleanup this eofCallData stuff | |
/*(message.depth > 0 && message.eofCallData === undefined) || | |
(message.depth === 0 && !isEOF(message.code))*/ | |
!isEOF(message.code) | |
) { | |
result = { ...result, ...INVALID_BYTECODE_RESULT(message.gasLimit) }; | |
} else { | |
result.executionGasUsed = totalGas; | |
} | |
} else { | |
result.executionGasUsed = totalGas; | |
} | |
} else { | |
if (this.common.gteHardfork(Hardfork.Homestead)) { | |
if (!allowedCodeSize) { | |
if (this.DEBUG) { | |
debug(`Code size exceeds maximum code size (>= SpuriousDragon)`); | |
} | |
result = { ...result, ...CodesizeExceedsMaximumError(message.gasLimit) }; | |
} else { | |
if (this.DEBUG) { | |
debug(`Contract creation: out of gas`); | |
} | |
result = { ...result, ...OOGResult(message.gasLimit) }; | |
} | |
} else { | |
if (totalGas - returnFee <= message.gasLimit) { | |
if (this.DEBUG) { | |
debug(`Not enough gas to pay the code deposit fee (Frontier)`); | |
} | |
result = { ...result, ...COOGResult(totalGas - returnFee) }; | |
CodestoreOOG = true; | |
} else { | |
if (this.DEBUG) { | |
debug(`Contract creation: out of gas`); | |
} | |
result = { ...result, ...OOGResult(message.gasLimit) }; | |
} | |
} | |
} | |
gasLimit = message.gasLimit - result.executionGasUsed; | |
if (!result.exceptionError && this.common.isActivatedEIP(6800)) { | |
const createCompleteAccessGas = message.accessWitness.touchAndChargeContractCreateCompleted( | |
message.to | |
); | |
gasLimit -= createCompleteAccessGas; | |
if (gasLimit < BIGINT_0) { | |
if (this.DEBUG) { | |
debug( | |
`ContractCreateComplete access gas (${createCompleteAccessGas}) caused OOG (-> ${gasLimit})` | |
); | |
} | |
result = { ...result, ...OOGResult(message.gasLimit) }; | |
} else { | |
debug( | |
`ContractCreateComplete access used (${createCompleteAccessGas}) gas (-> ${gasLimit})` | |
); | |
result.executionGasUsed += createCompleteAccessGas; | |
} | |
} | |
if (!result.exceptionError && result.returnValue !== void 0 && result.returnValue.length !== 0) { | |
if (this.common.isActivatedEIP(6800)) { | |
const byteCodeWriteAccessfee = message.accessWitness.touchCodeChunksRangeOnWriteAndChargeGas( | |
message.to, | |
0, | |
result.returnValue.length - 1 | |
); | |
gasLimit -= byteCodeWriteAccessfee; | |
if (gasLimit < BIGINT_0) { | |
if (this.DEBUG) { | |
debug( | |
`byteCodeWrite access gas (${byteCodeWriteAccessfee}) caused OOG (-> ${gasLimit})` | |
); | |
} | |
result = { ...result, ...OOGResult(message.gasLimit) }; | |
} else { | |
debug(`byteCodeWrite access used (${byteCodeWriteAccessfee}) gas (-> ${gasLimit})`); | |
result.executionGasUsed += byteCodeWriteAccessfee; | |
} | |
} | |
await this.stateManager.putCode(message.to, result.returnValue); | |
if (this.DEBUG) { | |
debug(`Code saved on new contract creation`); | |
} | |
} else if (CodestoreOOG) { | |
if (!this.common.gteHardfork(Hardfork.Homestead)) { | |
const account2 = await this.stateManager.getAccount(message.to); | |
await this.journal.putAccount(message.to, account2 ?? new Account()); | |
} | |
} | |
if (message.depth === 0) { | |
this.postMessageCleanup(); | |
} | |
return { | |
createdAddress: message.to, | |
execResult: result | |
}; | |
} | |
/** | |
* Starts the actual bytecode processing for a CALL or CREATE | |
*/ | |
async runInterpreter(message, opts = {}) { | |
var _a; | |
let contract = await this.stateManager.getAccount(message.to ?? createZeroAddress()); | |
if (!contract) { | |
contract = new Account(); | |
} | |
const env = { | |
address: message.to ?? createZeroAddress(), | |
caller: message.caller ?? createZeroAddress(), | |
callData: message.data ?? Uint8Array.from([0]), | |
callValue: message.value ?? BIGINT_0, | |
code: message.code, | |
isStatic: message.isStatic ?? false, | |
isCreate: message.isCreate ?? false, | |
depth: message.depth ?? 0, | |
gasPrice: this._tx.gasPrice, | |
origin: this._tx.origin ?? message.caller ?? createZeroAddress(), | |
block: this._block ?? defaultBlock(), | |
contract, | |
codeAddress: message.codeAddress, | |
gasRefund: message.gasRefund, | |
chargeCodeAccesses: message.chargeCodeAccesses, | |
blobVersionedHashes: message.blobVersionedHashes ?? [], | |
accessWitness: message.accessWitness, | |
createdAddresses: message.createdAddresses | |
}; | |
const interpreter = new Interpreter( | |
this, | |
this.stateManager, | |
this.blockchain, | |
env, | |
message.gasLimit, | |
this.journal, | |
this.performanceLogger, | |
this._optsCached.profiler | |
); | |
if (message.selfdestruct) { | |
interpreter._result.selfdestruct = message.selfdestruct; | |
} | |
if (message.createdAddresses) { | |
interpreter._result.createdAddresses = message.createdAddresses; | |
} | |
const interpreterRes = await interpreter.run(message.code, opts); | |
let result = interpreter._result; | |
let gasUsed = message.gasLimit - interpreterRes.runState.gasLeft; | |
if (interpreterRes.exceptionError) { | |
if (interpreterRes.exceptionError.error !== ERROR.REVERT && interpreterRes.exceptionError.error !== ERROR.INVALID_EOF_FORMAT) { | |
gasUsed = message.gasLimit; | |
} | |
result = { | |
...result, | |
logs: [], | |
selfdestruct: /* @__PURE__ */ new Set(), | |
createdAddresses: /* @__PURE__ */ new Set() | |
}; | |
} | |
return { | |
...result, | |
runState: { | |
...interpreterRes.runState, | |
...result, | |
...interpreter._env | |
}, | |
exceptionError: interpreterRes.exceptionError, | |
gas: (_a = interpreterRes.runState) == null ? void 0 : _a.gasLeft, | |
executionGasUsed: gasUsed, | |
gasRefund: interpreterRes.runState.gasRefund, | |
returnValue: result.returnValue ? result.returnValue : new Uint8Array(0) | |
}; | |
} | |
/** | |
* Executes an EVM message, determining whether it's a call or create | |
* based on the `to` address. It checkpoints the state and reverts changes | |
* if an exception happens during the message execution. | |
*/ | |
async runCall(opts) { | |
var _a, _b; | |
let timer; | |
if ((opts.depth === 0 || opts.message === void 0) && ((_a = this._optsCached.profiler) == null ? void 0 : _a.enabled) === true) { | |
timer = this.performanceLogger.startTimer("Initialization"); | |
} | |
let message = opts.message; | |
let callerAccount; | |
if (!message) { | |
this._block = opts.block ?? defaultBlock(); | |
this._tx = { | |
gasPrice: opts.gasPrice ?? BIGINT_0, | |
origin: opts.origin ?? opts.caller ?? createZeroAddress() | |
}; | |
const caller = opts.caller ?? createZeroAddress(); | |
const value = opts.value ?? BIGINT_0; | |
if (opts.skipBalance === true) { | |
callerAccount = await this.stateManager.getAccount(caller); | |
if (!callerAccount) { | |
callerAccount = new Account(); | |
} | |
if (callerAccount.balance < value) { | |
callerAccount.balance = value; | |
await this.journal.putAccount(caller, callerAccount); | |
} | |
} | |
message = new Message({ | |
caller, | |
gasLimit: opts.gasLimit ?? BigInt(16777215), | |
to: opts.to, | |
value, | |
data: opts.data, | |
code: opts.code, | |
depth: opts.depth, | |
isCompiled: opts.isCompiled, | |
isStatic: opts.isStatic, | |
salt: opts.salt, | |
selfdestruct: opts.selfdestruct ?? /* @__PURE__ */ new Set(), | |
createdAddresses: opts.createdAddresses ?? /* @__PURE__ */ new Set(), | |
delegatecall: opts.delegatecall, | |
blobVersionedHashes: opts.blobVersionedHashes, | |
accessWitness: opts.accessWitness | |
}); | |
} | |
if (message.depth === 0) { | |
if (!callerAccount) { | |
callerAccount = await this.stateManager.getAccount(message.caller); | |
} | |
if (!callerAccount) { | |
callerAccount = new Account(); | |
} | |
callerAccount.nonce++; | |
await this.journal.putAccount(message.caller, callerAccount); | |
if (this.DEBUG) { | |
debug(`Update fromAccount (caller) nonce (-> ${callerAccount.nonce}))`); | |
} | |
} | |
await this._emit("beforeMessage", message); | |
if (!message.to && this.common.isActivatedEIP(2929)) { | |
message.code = message.data; | |
this.journal.addWarmedAddress((await this._generateAddress(message)).bytes); | |
} | |
await this.journal.checkpoint(); | |
if (this.common.isActivatedEIP(1153)) this.transientStorage.checkpoint(); | |
if (this.DEBUG) { | |
debug("-".repeat(100)); | |
debug(`message checkpoint`); | |
} | |
let result; | |
if (this.DEBUG) { | |
const { caller, gasLimit, to, value, delegatecall } = message; | |
debug( | |
`New message caller=${caller} gasLimit=${gasLimit} to=${(to == null ? void 0 : to.toString()) ?? "none"} value=${value} delegatecall=${delegatecall ? "yes" : "no"}` | |
); | |
} | |
if (message.to) { | |
if (this.DEBUG) { | |
debug(`Message CALL execution (to: ${message.to})`); | |
} | |
result = await this._executeCall(message); | |
} else { | |
if (this.DEBUG) { | |
debug(`Message CREATE execution (to undefined)`); | |
} | |
result = await this._executeCreate(message); | |
} | |
if (this.DEBUG) { | |
const { executionGasUsed, exceptionError, returnValue } = result.execResult; | |
debug( | |
`Received message execResult: [ gasUsed=${executionGasUsed} exceptionError=${exceptionError ? `'${exceptionError.error}'` : "none"} returnValue=${short(returnValue)} gasRefund=${result.execResult.gasRefund ?? 0} ]` | |
); | |
} | |
const err = result.execResult.exceptionError; | |
if (err && err.error !== ERROR.CODESTORE_OUT_OF_GAS) { | |
result.execResult.selfdestruct = /* @__PURE__ */ new Set(); | |
result.execResult.createdAddresses = /* @__PURE__ */ new Set(); | |
result.execResult.gasRefund = BIGINT_0; | |
} | |
if (err && !(this.common.hardfork() === Hardfork.Chainstart && err.error === ERROR.CODESTORE_OUT_OF_GAS)) { | |
result.execResult.logs = []; | |
await this.journal.revert(); | |
if (this.common.isActivatedEIP(1153)) this.transientStorage.revert(); | |
if (this.DEBUG) { | |
debug(`message checkpoint reverted`); | |
} | |
} else { | |
await this.journal.commit(); | |
if (this.common.isActivatedEIP(1153)) this.transientStorage.commit(); | |
if (this.DEBUG) { | |
debug(`message checkpoint committed`); | |
} | |
} | |
await this._emit("afterMessage", result); | |
if (message.depth === 0 && ((_b = this._optsCached.profiler) == null ? void 0 : _b.enabled) === true) { | |
this.performanceLogger.stopTimer(timer, 0); | |
} | |
return result; | |
} | |
/** | |
* Bound to the global VM and therefore | |
* shouldn't be used directly from the evm class | |
*/ | |
async runCode(opts) { | |
this._block = opts.block ?? defaultBlock(); | |
this._tx = { | |
gasPrice: opts.gasPrice ?? BIGINT_0, | |
origin: opts.origin ?? opts.caller ?? createZeroAddress() | |
}; | |
const message = new Message({ | |
code: opts.code, | |
data: opts.data, | |
gasLimit: opts.gasLimit ?? BigInt(16777215), | |
to: opts.to ?? createZeroAddress(), | |
caller: opts.caller, | |
value: opts.value, | |
depth: opts.depth, | |
selfdestruct: opts.selfdestruct ?? /* @__PURE__ */ new Set(), | |
isStatic: opts.isStatic, | |
blobVersionedHashes: opts.blobVersionedHashes | |
}); | |
return this.runInterpreter(message, { pc: opts.pc }); | |
} | |
/** | |
* Returns code for precompile at the given address, or undefined | |
* if no such precompile exists. | |
*/ | |
getPrecompile(address) { | |
return this.precompiles.get(bytesToUnprefixedHex(address.bytes)); | |
} | |
/** | |
* Executes a precompiled contract with given data and gas limit. | |
*/ | |
runPrecompile(code, data, gasLimit) { | |
if (typeof code !== "function") { | |
throw new Error("Invalid precompile"); | |
} | |
const opts = { | |
data, | |
gasLimit, | |
common: this.common, | |
_EVM: this, | |
_debug: this.DEBUG ? debugPrecompiles : void 0, | |
stateManager: this.stateManager | |
}; | |
return code(opts); | |
} | |
async _loadCode(message) { | |
if (!message.code) { | |
const precompile = this.getPrecompile(message.codeAddress); | |
if (precompile) { | |
message.code = precompile; | |
message.isCompiled = true; | |
} else { | |
message.code = await this.stateManager.getCode(message.codeAddress); | |
if (this.common.isActivatedEIP(7702) && equalsBytes(message.code.slice(0, 3), DELEGATION_7702_FLAG)) { | |
const address = new Address(message.code.slice(3, 24)); | |
message.code = await this.stateManager.getCode(address); | |
if (message.depth === 0) { | |
this.journal.addAlwaysWarmAddress(address.toString()); | |
} | |
} | |
message.isCompiled = false; | |
message.chargeCodeAccesses = true; | |
} | |
} | |
} | |
async _generateAddress(message) { | |
let addr; | |
if (message.salt) { | |
addr = generateAddress2(message.caller.bytes, message.salt, message.code); | |
} else { | |
let acc = await this.stateManager.getAccount(message.caller); | |
if (!acc) { | |
acc = new Account(); | |
} | |
const newNonce = acc.nonce - BIGINT_1; | |
addr = generateAddress(message.caller.bytes, bigIntToBytes(newNonce)); | |
} | |
return new Address(addr); | |
} | |
async _reduceSenderBalance(account, message) { | |
account.balance -= message.value; | |
if (account.balance < BIGINT_0) { | |
throw new EvmError(ERROR.INSUFFICIENT_BALANCE); | |
} | |
const result = this.journal.putAccount(message.caller, account); | |
if (this.DEBUG) { | |
debug(`Reduced sender (${message.caller}) balance (-> ${account.balance})`); | |
} | |
return result; | |
} | |
async _addToBalance(toAccount, message) { | |
const newBalance = toAccount.balance + message.value; | |
if (newBalance > MAX_INTEGER) { | |
throw new EvmError(ERROR.VALUE_OVERFLOW); | |
} | |
toAccount.balance = newBalance; | |
const result = this.journal.putAccount(message.to, toAccount); | |
if (this.DEBUG) { | |
debug(`Added toAccount (${message.to}) balance (-> ${toAccount.balance})`); | |
} | |
return result; | |
} | |
/** | |
* Once the interpreter has finished depth 0, a post-message cleanup should be done | |
*/ | |
postMessageCleanup() { | |
if (this.common.isActivatedEIP(1153)) this.transientStorage.clear(); | |
} | |
/** | |
* This method copies the EVM, current HF and EIP settings | |
* and returns a new EVM instance. | |
* | |
* Note: this is only a shallow copy and both EVM instances | |
* will point to the same underlying state DB. | |
* | |
* @returns EVM | |
*/ | |
shallowCopy() { | |
const common2 = this.common.copy(); | |
common2.setHardfork(this.common.hardfork()); | |
const opts = { | |
...this._optsCached, | |
common: common2, | |
stateManager: this.stateManager.shallowCopy() | |
}; | |
opts.stateManager.common = common2; | |
return new _EVM(opts); | |
} | |
getPerformanceLogs() { | |
return this.performanceLogger.getLogs(); | |
} | |
clearPerformanceLogs() { | |
this.performanceLogger.clear(); | |
} | |
}; | |
_EVM.supportedHardforks = [ | |
Hardfork.Chainstart, | |
Hardfork.Homestead, | |
Hardfork.Dao, | |
Hardfork.TangerineWhistle, | |
Hardfork.SpuriousDragon, | |
Hardfork.Byzantium, | |
Hardfork.Constantinople, | |
Hardfork.Petersburg, | |
Hardfork.Istanbul, | |
Hardfork.MuirGlacier, | |
Hardfork.Berlin, | |
Hardfork.London, | |
Hardfork.ArrowGlacier, | |
Hardfork.GrayGlacier, | |
Hardfork.MergeForkIdTransition, | |
Hardfork.Paris, | |
Hardfork.Shanghai, | |
Hardfork.Cancun, | |
Hardfork.Prague, | |
Hardfork.Osaka | |
]; | |
let EVM = _EVM; | |
function OOGResult(gasLimit) { | |
return { | |
returnValue: new Uint8Array(0), | |
executionGasUsed: gasLimit, | |
exceptionError: new EvmError(ERROR.OUT_OF_GAS) | |
}; | |
} | |
function COOGResult(gasUsedCreateCode) { | |
return { | |
returnValue: new Uint8Array(0), | |
executionGasUsed: gasUsedCreateCode, | |
exceptionError: new EvmError(ERROR.CODESTORE_OUT_OF_GAS) | |
}; | |
} | |
function INVALID_BYTECODE_RESULT(gasLimit) { | |
return { | |
returnValue: new Uint8Array(0), | |
executionGasUsed: gasLimit, | |
exceptionError: new EvmError(ERROR.INVALID_BYTECODE_RESULT) | |
}; | |
} | |
function CodesizeExceedsMaximumError(gasUsed) { | |
return { | |
returnValue: new Uint8Array(0), | |
executionGasUsed: gasUsed, | |
exceptionError: new EvmError(ERROR.CODESIZE_EXCEEDS_MAXIMUM) | |
}; | |
} | |
function EvmErrorResult(error, gasUsed) { | |
return { | |
returnValue: new Uint8Array(0), | |
executionGasUsed: gasUsed, | |
exceptionError: error | |
}; | |
} | |
function defaultBlock() { | |
return { | |
header: { | |
number: BIGINT_0, | |
coinbase: createZeroAddress(), | |
timestamp: BIGINT_0, | |
difficulty: BIGINT_0, | |
prevRandao: zeros(32), | |
gasLimit: BIGINT_0, | |
baseFeePerGas: void 0, | |
getBlobGasPrice: () => void 0 | |
} | |
}; | |
} | |
class OriginalStorageCache { | |
constructor(getStorage) { | |
this.map = /* @__PURE__ */ new Map(); | |
this.getStorage = getStorage; | |
} | |
async get(address, key) { | |
const addressHex = bytesToUnprefixedHex(address.bytes); | |
const map = this.map.get(addressHex); | |
if (map !== void 0) { | |
const keyHex = bytesToUnprefixedHex(key); | |
const value2 = map.get(keyHex); | |
if (value2 !== void 0) { | |
return value2; | |
} | |
} | |
const value = await this.getStorage(address, key); | |
this.put(address, key, value); | |
return value; | |
} | |
put(address, key, value) { | |
const addressHex = bytesToUnprefixedHex(address.bytes); | |
let map = this.map.get(addressHex); | |
if (map === void 0) { | |
map = /* @__PURE__ */ new Map(); | |
this.map.set(addressHex, map); | |
} | |
const keyHex = bytesToUnprefixedHex(key); | |
if (map.has(keyHex) === false) { | |
map.set(keyHex, value); | |
} | |
} | |
clear() { | |
this.map = /* @__PURE__ */ new Map(); | |
} | |
} | |
async function modifyAccountFields(stateManager, address, accountFields) { | |
const account = await stateManager.getAccount(address) ?? new Account(); | |
account.nonce = accountFields.nonce ?? account.nonce; | |
account.balance = accountFields.balance ?? account.balance; | |
account.storageRoot = accountFields.storageRoot ?? account.storageRoot; | |
account.codeHash = accountFields.codeHash ?? account.codeHash; | |
account.codeSize = accountFields.codeSize ?? account.codeSize; | |
await stateManager.putAccount(address, account); | |
} | |
class SimpleStateManager { | |
constructor(opts = {}) { | |
this.accountStack = []; | |
this.codeStack = []; | |
this.storageStack = []; | |
this.checkpointSync(); | |
this.originalStorageCache = new OriginalStorageCache(this.getStorage.bind(this)); | |
this.common = opts.common; | |
} | |
topAccountStack() { | |
return this.accountStack[this.accountStack.length - 1]; | |
} | |
topCodeStack() { | |
return this.codeStack[this.codeStack.length - 1]; | |
} | |
topStorageStack() { | |
return this.storageStack[this.storageStack.length - 1]; | |
} | |
// Synchronous version of checkpoint() to allow to call from constructor | |
checkpointSync() { | |
const newTopA = new Map(this.topAccountStack()); | |
for (const [address, account] of newTopA) { | |
const accountCopy = account !== void 0 ? Object.assign(Object.create(Object.getPrototypeOf(account)), account) : void 0; | |
newTopA.set(address, accountCopy); | |
} | |
this.accountStack.push(newTopA); | |
this.codeStack.push(new Map(this.topCodeStack())); | |
this.storageStack.push(new Map(this.topStorageStack())); | |
} | |
async getAccount(address) { | |
return this.topAccountStack().get(address.toString()); | |
} | |
async putAccount(address, account) { | |
this.topAccountStack().set(address.toString(), account); | |
} | |
async deleteAccount(address) { | |
this.topAccountStack().set(address.toString(), void 0); | |
} | |
async modifyAccountFields(address, accountFields) { | |
await modifyAccountFields(this, address, accountFields); | |
} | |
async getCode(address) { | |
return this.topCodeStack().get(address.toString()) ?? new Uint8Array(0); | |
} | |
async putCode(address, value) { | |
var _a; | |
this.topCodeStack().set(address.toString(), value); | |
if (await this.getAccount(address) === void 0) { | |
await this.putAccount(address, new Account()); | |
} | |
await this.modifyAccountFields(address, { | |
codeHash: (((_a = this.common) == null ? void 0 : _a.customCrypto.keccak256) ?? keccak256)(value) | |
}); | |
} | |
async getCodeSize(address) { | |
const contractCode = await this.getCode(address); | |
return contractCode.length; | |
} | |
async getStorage(address, key) { | |
return this.topStorageStack().get(`${address.toString()}_${bytesToHex$1(key)}`) ?? new Uint8Array(0); | |
} | |
async putStorage(address, key, value) { | |
this.topStorageStack().set(`${address.toString()}_${bytesToHex$1(key)}`, value); | |
} | |
async clearStorage() { | |
} | |
async checkpoint() { | |
this.checkpointSync(); | |
} | |
async commit() { | |
this.accountStack.splice(-2, 1); | |
this.codeStack.splice(-2, 1); | |
this.storageStack.splice(-2, 1); | |
} | |
async revert() { | |
this.accountStack.pop(); | |
this.codeStack.pop(); | |
this.storageStack.pop(); | |
} | |
async flush() { | |
} | |
clearCaches() { | |
} | |
shallowCopy() { | |
const copy = new SimpleStateManager({ common: this.common }); | |
for (let i = 0; i < this.accountStack.length; i++) { | |
copy.accountStack.push(new Map(this.accountStack[i])); | |
copy.codeStack.push(new Map(this.codeStack[i])); | |
copy.storageStack.push(new Map(this.storageStack[i])); | |
} | |
return copy; | |
} | |
// State root functionality not implemented | |
getStateRoot() { | |
throw new Error("Method not implemented."); | |
} | |
setStateRoot() { | |
throw new Error("Method not implemented."); | |
} | |
hasStateRoot() { | |
throw new Error("Method not implemented."); | |
} | |
} | |
async function createEVM(createOpts) { | |
const opts = createOpts ?? {}; | |
opts.bn254 = new NobleBN254(); | |
if (opts.common === void 0) { | |
opts.common = new Common({ chain: Mainnet }); | |
} | |
if (opts.blockchain === void 0) { | |
opts.blockchain = new EVMMockBlockchain(); | |
} | |
if (opts.stateManager === void 0) { | |
opts.stateManager = new SimpleStateManager(); | |
} | |
return new EVM(opts); | |
} | |
export { | |
EOFContainer, | |
EVM, | |
ERROR as EVMErrorMessage, | |
EVMMockBlockchain, | |
EVMPerformanceLogger, | |
EvmError, | |
MCLBLS, | |
Message, | |
NobleBLS, | |
NobleBN254, | |
RustBN254, | |
Timer, | |
createEVM, | |
getActivePrecompiles, | |
getOpcodesForHF, | |
paramsEVM, | |
validateEOF | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment