github.com/ethereum/go-ethereum@v1.16.1/core/vm/program/program.go (about) 1 // Copyright 2024 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 // package program is a utility to create EVM bytecode for testing, but _not_ for production. As such: 18 // 19 // - There are not package guarantees. We might iterate heavily on this package, and do backwards-incompatible changes without warning 20 // - There are no quality-guarantees. These utilities may produce evm-code that is non-functional. YMMV. 21 // - There are no stability-guarantees. The utility will `panic` if the inputs do not align / make sense. 22 23 package program 24 25 import ( 26 "fmt" 27 "math/big" 28 29 "github.com/ethereum/go-ethereum/core/vm" 30 "github.com/holiman/uint256" 31 ) 32 33 // Program is a simple bytecode container. It can be used to construct 34 // simple EVM programs. Errors during construction of a Program typically 35 // cause panics: so avoid using these programs in production settings or on 36 // untrusted input. 37 // This package is mainly meant to aid in testing. This is not a production 38 // -level "compiler". 39 type Program struct { 40 code []byte 41 } 42 43 // New creates a new Program 44 func New() *Program { 45 return &Program{ 46 code: make([]byte, 0), 47 } 48 } 49 50 // add adds the op to the code. 51 func (p *Program) add(op byte) *Program { 52 p.code = append(p.code, op) 53 return p 54 } 55 56 // pushBig creates a PUSHX instruction and pushes the given val. 57 // - If the val is nil, it pushes zero 58 // - If the val is bigger than 32 bytes, it panics 59 func (p *Program) doPush(val *uint256.Int) { 60 if val == nil { 61 val = new(uint256.Int) 62 } 63 valBytes := val.Bytes() 64 if len(valBytes) == 0 { 65 valBytes = append(valBytes, 0) 66 } 67 bLen := len(valBytes) 68 p.add(byte(vm.PUSH1) - 1 + byte(bLen)) 69 p.Append(valBytes) 70 } 71 72 // Append appends the given data to the code. 73 func (p *Program) Append(data []byte) *Program { 74 p.code = append(p.code, data...) 75 return p 76 } 77 78 // Bytes returns the Program bytecode. OBS: This is not a copy. 79 func (p *Program) Bytes() []byte { 80 return p.code 81 } 82 83 // SetBytes sets the Program bytecode. The combination of Bytes and SetBytes means 84 // that external callers can implement missing functionality: 85 // 86 // ... 87 // prog.Push(1) 88 // code := prog.Bytes() 89 // manipulate(code) 90 // prog.SetBytes(code) 91 func (p *Program) SetBytes(code []byte) { 92 p.code = code 93 } 94 95 // Hex returns the Program bytecode as a hex string. 96 func (p *Program) Hex() string { 97 return fmt.Sprintf("%02x", p.Bytes()) 98 } 99 100 // Op appends the given opcode(s). 101 func (p *Program) Op(ops ...vm.OpCode) *Program { 102 for _, op := range ops { 103 p.add(byte(op)) 104 } 105 return p 106 } 107 108 // Push creates a PUSHX instruction with the data provided. If zero is being pushed, 109 // PUSH0 will be avoided in favour of [PUSH1 0], to ensure backwards compatibility. 110 func (p *Program) Push(val any) *Program { 111 switch v := val.(type) { 112 case int: 113 p.doPush(new(uint256.Int).SetUint64(uint64(v))) 114 case uint64: 115 p.doPush(new(uint256.Int).SetUint64(v)) 116 case uint32: 117 p.doPush(new(uint256.Int).SetUint64(uint64(v))) 118 case uint16: 119 p.doPush(new(uint256.Int).SetUint64(uint64(v))) 120 case *big.Int: 121 p.doPush(uint256.MustFromBig(v)) 122 case *uint256.Int: 123 p.doPush(v) 124 case uint256.Int: 125 p.doPush(&v) 126 case []byte: 127 p.doPush(new(uint256.Int).SetBytes(v)) 128 case byte: 129 p.doPush(new(uint256.Int).SetUint64(uint64(v))) 130 case interface{ Bytes() []byte }: 131 // Here, we jump through some hoops in order to avoid depending on 132 // go-ethereum types.Address and common.Hash, and instead use the 133 // interface. This works on both values and pointers! 134 p.doPush(new(uint256.Int).SetBytes(v.Bytes())) 135 case nil: 136 p.doPush(nil) 137 default: 138 panic(fmt.Sprintf("unsupported type %T", v)) 139 } 140 return p 141 } 142 143 // Push0 implements PUSH0 (0x5f). 144 func (p *Program) Push0() *Program { 145 return p.Op(vm.PUSH0) 146 } 147 148 // ExtcodeCopy performs an extcodecopy invocation. 149 func (p *Program) ExtcodeCopy(address, memOffset, codeOffset, length any) *Program { 150 p.Push(length) 151 p.Push(codeOffset) 152 p.Push(memOffset) 153 p.Push(address) 154 return p.Op(vm.EXTCODECOPY) 155 } 156 157 // Call is a convenience function to make a call. If 'gas' is nil, the opcode GAS will 158 // be used to provide all gas. 159 func (p *Program) Call(gas *uint256.Int, address, value, inOffset, inSize, outOffset, outSize any) *Program { 160 if outOffset == outSize && inSize == outSize && inOffset == outSize && value == outSize { 161 p.Push(outSize).Op(vm.DUP1, vm.DUP1, vm.DUP1, vm.DUP1) 162 } else { 163 p.Push(outSize).Push(outOffset).Push(inSize).Push(inOffset).Push(value) 164 } 165 p.Push(address) 166 if gas == nil { 167 p.Op(vm.GAS) 168 } else { 169 p.doPush(gas) 170 } 171 return p.Op(vm.CALL) 172 } 173 174 // DelegateCall is a convenience function to make a delegatecall. If 'gas' is nil, the opcode GAS will 175 // be used to provide all gas. 176 func (p *Program) DelegateCall(gas *uint256.Int, address, inOffset, inSize, outOffset, outSize any) *Program { 177 if outOffset == outSize && inSize == outSize && inOffset == outSize { 178 p.Push(outSize).Op(vm.DUP1, vm.DUP1, vm.DUP1) 179 } else { 180 p.Push(outSize).Push(outOffset).Push(inSize).Push(inOffset) 181 } 182 p.Push(address) 183 if gas == nil { 184 p.Op(vm.GAS) 185 } else { 186 p.doPush(gas) 187 } 188 return p.Op(vm.DELEGATECALL) 189 } 190 191 // StaticCall is a convenience function to make a staticcall. If 'gas' is nil, the opcode GAS will 192 // be used to provide all gas. 193 func (p *Program) StaticCall(gas *uint256.Int, address, inOffset, inSize, outOffset, outSize any) *Program { 194 if outOffset == outSize && inSize == outSize && inOffset == outSize { 195 p.Push(outSize).Op(vm.DUP1, vm.DUP1, vm.DUP1) 196 } else { 197 p.Push(outSize).Push(outOffset).Push(inSize).Push(inOffset) 198 } 199 p.Push(address) 200 if gas == nil { 201 p.Op(vm.GAS) 202 } else { 203 p.doPush(gas) 204 } 205 return p.Op(vm.STATICCALL) 206 } 207 208 // CallCode is a convenience function to make a callcode. If 'gas' is nil, the opcode GAS will 209 // be used to provide all gas. 210 func (p *Program) CallCode(gas *uint256.Int, address, value, inOffset, inSize, outOffset, outSize any) *Program { 211 if outOffset == outSize && inSize == outSize && inOffset == outSize { 212 p.Push(outSize).Op(vm.DUP1, vm.DUP1, vm.DUP1) 213 } else { 214 p.Push(outSize).Push(outOffset).Push(inSize).Push(inOffset) 215 } 216 p.Push(value) 217 p.Push(address) 218 if gas == nil { 219 p.Op(vm.GAS) 220 } else { 221 p.doPush(gas) 222 } 223 return p.Op(vm.CALLCODE) 224 } 225 226 // Label returns the PC (of the next instruction). 227 func (p *Program) Label() uint64 { 228 return uint64(len(p.code)) 229 } 230 231 // Jumpdest adds a JUMPDEST op, and returns the PC of that instruction. 232 func (p *Program) Jumpdest() (*Program, uint64) { 233 here := p.Label() 234 p.Op(vm.JUMPDEST) 235 return p, here 236 } 237 238 // Jump pushes the destination and adds a JUMP. 239 func (p *Program) Jump(loc any) *Program { 240 p.Push(loc) 241 p.Op(vm.JUMP) 242 return p 243 } 244 245 // JumpIf implements JUMPI. 246 func (p *Program) JumpIf(loc any, condition any) *Program { 247 p.Push(condition) 248 p.Push(loc) 249 p.Op(vm.JUMPI) 250 return p 251 } 252 253 // Size returns the current size of the bytecode. 254 func (p *Program) Size() int { 255 return len(p.code) 256 } 257 258 // InputAddressToStack stores the input (calldata) to memory as address (20 bytes). 259 func (p *Program) InputAddressToStack(inputOffset uint32) *Program { 260 p.Push(inputOffset) 261 p.Op(vm.CALLDATALOAD) // Loads [n -> n + 32] of input data to stack top 262 mask, _ := big.NewInt(0).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 16) 263 p.Push(mask) // turn into address 264 return p.Op(vm.AND) 265 } 266 267 // Mstore stores the provided data (into the memory area starting at memStart). 268 func (p *Program) Mstore(data []byte, memStart uint32) *Program { 269 var idx = 0 270 // We need to store it in chunks of 32 bytes 271 for ; idx+32 <= len(data); idx += 32 { 272 chunk := data[idx : idx+32] 273 // push the value 274 p.Push(chunk) 275 // push the memory index 276 p.Push(uint32(idx) + memStart) 277 p.Op(vm.MSTORE) 278 } 279 // Remainders become stored using MSTORE8 280 for ; idx < len(data); idx++ { 281 b := data[idx] 282 // push the byte 283 p.Push(b) 284 p.Push(uint32(idx) + memStart) 285 p.Op(vm.MSTORE8) 286 } 287 return p 288 } 289 290 // MstoreSmall stores the provided data, which must be smaller than 32 bytes, 291 // into the memory area starting at memStart. 292 // The data will be LHS zero-added to align on 32 bytes. 293 // For example, providing data 0x1122, it will do a PUSH2: 294 // PUSH2 0x1122, resulting in 295 // stack: 0x0000000000000000000000000000000000000000000000000000000000001122 296 // followed by MSTORE(0,0) 297 // And thus, the resulting memory will be 298 // [ 0000000000000000000000000000000000000000000000000000000000001122 ] 299 func (p *Program) MstoreSmall(data []byte, memStart uint32) *Program { 300 if len(data) > 32 { 301 // For larger sizes, use Mstore instead. 302 panic("only <=32 byte data size supported") 303 } 304 if len(data) == 0 { 305 // Storing 0-length data smells of an error somewhere. 306 panic("data is zero length") 307 } 308 // push the value 309 p.Push(data) 310 // push the memory index 311 p.Push(memStart) 312 p.Op(vm.MSTORE) 313 return p 314 } 315 316 // MemToStorage copies the given memory area into SSTORE slots, 317 // It expects data to be aligned to 32 byte, and does not zero out 318 // remainders if some data is not 319 // I.e, if given a 1-byte area, it will still copy the full 32 bytes to storage. 320 func (p *Program) MemToStorage(memStart, memSize, startSlot int) *Program { 321 // We need to store it in chunks of 32 bytes 322 for idx := memStart; idx < (memStart + memSize); idx += 32 { 323 dataStart := idx 324 // Mload the chunk 325 p.Push(dataStart) 326 p.Op(vm.MLOAD) 327 // Value is now on stack, 328 p.Push(startSlot) 329 p.Op(vm.SSTORE) 330 startSlot++ 331 } 332 return p 333 } 334 335 // ReturnViaCodeCopy utilises CODECOPY to place the given data in the bytecode of 336 // p, loads into memory (offset 0) and returns the code. 337 // This is a typical "constructor". 338 // Note: since all indexing is calculated immediately, the preceding bytecode 339 // must not be expanded or shortened. 340 func (p *Program) ReturnViaCodeCopy(data []byte) *Program { 341 p.Push(len(data)) 342 // For convenience, we'll use PUSH2 for the offset. Then we know we can always 343 // fit, since code is limited to 0xc000 344 p.Op(vm.PUSH2) 345 offsetPos := p.Size() // Need to update this position later on 346 p.Append([]byte{0, 0}) // Offset of the code to be copied 347 p.Push(0) // Offset in memory (destination) 348 p.Op(vm.CODECOPY) // Copy from code[offset:offset+len] to memory[0:] 349 p.Return(0, len(data)) // Return memory[0:len] 350 offset := p.Size() 351 p.Append(data) // And add the data 352 353 // Now, go back and fix the offset 354 p.code[offsetPos] = byte(offset >> 8) 355 p.code[offsetPos+1] = byte(offset) 356 return p 357 } 358 359 // Sstore stores the given byte array to the given slot. 360 // OBS! Does not verify that the value indeed fits into 32 bytes. 361 // If it does not, it will panic later on via doPush. 362 func (p *Program) Sstore(slot any, value any) *Program { 363 p.Push(value) 364 p.Push(slot) 365 return p.Op(vm.SSTORE) 366 } 367 368 // Tstore stores the given byte array to the given t-slot. 369 // OBS! Does not verify that the value indeed fits into 32 bytes. 370 // If it does not, it will panic later on via doPush. 371 func (p *Program) Tstore(slot any, value any) *Program { 372 p.Push(value) 373 p.Push(slot) 374 return p.Op(vm.TSTORE) 375 } 376 377 // Return implements RETURN 378 func (p *Program) Return(offset, len int) *Program { 379 p.Push(len) 380 p.Push(offset) 381 return p.Op(vm.RETURN) 382 } 383 384 // ReturnData loads the given data into memory, and does a return with it 385 func (p *Program) ReturnData(data []byte) *Program { 386 p.Mstore(data, 0) 387 return p.Return(0, len(data)) 388 } 389 390 // Create2 uses create2 to construct a contract with the given bytecode. 391 // This operation leaves either '0' or address on the stack. 392 func (p *Program) Create2(code []byte, salt any) *Program { 393 var ( 394 value = 0 395 offset = 0 396 size = len(code) 397 ) 398 // Load the code into mem 399 p.Mstore(code, 0) 400 // Create it 401 return p.Push(salt). 402 Push(size). 403 Push(offset). 404 Push(value). 405 Op(vm.CREATE2) 406 // On the stack now, is either 407 // - zero: in case of failure, OR 408 // - address: in case of success 409 } 410 411 // Create2ThenCall calls create2 with the given initcode and salt, and then calls 412 // into the created contract (or calls into zero, if the creation failed). 413 func (p *Program) Create2ThenCall(code []byte, salt any) *Program { 414 p.Create2(code, salt) 415 // If there happen to be a zero on the stack, it doesn't matter, we're 416 // not sending any value anyway 417 p.Push(0).Push(0) // mem out 418 p.Push(0).Push(0) // mem in 419 p.Push(0) // value 420 p.Op(vm.DUP6) // address 421 p.Op(vm.GAS) 422 p.Op(vm.CALL) 423 p.Op(vm.POP) // pop the retval 424 return p.Op(vm.POP) // pop the address 425 } 426 427 // Selfdestruct pushes beneficiary and invokes selfdestruct. 428 func (p *Program) Selfdestruct(beneficiary any) *Program { 429 p.Push(beneficiary) 430 return p.Op(vm.SELFDESTRUCT) 431 }