github.com/igggame/nebulas-go@v2.1.0+incompatible/core/transaction_call_payload.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 core
    20  
    21  import (
    22  	"encoding/json"
    23  	"fmt"
    24  
    25  	"github.com/nebulasio/go-nebulas/util"
    26  	"github.com/nebulasio/go-nebulas/util/byteutils"
    27  	"github.com/nebulasio/go-nebulas/util/logging"
    28  	"github.com/sirupsen/logrus"
    29  )
    30  
    31  // CallPayload carry function call information
    32  type CallPayload struct {
    33  	Function string
    34  	Args     string
    35  }
    36  
    37  // LoadCallPayload from bytes
    38  func LoadCallPayload(bytes []byte) (*CallPayload, error) {
    39  	payload := &CallPayload{}
    40  	if err := json.Unmarshal(bytes, payload); err != nil {
    41  		return nil, ErrInvalidArgument
    42  	}
    43  	return NewCallPayload(payload.Function, payload.Args)
    44  }
    45  
    46  // NewCallPayload with function & args
    47  func NewCallPayload(function, args string) (*CallPayload, error) {
    48  
    49  	if PublicFuncNameChecker.MatchString(function) == false {
    50  		return nil, ErrInvalidCallFunction
    51  	}
    52  
    53  	if err := CheckContractArgs(args); err != nil {
    54  		return nil, ErrInvalidArgument
    55  	}
    56  
    57  	return &CallPayload{
    58  		Function: function,
    59  		Args:     args,
    60  	}, nil
    61  }
    62  
    63  // ToBytes serialize payload
    64  func (payload *CallPayload) ToBytes() ([]byte, error) {
    65  	return json.Marshal(payload)
    66  }
    67  
    68  // BaseGasCount returns base gas count
    69  func (payload *CallPayload) BaseGasCount() *util.Uint128 {
    70  	base, _ := util.NewUint128FromInt(60)
    71  	return base
    72  }
    73  
    74  var (
    75  	TestCompatArr = []string{"5b6a9ed8a48cfb0e6415f0df9f79cbbdac565dd139779c7972069b37c99a3913",
    76  		"918d116f5d42b253e84497d65d2a6508fb5c4c1dbc5c1c2a1718ab718a50a509"}
    77  	MainCompatArr = []string{"ee90d2cc5f930fe627363e9e05f1e98ea20025898201c849125659d6c0079242",
    78  		"3db72f0d02daa26407d13ca9efc820ec618407d10d55ac15433784aaef93c659"}
    79  )
    80  
    81  // IsCompatibleStack return if compatible stack
    82  func IsCompatibleStack(chainID uint32, hash byteutils.Hash) bool {
    83  	if chainID == MainNetID {
    84  		for i := 0; i < len(MainCompatArr); i++ {
    85  			compatStr := MainCompatArr[i]
    86  			if compatStr == hash.String() {
    87  				return true
    88  			}
    89  		}
    90  
    91  	} else if chainID == TestNetID {
    92  		for i := 0; i < len(TestCompatArr); i++ {
    93  			compatStr := TestCompatArr[i]
    94  			if compatStr == hash.String() {
    95  				return true
    96  			}
    97  		}
    98  	}
    99  	return false
   100  }
   101  
   102  // Execute the call payload in tx, call a function
   103  func (payload *CallPayload) Execute(limitedGas *util.Uint128, tx *Transaction, block *Block, ws WorldState) (*util.Uint128, string, error) {
   104  	if block == nil || tx == nil {
   105  		return util.NewUint128(), "", ErrNilArgument
   106  	}
   107  
   108  	// payloadGasLimit <= 0, v8 engine not limit the execution instructions
   109  	if limitedGas.Cmp(util.NewUint128()) <= 0 {
   110  		return util.NewUint128(), "", ErrOutOfGasLimit
   111  	}
   112  
   113  	// contract address is tx.to.
   114  	contract, err := CheckContract(tx.to, ws)
   115  	if err != nil {
   116  		return util.NewUint128(), "", err
   117  	}
   118  
   119  	birthTx, err := GetTransaction(contract.BirthPlace(), ws)
   120  	if err != nil {
   121  		return util.NewUint128(), "", err
   122  	}
   123  	/* // useless owner.
   124  	owner, err := ws.GetOrCreateUserAccount(birthTx.from.Bytes())
   125  	if err != nil {
   126  		return util.NewUint128(), "", err
   127  	} */
   128  	deploy, err := LoadDeployPayload(birthTx.data.Payload) // ToConfirm: move deploy payload in ctx.
   129  	if err != nil {
   130  		return util.NewUint128(), "", err
   131  	}
   132  
   133  	engine, err := block.nvm.CreateEngine(block, tx, contract, ws)
   134  	if err != nil {
   135  		return util.NewUint128(), "", err
   136  	}
   137  	defer engine.Dispose()
   138  
   139  	if IsCompatibleStack(block.header.chainID, tx.hash) == true {
   140  		if err := engine.SetExecutionLimits(2000, DefaultLimitsOfTotalMemorySize); err != nil {
   141  			return util.NewUint128(), "", err
   142  		}
   143  	} else {
   144  		if err := engine.SetExecutionLimits(limitedGas.Uint64(), DefaultLimitsOfTotalMemorySize); err != nil {
   145  			return util.NewUint128(), "", err
   146  		}
   147  	}
   148  
   149  	result, exeErr := engine.Call(deploy.Source, deploy.SourceType, payload.Function, payload.Args)
   150  	gasCount := engine.ExecutionInstructions()
   151  	instructions, err := util.NewUint128FromInt(int64(gasCount))
   152  
   153  	if err != nil || exeErr == ErrUnexpected {
   154  		logging.VLog().WithFields(logrus.Fields{
   155  			"err":      err,
   156  			"exeErr":   exeErr,
   157  			"gasCount": gasCount,
   158  		}).Error("Unexpected error when executing call")
   159  		return util.NewUint128(), "", ErrUnexpected
   160  	}
   161  
   162  	if IsCompatibleStack(block.header.chainID, tx.hash) {
   163  		instructions = limitedGas
   164  	}
   165  	if exeErr == ErrExecutionFailed && len(result) > 0 {
   166  		exeErr = fmt.Errorf("Call: %s", result)
   167  	}
   168  	// if exeErr == ErrInnerExecutionFailed && len(result) > 0 {
   169  	// 	exeErr = fmt.Errorf("Inner Call: %s", result)
   170  	// }
   171  
   172  	logging.VLog().WithFields(logrus.Fields{
   173  		"tx.hash":      tx.Hash(),
   174  		"instructions": instructions,
   175  		"limitedGas":   limitedGas,
   176  	}).Debug("record gas of v8")
   177  
   178  	return instructions, result, exeErr
   179  }