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 }