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  }