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  }