github.com/igggame/nebulas-go@v2.1.0+incompatible/nf/nvm/blockchain.go (about) 1 // Copyright (C) 2017 go-nebulas authors 2 // 3 // This file is part of the go-nebulas library. 4 // 5 // the go-nebulas library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // the go-nebulas library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU General Public License for more details. 14 // 15 // You should have received a copy of the GNU General Public License 16 // along with the go-nebulas library. If not, see <http://www.gnu.org/licenses/>. 17 // 18 19 package nvm 20 21 /* 22 #include "v8/lib/nvm_error.h" 23 */ 24 import "C" 25 26 import ( 27 "fmt" 28 "unsafe" 29 30 "github.com/nebulasio/go-nebulas/nr" 31 32 "encoding/json" 33 34 "github.com/gogo/protobuf/proto" 35 "github.com/nebulasio/go-nebulas/core" 36 "github.com/nebulasio/go-nebulas/core/pb" 37 "github.com/nebulasio/go-nebulas/core/state" 38 "github.com/nebulasio/go-nebulas/util" 39 "github.com/nebulasio/go-nebulas/util/byteutils" 40 "github.com/nebulasio/go-nebulas/util/logging" 41 "github.com/sirupsen/logrus" 42 ) 43 44 // GetTxByHashFunc returns tx info by hash 45 //export GetTxByHashFunc 46 func GetTxByHashFunc(handler unsafe.Pointer, hash *C.char, gasCnt *C.size_t) *C.char { 47 engine, _ := getEngineByStorageHandler(uint64(uintptr(handler))) 48 if engine == nil || engine.ctx.block == nil { 49 return nil 50 } 51 52 // calculate Gas. 53 *gasCnt = C.size_t(GetTxByHashGasBase) 54 55 txHash, err := byteutils.FromHex(C.GoString(hash)) 56 if err != nil { 57 return nil 58 } 59 txBytes, err := engine.ctx.state.GetTx(txHash) 60 if err != nil { 61 logging.VLog().WithFields(logrus.Fields{ 62 "handler": uint64(uintptr(handler)), 63 "key": C.GoString(hash), 64 "err": err, 65 }).Debug("GetTxByHashFunc get tx failed.") 66 return nil 67 } 68 sTx, err := toSerializableTransactionFromBytes(txBytes) 69 if err != nil { 70 logging.VLog().WithFields(logrus.Fields{ 71 "handler": uint64(uintptr(handler)), 72 "key": C.GoString(hash), 73 "err": err, 74 }).Debug("GetTxByHashFunc get tx failed.") 75 return nil 76 } 77 txJSON, err := json.Marshal(sTx) 78 if err != nil { 79 logging.VLog().WithFields(logrus.Fields{ 80 "handler": uint64(uintptr(handler)), 81 "key": C.GoString(hash), 82 "err": err, 83 }).Debug("GetTxByHashFunc get tx failed.") 84 return nil 85 } 86 87 return C.CString(string(txJSON)) 88 } 89 90 // GetAccountStateFunc returns account info by address 91 //export GetAccountStateFunc 92 func GetAccountStateFunc(handler unsafe.Pointer, address *C.char, gasCnt *C.size_t, 93 result **C.char, exceptionInfo **C.char) int { 94 *result = nil 95 *exceptionInfo = nil 96 engine, _ := getEngineByStorageHandler(uint64(uintptr(handler))) 97 if engine == nil || engine.ctx.block == nil { 98 logging.VLog().Error("Unexpected error: failed to get engine") 99 return C.NVM_UNEXPECTED_ERR 100 } 101 102 // calculate Gas. 103 *gasCnt = C.size_t(GetAccountStateGasBase) 104 105 addr, err := core.AddressParse(C.GoString(address)) 106 if err != nil { 107 *exceptionInfo = C.CString("Blockchain.getAccountState(), parse address failed") 108 return C.NVM_EXCEPTION_ERR 109 } 110 111 acc, err := engine.ctx.state.GetOrCreateUserAccount(addr.Bytes()) 112 if err != nil { 113 logging.VLog().WithFields(logrus.Fields{ 114 "handler": uint64(uintptr(handler)), 115 "address": addr, 116 "err": err, 117 }).Error("Unexpected error: GetAccountStateFunc get account state failed") 118 return C.NVM_UNEXPECTED_ERR 119 } 120 state := toSerializableAccount(acc) 121 json, err := json.Marshal(state) 122 if err != nil { 123 logging.VLog().WithFields(logrus.Fields{ 124 "state": state, 125 "json": json, 126 "err": err, 127 }).Error("Unexpected error: GetAccountStateFunc failed to mashal account state") 128 return C.NVM_UNEXPECTED_ERR 129 } 130 131 *result = C.CString(string(json)) 132 return C.NVM_SUCCESS 133 } 134 135 func recordTransferEvent(errNo int, from string, to string, value string, 136 height uint64, wsState WorldState, txHash byteutils.Hash) { 137 138 if errNo == SuccessTransferFunc && core.TransferFromContractEventRecordableAtHeight(height) { 139 event := &TransferFromContractEvent{ 140 Amount: value, 141 From: from, 142 To: to, 143 } 144 eData, err := json.Marshal(event) 145 if err != nil { 146 logging.VLog().WithFields(logrus.Fields{ 147 "from": from, 148 "to": to, 149 "amount": value, 150 "err": err, 151 }).Fatal("failed to marshal TransferFromContractEvent") 152 } 153 wsState.RecordEvent(txHash, &state.Event{Topic: core.TopicTransferFromContract, Data: string(eData)}) 154 155 } else if core.TransferFromContractFailureEventRecordableAtHeight(height) { 156 var errMsg string 157 switch errNo { 158 case SuccessTransferFunc: 159 errMsg = "" 160 case ErrTransferAddressParse: 161 errMsg = "failed to parse to address" 162 case ErrTransferStringToUint128: 163 errMsg = "failed to parse transfer amount" 164 if !core.TransferFromContractFailureEventRecordableAtHeight2(height) { 165 value = "" 166 } 167 case ErrTransferSubBalance: 168 errMsg = "failed to sub balace from contract address" 169 default: 170 logging.VLog().WithFields(logrus.Fields{ 171 "from": from, 172 "to": to, 173 "amount": value, 174 "errNo": errNo, 175 }).Error("unexpected error to handle") 176 return 177 } 178 179 status := uint8(1) 180 if errNo != SuccessTransferFunc { 181 status = 0 182 } 183 184 event := &TransferFromContractFailureEvent{ 185 Amount: value, 186 From: from, 187 To: to, 188 Status: status, 189 Error: errMsg, 190 } 191 192 eData, err := json.Marshal(event) 193 if err != nil { 194 logging.VLog().WithFields(logrus.Fields{ 195 "from": from, 196 "to": to, 197 "amount": value, 198 "status": event.Status, 199 "error": err, 200 }).Fatal("failed to marshal TransferFromContractEvent") 201 } 202 203 wsState.RecordEvent(txHash, &state.Event{Topic: core.TopicTransferFromContract, Data: string(eData)}) 204 205 } 206 } 207 208 //TransferByAddress value from to 209 func TransferByAddress(handler unsafe.Pointer, from *core.Address, to *core.Address, value *util.Uint128) int { 210 engine, _ := getEngineByStorageHandler(uint64(uintptr(handler))) 211 if engine == nil || engine.ctx == nil || engine.ctx.block == nil || 212 engine.ctx.state == nil || engine.ctx.tx == nil { 213 logging.VLog().Fatal("Unexpected error: failed to get engine.") 214 } 215 216 // *gasCnt = uint64(TransferGasBase) 217 iRtn := transfer(engine, from, to, value) 218 if iRtn != SuccessTransfer { 219 return iRtn 220 } 221 222 return SuccessTransferFunc 223 } 224 225 func transfer(e *V8Engine, from *core.Address, to *core.Address, amount *util.Uint128) int { 226 toAcc, err := e.ctx.state.GetOrCreateUserAccount(to.Bytes()) 227 if err != nil { 228 logging.VLog().WithFields(logrus.Fields{ 229 "handler": uint64(e.lcsHandler), 230 "address": to, 231 "err": err, 232 }).Error("Failed to get to account state") 233 return ErrTransferGetAccount 234 } 235 236 fromAcc, err := e.ctx.state.GetOrCreateUserAccount(from.Bytes()) 237 if err != nil { 238 logging.VLog().WithFields(logrus.Fields{ 239 "handler": uint64(e.lcsHandler), 240 "address": from, 241 "err": err, 242 }).Error("Failed to get from account state") 243 return ErrTransferGetAccount 244 } 245 // TestNet sync adjust 246 if amount == nil { 247 logging.VLog().WithFields(logrus.Fields{ 248 "handler": uint64(e.lcsHandler), 249 "address": from, 250 "err": err, 251 }).Error("Failed to get amount failed.") 252 return ErrTransferStringToUint128 253 } 254 255 // update balance 256 if amount.Cmp(util.NewUint128()) > 0 { 257 err = fromAcc.SubBalance(amount) //TODO: add unit amount不足,超大, NaN 258 if err != nil { 259 logging.VLog().WithFields(logrus.Fields{ 260 "handler": uint64(e.lcsHandler), 261 "account": fromAcc, 262 "from": from, 263 "amount": amount, 264 "err": err, 265 }).Error("Failed to sub balance") 266 return ErrTransferSubBalance 267 } 268 269 err = toAcc.AddBalance(amount) 270 if err != nil { 271 logging.VLog().WithFields(logrus.Fields{ 272 "account": toAcc, 273 "amount": amount, 274 "address": to, 275 "err": err, 276 }).Error("Failed to add balance") 277 //TODO: Failed to / Successed to / Unexpected error 278 return ErrTransferAddBalance 279 } 280 } 281 return SuccessTransfer 282 } 283 284 // TransferFunc transfer vale to address 285 //export TransferFunc 286 func TransferFunc(handler unsafe.Pointer, to *C.char, v *C.char, gasCnt *C.size_t) int { 287 engine, _ := getEngineByStorageHandler(uint64(uintptr(handler))) 288 if engine == nil || engine.ctx == nil || engine.ctx.block == nil || 289 engine.ctx.state == nil || engine.ctx.tx == nil { 290 logging.VLog().Fatal("Unexpected error: failed to get engine.") 291 } 292 293 wsState := engine.ctx.state 294 height := engine.ctx.block.Height() 295 txHash := engine.ctx.tx.Hash() 296 297 cAddr, err := core.AddressParseFromBytes(engine.ctx.contract.Address()) 298 if err != nil { 299 logging.VLog().WithFields(logrus.Fields{ 300 "txhash": engine.ctx.tx.Hash().String(), 301 "address": engine.ctx.contract.Address(), 302 "err": err, 303 }).Fatal("Unexpected error: failed to parse contract address") 304 } 305 306 // calculate Gas. 307 *gasCnt = C.size_t(TransferGasBase) 308 309 addr, err := core.AddressParse(C.GoString(to)) 310 if err != nil { 311 logging.VLog().WithFields(logrus.Fields{ 312 "handler": uint64(uintptr(handler)), 313 "toAddress": C.GoString(to), 314 }).Debug("TransferFunc parse address failed.") 315 recordTransferEvent(ErrTransferAddressParse, cAddr.String(), "", "", height, wsState, txHash) 316 return ErrTransferAddressParse 317 } 318 319 // in old world state, toAcc accountstate create before amount check 320 if !core.TransferFromContractFailureEventRecordableAtHeight2(height) { 321 _, err := engine.ctx.state.GetOrCreateUserAccount(addr.Bytes()) 322 if err != nil { 323 logging.VLog().WithFields(logrus.Fields{ 324 "handler": uint64(uintptr(handler)), 325 "address": addr, 326 "err": err, 327 }).Error("GetAccountStateFunc get account state failed.") 328 recordTransferEvent(ErrTransferGetAccount, cAddr.String(), addr.String(), "", height, wsState, txHash) 329 return ErrTransferGetAccount 330 } 331 } 332 333 transferValueStr := C.GoString(v) 334 recordValue := transferValueStr 335 amount, err := util.NewUint128FromString(transferValueStr) 336 if core.NvmValueCheckUpdateHeight(height) { 337 if err != nil { 338 logging.VLog().WithFields(logrus.Fields{ 339 "handler": uint64(uintptr(handler)), 340 "address": addr.String(), 341 "err": err, 342 "val": transferValueStr, 343 }).Error("Failed to get amount failed.") 344 recordTransferEvent(ErrTransferStringToUint128, cAddr.String(), addr.String(), transferValueStr, height, wsState, txHash) 345 return ErrTransferStringToUint128 346 } 347 } else { 348 // in old version, record value is empty when it cannot convert to uint128 349 if err != nil { 350 recordValue = "" 351 } 352 } 353 ret := TransferByAddress(handler, cAddr, addr, amount) 354 355 if ret != ErrTransferStringToUint128 && ret != ErrTransferSubBalance && ret != SuccessTransferFunc { // Unepected to happen, should not to be on chain 356 logging.VLog().WithFields(logrus.Fields{ 357 "height": engine.ctx.block.Height(), 358 "txhash": engine.ctx.tx.Hash().String(), 359 "fromAddress": cAddr.String(), 360 "toAddress": addr.String(), 361 "value": transferValueStr, 362 "ret": ret, 363 }).Error("Unexpected error") 364 } 365 366 recordTransferEvent(ret, cAddr.String(), addr.String(), recordValue, height, wsState, txHash) 367 return ret 368 } 369 370 // VerifyAddressFunc verify address is valid 371 //export VerifyAddressFunc 372 func VerifyAddressFunc(handler unsafe.Pointer, address *C.char, gasCnt *C.size_t) int { 373 // calculate Gas. 374 *gasCnt = C.size_t(VerifyAddressGasBase) 375 376 addr, err := core.AddressParse(C.GoString(address)) 377 if err != nil { 378 return 0 379 } 380 return int(addr.Type()) 381 } 382 383 // GetPreBlockHashFunc returns hash of the block before current tail by n 384 //export GetPreBlockHashFunc 385 func GetPreBlockHashFunc(handler unsafe.Pointer, offset C.ulonglong, 386 gasCnt *C.size_t, result **C.char, exceptionInfo **C.char) int { 387 *result = nil 388 *exceptionInfo = nil 389 n := uint64(offset) 390 if n > uint64(maxBlockOffset) { //31 days 391 *exceptionInfo = C.CString("Blockchain.GetPreBlockHash(), argument out of range") 392 return C.NVM_EXCEPTION_ERR 393 } 394 395 engine, _ := getEngineByStorageHandler(uint64(uintptr(handler))) 396 if engine == nil || engine.ctx == nil || engine.ctx.block == nil || engine.ctx.state == nil { 397 logging.VLog().Error("Unexpected error: failed to get engine.") 398 return C.NVM_UNEXPECTED_ERR 399 } 400 wsState := engine.ctx.state 401 // calculate Gas. 402 *gasCnt = C.size_t(GetPreBlockHashGasBase) 403 404 //get height 405 height := engine.ctx.block.Height() 406 if n >= height { // have checked it in lib js 407 logging.VLog().WithFields(logrus.Fields{ 408 "height": height, 409 "offset": n, 410 }).Debug("offset is large than height") 411 *exceptionInfo = C.CString("Blockchain.GetPreBlockHash(), argument[offset] is large than current height") 412 return C.NVM_EXCEPTION_ERR 413 } 414 height -= n 415 416 blockHash, err := wsState.GetBlockHashByHeight(height) 417 if err != nil { 418 logging.VLog().WithFields(logrus.Fields{ 419 "height": height, 420 "err": err, 421 }).Error("Unexpected error: Failed to get block hash from wsState by height") 422 return C.NVM_UNEXPECTED_ERR 423 } 424 425 *result = C.CString(byteutils.Hex(blockHash)) 426 return C.NVM_SUCCESS 427 } 428 429 // GetPreBlockSeedFunc returns hash of the block before current tail by n 430 //export GetPreBlockSeedFunc 431 func GetPreBlockSeedFunc(handler unsafe.Pointer, offset C.ulonglong, 432 gasCnt *C.size_t, result **C.char, exceptionInfo **C.char) int { 433 *result = nil 434 *exceptionInfo = nil 435 436 n := uint64(offset) 437 if n > uint64(maxBlockOffset) { //31 days 438 *exceptionInfo = C.CString("Blockchain.GetPreBlockSeed(), argument out of range") 439 return C.NVM_EXCEPTION_ERR 440 } 441 442 engine, _ := getEngineByStorageHandler(uint64(uintptr(handler))) 443 if engine == nil || engine.ctx == nil || engine.ctx.block == nil || engine.ctx.state == nil { 444 logging.VLog().Error("Unexpected error: failed to get engine") 445 return C.NVM_UNEXPECTED_ERR 446 } 447 wsState := engine.ctx.state 448 // calculate Gas. 449 *gasCnt = C.size_t(GetPreBlockSeedGasBase) 450 451 //get height 452 height := engine.ctx.block.Height() 453 if n >= height { // have checked it in lib js 454 logging.VLog().WithFields(logrus.Fields{ 455 "height": height, 456 "offset": n, 457 }).Debug("offset is large than height") 458 *exceptionInfo = C.CString("Blockchain.GetPreBlockSeed(), argument[offset] is large than current height") 459 return C.NVM_EXCEPTION_ERR 460 } 461 462 height -= n 463 if !core.RandomAvailableAtHeight(height) { 464 *exceptionInfo = C.CString("Blockchain.GetPreBlockSeed(), seed is not available at this height") 465 return C.NVM_EXCEPTION_ERR 466 } 467 468 blockHash, err := wsState.GetBlockHashByHeight(height) 469 if err != nil { 470 logging.VLog().WithFields(logrus.Fields{ 471 "height": height, 472 "err": err, 473 "blockHash": blockHash, 474 }).Error("Unexpected error: Failed to get block hash from wsState by height") 475 return C.NVM_UNEXPECTED_ERR 476 } 477 478 bytes, err := wsState.GetBlock(blockHash) 479 if err != nil { 480 logging.VLog().WithFields(logrus.Fields{ 481 "height": height, 482 "err": err, 483 "blockHash": blockHash, 484 }).Error("Unexpected error: Failed to get block from wsState by hash") 485 return C.NVM_UNEXPECTED_ERR 486 } 487 488 pbBlock := new(corepb.Block) 489 if err = proto.Unmarshal(bytes, pbBlock); err != nil { 490 logging.VLog().WithFields(logrus.Fields{ 491 "bytes": bytes, 492 "height": height, 493 "err": err, 494 }).Error("Unexpected error: Failed to unmarshal pbBlock") 495 return C.NVM_UNEXPECTED_ERR 496 } 497 498 if pbBlock.GetHeader() == nil || pbBlock.GetHeader().GetRandom() == nil || 499 pbBlock.GetHeader().GetRandom().GetVrfSeed() == nil { 500 logging.VLog().WithFields(logrus.Fields{ 501 "pbBlock": pbBlock, 502 "height": height, 503 }).Error("Unexpected error: No random found in block header") 504 return C.NVM_UNEXPECTED_ERR 505 } 506 507 *result = C.CString(byteutils.Hex(pbBlock.GetHeader().GetRandom().GetVrfSeed())) 508 return C.NVM_SUCCESS 509 } 510 511 //getPayloadByAddress 512 func getPayloadByAddress(ws WorldState, address string) (*Payload, error) { 513 addr, err := core.AddressParse(address) 514 if err != nil { 515 return nil, err 516 } 517 contract, err := core.CheckContract(addr, ws) 518 if err != nil { 519 return nil, err 520 } 521 522 birthTx, err := core.GetTransaction(contract.BirthPlace(), ws) 523 if err != nil { 524 return nil, err 525 } 526 527 deploy, err := core.LoadDeployPayload(birthTx.Data()) // ToConfirm: move deploy payload in ctx. 528 if err != nil { 529 return nil, err 530 } 531 return &Payload{deploy, contract}, nil 532 } 533 534 // GetContractSourceFunc get contract code by address 535 //export GetContractSourceFunc 536 func GetContractSourceFunc(handler unsafe.Pointer, address *C.char, gasCnt *C.size_t) *C.char { 537 // calculate Gas. 538 engine, _ := getEngineByStorageHandler(uint64(uintptr(handler))) 539 if engine == nil || engine.ctx.block == nil { 540 logging.VLog().Error("Failed to get engine.") 541 return nil 542 } 543 *gasCnt = C.size_t(GetContractSourceGasBase) 544 ws := engine.ctx.state 545 546 payload, err := getPayloadByAddress(ws, C.GoString(address)) 547 if err != nil { 548 logging.VLog().WithFields(logrus.Fields{ 549 "address": address, 550 "err": err, 551 }).Error("getPayLoadByAddress err") 552 553 return nil 554 } 555 556 return C.CString(string(payload.deploy.Source)) 557 } 558 559 //packErrInfoAndSetHead->packInner 560 func setHeadErrAndLog(e *V8Engine, index uint32, err error, result string, flag bool) string { 561 //rStr := packErrInfo(errType, rerrType, rerr, format, a...) 562 formatEx := InnerTransactionErrPrefix + err.Error() + InnerTransactionResult + result + InnerTransactionErrEnding 563 rStr := fmt.Sprintf(formatEx, index) 564 565 if flag == true { 566 logging.VLog().Errorf(rStr) 567 } 568 if index == 0 { 569 e.innerErrMsg = result 570 e.innerErr = err 571 } else { 572 setHeadV8ErrMsg(e.ctx.head, err, result) 573 } 574 return rStr 575 } 576 577 //setHeadV8ErrMsg set head node err info 578 func setHeadV8ErrMsg(handler unsafe.Pointer, err error, result string) { 579 if handler == nil { 580 logging.VLog().Errorf("invalid handler is nil") 581 return 582 } 583 engine := getEngineByEngineHandler(handler) 584 if engine == nil { 585 logging.VLog().Errorf("not found the v8 engine") 586 return 587 } 588 engine.innerErr = err 589 engine.innerErrMsg = result 590 } 591 592 //createInnerContext is private func only in InnerContractFunc 593 func createInnerContext(engine *V8Engine, fromAddr *core.Address, toAddr *core.Address, value *util.Uint128, funcName string, args string) (innerCtx *Context, err error) { 594 ws := engine.ctx.state 595 contract, err := core.CheckContract(toAddr, ws) 596 if err != nil { 597 return nil, err 598 } 599 logging.VLog().Infof("inner contract:%v", contract.ContractMeta()) //FIXME: ver limit 600 payloadType := core.TxPayloadCallType 601 callpayload, err := core.NewCallPayload(funcName, args) 602 if err != nil { 603 return nil, err 604 } 605 newPayloadHex, err := callpayload.ToBytes() 606 if err != nil { 607 return nil, err 608 } 609 610 // test sync adaptation 611 // In Testnet, before nbre available height, inner to address is fromAddr that is a bug. 612 innerToAddr := toAddr 613 if engine.ctx.tx.ChainID() == core.TestNetID && 614 !core.NbreAvailableHeight(engine.ctx.block.Height()) { 615 innerToAddr = fromAddr 616 } 617 parentTx := engine.ctx.tx 618 newTx, err := parentTx.NewInnerTransaction(parentTx.To(), innerToAddr, value, payloadType, newPayloadHex) 619 if err != nil { 620 logging.VLog().WithFields(logrus.Fields{ 621 "from": fromAddr.String(), 622 "to": toAddr.String(), 623 "value": value.String(), 624 "err": err, 625 }).Error("failed to create new tx") 626 return nil, err 627 } 628 var head unsafe.Pointer 629 if engine.ctx.head == nil { 630 head = unsafe.Pointer(engine.v8engine) 631 } else { 632 head = engine.ctx.head 633 } 634 newCtx, err := NewInnerContext(engine.ctx.block, newTx, contract, engine.ctx.state, head, engine.ctx.index+1, engine.ctx.contextRand) 635 if err != nil { 636 return nil, err 637 } 638 return newCtx, nil 639 } 640 641 //recordInnerContractEvent private func only in InnerContractFunc 642 func recordInnerContractEvent(e *V8Engine, err error, from string, to string, value string, innerFunc string, innerArgs string, wsState WorldState, txHash byteutils.Hash) { 643 errStr := "" 644 if err != nil { 645 errStr = err.Error() 646 } 647 event := &InnerContractEvent{ 648 From: from, 649 To: to, 650 Value: value, 651 Err: errStr, 652 } 653 654 if core.NbreSplitAtHeight(e.ctx.block.Height()) { 655 event.Function = innerFunc 656 event.Args = innerArgs 657 } 658 659 eData, errMarshal := json.Marshal(event) 660 if errMarshal != nil { 661 logging.VLog().WithFields(logrus.Fields{ 662 "from": from, 663 "to": to, 664 "value": value, 665 "err": errStr, 666 }).Fatal("failed to marshal TransferFromContractEvent") 667 } 668 wsState.RecordEvent(txHash, &state.Event{Topic: core.TopicInnerContract, Data: string(eData)}) 669 670 } 671 672 // In earlier versions of inter-contract invocation, inconsistent logic resulted in inconsistent data on the chain, requiring adaptation. 673 func earlierTestnetInnerTxCompatibility(engine *V8Engine) bool { 674 testnetUpdateHeight := uint64(845750) 675 return engine.ctx.tx.ChainID() == core.TestNetID && engine.ctx.block.Height() < testnetUpdateHeight 676 } 677 678 // InnerContractFunc multi run contract. output[c standard]: if err return nil else return "*" 679 //export InnerContractFunc 680 func InnerContractFunc(handler unsafe.Pointer, address *C.char, funcName *C.char, v *C.char, args *C.char, gasCnt *C.size_t) *C.char { 681 engine, _ := getEngineByStorageHandler(uint64(uintptr(handler))) 682 if engine == nil || engine.ctx.block == nil { 683 logging.VLog().Errorf(ErrEngineNotFound.Error()) 684 return nil 685 } 686 index := engine.ctx.index 687 if engine.ctx.index >= uint32(MaxInnerContractLevel) { 688 setHeadErrAndLog(engine, index, core.ErrExecutionFailed, ErrMaxInnerContractLevelLimit.Error(), true) 689 return nil 690 } 691 gasSum := uint64(InnerContractGasBase) 692 *gasCnt = C.size_t(gasSum) 693 ws := engine.ctx.state 694 695 addr, err := core.AddressParse(C.GoString(address)) 696 if err != nil { 697 setHeadErrAndLog(engine, index, core.ErrExecutionFailed, err.Error(), true) 698 return nil 699 } 700 701 var ( 702 newCtx *Context 703 deploy *core.DeployPayload 704 fromAddr *core.Address 705 toValue *util.Uint128 706 ) 707 708 parentTx := engine.ctx.tx 709 innerTxValueStr := C.GoString(v) 710 if earlierTestnetInnerTxCompatibility(engine) { 711 contract, err := core.CheckContract(addr, ws) 712 if err != nil { 713 setHeadErrAndLog(engine, index, core.ErrExecutionFailed, err.Error(), true) 714 return nil 715 } 716 logging.VLog().Infof("inner contract:%v", contract.ContractMeta()) //FIXME: ver limit 717 718 payload, err := getPayloadByAddress(ws, C.GoString(address)) 719 if err != nil { 720 setHeadErrAndLog(engine, index, core.ErrExecutionFailed, err.Error(), true) 721 return nil 722 } 723 deploy = payload.deploy 724 //run 725 payloadType := core.TxPayloadCallType 726 callpayload, err := core.NewCallPayload(C.GoString(funcName), C.GoString(args)) 727 if err != nil { 728 setHeadErrAndLog(engine, index, core.ErrExecutionFailed, err.Error(), true) 729 return nil 730 } 731 newPayloadHex, err := callpayload.ToBytes() 732 if err != nil { 733 setHeadErrAndLog(engine, index, core.ErrExecutionFailed, err.Error(), true) 734 return nil 735 } 736 737 from := engine.ctx.contract.Address() 738 fromAddr, err = core.AddressParseFromBytes(from) 739 if err != nil { 740 setHeadErrAndLog(engine, index, core.ErrExecutionFailed, err.Error(), true) 741 return nil 742 } 743 //transfer 744 // var transferCostGas uint64 745 toValue, err = util.NewUint128FromString(innerTxValueStr) 746 if err != nil { 747 setHeadErrAndLog(engine, index, core.ErrExecutionFailed, err.Error(), true) 748 return nil 749 } 750 iRet := TransferByAddress(handler, fromAddr, addr, toValue) 751 if iRet != 0 { 752 setHeadErrAndLog(engine, index, core.ErrExecutionFailed, ErrInnerTransferFailed.Error(), true) 753 return nil 754 } 755 756 newTx, err := parentTx.NewInnerTransaction(parentTx.To(), addr, toValue, payloadType, newPayloadHex) 757 if err != nil { 758 setHeadErrAndLog(engine, index, core.ErrExecutionFailed, err.Error(), false) 759 logging.VLog().WithFields(logrus.Fields{ 760 "from": fromAddr.String(), 761 "to": addr.String(), 762 "value": innerTxValueStr, 763 "err": err, 764 }).Error("failed to create new tx") 765 return nil 766 } 767 // event address need to user 768 var head unsafe.Pointer 769 if engine.ctx.head == nil { 770 head = unsafe.Pointer(engine.v8engine) 771 } else { 772 head = engine.ctx.head 773 } 774 newCtx, err = NewInnerContext(engine.ctx.block, newTx, contract, engine.ctx.state, head, engine.ctx.index+1, engine.ctx.contextRand) 775 if err != nil { 776 setHeadErrAndLog(engine, index, core.ErrExecutionFailed, err.Error(), true) 777 return nil 778 } 779 } else { 780 payload, err := getPayloadByAddress(ws, C.GoString(address)) 781 if err != nil { 782 setHeadErrAndLog(engine, index, core.ErrExecutionFailed, err.Error(), true) 783 return nil 784 } 785 deploy = payload.deploy 786 787 from := engine.ctx.contract.Address() 788 fromAddr, err = core.AddressParseFromBytes(from) 789 if err != nil { 790 setHeadErrAndLog(engine, index, core.ErrExecutionFailed, err.Error(), true) 791 return nil 792 } 793 //transfer 794 toValue, err = util.NewUint128FromString(innerTxValueStr) 795 if err != nil { 796 setHeadErrAndLog(engine, index, core.ErrExecutionFailed, err.Error(), true) 797 return nil 798 } 799 iRet := TransferByAddress(handler, fromAddr, addr, toValue) 800 if iRet != 0 { 801 setHeadErrAndLog(engine, index, core.ErrExecutionFailed, ErrInnerTransferFailed.Error(), true) 802 return nil 803 } 804 805 newCtx, err = createInnerContext(engine, fromAddr, addr, toValue, C.GoString(funcName), C.GoString(args)) 806 if err != nil { 807 setHeadErrAndLog(engine, index, core.ErrExecutionFailed, err.Error(), true) 808 return nil 809 } 810 } 811 812 remainInstruction, remainMem := engine.GetNVMLeftResources() 813 if remainInstruction <= uint64(InnerContractGasBase) { 814 logging.VLog().WithFields(logrus.Fields{ 815 "remainInstruction": remainInstruction, 816 "mem": remainMem, 817 "err": ErrInnerInsufficientGas.Error(), 818 }).Error("failed to prepare create nvm") 819 setHeadErrAndLog(engine, index, ErrInsufficientGas, "null", false) 820 return nil 821 } else { 822 remainInstruction -= InnerContractGasBase 823 } 824 if remainMem <= 0 { 825 logging.VLog().WithFields(logrus.Fields{ 826 "remainInstruction": remainInstruction, 827 "mem": remainMem, 828 "err": ErrInnerInsufficientMem.Error(), 829 }).Error("failed to prepare create nvm") 830 setHeadErrAndLog(engine, index, ErrExceedMemoryLimits, "null", false) 831 return nil 832 } 833 834 logging.VLog().Debugf("begin create New V8,intance:%v, mem:%v", remainInstruction, remainMem) 835 engineNew := NewV8Engine(newCtx) 836 defer engineNew.Dispose() 837 engineNew.SetExecutionLimits(remainInstruction, remainMem) 838 839 innerFunc := C.GoString(funcName) 840 innerArgs := C.GoString(args) 841 val, err := engineNew.Call(string(deploy.Source), deploy.SourceType, innerFunc, innerArgs) 842 gasCout := engineNew.ExecutionInstructions() 843 gasSum += gasCout 844 *gasCnt = C.size_t(gasSum) 845 recordInnerContractEvent(engine, err, fromAddr.String(), addr.String(), toValue.String(), innerFunc, innerArgs, ws, parentTx.Hash()) 846 if err != nil { 847 if err == core.ErrInnerExecutionFailed { 848 logging.VLog().Errorf("check inner err, engine index:%v", index) 849 } else { 850 errLog := setHeadErrAndLog(engine, index, err, val, false) 851 logging.VLog().Errorf(errLog) 852 } 853 return nil 854 } 855 856 logging.VLog().Infof("end cal val:%v,gascount:%v,gasSum:%v, engine index:%v", val, gasCout, gasSum, index) 857 return C.CString(string(val)) 858 } 859 860 // GetLatestNebulasRankFunc returns nebulas rank value of given account address 861 //export GetLatestNebulasRankFunc 862 func GetLatestNebulasRankFunc(handler unsafe.Pointer, address *C.char, 863 gasCnt *C.size_t, result **C.char, exceptionInfo **C.char) int { 864 865 engine, _ := getEngineByStorageHandler(uint64(uintptr(handler))) 866 if engine == nil || engine.ctx.block == nil { 867 logging.VLog().Error("Unexpected error: failed to get engine") 868 return C.NVM_UNEXPECTED_ERR 869 } 870 871 *result = nil 872 *exceptionInfo = nil 873 *gasCnt = C.size_t(GetLatestNebulasRankGasBase) 874 875 addr, err := core.AddressParse(C.GoString(address)) 876 if err != nil { 877 *exceptionInfo = C.CString("Address is invalid") 878 return C.NVM_EXCEPTION_ERR 879 } 880 881 data, err := engine.ctx.block.NR().GetNRListByHeight(engine.ctx.block.Height()) 882 if err != nil { 883 logging.VLog().WithFields(logrus.Fields{ 884 "height": engine.ctx.block.Height(), 885 "addr": addr, 886 "err": err, 887 }).Debug("Failed to get nr list") 888 *exceptionInfo = C.CString("Failed to get nr list") 889 return C.NVM_EXCEPTION_ERR 890 } 891 892 nrData := data.(*nr.NRData) 893 nr, err := func(list *nr.NRData, addr *core.Address) (*nr.NRItem, error) { 894 for _, nr := range nrData.Nrs { 895 if nr.Address == addr.String() { 896 return nr, nil 897 } 898 } 899 return nil, nr.ErrNRNotFound 900 }(nrData, addr) 901 if err != nil { 902 logging.VLog().WithFields(logrus.Fields{ 903 "height": engine.ctx.block.Height(), 904 "addr": addr, 905 "err": err, 906 }).Debug("Failed to find nr value") 907 *exceptionInfo = C.CString("Failed to find nr value") 908 return C.NVM_EXCEPTION_ERR 909 } 910 911 *result = C.CString(nr.Score) 912 return C.NVM_SUCCESS 913 } 914 915 // GetLatestNebulasRankSummaryFunc returns nebulas rank summary info. 916 //export GetLatestNebulasRankSummaryFunc 917 func GetLatestNebulasRankSummaryFunc(handler unsafe.Pointer, 918 gasCnt *C.size_t, result **C.char, exceptionInfo **C.char) int { 919 920 engine, _ := getEngineByStorageHandler(uint64(uintptr(handler))) 921 if engine == nil || engine.ctx.block == nil { 922 logging.VLog().Error("Unexpected error: failed to get engine") 923 return C.NVM_UNEXPECTED_ERR 924 } 925 926 *result = nil 927 *exceptionInfo = nil 928 *gasCnt = C.size_t(GetLatestNebulasRankSummaryGasBase) 929 930 data, err := engine.ctx.block.NR().GetNRSummary(engine.ctx.block.Height()) 931 if err != nil { 932 logging.VLog().WithFields(logrus.Fields{ 933 "height": engine.ctx.block.Height(), 934 "err": err, 935 }).Debug("Failed to get nr summary info") 936 *exceptionInfo = C.CString("Failed to get nr summary") 937 return C.NVM_EXCEPTION_ERR 938 } 939 940 bytes, err := data.ToBytes() 941 if err != nil { 942 logging.VLog().WithFields(logrus.Fields{ 943 "height": engine.ctx.block.Height(), 944 "err": err, 945 }).Debug("Failed to serialize nr summary info") 946 *exceptionInfo = C.CString("Failed to serialize nr summary") 947 return C.NVM_EXCEPTION_ERR 948 } 949 *result = C.CString(string(bytes)) 950 return C.NVM_SUCCESS 951 }