OSDN Git Service

Hulk did something
[bytom/vapor.git] / protocol / bc / types / txinput.go
diff --git a/protocol/bc/types/txinput.go b/protocol/bc/types/txinput.go
new file mode 100644 (file)
index 0000000..a7e17a1
--- /dev/null
@@ -0,0 +1,286 @@
+package types
+
+import (
+       "fmt"
+       "io"
+
+       "github.com/vapor/encoding/blockchain"
+       "github.com/vapor/errors"
+       "github.com/vapor/protocol/bc"
+)
+
+// serflag variables for input types.
+const (
+       IssuanceInputType uint8 = iota
+       SpendInputType
+       CoinbaseInputType
+)
+
+type (
+       // TxInput is the top level struct of tx input.
+       TxInput struct {
+               AssetVersion uint64
+               TypedInput
+               CommitmentSuffix []byte
+               WitnessSuffix    []byte
+       }
+
+       // TypedInput return the txinput type.
+       TypedInput interface {
+               InputType() uint8
+       }
+)
+
+var errBadAssetID = errors.New("asset ID does not match other issuance parameters")
+
+// AssetAmount return the asset id and amount of the txinput.
+func (t *TxInput) AssetAmount() bc.AssetAmount {
+       switch inp := t.TypedInput.(type) {
+       case *IssuanceInput:
+               assetID := inp.AssetID()
+               return bc.AssetAmount{
+                       AssetId: &assetID,
+                       Amount:  inp.Amount,
+               }
+       case *SpendInput:
+               return inp.AssetAmount
+       }
+       return bc.AssetAmount{}
+}
+
+// AssetID return the assetID of the txinput
+func (t *TxInput) AssetID() bc.AssetID {
+       switch inp := t.TypedInput.(type) {
+       case *IssuanceInput:
+               return inp.AssetID()
+       case *SpendInput:
+               return *inp.AssetId
+
+       }
+       return bc.AssetID{}
+}
+
+// Amount return the asset amount of the txinput
+func (t *TxInput) Amount() uint64 {
+       switch inp := t.TypedInput.(type) {
+       case *IssuanceInput:
+               return inp.Amount
+       case *SpendInput:
+               return inp.Amount
+       }
+       return 0
+}
+
+// ControlProgram return the control program of the spend input
+func (t *TxInput) ControlProgram() []byte {
+       if si, ok := t.TypedInput.(*SpendInput); ok {
+               return si.ControlProgram
+       }
+       return nil
+}
+
+// IssuanceProgram return the control program of the issuance input
+func (t *TxInput) IssuanceProgram() []byte {
+       if ii, ok := t.TypedInput.(*IssuanceInput); ok {
+               return ii.IssuanceProgram
+       }
+       return nil
+}
+
+// AssetDefinition return the asset definition of the issuance input
+func (t *TxInput) AssetDefinition() []byte {
+       if ii, ok := t.TypedInput.(*IssuanceInput); ok {
+               return ii.AssetDefinition
+       }
+       return nil
+}
+
+// Arguments get the args for the input
+func (t *TxInput) Arguments() [][]byte {
+       switch inp := t.TypedInput.(type) {
+       case *IssuanceInput:
+               return inp.Arguments
+       case *SpendInput:
+               return inp.Arguments
+       }
+       return nil
+}
+
+// SetArguments set the args for the input
+func (t *TxInput) SetArguments(args [][]byte) {
+       switch inp := t.TypedInput.(type) {
+       case *IssuanceInput:
+               inp.Arguments = args
+       case *SpendInput:
+               inp.Arguments = args
+       }
+}
+
+// SpentOutputID calculate the hash of spended output
+func (t *TxInput) SpentOutputID() (o bc.Hash, err error) {
+       if si, ok := t.TypedInput.(*SpendInput); ok {
+               o, err = ComputeOutputID(&si.SpendCommitment)
+       }
+       return o, err
+}
+
+func (t *TxInput) readFrom(r *blockchain.Reader) (err error) {
+       if t.AssetVersion, err = blockchain.ReadVarint63(r); err != nil {
+               return err
+       }
+
+       var assetID bc.AssetID
+       t.CommitmentSuffix, err = blockchain.ReadExtensibleString(r, func(r *blockchain.Reader) error {
+               if t.AssetVersion != 1 {
+                       return nil
+               }
+               var icType [1]byte
+               if _, err = io.ReadFull(r, icType[:]); err != nil {
+                       return errors.Wrap(err, "reading input commitment type")
+               }
+               switch icType[0] {
+               case IssuanceInputType:
+                       ii := new(IssuanceInput)
+                       t.TypedInput = ii
+
+                       if ii.Nonce, err = blockchain.ReadVarstr31(r); err != nil {
+                               return err
+                       }
+                       if _, err = assetID.ReadFrom(r); err != nil {
+                               return err
+                       }
+                       if ii.Amount, err = blockchain.ReadVarint63(r); err != nil {
+                               return err
+                       }
+
+               case SpendInputType:
+                       si := new(SpendInput)
+                       t.TypedInput = si
+                       if si.SpendCommitmentSuffix, err = si.SpendCommitment.readFrom(r, 1); err != nil {
+                               return err
+                       }
+
+               case CoinbaseInputType:
+                       ci := new(CoinbaseInput)
+                       t.TypedInput = ci
+                       if ci.Arbitrary, err = blockchain.ReadVarstr31(r); err != nil {
+                               return err
+                       }
+
+               default:
+                       return fmt.Errorf("unsupported input type %d", icType[0])
+               }
+               return nil
+       })
+       if err != nil {
+               return err
+       }
+
+       t.WitnessSuffix, err = blockchain.ReadExtensibleString(r, func(r *blockchain.Reader) error {
+               if t.AssetVersion != 1 {
+                       return nil
+               }
+
+               switch inp := t.TypedInput.(type) {
+               case *IssuanceInput:
+                       if inp.AssetDefinition, err = blockchain.ReadVarstr31(r); err != nil {
+                               return err
+                       }
+                       if inp.VMVersion, err = blockchain.ReadVarint63(r); err != nil {
+                               return err
+                       }
+                       if inp.IssuanceProgram, err = blockchain.ReadVarstr31(r); err != nil {
+                               return err
+                       }
+                       if inp.AssetID() != assetID {
+                               return errBadAssetID
+                       }
+                       if inp.Arguments, err = blockchain.ReadVarstrList(r); err != nil {
+                               return err
+                       }
+
+               case *SpendInput:
+                       if inp.Arguments, err = blockchain.ReadVarstrList(r); err != nil {
+                               return err
+                       }
+               }
+               return nil
+       })
+
+       return err
+}
+
+func (t *TxInput) writeTo(w io.Writer) error {
+       if _, err := blockchain.WriteVarint63(w, t.AssetVersion); err != nil {
+               return errors.Wrap(err, "writing asset version")
+       }
+
+       if _, err := blockchain.WriteExtensibleString(w, t.CommitmentSuffix, t.writeInputCommitment); err != nil {
+               return errors.Wrap(err, "writing input commitment")
+       }
+
+       _, err := blockchain.WriteExtensibleString(w, t.WitnessSuffix, t.writeInputWitness)
+       return errors.Wrap(err, "writing input witness")
+}
+
+func (t *TxInput) writeInputCommitment(w io.Writer) (err error) {
+       if t.AssetVersion != 1 {
+               return nil
+       }
+
+       switch inp := t.TypedInput.(type) {
+       case *IssuanceInput:
+               if _, err = w.Write([]byte{IssuanceInputType}); err != nil {
+                       return err
+               }
+               if _, err = blockchain.WriteVarstr31(w, inp.Nonce); err != nil {
+                       return err
+               }
+               assetID := t.AssetID()
+               if _, err = assetID.WriteTo(w); err != nil {
+                       return err
+               }
+               _, err = blockchain.WriteVarint63(w, inp.Amount)
+               return err
+
+       case *SpendInput:
+               if _, err = w.Write([]byte{SpendInputType}); err != nil {
+                       return err
+               }
+               return inp.SpendCommitment.writeExtensibleString(w, inp.SpendCommitmentSuffix, t.AssetVersion)
+
+       case *CoinbaseInput:
+               if _, err = w.Write([]byte{CoinbaseInputType}); err != nil {
+                       return err
+               }
+               if _, err = blockchain.WriteVarstr31(w, inp.Arbitrary); err != nil {
+                       return errors.Wrap(err, "writing coinbase arbitrary")
+               }
+       }
+       return nil
+}
+
+func (t *TxInput) writeInputWitness(w io.Writer) error {
+       if t.AssetVersion != 1 {
+               return nil
+       }
+       switch inp := t.TypedInput.(type) {
+       case *IssuanceInput:
+               if _, err := blockchain.WriteVarstr31(w, inp.AssetDefinition); err != nil {
+                       return err
+               }
+               if _, err := blockchain.WriteVarint63(w, inp.VMVersion); err != nil {
+                       return err
+               }
+               if _, err := blockchain.WriteVarstr31(w, inp.IssuanceProgram); err != nil {
+                       return err
+               }
+               _, err := blockchain.WriteVarstrList(w, inp.Arguments)
+               return err
+
+       case *SpendInput:
+               _, err := blockchain.WriteVarstrList(w, inp.Arguments)
+               return err
+       }
+       return nil
+}