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  }