github.com/iotexproject/iotex-core@v1.14.1-rc1/action/protocol/account/transfer.go (about) 1 // Copyright (c) 2019 IoTeX Foundation 2 // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability 3 // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed. 4 // This source code is governed by Apache License 2.0 that can be found in the LICENSE file. 5 6 package account 7 8 import ( 9 "context" 10 "math/big" 11 12 "github.com/iotexproject/iotex-address/address" 13 "github.com/pkg/errors" 14 15 "github.com/iotexproject/iotex-core/action" 16 "github.com/iotexproject/iotex-core/action/protocol" 17 accountutil "github.com/iotexproject/iotex-core/action/protocol/account/util" 18 "github.com/iotexproject/iotex-core/state" 19 "github.com/iotexproject/iotex-proto/golang/iotextypes" 20 ) 21 22 // TransferSizeLimit is the maximum size of transfer allowed 23 const TransferSizeLimit = 32 * 1024 24 25 // handleTransfer handles a transfer 26 func (p *Protocol) handleTransfer(ctx context.Context, tsf *action.Transfer, sm protocol.StateManager) (*action.Receipt, error) { 27 var ( 28 fCtx = protocol.MustGetFeatureCtx(ctx) 29 actionCtx = protocol.MustGetActionCtx(ctx) 30 blkCtx = protocol.MustGetBlockCtx(ctx) 31 ) 32 accountCreationOpts := []state.AccountCreationOption{} 33 if fCtx.CreateLegacyNonceAccount { 34 accountCreationOpts = append(accountCreationOpts, state.LegacyNonceAccountTypeOption()) 35 } 36 // check sender 37 sender, err := accountutil.LoadOrCreateAccount(sm, actionCtx.Caller, accountCreationOpts...) 38 if err != nil { 39 return nil, errors.Wrapf(err, "failed to load or create the account of sender %s", actionCtx.Caller.String()) 40 } 41 42 gasFee := big.NewInt(0).Mul(tsf.GasPrice(), big.NewInt(0).SetUint64(actionCtx.IntrinsicGas)) 43 if !sender.HasSufficientBalance(big.NewInt(0).Add(tsf.Amount(), gasFee)) { 44 return nil, errors.Wrapf( 45 state.ErrNotEnoughBalance, 46 "sender %s balance %s, required amount %s", 47 actionCtx.Caller.String(), 48 sender.Balance, 49 big.NewInt(0).Add(tsf.Amount(), gasFee), 50 ) 51 } 52 53 var depositLog *action.TransactionLog 54 if !fCtx.FixDoubleChargeGas { 55 // charge sender gas 56 if err := sender.SubBalance(gasFee); err != nil { 57 return nil, errors.Wrapf(err, "failed to charge the gas for sender %s", actionCtx.Caller.String()) 58 } 59 if p.depositGas != nil { 60 depositLog, err = p.depositGas(ctx, sm, gasFee) 61 if err != nil { 62 return nil, err 63 } 64 } 65 } 66 67 if fCtx.FixGasAndNonceUpdate || tsf.Nonce() != 0 { 68 // update sender Nonce 69 if err := sender.SetPendingNonce(tsf.Nonce() + 1); err != nil { 70 return nil, errors.Wrapf(err, "failed to update pending nonce of sender %s", actionCtx.Caller.String()) 71 } 72 } 73 74 var recipientAddr address.Address 75 if fCtx.TolerateLegacyAddress { 76 recipientAddr, err = address.FromStringLegacy(tsf.Recipient()) 77 } else { 78 recipientAddr, err = address.FromString(tsf.Recipient()) 79 } 80 if err != nil { 81 return nil, errors.Wrapf(err, "failed to decode recipient address %s", tsf.Recipient()) 82 } 83 recipientAcct, err := accountutil.LoadAccount(sm, recipientAddr, accountCreationOpts...) 84 if err != nil { 85 return nil, errors.Wrapf(err, "failed to load address %s", tsf.Recipient()) 86 } 87 if recipientAcct.IsContract() { 88 // put updated sender's state to trie 89 if err := accountutil.StoreAccount(sm, actionCtx.Caller, sender); err != nil { 90 return nil, errors.Wrap(err, "failed to update pending account changes to trie") 91 } 92 if fCtx.FixDoubleChargeGas { 93 if p.depositGas != nil { 94 depositLog, err = p.depositGas(ctx, sm, gasFee) 95 if err != nil { 96 return nil, err 97 } 98 } 99 } 100 receipt := &action.Receipt{ 101 Status: uint64(iotextypes.ReceiptStatus_Failure), 102 BlockHeight: blkCtx.BlockHeight, 103 ActionHash: actionCtx.ActionHash, 104 GasConsumed: actionCtx.IntrinsicGas, 105 ContractAddress: p.addr.String(), 106 } 107 receipt.AddTransactionLogs(depositLog) 108 return receipt, nil 109 } 110 111 // update sender Balance 112 if err := sender.SubBalance(tsf.Amount()); err != nil { 113 return nil, errors.Wrapf(err, "failed to update the Balance of sender %s", actionCtx.Caller.String()) 114 } 115 116 // put updated sender's state to trie 117 if err := accountutil.StoreAccount(sm, actionCtx.Caller, sender); err != nil { 118 return nil, errors.Wrap(err, "failed to update pending account changes to trie") 119 } 120 121 // check recipient 122 recipient, err := accountutil.LoadOrCreateAccount(sm, recipientAddr, accountCreationOpts...) 123 if err != nil { 124 return nil, errors.Wrapf(err, "failed to load or create the account of recipient %s", tsf.Recipient()) 125 } 126 if err := recipient.AddBalance(tsf.Amount()); err != nil { 127 return nil, errors.Wrapf(err, "failed to add balance %s", tsf.Amount()) 128 } 129 // put updated recipient's state to trie 130 if err := accountutil.StoreAccount(sm, recipientAddr, recipient); err != nil { 131 return nil, errors.Wrap(err, "failed to update pending account changes to trie") 132 } 133 134 if fCtx.FixDoubleChargeGas { 135 if p.depositGas != nil { 136 depositLog, err = p.depositGas(ctx, sm, gasFee) 137 if err != nil { 138 return nil, err 139 } 140 } 141 } 142 143 receipt := &action.Receipt{ 144 Status: uint64(iotextypes.ReceiptStatus_Success), 145 BlockHeight: blkCtx.BlockHeight, 146 ActionHash: actionCtx.ActionHash, 147 GasConsumed: actionCtx.IntrinsicGas, 148 ContractAddress: p.addr.String(), 149 } 150 receipt.AddTransactionLogs(&action.TransactionLog{ 151 Type: iotextypes.TransactionLogType_NATIVE_TRANSFER, 152 Sender: actionCtx.Caller.String(), 153 Recipient: tsf.Recipient(), 154 Amount: tsf.Amount(), 155 }, depositLog) 156 157 return receipt, nil 158 } 159 160 // validateTransfer validates a transfer 161 func (p *Protocol) validateTransfer(ctx context.Context, tsf *action.Transfer) error { 162 // Reject oversized transfer 163 if tsf.TotalSize() > TransferSizeLimit { 164 return action.ErrOversizedData 165 } 166 var ( 167 fCtx = protocol.MustGetFeatureCtx(ctx) 168 err error 169 ) 170 if fCtx.TolerateLegacyAddress { 171 _, err = address.FromStringLegacy(tsf.Recipient()) 172 } else { 173 _, err = address.FromString(tsf.Recipient()) 174 } 175 return err 176 }