github.com/Bytom/bytom@v1.1.2-0.20210127130405-ae40204c0b09/protocol/bc/types/txinput.go (about) 1 package types 2 3 import ( 4 "fmt" 5 "io" 6 7 "github.com/bytom/bytom/encoding/blockchain" 8 "github.com/bytom/bytom/errors" 9 "github.com/bytom/bytom/protocol/bc" 10 ) 11 12 // serflag variables for input types. 13 const ( 14 IssuanceInputType uint8 = iota 15 SpendInputType 16 CoinbaseInputType 17 ) 18 19 type ( 20 // TxInput is the top level struct of tx input. 21 TxInput struct { 22 AssetVersion uint64 23 TypedInput 24 CommitmentSuffix []byte 25 WitnessSuffix []byte 26 } 27 28 // TypedInput return the txinput type. 29 TypedInput interface { 30 InputType() uint8 31 } 32 ) 33 34 var errBadAssetID = errors.New("asset ID does not match other issuance parameters") 35 36 // AssetAmount return the asset id and amount of the txinput. 37 func (t *TxInput) AssetAmount() bc.AssetAmount { 38 switch inp := t.TypedInput.(type) { 39 case *IssuanceInput: 40 assetID := inp.AssetID() 41 return bc.AssetAmount{ 42 AssetId: &assetID, 43 Amount: inp.Amount, 44 } 45 case *SpendInput: 46 return inp.AssetAmount 47 } 48 return bc.AssetAmount{} 49 } 50 51 // AssetID return the assetID of the txinput 52 func (t *TxInput) AssetID() bc.AssetID { 53 switch inp := t.TypedInput.(type) { 54 case *IssuanceInput: 55 return inp.AssetID() 56 case *SpendInput: 57 return *inp.AssetId 58 59 } 60 return bc.AssetID{} 61 } 62 63 // Amount return the asset amount of the txinput 64 func (t *TxInput) Amount() uint64 { 65 switch inp := t.TypedInput.(type) { 66 case *IssuanceInput: 67 return inp.Amount 68 case *SpendInput: 69 return inp.Amount 70 } 71 return 0 72 } 73 74 // ControlProgram return the control program of the spend input 75 func (t *TxInput) ControlProgram() []byte { 76 if si, ok := t.TypedInput.(*SpendInput); ok { 77 return si.ControlProgram 78 } 79 return nil 80 } 81 82 // IssuanceProgram return the control program of the issuance input 83 func (t *TxInput) IssuanceProgram() []byte { 84 if ii, ok := t.TypedInput.(*IssuanceInput); ok { 85 return ii.IssuanceProgram 86 } 87 return nil 88 } 89 90 // AssetDefinition return the asset definition of the issuance input 91 func (t *TxInput) AssetDefinition() []byte { 92 if ii, ok := t.TypedInput.(*IssuanceInput); ok { 93 return ii.AssetDefinition 94 } 95 return nil 96 } 97 98 // Arguments get the args for the input 99 func (t *TxInput) Arguments() [][]byte { 100 switch inp := t.TypedInput.(type) { 101 case *IssuanceInput: 102 return inp.Arguments 103 case *SpendInput: 104 return inp.Arguments 105 } 106 return nil 107 } 108 109 // SetArguments set the args for the input 110 func (t *TxInput) SetArguments(args [][]byte) { 111 switch inp := t.TypedInput.(type) { 112 case *IssuanceInput: 113 inp.Arguments = args 114 case *SpendInput: 115 inp.Arguments = args 116 } 117 } 118 119 // SpentOutputID calculate the hash of spended output 120 func (t *TxInput) SpentOutputID() (o bc.Hash, err error) { 121 if si, ok := t.TypedInput.(*SpendInput); ok { 122 o, err = ComputeOutputID(&si.SpendCommitment) 123 } 124 return o, err 125 } 126 127 func (t *TxInput) readFrom(r *blockchain.Reader) (err error) { 128 if t.AssetVersion, err = blockchain.ReadVarint63(r); err != nil { 129 return err 130 } 131 132 var assetID bc.AssetID 133 t.CommitmentSuffix, err = blockchain.ReadExtensibleString(r, func(r *blockchain.Reader) error { 134 if t.AssetVersion != 1 { 135 return nil 136 } 137 var icType [1]byte 138 if _, err = io.ReadFull(r, icType[:]); err != nil { 139 return errors.Wrap(err, "reading input commitment type") 140 } 141 switch icType[0] { 142 case IssuanceInputType: 143 ii := new(IssuanceInput) 144 t.TypedInput = ii 145 146 if ii.Nonce, err = blockchain.ReadVarstr31(r); err != nil { 147 return err 148 } 149 if _, err = assetID.ReadFrom(r); err != nil { 150 return err 151 } 152 if ii.Amount, err = blockchain.ReadVarint63(r); err != nil { 153 return err 154 } 155 156 case SpendInputType: 157 si := new(SpendInput) 158 t.TypedInput = si 159 if si.SpendCommitmentSuffix, err = si.SpendCommitment.readFrom(r, 1); err != nil { 160 return err 161 } 162 163 case CoinbaseInputType: 164 ci := new(CoinbaseInput) 165 t.TypedInput = ci 166 if ci.Arbitrary, err = blockchain.ReadVarstr31(r); err != nil { 167 return err 168 } 169 170 default: 171 return fmt.Errorf("unsupported input type %d", icType[0]) 172 } 173 return nil 174 }) 175 if err != nil { 176 return err 177 } 178 179 t.WitnessSuffix, err = blockchain.ReadExtensibleString(r, func(r *blockchain.Reader) error { 180 if t.AssetVersion != 1 { 181 return nil 182 } 183 184 switch inp := t.TypedInput.(type) { 185 case *IssuanceInput: 186 if inp.AssetDefinition, err = blockchain.ReadVarstr31(r); err != nil { 187 return err 188 } 189 if inp.VMVersion, err = blockchain.ReadVarint63(r); err != nil { 190 return err 191 } 192 if inp.IssuanceProgram, err = blockchain.ReadVarstr31(r); err != nil { 193 return err 194 } 195 if inp.AssetID() != assetID { 196 return errBadAssetID 197 } 198 if inp.Arguments, err = blockchain.ReadVarstrList(r); err != nil { 199 return err 200 } 201 202 case *SpendInput: 203 if inp.Arguments, err = blockchain.ReadVarstrList(r); err != nil { 204 return err 205 } 206 } 207 return nil 208 }) 209 210 return err 211 } 212 213 func (t *TxInput) writeTo(w io.Writer) error { 214 if _, err := blockchain.WriteVarint63(w, t.AssetVersion); err != nil { 215 return errors.Wrap(err, "writing asset version") 216 } 217 218 if _, err := blockchain.WriteExtensibleString(w, t.CommitmentSuffix, t.writeInputCommitment); err != nil { 219 return errors.Wrap(err, "writing input commitment") 220 } 221 222 _, err := blockchain.WriteExtensibleString(w, t.WitnessSuffix, t.writeInputWitness) 223 return errors.Wrap(err, "writing input witness") 224 } 225 226 func (t *TxInput) writeInputCommitment(w io.Writer) (err error) { 227 if t.AssetVersion != 1 { 228 return nil 229 } 230 231 switch inp := t.TypedInput.(type) { 232 case *IssuanceInput: 233 if _, err = w.Write([]byte{IssuanceInputType}); err != nil { 234 return err 235 } 236 if _, err = blockchain.WriteVarstr31(w, inp.Nonce); err != nil { 237 return err 238 } 239 assetID := t.AssetID() 240 if _, err = assetID.WriteTo(w); err != nil { 241 return err 242 } 243 _, err = blockchain.WriteVarint63(w, inp.Amount) 244 return err 245 246 case *SpendInput: 247 if _, err = w.Write([]byte{SpendInputType}); err != nil { 248 return err 249 } 250 return inp.SpendCommitment.writeExtensibleString(w, inp.SpendCommitmentSuffix, t.AssetVersion) 251 252 case *CoinbaseInput: 253 if _, err = w.Write([]byte{CoinbaseInputType}); err != nil { 254 return err 255 } 256 if _, err = blockchain.WriteVarstr31(w, inp.Arbitrary); err != nil { 257 return errors.Wrap(err, "writing coinbase arbitrary") 258 } 259 } 260 return nil 261 } 262 263 func (t *TxInput) writeInputWitness(w io.Writer) error { 264 if t.AssetVersion != 1 { 265 return nil 266 } 267 switch inp := t.TypedInput.(type) { 268 case *IssuanceInput: 269 if _, err := blockchain.WriteVarstr31(w, inp.AssetDefinition); err != nil { 270 return err 271 } 272 if _, err := blockchain.WriteVarint63(w, inp.VMVersion); err != nil { 273 return err 274 } 275 if _, err := blockchain.WriteVarstr31(w, inp.IssuanceProgram); err != nil { 276 return err 277 } 278 _, err := blockchain.WriteVarstrList(w, inp.Arguments) 279 return err 280 281 case *SpendInput: 282 _, err := blockchain.WriteVarstrList(w, inp.Arguments) 283 return err 284 } 285 return nil 286 }