github.com/aergoio/aergo@v1.3.1/contract/contract.go (about) 1 package contract 2 3 import "C" 4 import ( 5 "bytes" 6 "fmt" 7 "math/big" 8 "strconv" 9 10 "github.com/aergoio/aergo/fee" 11 "github.com/aergoio/aergo/state" 12 "github.com/aergoio/aergo/types" 13 "github.com/minio/sha256-simd" 14 ) 15 16 type loadedReply struct { 17 tx *types.Tx 18 ex *Executor 19 err error 20 } 21 22 type preLoadReq struct { 23 preLoadService int 24 bs *state.BlockState 25 next *types.Tx 26 current *types.Tx 27 } 28 29 type preLoadInfo struct { 30 requestedTx *types.Tx 31 replyCh chan *loadedReply 32 } 33 34 var ( 35 loadReqCh chan *preLoadReq 36 preLoadInfos [2]preLoadInfo 37 PubNet bool 38 TraceBlockNo uint64 39 maxSQLDBSize uint32 40 ) 41 42 const BlockFactory = 0 43 const ChainService = 1 44 45 func init() { 46 loadReqCh = make(chan *preLoadReq, 10) 47 preLoadInfos[BlockFactory].replyCh = make(chan *loadedReply, 4) 48 preLoadInfos[ChainService].replyCh = make(chan *loadedReply, 4) 49 50 go preLoadWorker() 51 } 52 53 func SetPreloadTx(tx *types.Tx, service int) { 54 preLoadInfos[service].requestedTx = tx 55 } 56 57 func Execute(bs *state.BlockState, cdb ChainAccessor, tx *types.Tx, blockNo uint64, ts int64, prevBlockHash []byte, 58 sender, receiver *state.V, preLoadService int) (rv string, events []*types.Event, usedFee *big.Int, err error) { 59 60 txBody := tx.GetBody() 61 62 usedFee = fee.PayloadTxFee(len(txBody.GetPayload())) 63 64 // Transfer balance 65 if sender.AccountID() != receiver.AccountID() { 66 if sender.Balance().Cmp(txBody.GetAmountBigInt()) < 0 { 67 err = types.ErrInsufficientBalance 68 return 69 } 70 sender.SubBalance(txBody.GetAmountBigInt()) 71 receiver.AddBalance(txBody.GetAmountBigInt()) 72 } 73 74 if !receiver.IsDeploy() && len(receiver.State().CodeHash) == 0 { 75 return 76 } 77 78 contractState, err := bs.OpenContractState(receiver.AccountID(), receiver.State()) 79 if err != nil { 80 return 81 } 82 if receiver.IsRedeploy() { 83 if err = checkRedeploy(sender, receiver, contractState); err != nil { 84 return 85 } 86 bs.CodeMap.Remove(receiver.AccountID()) 87 } 88 89 var ex *Executor 90 if !receiver.IsDeploy() && preLoadInfos[preLoadService].requestedTx == tx { 91 replyCh := preLoadInfos[preLoadService].replyCh 92 for { 93 preload := <-replyCh 94 if preload.tx != tx { 95 preload.ex.close() 96 continue 97 } 98 ex = preload.ex 99 err = preload.err 100 break 101 } 102 if err != nil { 103 return 104 } 105 } 106 107 var cFee *big.Int 108 if ex != nil { 109 rv, events, cFee, err = PreCall(ex, bs, sender, contractState, blockNo, ts, receiver.RP(), prevBlockHash) 110 } else { 111 stateSet := NewContext(bs, cdb, sender, receiver, contractState, sender.ID(), 112 tx.GetHash(), blockNo, ts, prevBlockHash, "", true, 113 false, receiver.RP(), preLoadService, txBody.GetAmountBigInt()) 114 if stateSet.traceFile != nil { 115 defer stateSet.traceFile.Close() 116 } 117 if receiver.IsDeploy() { 118 rv, events, cFee, err = Create(contractState, txBody.Payload, receiver.ID(), stateSet) 119 } else { 120 rv, events, cFee, err = Call(contractState, txBody.Payload, receiver.ID(), stateSet) 121 } 122 } 123 124 usedFee.Add(usedFee, cFee) 125 126 if err != nil { 127 if isSystemError(err) { 128 return "", events, usedFee, err 129 } 130 return "", events, usedFee, newVmError(err) 131 } 132 133 err = bs.StageContractState(contractState) 134 if err != nil { 135 return "", events, usedFee, err 136 } 137 138 return rv, events, usedFee, nil 139 } 140 141 func PreLoadRequest(bs *state.BlockState, next, current *types.Tx, preLoadService int) { 142 loadReqCh <- &preLoadReq{preLoadService, bs, next, current} 143 } 144 145 func preLoadWorker() { 146 for { 147 var err error 148 reqInfo := <-loadReqCh 149 replyCh := preLoadInfos[reqInfo.preLoadService].replyCh 150 151 if len(replyCh) > 2 { 152 select { 153 case preload := <-replyCh: 154 preload.ex.close() 155 default: 156 } 157 } 158 159 bs := reqInfo.bs 160 tx := reqInfo.next 161 txBody := tx.GetBody() 162 recipient := txBody.Recipient 163 164 if txBody.Type != types.TxType_NORMAL || len(recipient) == 0 { 165 continue 166 } 167 168 if reqInfo.current.GetBody().Type == types.TxType_REDEPLOY { 169 currentTxBody := reqInfo.current.GetBody() 170 if bytes.Equal(recipient, currentTxBody.Recipient) { 171 replyCh <- &loadedReply{tx, nil, nil} 172 continue 173 } 174 } 175 176 receiver, err := bs.GetAccountStateV(recipient) 177 if err != nil { 178 replyCh <- &loadedReply{tx, nil, err} 179 continue 180 } 181 /* When deploy and call in same block and not deployed yet*/ 182 if receiver.IsNew() || len(receiver.State().CodeHash) == 0 { 183 replyCh <- &loadedReply{tx, nil, nil} 184 continue 185 } 186 contractState, err := bs.OpenContractState(receiver.AccountID(), receiver.State()) 187 if err != nil { 188 replyCh <- &loadedReply{tx, nil, err} 189 continue 190 } 191 stateSet := NewContext(bs, nil, nil, receiver, contractState, txBody.GetAccount(), 192 tx.GetHash(), 0, 0, nil, "", false, 193 false, receiver.RP(), reqInfo.preLoadService, txBody.GetAmountBigInt()) 194 195 ex, err := PreloadEx(bs, contractState, receiver.AccountID(), txBody.Payload, receiver.ID(), stateSet) 196 replyCh <- &loadedReply{tx, ex, err} 197 } 198 } 199 200 func CreateContractID(account []byte, nonce uint64) []byte { 201 h := sha256.New() 202 h.Write(account) 203 h.Write([]byte(strconv.FormatUint(nonce, 10))) 204 recipientHash := h.Sum(nil) // byte array with length 32 205 return append([]byte{0x0C}, recipientHash...) // prepend 0x0C to make it same length as account addresses 206 } 207 208 func checkRedeploy(sender, receiver *state.V, contractState *state.ContractState) error { 209 if len(receiver.State().CodeHash) == 0 || receiver.IsNew() { 210 receiverAddr := types.EncodeAddress(receiver.ID()) 211 logger.Warn().Str("error", "not found contract").Str("contract", receiverAddr).Msg("redeploy") 212 return newVmError(fmt.Errorf("not found contract %s", receiverAddr)) 213 } 214 creator, err := contractState.GetData(creatorMetaKey) 215 if err != nil { 216 return err 217 } 218 if !bytes.Equal(creator, []byte(types.EncodeAddress(sender.ID()))) { 219 return newVmError(types.ErrCreatorNotMatch) 220 } 221 return nil 222 } 223 224 func SetStateSQLMaxDBSize(size uint32) { 225 if size > stateSQLMaxDBSize { 226 maxSQLDBSize = stateSQLMaxDBSize 227 } else if size < stateSQLMinDBSize { 228 maxSQLDBSize = stateSQLMinDBSize 229 } else { 230 maxSQLDBSize = size 231 } 232 }