|
1 | 1 | import Common from 'ethereumjs-common'
|
2 | 2 |
|
3 |
| -export interface Opcode { |
4 |
| - name: string |
5 |
| - fee: number |
6 |
| - isAsync: boolean |
| 3 | +export class Opcode { |
| 4 | + readonly code: number |
| 5 | + readonly name: string |
| 6 | + readonly fullName: string |
| 7 | + readonly fee: number |
| 8 | + readonly isAsync: boolean |
| 9 | + |
| 10 | + constructor({ |
| 11 | + code, |
| 12 | + name, |
| 13 | + fullName, |
| 14 | + fee, |
| 15 | + isAsync, |
| 16 | + }: { |
| 17 | + code: number |
| 18 | + name: string |
| 19 | + fullName: string |
| 20 | + fee: number |
| 21 | + isAsync: boolean |
| 22 | + }) { |
| 23 | + this.code = code |
| 24 | + this.name = name |
| 25 | + this.fullName = fullName |
| 26 | + this.fee = fee |
| 27 | + this.isAsync = isAsync |
| 28 | + |
| 29 | + // Opcode isn't subject to change, thus all futher modifications are prevented. |
| 30 | + Object.freeze(this) |
| 31 | + } |
7 | 32 | }
|
8 | 33 |
|
9 | 34 | export interface OpcodeList {
|
10 | 35 | [code: number]: Opcode
|
11 | 36 | }
|
12 | 37 |
|
13 |
| -const opcodes: OpcodeList = { |
| 38 | +const opcodes: OpcodeList = createOpcodes({ |
14 | 39 | // 0x0 range - arithmetic ops
|
15 | 40 | // name, baseCost, async
|
16 | 41 | 0x00: { name: 'STOP', fee: 0, isAsync: false },
|
@@ -172,17 +197,69 @@ const opcodes: OpcodeList = {
|
172 | 197 | // '0x70', range - other
|
173 | 198 | 0xfe: { name: 'INVALID', fee: 0, isAsync: false },
|
174 | 199 | 0xff: { name: 'SELFDESTRUCT', fee: 5000, isAsync: true },
|
175 |
| -} |
| 200 | +}) |
176 | 201 |
|
177 |
| -const istanbulOpcodes: OpcodeList = { |
| 202 | +const istanbulOpcodes: OpcodeList = createOpcodes({ |
178 | 203 | 0x31: { name: 'BALANCE', fee: 700, isAsync: true },
|
179 | 204 | 0x3f: { name: 'EXTCODEHASH', fee: 700, isAsync: true },
|
180 | 205 | 0x46: { name: 'CHAINID', fee: 2, isAsync: false },
|
181 | 206 | 0x47: { name: 'SELFBALANCE', fee: 5, isAsync: false },
|
182 | 207 | 0x54: { name: 'SLOAD', fee: 800, isAsync: true },
|
| 208 | +}) |
| 209 | + |
| 210 | +/** |
| 211 | + * Convert basic opcode info dictonary into complete OpcodeList instance. |
| 212 | + * |
| 213 | + * @param opcodes {Object} Receive basic opcodes info dictionary. |
| 214 | + * @returns {OpcodeList} Complete Opcode list |
| 215 | + */ |
| 216 | +function createOpcodes(opcodes: { |
| 217 | + [key: string]: { name: string; fee: number; isAsync: boolean } |
| 218 | +}): OpcodeList { |
| 219 | + const result: OpcodeList = {} |
| 220 | + for (const [key, value] of Object.entries(opcodes)) { |
| 221 | + const code = parseInt(key, 10) |
| 222 | + result[<any>key] = new Opcode({ |
| 223 | + code, |
| 224 | + fullName: getFullname(code, value.name), |
| 225 | + ...value, |
| 226 | + }) |
| 227 | + } |
| 228 | + return result |
| 229 | +} |
| 230 | + |
| 231 | +/** |
| 232 | + * Get full opcode name from its name and code. |
| 233 | + * |
| 234 | + * @param code {number} Integer code of opcode. |
| 235 | + * @param name {string} Short name of the opcode. |
| 236 | + * @returns {string} Full opcode name |
| 237 | + */ |
| 238 | +function getFullname(code: number, name: string): string { |
| 239 | + switch (name) { |
| 240 | + case 'LOG': |
| 241 | + name += code - 0xa0 |
| 242 | + break |
| 243 | + case 'PUSH': |
| 244 | + name += code - 0x5f |
| 245 | + break |
| 246 | + case 'DUP': |
| 247 | + name += code - 0x7f |
| 248 | + break |
| 249 | + case 'SWAP': |
| 250 | + name += code - 0x8f |
| 251 | + break |
| 252 | + } |
| 253 | + return name |
183 | 254 | }
|
184 | 255 |
|
185 |
| -export function getOpcodesForHF(common: Common) { |
| 256 | +/** |
| 257 | + * Get suitable opcodes for the required hardfork. |
| 258 | + * |
| 259 | + * @param common {Common} Ethereumjs Common metadaata object. |
| 260 | + * @returns {OpcodeList} Opcodes dictionary object. |
| 261 | + */ |
| 262 | +export function getOpcodesForHF(common: Common): OpcodeList { |
186 | 263 | if (common.gteHardfork('istanbul')) {
|
187 | 264 | return { ...opcodes, ...istanbulOpcodes }
|
188 | 265 | } else {
|
|
0 commit comments