github.com/hyperledger/burrow@v0.34.5-0.20220512172541-77f09336001d/execution/wasm/contract.go (about) 1 package wasm 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "fmt" 7 "math/big" 8 9 "github.com/go-interpreter/wagon/wasm/leb128" 10 lifeExec "github.com/perlin-network/life/exec" 11 hex "github.com/tmthrgd/go-hex" 12 13 bin "github.com/hyperledger/burrow/binary" 14 "github.com/hyperledger/burrow/crypto" 15 "github.com/hyperledger/burrow/execution/engine" 16 "github.com/hyperledger/burrow/execution/errors" 17 "github.com/hyperledger/burrow/execution/evm" 18 "github.com/hyperledger/burrow/execution/exec" 19 "github.com/hyperledger/burrow/permission" 20 "github.com/hyperledger/burrow/txs" 21 ) 22 23 type Contract struct { 24 vm *WVM 25 code []byte 26 } 27 28 const Success = 0 29 const Error = 1 30 const Revert = 2 31 32 const ValueByteSize = 16 33 34 func (c *Contract) Call(state engine.State, params engine.CallParams) (output []byte, err error) { 35 return engine.Call(state, params, c.execute) 36 } 37 38 func (c *Contract) execute(state engine.State, params engine.CallParams) ([]byte, error) { 39 const errHeader = "ewasm" 40 41 // Since Life runs the execution for us we push the arguments into the import resolver state 42 ctx := &context{ 43 Contract: c, 44 state: state, 45 params: params, 46 code: c.code, 47 } 48 49 // panics in ResolveFunc() will be recovered for us, no need for our own 50 vm, err := lifeExec.NewVirtualMachine(c.code[0:int(wasmSize(c.code))], c.vm.vmConfig, ctx, nil) 51 if err != nil { 52 return nil, errors.Errorf(errors.Codes.InvalidContract, "%s: motherfucker %v", errHeader, err) 53 } 54 55 entryID, ok := vm.GetFunctionExport("main") 56 if !ok { 57 return nil, errors.Codes.UnresolvedSymbols 58 } 59 60 _, err = vm.Run(entryID) 61 if err != nil && errors.GetCode(err) == errors.Codes.ExecutionReverted { 62 return nil, err 63 } 64 65 if err != nil && errors.GetCode(err) != errors.Codes.None { 66 return nil, errors.Errorf(errors.Codes.ExecutionAborted, "%s: %v", errHeader, err) 67 } 68 69 return ctx.output, nil 70 } 71 72 type context struct { 73 *Contract 74 state engine.State 75 params engine.CallParams 76 code []byte 77 output []byte 78 returnData []byte 79 sequence uint64 80 } 81 82 var _ lifeExec.ImportResolver = (*context)(nil) 83 84 func (ctx *context) ResolveGlobal(module, field string) int64 { 85 panic(fmt.Sprintf("global %s module %s not found", field, module)) 86 } 87 88 func (ctx *context) ResolveFunc(module, field string) lifeExec.FunctionImport { 89 if module == "debug" { 90 // See https://github.com/ewasm/hera#interfaces 91 switch field { 92 case "print32": 93 return func(vm *lifeExec.VirtualMachine) int64 { 94 n := int32(vm.GetCurrentFrame().Locals[0]) 95 96 s := fmt.Sprintf("%d", n) 97 98 err := ctx.state.EventSink.Print(&exec.PrintEvent{ 99 Address: ctx.params.Callee, 100 Data: []byte(s), 101 }) 102 103 if err != nil { 104 panic(fmt.Sprintf(" => print32 failed: %v", err)) 105 } 106 107 return Success 108 } 109 110 case "print64": 111 return func(vm *lifeExec.VirtualMachine) int64 { 112 n := int64(vm.GetCurrentFrame().Locals[0]) 113 114 s := fmt.Sprintf("%d", n) 115 116 err := ctx.state.EventSink.Print(&exec.PrintEvent{ 117 Address: ctx.params.Callee, 118 Data: []byte(s), 119 }) 120 121 if err != nil { 122 panic(fmt.Sprintf(" => print32 failed: %v", err)) 123 } 124 125 return Success 126 } 127 128 case "printMem": 129 return func(vm *lifeExec.VirtualMachine) int64 { 130 dataPtr := int(uint32(vm.GetCurrentFrame().Locals[0])) 131 dataLen := int(uint32(vm.GetCurrentFrame().Locals[1])) 132 133 s := vm.Memory[dataPtr : dataPtr+dataLen] 134 135 err := ctx.state.EventSink.Print(&exec.PrintEvent{ 136 Address: ctx.params.Callee, 137 Data: s, 138 }) 139 140 if err != nil { 141 panic(fmt.Sprintf(" => printMem failed: %v", err)) 142 } 143 144 return Success 145 } 146 147 case "printMemHex": 148 return func(vm *lifeExec.VirtualMachine) int64 { 149 dataPtr := int(uint32(vm.GetCurrentFrame().Locals[0])) 150 dataLen := int(uint32(vm.GetCurrentFrame().Locals[1])) 151 152 s := hex.EncodeToString(vm.Memory[dataPtr : dataPtr+dataLen]) 153 154 err := ctx.state.EventSink.Print(&exec.PrintEvent{ 155 Address: ctx.params.Callee, 156 Data: []byte(s), 157 }) 158 159 if err != nil { 160 panic(fmt.Sprintf(" => printMemHex failed: %v", err)) 161 } 162 163 return Success 164 } 165 166 case "printStorage": 167 return func(vm *lifeExec.VirtualMachine) int64 { 168 keyPtr := int(uint32(vm.GetCurrentFrame().Locals[0])) 169 170 key := bin.Word256{} 171 172 copy(key[:], vm.Memory[keyPtr:keyPtr+32]) 173 174 val, err := ctx.state.GetStorage(ctx.params.Callee, key) 175 if err != nil { 176 panic(err) 177 } 178 179 err = ctx.state.EventSink.Print(&exec.PrintEvent{ 180 Address: ctx.params.Callee, 181 Data: val, 182 }) 183 184 if err != nil { 185 panic(fmt.Sprintf(" => printStorage failed: %v", err)) 186 } 187 188 return Success 189 } 190 191 case "printStorageHex": 192 return func(vm *lifeExec.VirtualMachine) int64 { 193 keyPtr := int(uint32(vm.GetCurrentFrame().Locals[0])) 194 195 key := bin.Word256{} 196 197 copy(key[:], vm.Memory[keyPtr:keyPtr+32]) 198 199 val, err := ctx.state.GetStorage(ctx.params.Callee, key) 200 if err != nil { 201 panic(err) 202 } 203 204 s := hex.EncodeToString(val) 205 206 err = ctx.state.EventSink.Print(&exec.PrintEvent{ 207 Address: ctx.params.Callee, 208 Data: []byte(s), 209 }) 210 211 if err != nil { 212 panic(fmt.Sprintf(" => printStorage failed: %v", err)) 213 } 214 215 return Success 216 } 217 218 default: 219 panic(fmt.Sprintf("function %s unknown for debug module", field)) 220 } 221 } 222 223 if module != "ethereum" { 224 panic(fmt.Sprintf("unknown module %s", module)) 225 } 226 227 switch field { 228 case "create": 229 return func(vm *lifeExec.VirtualMachine) int64 { 230 valuePtr := int(uint32(vm.GetCurrentFrame().Locals[0])) 231 dataPtr := uint32(vm.GetCurrentFrame().Locals[1]) 232 dataLen := uint32(vm.GetCurrentFrame().Locals[2]) 233 resultPtr := uint32(vm.GetCurrentFrame().Locals[3]) 234 235 // TODO: is this guaranteed to be okay? Should be avoid panic here if out of bounds? 236 value := bin.BigIntFromLittleEndianBytes(vm.Memory[valuePtr : valuePtr+ValueByteSize]) 237 238 var data []byte 239 copy(data, vm.Memory[dataPtr:dataPtr+dataLen]) 240 241 ctx.sequence++ 242 nonce := make([]byte, txs.HashLength+8) 243 copy(nonce, ctx.vm.options.Nonce) 244 binary.BigEndian.PutUint64(nonce[txs.HashLength:], ctx.sequence) 245 newAccountAddress := crypto.NewContractAddress(ctx.params.Callee, nonce) 246 247 err := engine.EnsurePermission(ctx.state.CallFrame, ctx.params.Callee, permission.CreateContract) 248 if err != nil { 249 return Error 250 } 251 252 err = ctx.state.CallFrame.CreateAccount(ctx.params.Caller, newAccountAddress) 253 if err != nil { 254 return Error 255 } 256 257 res, err := ctx.vm.Contract(vm.Memory[dataPtr:dataPtr+dataLen]).Call(ctx.state, engine.CallParams{ 258 Caller: ctx.params.Caller, 259 Callee: newAccountAddress, 260 Input: nil, 261 Value: *value, 262 Gas: ctx.params.Gas, 263 }) 264 265 if err != nil { 266 if errors.GetCode(err) == errors.Codes.ExecutionReverted { 267 return Revert 268 } 269 panic(err) 270 } 271 err = engine.InitWASMCode(ctx.state, newAccountAddress, res) 272 if err != nil { 273 if errors.GetCode(err) == errors.Codes.ExecutionReverted { 274 return Revert 275 } 276 panic(err) 277 } 278 279 copy(vm.Memory[resultPtr:], newAccountAddress.Bytes()) 280 281 return Success 282 } 283 284 case "getBlockDifficulty": 285 return func(vm *lifeExec.VirtualMachine) int64 { 286 resultPtr := int(vm.GetCurrentFrame().Locals[0]) 287 288 // set it to 1 289 copy(vm.Memory[resultPtr:resultPtr+32], bin.RightPadBytes([]byte{1}, 32)) 290 return Success 291 } 292 293 case "getTxGasPrice": 294 return func(vm *lifeExec.VirtualMachine) int64 { 295 resultPtr := int(vm.GetCurrentFrame().Locals[0]) 296 297 // set it to 1 298 copy(vm.Memory[resultPtr:resultPtr+16], bin.RightPadBytes([]byte{1}, 16)) 299 return Success 300 } 301 302 case "selfDestruct": 303 return func(vm *lifeExec.VirtualMachine) int64 { 304 receiverPtr := int(vm.GetCurrentFrame().Locals[0]) 305 306 var receiver crypto.Address 307 copy(receiver[:], vm.Memory[receiverPtr:receiverPtr+crypto.AddressLength]) 308 309 receiverAcc, err := ctx.state.GetAccount(receiver) 310 if err != nil { 311 panic(err) 312 } 313 if receiverAcc == nil { 314 err := ctx.state.CallFrame.CreateAccount(ctx.params.Callee, receiver) 315 if err != nil { 316 panic(err) 317 } 318 } 319 acc, err := ctx.state.GetAccount(ctx.params.Callee) 320 if err != nil { 321 panic(err) 322 } 323 balance := acc.Balance 324 err = acc.AddToBalance(balance) 325 if err != nil { 326 panic(err) 327 } 328 329 err = ctx.state.CallFrame.UpdateAccount(acc) 330 if err != nil { 331 panic(err) 332 } 333 err = ctx.state.CallFrame.RemoveAccount(ctx.params.Callee) 334 if err != nil { 335 panic(err) 336 } 337 panic(errors.Codes.None) 338 } 339 340 case "call", "callCode", "callDelegate", "callStatic": 341 return func(vm *lifeExec.VirtualMachine) int64 { 342 gasLimit := big.NewInt(vm.GetCurrentFrame().Locals[0]) 343 addressPtr := uint32(vm.GetCurrentFrame().Locals[1]) 344 i := 2 345 var valuePtr int 346 if field == "call" || field == "callCode" { 347 valuePtr = int(uint32(vm.GetCurrentFrame().Locals[i])) 348 i++ 349 } 350 dataPtr := uint32(vm.GetCurrentFrame().Locals[i]) 351 dataLen := uint32(vm.GetCurrentFrame().Locals[i+1]) 352 353 // TODO: avoid panic? Or at least panic with coded out-of-bounds 354 target := crypto.MustAddressFromBytes(vm.Memory[addressPtr : addressPtr+crypto.AddressLength]) 355 356 // TODO: is this guaranteed to be okay? Should be avoid panic here if out of bounds? 357 value := bin.BigIntFromLittleEndianBytes(vm.Memory[valuePtr : valuePtr+ValueByteSize]) 358 359 var callType exec.CallType 360 361 switch field { 362 case "call": 363 callType = exec.CallTypeCall 364 case "callCode": 365 callType = exec.CallTypeCode 366 case "callStatic": 367 callType = exec.CallTypeStatic 368 case "callDeletegate": 369 callType = exec.CallTypeDelegate 370 default: 371 panic("should not happen") 372 } 373 374 var err error 375 ctx.returnData, err = engine.CallFromSite(ctx.state, ctx.vm.externalDispatcher, ctx.params, 376 engine.CallParams{ 377 CallType: callType, 378 Callee: target, 379 Input: vm.Memory[dataPtr : dataPtr+dataLen], 380 Value: *value, 381 Gas: gasLimit, 382 }) 383 384 // Refund any remaining gas to be used on subsequent calls 385 ctx.params.Gas.Add(ctx.params.Gas, gasLimit) 386 387 // TODO[Silas]: we may need to consider trapping and non-trapping errors here in a bit more of a principled way 388 // (e.g. we may be currently handling things that should abort execution, it might be better to clasify 389 // all of our coded errors as trapping (fatal abort WASM) or non-trapping (return error to WASM caller) 390 // I'm not sure this is consistent in EVM either. 391 if err != nil { 392 if errors.GetCode(err) == errors.Codes.ExecutionReverted { 393 return Revert 394 } 395 // Spec says return 1 for error, but not sure when to do that (as opposed to abort): 396 // https://github.com/ewasm/design/blob/master/eth_interface.md#call 397 panic(err) 398 } 399 return Success 400 } 401 402 case "getCallDataSize": 403 return func(vm *lifeExec.VirtualMachine) int64 { 404 return int64(len(ctx.params.Input)) 405 } 406 407 case "callDataCopy": 408 return func(vm *lifeExec.VirtualMachine) int64 { 409 destPtr := int(uint32(vm.GetCurrentFrame().Locals[0])) 410 dataOffset := int(uint32(vm.GetCurrentFrame().Locals[1])) 411 dataLen := int(uint32(vm.GetCurrentFrame().Locals[2])) 412 413 if dataLen > 0 { 414 copy(vm.Memory[destPtr:], ctx.params.Input[dataOffset:dataOffset+dataLen]) 415 } 416 417 return Success 418 } 419 420 case "getReturnDataSize": 421 return func(vm *lifeExec.VirtualMachine) int64 { 422 return int64(len(ctx.returnData)) 423 } 424 425 case "returnDataCopy": 426 return func(vm *lifeExec.VirtualMachine) int64 { 427 destPtr := int(uint32(vm.GetCurrentFrame().Locals[0])) 428 dataOffset := int(uint32(vm.GetCurrentFrame().Locals[1])) 429 dataLen := int(uint32(vm.GetCurrentFrame().Locals[2])) 430 431 if dataLen > 0 { 432 copy(vm.Memory[destPtr:], ctx.returnData[dataOffset:dataOffset+dataLen]) 433 } 434 435 return Success 436 } 437 438 case "getCodeSize": 439 return func(vm *lifeExec.VirtualMachine) int64 { 440 return int64(len(ctx.code)) 441 } 442 443 case "codeCopy": 444 return func(vm *lifeExec.VirtualMachine) int64 { 445 destPtr := int(uint32(vm.GetCurrentFrame().Locals[0])) 446 dataOffset := int(uint32(vm.GetCurrentFrame().Locals[1])) 447 dataLen := int(uint32(vm.GetCurrentFrame().Locals[2])) 448 449 if dataLen > 0 { 450 copy(vm.Memory[destPtr:], ctx.code[dataOffset:dataOffset+dataLen]) 451 } 452 453 return Success 454 } 455 456 case "storageStore": 457 return func(vm *lifeExec.VirtualMachine) int64 { 458 keyPtr := int(uint32(vm.GetCurrentFrame().Locals[0])) 459 dataPtr := int(uint32(vm.GetCurrentFrame().Locals[1])) 460 461 key := bin.Word256{} 462 value := make([]byte, 32) 463 464 copy(key[:], vm.Memory[keyPtr:keyPtr+32]) 465 copy(value, vm.Memory[dataPtr:dataPtr+32]) 466 467 err := ctx.state.SetStorage(ctx.params.Callee, key, value) 468 if err != nil { 469 panic(err) 470 } 471 return Success 472 } 473 474 case "storageLoad": 475 return func(vm *lifeExec.VirtualMachine) int64 { 476 477 keyPtr := int(uint32(vm.GetCurrentFrame().Locals[0])) 478 dataPtr := int(uint32(vm.GetCurrentFrame().Locals[1])) 479 480 key := bin.Word256{} 481 482 copy(key[:], vm.Memory[keyPtr:keyPtr+32]) 483 484 val, err := ctx.state.GetStorage(ctx.params.Callee, key) 485 if err != nil { 486 panic(err) 487 } 488 copy(vm.Memory[dataPtr:], val) 489 490 return Success 491 } 492 493 case "finish": 494 return func(vm *lifeExec.VirtualMachine) int64 { 495 dataPtr := int(uint32(vm.GetCurrentFrame().Locals[0])) 496 dataLen := int(uint32(vm.GetCurrentFrame().Locals[1])) 497 498 ctx.output = vm.Memory[dataPtr : dataPtr+dataLen] 499 500 panic(errors.Codes.None) 501 } 502 503 case "revert": 504 return func(vm *lifeExec.VirtualMachine) int64 { 505 506 dataPtr := int(uint32(vm.GetCurrentFrame().Locals[0])) 507 dataLen := int(uint32(vm.GetCurrentFrame().Locals[1])) 508 509 ctx.output = vm.Memory[dataPtr : dataPtr+dataLen] 510 511 panic(errors.Codes.ExecutionReverted) 512 } 513 514 case "getAddress": 515 return func(vm *lifeExec.VirtualMachine) int64 { 516 addressPtr := int(uint32(vm.GetCurrentFrame().Locals[0])) 517 518 copy(vm.Memory[addressPtr:], ctx.params.Callee.Bytes()) 519 520 return Success 521 } 522 523 case "getCallValue": 524 return func(vm *lifeExec.VirtualMachine) int64 { 525 valuePtr := int(uint32(vm.GetCurrentFrame().Locals[0])) 526 527 // ewasm value is little endian 128 bit value 528 copy(vm.Memory[valuePtr:], bin.BigIntToLittleEndianBytes(&ctx.params.Value)) 529 530 return Success 531 } 532 533 case "getExternalBalance": 534 return func(vm *lifeExec.VirtualMachine) int64 { 535 addressPtr := int(uint32(vm.GetCurrentFrame().Locals[0])) 536 balancePtr := int(uint32(vm.GetCurrentFrame().Locals[1])) 537 538 address := crypto.Address{} 539 540 copy(address[:], vm.Memory[addressPtr:addressPtr+crypto.AddressLength]) 541 acc, err := ctx.state.GetAccount(address) 542 if err != nil { 543 panic(errors.Codes.InvalidAddress) 544 } 545 546 // ewasm value is little endian 128 bit value 547 bs := make([]byte, 16) 548 binary.LittleEndian.PutUint64(bs, acc.Balance) 549 550 copy(vm.Memory[balancePtr:], bs) 551 552 return Success 553 } 554 555 case "getBlockTimestamp": 556 return func(vm *lifeExec.VirtualMachine) int64 { 557 return int64(ctx.state.Blockchain.LastBlockTime().Unix()) 558 } 559 560 case "getBlockNumber": 561 return func(vm *lifeExec.VirtualMachine) int64 { 562 return int64(ctx.state.Blockchain.LastBlockHeight()) 563 } 564 565 case "getTxOrigin": 566 return func(vm *lifeExec.VirtualMachine) int64 { 567 addressPtr := int(uint32(vm.GetCurrentFrame().Locals[0])) 568 569 copy(vm.Memory[addressPtr:addressPtr+crypto.AddressLength], ctx.params.Origin.Bytes()) 570 571 return Success 572 } 573 574 case "getCaller": 575 return func(vm *lifeExec.VirtualMachine) int64 { 576 addressPtr := int(uint32(vm.GetCurrentFrame().Locals[0])) 577 578 copy(vm.Memory[addressPtr:addressPtr+crypto.AddressLength], ctx.params.Caller.Bytes()) 579 580 return Success 581 } 582 583 case "getBlockGasLimit": 584 return func(vm *lifeExec.VirtualMachine) int64 { 585 return ctx.params.Gas.Int64() 586 } 587 588 case "getGasLeft": 589 return func(vm *lifeExec.VirtualMachine) int64 { 590 // do the same as EVM 591 return ctx.params.Gas.Int64() 592 } 593 594 case "getBlockCoinbase": 595 return func(vm *lifeExec.VirtualMachine) int64 { 596 // do the same as EVM 597 addressPtr := int(uint32(vm.GetCurrentFrame().Locals[0])) 598 599 copy(vm.Memory[addressPtr:addressPtr+crypto.AddressLength], crypto.ZeroAddress.Bytes()) 600 601 return Success 602 } 603 604 case "getBlockHash": 605 return func(vm *lifeExec.VirtualMachine) int64 { 606 blockNumber := uint64(vm.GetCurrentFrame().Locals[0]) 607 hashPtr := int(vm.GetCurrentFrame().Locals[1]) 608 609 lastBlockHeight := ctx.state.Blockchain.LastBlockHeight() 610 if blockNumber >= lastBlockHeight { 611 panic(fmt.Sprintf(" => attempted to get block hash of a non-existent block: %v", blockNumber)) 612 } else if lastBlockHeight-blockNumber > evm.MaximumAllowedBlockLookBack { 613 panic(fmt.Sprintf(" => attempted to get block hash of a block %d outside of the allowed range "+ 614 "(must be within %d blocks)", blockNumber, evm.MaximumAllowedBlockLookBack)) 615 } else { 616 hash, err := ctx.state.Blockchain.BlockHash(blockNumber) 617 if err != nil { 618 panic(fmt.Sprintf(" => blockhash failed: %v", err)) 619 } 620 621 copy(vm.Memory[hashPtr:hashPtr+len(hash)], hash) 622 } 623 624 return Success 625 } 626 627 case "log": 628 return func(vm *lifeExec.VirtualMachine) int64 { 629 dataPtr := int(uint32(vm.GetCurrentFrame().Locals[0])) 630 dataLen := int(uint32(vm.GetCurrentFrame().Locals[1])) 631 632 data := vm.Memory[dataPtr : dataPtr+dataLen] 633 634 topicCount := uint32(vm.GetCurrentFrame().Locals[2]) 635 topics := make([]bin.Word256, topicCount) 636 637 if topicCount > 4 { 638 panic(fmt.Sprintf("%d topics not permitted", topicCount)) 639 } 640 641 for i := uint32(0); i < topicCount; i++ { 642 topicPtr := int(uint32(vm.GetCurrentFrame().Locals[3+i])) 643 topicData := vm.Memory[topicPtr : topicPtr+bin.Word256Bytes] 644 topics[i] = bin.RightPadWord256(topicData) 645 } 646 647 err := ctx.state.EventSink.Log(&exec.LogEvent{ 648 Address: ctx.params.Callee, 649 Topics: topics, 650 Data: data, 651 }) 652 653 if err != nil { 654 panic(fmt.Sprintf(" => log failed: %v", err)) 655 } 656 657 return Success 658 } 659 660 default: 661 panic(fmt.Sprintf("unknown function %s", field)) 662 } 663 } 664 665 // When deploying wasm code, the abi encoded arguments to the constructor are added to the code. Wagon 666 // does not like seeing this data, so strip this off. We have to walk the wasm format to the last section 667 668 // There might be a better solution to this. 669 func wasmSize(code []byte) int64 { 670 reader := bytes.NewReader(code) 671 top := int64(8) 672 for { 673 reader.Seek(top, 0) 674 id, err := reader.ReadByte() 675 if err != nil || id == 0 || id > 11 { 676 // invalid section id 677 break 678 } 679 size, err := leb128.ReadVarUint32(reader) 680 if err != nil { 681 break 682 } 683 pos, _ := reader.Seek(0, 1) 684 if pos+int64(size) > int64(len(code)) { 685 break 686 } 687 top = pos + int64(size) 688 } 689 690 return top 691 }