github.com/MetalBlockchain/metalgo@v1.11.9/vms/example/xsvm/execute/tx.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package execute 5 6 import ( 7 "context" 8 "errors" 9 10 "github.com/MetalBlockchain/metalgo/database" 11 "github.com/MetalBlockchain/metalgo/ids" 12 "github.com/MetalBlockchain/metalgo/snow" 13 "github.com/MetalBlockchain/metalgo/snow/engine/snowman/block" 14 "github.com/MetalBlockchain/metalgo/utils/hashing" 15 "github.com/MetalBlockchain/metalgo/utils/wrappers" 16 "github.com/MetalBlockchain/metalgo/vms/example/xsvm/state" 17 "github.com/MetalBlockchain/metalgo/vms/example/xsvm/tx" 18 "github.com/MetalBlockchain/metalgo/vms/platformvm/warp" 19 ) 20 21 const ( 22 QuorumNumerator = 2 23 QuorumDenominator = 3 24 ) 25 26 var ( 27 _ tx.Visitor = (*Tx)(nil) 28 29 errFeeTooHigh = errors.New("fee too high") 30 errWrongChainID = errors.New("wrong chainID") 31 errMissingBlockContext = errors.New("missing block context") 32 errDuplicateImport = errors.New("duplicate import") 33 ) 34 35 type Tx struct { 36 Context context.Context 37 ChainContext *snow.Context 38 Database database.KeyValueReaderWriterDeleter 39 40 SkipVerify bool 41 BlockContext *block.Context 42 43 TxID ids.ID 44 Sender ids.ShortID 45 TransferFee uint64 46 ExportFee uint64 47 ImportFee uint64 48 } 49 50 func (t *Tx) Transfer(tf *tx.Transfer) error { 51 if tf.MaxFee < t.TransferFee { 52 return errFeeTooHigh 53 } 54 if tf.ChainID != t.ChainContext.ChainID { 55 return errWrongChainID 56 } 57 58 return errors.Join( 59 state.IncrementNonce(t.Database, t.Sender, tf.Nonce), 60 state.DecreaseBalance(t.Database, t.Sender, tf.ChainID, t.TransferFee), 61 state.DecreaseBalance(t.Database, t.Sender, tf.AssetID, tf.Amount), 62 state.IncreaseBalance(t.Database, tf.To, tf.AssetID, tf.Amount), 63 ) 64 } 65 66 func (t *Tx) Export(e *tx.Export) error { 67 if e.MaxFee < t.ExportFee { 68 return errFeeTooHigh 69 } 70 if e.ChainID != t.ChainContext.ChainID { 71 return errWrongChainID 72 } 73 74 payload, err := tx.NewPayload( 75 t.Sender, 76 e.Nonce, 77 e.IsReturn, 78 e.Amount, 79 e.To, 80 ) 81 if err != nil { 82 return err 83 } 84 85 message, err := warp.NewUnsignedMessage( 86 t.ChainContext.NetworkID, 87 e.ChainID, 88 payload.Bytes(), 89 ) 90 if err != nil { 91 return err 92 } 93 94 var errs wrappers.Errs 95 errs.Add( 96 state.IncrementNonce(t.Database, t.Sender, e.Nonce), 97 state.DecreaseBalance(t.Database, t.Sender, e.ChainID, t.ExportFee), 98 ) 99 100 if e.IsReturn { 101 errs.Add( 102 state.DecreaseBalance(t.Database, t.Sender, e.PeerChainID, e.Amount), 103 ) 104 } else { 105 errs.Add( 106 state.DecreaseBalance(t.Database, t.Sender, e.ChainID, e.Amount), 107 state.IncreaseLoan(t.Database, e.PeerChainID, e.Amount), 108 ) 109 } 110 111 errs.Add( 112 state.SetMessage(t.Database, t.TxID, message), 113 ) 114 return errs.Err 115 } 116 117 func (t *Tx) Import(i *tx.Import) error { 118 if i.MaxFee < t.ImportFee { 119 return errFeeTooHigh 120 } 121 if t.BlockContext == nil { 122 return errMissingBlockContext 123 } 124 125 message, err := warp.ParseMessage(i.Message) 126 if err != nil { 127 return err 128 } 129 130 var errs wrappers.Errs 131 errs.Add( 132 state.IncrementNonce(t.Database, t.Sender, i.Nonce), 133 state.DecreaseBalance(t.Database, t.Sender, t.ChainContext.ChainID, t.ImportFee), 134 ) 135 136 payload, err := tx.ParsePayload(message.Payload) 137 if err != nil { 138 return err 139 } 140 141 if payload.IsReturn { 142 errs.Add( 143 state.IncreaseBalance(t.Database, payload.To, t.ChainContext.ChainID, payload.Amount), 144 state.DecreaseLoan(t.Database, message.SourceChainID, payload.Amount), 145 ) 146 } else { 147 errs.Add( 148 state.IncreaseBalance(t.Database, payload.To, message.SourceChainID, payload.Amount), 149 ) 150 } 151 152 var loanID ids.ID = hashing.ComputeHash256Array(message.UnsignedMessage.Bytes()) 153 hasLoanID, err := state.HasLoanID(t.Database, message.SourceChainID, loanID) 154 if hasLoanID { 155 return errDuplicateImport 156 } 157 158 errs.Add( 159 err, 160 state.AddLoanID(t.Database, message.SourceChainID, loanID), 161 ) 162 163 if t.SkipVerify || errs.Errored() { 164 return errs.Err 165 } 166 167 return message.Signature.Verify( 168 t.Context, 169 &message.UnsignedMessage, 170 t.ChainContext.NetworkID, 171 t.ChainContext.ValidatorState, 172 t.BlockContext.PChainHeight, 173 QuorumNumerator, 174 QuorumDenominator, 175 ) 176 }