github.com/bytom/bytom@v1.1.2-0.20221014091027-bbcba3df6075/protocol/bc/types/transaction.go (about) 1 package types 2 3 import ( 4 "bytes" 5 "encoding/hex" 6 "fmt" 7 "io" 8 9 "github.com/bytom/bytom/consensus" 10 "github.com/bytom/bytom/encoding/blockchain" 11 "github.com/bytom/bytom/errors" 12 "github.com/bytom/bytom/protocol/bc" 13 ) 14 15 const serRequired = 0x7 // Bit mask accepted serialization flag. 16 17 // Tx holds a transaction along with its hash. 18 type Tx struct { 19 TxData 20 *bc.Tx `json:"-"` 21 } 22 23 // NewTx returns a new Tx containing data and its hash. If you have already 24 // computed the hash, use struct literal notation to make a Tx object directly. 25 func NewTx(data TxData) *Tx { 26 return &Tx{ 27 TxData: data, 28 Tx: MapTx(&data), 29 } 30 } 31 32 // OutputID return the hash of the output position 33 func (tx *Tx) OutputID(outputIndex int) *bc.Hash { 34 return tx.ResultIds[outputIndex] 35 } 36 37 // UnmarshalText fulfills the encoding.TextUnmarshaler interface. 38 func (tx *Tx) UnmarshalText(p []byte) error { 39 if err := tx.TxData.UnmarshalText(p); err != nil { 40 return err 41 } 42 43 tx.Tx = MapTx(&tx.TxData) 44 return nil 45 } 46 47 // SetInputArguments sets the Arguments field in input n. 48 func (tx *Tx) SetInputArguments(n uint32, args [][]byte) { 49 tx.Inputs[n].SetArguments(args) 50 id := tx.Tx.InputIDs[n] 51 e := tx.Entries[id] 52 switch e := e.(type) { 53 case *bc.Issuance: 54 e.WitnessArguments = args 55 case *bc.Spend: 56 e.WitnessArguments = args 57 case *bc.VetoInput: 58 e.WitnessArguments = args 59 } 60 } 61 62 // TxData encodes a transaction in the blockchain. 63 type TxData struct { 64 Version uint64 65 SerializedSize uint64 66 TimeRange uint64 67 Inputs []*TxInput 68 Outputs []*TxOutput 69 } 70 71 // MarshalText fulfills the json.Marshaler interface. 72 func (tx *TxData) MarshalText() ([]byte, error) { 73 var buf bytes.Buffer 74 if _, err := tx.WriteTo(&buf); err != nil { 75 return nil, err 76 } 77 78 b := make([]byte, hex.EncodedLen(buf.Len())) 79 hex.Encode(b, buf.Bytes()) 80 return b, nil 81 } 82 83 // UnmarshalText fulfills the encoding.TextUnmarshaler interface. 84 func (tx *TxData) UnmarshalText(p []byte) error { 85 b := make([]byte, hex.DecodedLen(len(p))) 86 if _, err := hex.Decode(b, p); err != nil { 87 return err 88 } 89 90 r := blockchain.NewReader(b) 91 if err := tx.readFrom(r); err != nil { 92 return err 93 } 94 95 if trailing := r.Len(); trailing > 0 { 96 return fmt.Errorf("trailing garbage (%d bytes)", trailing) 97 } 98 return nil 99 } 100 101 func (tx *TxData) Fee() uint64 { 102 inputBTM, outputBTM := uint64(0), uint64(0) 103 for _, input := range tx.Inputs { 104 if input.AssetID() == *consensus.BTMAssetID { 105 inputBTM += input.Amount() 106 } 107 } 108 109 for _, output := range tx.Outputs { 110 if *output.AssetId == *consensus.BTMAssetID { 111 outputBTM += output.Amount 112 } 113 } 114 115 if inputBTM > outputBTM { 116 return inputBTM - outputBTM 117 } 118 119 return 0 120 } 121 122 func (tx *TxData) readFrom(r *blockchain.Reader) (err error) { 123 startSerializedSize := r.Len() 124 var serflags [1]byte 125 if _, err = io.ReadFull(r, serflags[:]); err != nil { 126 return errors.Wrap(err, "reading serialization flags") 127 } 128 129 if serflags[0] != serRequired { 130 return fmt.Errorf("unsupported serflags %#x", serflags[0]) 131 } 132 133 if tx.Version, err = blockchain.ReadVarint63(r); err != nil { 134 return errors.Wrap(err, "reading transaction version") 135 } 136 137 if tx.TimeRange, err = blockchain.ReadVarint63(r); err != nil { 138 return err 139 } 140 141 n, err := blockchain.ReadVarint31(r) 142 if err != nil { 143 return errors.Wrap(err, "reading number of transaction inputs") 144 } 145 146 for ; n > 0; n-- { 147 ti := new(TxInput) 148 if err = ti.readFrom(r); err != nil { 149 return errors.Wrapf(err, "reading input %d", len(tx.Inputs)) 150 } 151 152 tx.Inputs = append(tx.Inputs, ti) 153 } 154 155 n, err = blockchain.ReadVarint31(r) 156 if err != nil { 157 return errors.Wrap(err, "reading number of transaction outputs") 158 } 159 160 for ; n > 0; n-- { 161 to := new(TxOutput) 162 if err = to.readFrom(r); err != nil { 163 return errors.Wrapf(err, "reading output %d", len(tx.Outputs)) 164 } 165 166 tx.Outputs = append(tx.Outputs, to) 167 } 168 169 tx.SerializedSize = uint64(startSerializedSize - r.Len()) 170 return nil 171 } 172 173 // WriteTo writes tx to w. 174 func (tx *TxData) WriteTo(w io.Writer) (int64, error) { 175 ew := errors.NewWriter(w) 176 if err := tx.writeTo(ew, serRequired); err != nil { 177 return 0, err 178 } 179 180 return ew.Written(), ew.Err() 181 } 182 183 func (tx *TxData) writeTo(w io.Writer, serflags byte) error { 184 if _, err := w.Write([]byte{serflags}); err != nil { 185 return errors.Wrap(err, "writing serialization flags") 186 } 187 188 if _, err := blockchain.WriteVarint63(w, tx.Version); err != nil { 189 return errors.Wrap(err, "writing transaction version") 190 } 191 192 if _, err := blockchain.WriteVarint63(w, tx.TimeRange); err != nil { 193 return errors.Wrap(err, "writing transaction maxtime") 194 } 195 196 if _, err := blockchain.WriteVarint31(w, uint64(len(tx.Inputs))); err != nil { 197 return errors.Wrap(err, "writing tx input count") 198 } 199 200 for i, ti := range tx.Inputs { 201 if err := ti.writeTo(w); err != nil { 202 return errors.Wrapf(err, "writing tx input %d", i) 203 } 204 } 205 206 if _, err := blockchain.WriteVarint31(w, uint64(len(tx.Outputs))); err != nil { 207 return errors.Wrap(err, "writing tx output count") 208 } 209 210 for i, to := range tx.Outputs { 211 if err := to.writeTo(w); err != nil { 212 return errors.Wrapf(err, "writing tx output %d", i) 213 } 214 } 215 return nil 216 }