OSDN Git Service

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