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  }