gitlab.com/flarenetwork/coreth@v0.1.1/plugin/evm/tx.go (about) 1 // (c) 2019-2020, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package evm 5 6 import ( 7 "bytes" 8 "errors" 9 "fmt" 10 "math/big" 11 "sort" 12 13 "github.com/ethereum/go-ethereum/common" 14 15 "gitlab.com/flarenetwork/coreth/core/state" 16 "gitlab.com/flarenetwork/coreth/params" 17 18 "github.com/ava-labs/avalanchego/codec" 19 "github.com/ava-labs/avalanchego/database" 20 "github.com/ava-labs/avalanchego/ids" 21 "github.com/ava-labs/avalanchego/snow" 22 "github.com/ava-labs/avalanchego/utils" 23 "github.com/ava-labs/avalanchego/utils/crypto" 24 "github.com/ava-labs/avalanchego/utils/hashing" 25 "github.com/ava-labs/avalanchego/utils/wrappers" 26 "github.com/ava-labs/avalanchego/vms/components/verify" 27 "github.com/ava-labs/avalanchego/vms/secp256k1fx" 28 ) 29 30 var ( 31 errWrongBlockchainID = errors.New("wrong blockchain ID provided") 32 errWrongNetworkID = errors.New("tx was issued with a different network ID") 33 errNilTx = errors.New("tx is nil") 34 errNoValueOutput = errors.New("output has no value") 35 errNoValueInput = errors.New("input has no value") 36 errNilOutput = errors.New("nil output") 37 errNilInput = errors.New("nil input") 38 errEmptyAssetID = errors.New("empty asset ID is not valid") 39 errNilBaseFee = errors.New("cannot calculate dynamic fee with nil baseFee") 40 errFeeOverflow = errors.New("overflow occurred while calculating the fee") 41 ) 42 43 // Constants for calculating the gas consumed by atomic transactions 44 var ( 45 TxBytesGas uint64 = 1 46 EVMOutputGas uint64 = (common.AddressLength + wrappers.LongLen + hashing.HashLen) * TxBytesGas 47 EVMInputGas uint64 = (common.AddressLength+wrappers.LongLen+hashing.HashLen+wrappers.LongLen)*TxBytesGas + secp256k1fx.CostPerSignature 48 ) 49 50 // EVMOutput defines an output that is added to the EVM state created by import transactions 51 type EVMOutput struct { 52 Address common.Address `serialize:"true" json:"address"` 53 Amount uint64 `serialize:"true" json:"amount"` 54 AssetID ids.ID `serialize:"true" json:"assetID"` 55 } 56 57 // EVMInput defines an input created from the EVM state to fund export transactions 58 type EVMInput struct { 59 Address common.Address `serialize:"true" json:"address"` 60 Amount uint64 `serialize:"true" json:"amount"` 61 AssetID ids.ID `serialize:"true" json:"assetID"` 62 Nonce uint64 `serialize:"true" json:"nonce"` 63 } 64 65 // Verify ... 66 func (out *EVMOutput) Verify() error { 67 switch { 68 case out == nil: 69 return errNilOutput 70 case out.Amount == 0: 71 return errNoValueOutput 72 case out.AssetID == ids.Empty: 73 return errEmptyAssetID 74 } 75 return nil 76 } 77 78 // Verify ... 79 func (in *EVMInput) Verify() error { 80 switch { 81 case in == nil: 82 return errNilInput 83 case in.Amount == 0: 84 return errNoValueInput 85 case in.AssetID == ids.Empty: 86 return errEmptyAssetID 87 } 88 return nil 89 } 90 91 // UnsignedTx is an unsigned transaction 92 type UnsignedTx interface { 93 Initialize(unsignedBytes, signedBytes []byte) 94 ID() ids.ID 95 Cost() (uint64, error) 96 UnsignedBytes() []byte 97 Bytes() []byte 98 } 99 100 // UnsignedAtomicTx is an unsigned operation that can be atomically accepted 101 type UnsignedAtomicTx interface { 102 UnsignedTx 103 104 // UTXOs this tx consumes 105 InputUTXOs() ids.Set 106 // Attempts to verify this transaction with the provided state. 107 SemanticVerify(vm *VM, stx *Tx, parent *Block, baseFee *big.Int, rules params.Rules) error 108 109 // Accept this transaction with the additionally provided state transitions. 110 Accept(ctx *snow.Context, batch database.Batch) error 111 112 EVMStateTransfer(ctx *snow.Context, state *state.StateDB) error 113 } 114 115 // Tx is a signed transaction 116 type Tx struct { 117 // The body of this transaction 118 UnsignedAtomicTx `serialize:"true" json:"unsignedTx"` 119 120 // The credentials of this transaction 121 Creds []verify.Verifiable `serialize:"true" json:"credentials"` 122 } 123 124 // Sign this transaction with the provided signers 125 func (tx *Tx) Sign(c codec.Manager, signers [][]*crypto.PrivateKeySECP256K1R) error { 126 unsignedBytes, err := c.Marshal(codecVersion, &tx.UnsignedAtomicTx) 127 if err != nil { 128 return fmt.Errorf("couldn't marshal UnsignedAtomicTx: %w", err) 129 } 130 131 // Attach credentials 132 hash := hashing.ComputeHash256(unsignedBytes) 133 for _, keys := range signers { 134 cred := &secp256k1fx.Credential{ 135 Sigs: make([][crypto.SECP256K1RSigLen]byte, len(keys)), 136 } 137 for i, key := range keys { 138 sig, err := key.SignHash(hash) // Sign hash 139 if err != nil { 140 return fmt.Errorf("problem generating credential: %w", err) 141 } 142 copy(cred.Sigs[i][:], sig) 143 } 144 tx.Creds = append(tx.Creds, cred) // Attach credential 145 } 146 147 signedBytes, err := c.Marshal(codecVersion, tx) 148 if err != nil { 149 return fmt.Errorf("couldn't marshal Tx: %w", err) 150 } 151 tx.Initialize(unsignedBytes, signedBytes) 152 return nil 153 } 154 155 // innerSortInputsAndSigners implements sort.Interface for EVMInput 156 type innerSortInputsAndSigners struct { 157 inputs []EVMInput 158 signers [][]*crypto.PrivateKeySECP256K1R 159 } 160 161 func (ins *innerSortInputsAndSigners) Less(i, j int) bool { 162 addrComp := bytes.Compare(ins.inputs[i].Address.Bytes(), ins.inputs[j].Address.Bytes()) 163 if addrComp != 0 { 164 return addrComp < 0 165 } 166 return bytes.Compare(ins.inputs[i].AssetID[:], ins.inputs[j].AssetID[:]) < 0 167 } 168 169 func (ins *innerSortInputsAndSigners) Len() int { return len(ins.inputs) } 170 171 func (ins *innerSortInputsAndSigners) Swap(i, j int) { 172 ins.inputs[j], ins.inputs[i] = ins.inputs[i], ins.inputs[j] 173 ins.signers[j], ins.signers[i] = ins.signers[i], ins.signers[j] 174 } 175 176 // SortEVMInputsAndSigners sorts the list of EVMInputs based on the addresses and assetIDs 177 func SortEVMInputsAndSigners(inputs []EVMInput, signers [][]*crypto.PrivateKeySECP256K1R) { 178 sort.Sort(&innerSortInputsAndSigners{inputs: inputs, signers: signers}) 179 } 180 181 // IsSortedAndUniqueEVMInputs returns true if the EVM Inputs are sorted and unique 182 // based on the account addresses 183 func IsSortedAndUniqueEVMInputs(inputs []EVMInput) bool { 184 return utils.IsSortedAndUnique(&innerSortInputsAndSigners{inputs: inputs}) 185 } 186 187 // innerSortEVMOutputs implements sort.Interface for EVMOutput 188 type innerSortEVMOutputs struct { 189 outputs []EVMOutput 190 } 191 192 func (outs *innerSortEVMOutputs) Less(i, j int) bool { 193 addrComp := bytes.Compare(outs.outputs[i].Address.Bytes(), outs.outputs[j].Address.Bytes()) 194 if addrComp != 0 { 195 return addrComp < 0 196 } 197 return bytes.Compare(outs.outputs[i].AssetID[:], outs.outputs[j].AssetID[:]) < 0 198 } 199 200 func (outs *innerSortEVMOutputs) Len() int { return len(outs.outputs) } 201 202 func (outs *innerSortEVMOutputs) Swap(i, j int) { 203 outs.outputs[j], outs.outputs[i] = outs.outputs[i], outs.outputs[j] 204 } 205 206 // SortEVMOutputs sorts the list of EVMOutputs based on the addresses and assetIDs 207 // of the outputs 208 func SortEVMOutputs(outputs []EVMOutput) { 209 sort.Sort(&innerSortEVMOutputs{outputs: outputs}) 210 } 211 212 // IsSortedEVMOutputs returns true if the EVMOutputs are sorted 213 // based on the account addresses and assetIDs 214 func IsSortedEVMOutputs(outputs []EVMOutput) bool { 215 return sort.IsSorted(&innerSortEVMOutputs{outputs: outputs}) 216 } 217 218 // IsSortedAndUniqueEVMOutputs returns true if the EVMOutputs are sorted 219 // and unique based on the account addresses and assetIDs 220 func IsSortedAndUniqueEVMOutputs(outputs []EVMOutput) bool { 221 return utils.IsSortedAndUnique(&innerSortEVMOutputs{outputs: outputs}) 222 } 223 224 // calculates the amount of AVAX that must be burned by an atomic transaction 225 // that consumes [cost] at [baseFee]. 226 func calculateDynamicFee(cost uint64, baseFee *big.Int) (uint64, error) { 227 if baseFee == nil { 228 return 0, errNilBaseFee 229 } 230 bigCost := new(big.Int).SetUint64(cost) 231 fee := new(big.Int).Mul(bigCost, baseFee) 232 feeToRoundUp := new(big.Int).Add(fee, x2cRateMinus1) 233 feeInNAVAX := new(big.Int).Div(feeToRoundUp, x2cRate) 234 if !feeInNAVAX.IsUint64() { 235 // the fee is more than can fit in a uint64 236 return 0, errFeeOverflow 237 } 238 return feeInNAVAX.Uint64(), nil 239 } 240 241 func calcBytesCost(len int) uint64 { 242 return uint64(len) * TxBytesGas 243 }