1
+ import { Trie } from "@ethereumjs/trie" ;
2
+ import { isHexString , arrayify , hexlify } from "@ethersproject/bytes" ;
3
+ import Opcodes from "../../opcodes" ;
4
+ import Instruction from "../instruction" ;
5
+ import Memory from "../memory" ;
6
+ import Stack from "../stack" ;
7
+ import {
8
+ InvalidBytecode ,
9
+ InvalidJump ,
10
+ InvalidProgramCounterIndex ,
11
+ OutofGas ,
12
+ UnknownOpcode ,
13
+ } from "./errors" ;
14
+
15
+ class ExecutionContext {
16
+ private readonly code : Uint8Array ;
17
+ public stack : Stack ;
18
+ public memory : Memory ;
19
+ private pc : number ;
20
+ private stopped : boolean ;
21
+ public output : bigint = BigInt ( 0 ) ;
22
+ public storage : Trie ;
23
+ public readonly originalStorage : Trie ;
24
+ public gas : bigint ;
25
+
26
+ constructor ( code : string , gas : bigint , storage : Trie ) {
27
+ if ( ! isHexString ( code ) || code . length % 2 !== 0 )
28
+ throw new InvalidBytecode ( ) ;
29
+ this . code = arrayify ( code ) ;
30
+ this . stack = new Stack ( ) ;
31
+ this . memory = new Memory ( ) ;
32
+ this . pc = 0 ;
33
+ this . stopped = false ;
34
+ this . storage = storage ;
35
+ this . originalStorage = storage . copy ( )
36
+ this . gas = gas ;
37
+ }
38
+
39
+ public stop ( ) : void {
40
+ this . stopped = true ;
41
+ }
42
+
43
+ public async run ( ) {
44
+ while ( ! this . stopped ) {
45
+ const currentPc = this . pc ;
46
+
47
+ const instruction = this . fetchInstruction ( ) ;
48
+ const currentAvailableGas = this . gas ;
49
+ const { gasFee } = await instruction . execute ( this ) ;
50
+
51
+ console . info ( `${ instruction . name } \t @pc=${ currentPc } \t gas=${ currentAvailableGas } \t const=${ gasFee } ` ) ;
52
+
53
+ this . memory . print ( ) ;
54
+ this . stack . print ( ) ;
55
+ console . log ( "" ) ;
56
+ }
57
+
58
+ console . log ( "Output:\t" , hexlify ( this . output ) ) ;
59
+ console . log ( "Root:\t" , hexlify ( this . storage . root ( ) ) ) ;
60
+ }
61
+
62
+ public useGas ( fee : number ) {
63
+ this . gas -= BigInt ( fee ) ;
64
+ if ( this . gas < 0 ) throw new OutofGas ( ) ;
65
+ }
66
+
67
+ private fetchInstruction ( ) : Instruction {
68
+ if ( this . pc >= this . code . length ) return Opcodes [ 0 ] ;
69
+
70
+ if ( this . pc < 0 ) throw new InvalidProgramCounterIndex ( ) ;
71
+
72
+ const opcode = this . readBytesFromCode ( 1 ) ;
73
+
74
+ const instruction = Opcodes [ Number ( opcode ) ] ;
75
+
76
+ if ( ! instruction ) throw new UnknownOpcode ( ) ;
77
+
78
+ return instruction ;
79
+ }
80
+
81
+ public readBytesFromCode ( bytes = 1 ) : bigint {
82
+ const hexValues = this . code . slice ( this . pc , this . pc + bytes ) ; // Recortar un pedazo the tamaño bytes
83
+ const values = BigInt ( hexlify ( hexValues ) ) ;
84
+
85
+ this . pc += bytes ;
86
+
87
+ return values ;
88
+ }
89
+
90
+ public jump ( destination : bigint ) : void {
91
+ if ( ! this . isValidJump ( destination ) ) throw new InvalidJump ( ) ;
92
+ this . pc = Number ( destination ) ;
93
+ }
94
+
95
+ private isValidJump ( destination : bigint ) : boolean {
96
+ return this . code [ Number ( destination ) - 1 ] === Opcodes [ 0x5b ] . opcode ;
97
+ }
98
+ }
99
+
100
+ export default ExecutionContext ;
0 commit comments