OSDN Git Service

Merge pull request #375 from Bytom/dev
[bytom/bytom-spv.git] / protocol / bc / legacy / transaction.go
1 package legacy
2
3 import (
4         "bytes"
5         "encoding/hex"
6         "fmt"
7         "io"
8
9         "github.com/bytom/crypto/sha3pool"
10         "github.com/bytom/encoding/blockchain"
11         "github.com/bytom/errors"
12         "github.com/bytom/protocol/bc"
13 )
14
15 // CurrentTransactionVersion is the current latest
16 // supported transaction version.
17 const CurrentTransactionVersion = 1
18
19 // Tx holds a transaction along with its hash.
20 type Tx struct {
21         TxData
22         *bc.Tx `json:"-"`
23 }
24
25 func (tx *Tx) UnmarshalText(p []byte) error {
26         if err := tx.TxData.UnmarshalText(p); err != nil {
27                 return err
28         }
29
30         tx.Tx = MapTx(&tx.TxData)
31         return nil
32 }
33
34 // SetInputArguments sets the Arguments field in input n.
35 func (tx *Tx) SetInputArguments(n uint32, args [][]byte) {
36         tx.Inputs[n].SetArguments(args)
37         id := tx.Tx.InputIDs[n]
38         e := tx.Entries[id]
39         switch e := e.(type) {
40         case *bc.Issuance:
41                 e.WitnessArguments = args
42         case *bc.Spend:
43                 e.WitnessArguments = args
44         }
45 }
46
47 func (tx *Tx) IssuanceHash(n int) bc.Hash {
48         return tx.Tx.InputIDs[n]
49 }
50
51 func (tx *Tx) OutputID(outputIndex int) *bc.Hash {
52         return tx.ResultIds[outputIndex]
53 }
54
55 // NewTx returns a new Tx containing data and its hash.
56 // If you have already computed the hash, use struct literal
57 // notation to make a Tx object directly.
58 func NewTx(data TxData) *Tx {
59         return &Tx{
60                 TxData: data,
61                 Tx:     MapTx(&data),
62         }
63 }
64
65 // These flags are part of the wire protocol;
66 // they must not change.
67 const (
68         SerWitness uint8 = 1 << iota
69         SerPrevout
70         SerMetadata
71
72         // Bit mask for accepted serialization flags.
73         // All other flag bits must be 0.
74         SerTxHash   = 0x0 // this is used only for computing transaction hash - prevout and refdata are replaced with their hashes
75         SerValid    = 0x7
76         serRequired = 0x7 // we support only this combination of flags
77 )
78
79 // TxData encodes a transaction in the blockchain.
80 // Most users will want to use Tx instead;
81 // it includes the hash.
82 type TxData struct {
83         Version        uint64
84         SerializedSize uint64
85         Inputs         []*TxInput
86         Outputs        []*TxOutput
87
88         TimeRange uint64
89
90         // The unconsumed suffix of the common fields extensible string
91         CommonFieldsSuffix []byte
92
93         // The unconsumed suffix of the common witness extensible string
94         CommonWitnessSuffix []byte
95
96         ReferenceData []byte
97 }
98
99 // HasIssuance returns true if this transaction has an issuance input.
100 func (tx *TxData) HasIssuance() bool {
101         for _, in := range tx.Inputs {
102                 if in.IsIssuance() {
103                         return true
104                 }
105         }
106         return false
107 }
108
109 func (tx *TxData) UnmarshalText(p []byte) error {
110         b := make([]byte, hex.DecodedLen(len(p)))
111         if _, err := hex.Decode(b, p); err != nil {
112                 return err
113         }
114
115         r := blockchain.NewReader(b)
116         if err := tx.readFrom(r); err != nil {
117                 return err
118         }
119         if trailing := r.Len(); trailing > 0 {
120                 return fmt.Errorf("trailing garbage (%d bytes)", trailing)
121         }
122         return nil
123 }
124
125 func (tx *TxData) readFrom(r *blockchain.Reader) (err error) {
126         tx.SerializedSize = uint64(r.Len())
127         var serflags [1]byte
128         if _, err = io.ReadFull(r, serflags[:]); err != nil {
129                 return errors.Wrap(err, "reading serialization flags")
130         }
131         if serflags[0] != serRequired {
132                 return fmt.Errorf("unsupported serflags %#x", serflags[0])
133         }
134
135         tx.Version, err = blockchain.ReadVarint63(r)
136         if err != nil {
137                 return errors.Wrap(err, "reading transaction version")
138         }
139
140         if tx.TimeRange, err = blockchain.ReadVarint63(r); err != nil {
141                 return err
142         }
143         // Common witness
144         tx.CommonWitnessSuffix, err = blockchain.ReadExtensibleString(r, tx.readCommonWitness)
145         if err != nil {
146                 return errors.Wrap(err, "reading transaction common witness")
147         }
148
149         n, err := blockchain.ReadVarint31(r)
150         if err != nil {
151                 return errors.Wrap(err, "reading number of transaction inputs")
152         }
153         for ; n > 0; n-- {
154                 ti := new(TxInput)
155                 if err = ti.readFrom(r); err != nil {
156                         return errors.Wrapf(err, "reading input %d", len(tx.Inputs))
157                 }
158                 tx.Inputs = append(tx.Inputs, ti)
159         }
160
161         n, err = blockchain.ReadVarint31(r)
162         if err != nil {
163                 return errors.Wrap(err, "reading number of transaction outputs")
164         }
165         for ; n > 0; n-- {
166                 to := new(TxOutput)
167                 if err = to.readFrom(r, tx.Version); err != nil {
168                         return errors.Wrapf(err, "reading output %d", len(tx.Outputs))
169                 }
170                 tx.Outputs = append(tx.Outputs, to)
171         }
172
173         tx.ReferenceData, err = blockchain.ReadVarstr31(r)
174         return errors.Wrap(err, "reading transaction reference data")
175 }
176
177 // does not read the enclosing extensible string
178 func (tx *TxData) readCommonWitness(r *blockchain.Reader) error {
179         return nil
180 }
181
182 func (tx *TxData) MarshalText() ([]byte, error) {
183         var buf bytes.Buffer
184         tx.WriteTo(&buf) // error is impossible
185         b := make([]byte, hex.EncodedLen(buf.Len()))
186         hex.Encode(b, buf.Bytes())
187         return b, nil
188 }
189
190 // WriteTo writes tx to w.
191 func (tx *TxData) WriteTo(w io.Writer) (int64, error) {
192         ew := errors.NewWriter(w)
193         if err := tx.writeTo(ew, serRequired); err != nil {
194                 return ew.Written(), ew.Err()
195         }
196         return ew.Written(), ew.Err()
197 }
198
199 func (tx *TxData) writeTo(w io.Writer, serflags byte) error {
200         if _, err := w.Write([]byte{serflags}); err != nil {
201                 return errors.Wrap(err, "writing serialization flags")
202         }
203
204         if _, err := blockchain.WriteVarint63(w, tx.Version); err != nil {
205                 return errors.Wrap(err, "writing transaction version")
206         }
207
208         if _, err := blockchain.WriteVarint63(w, tx.TimeRange); err != nil {
209                 return errors.Wrap(err, "writing transaction maxtime")
210         }
211         // common witness
212         if _, err := blockchain.WriteExtensibleString(w, tx.CommonWitnessSuffix, tx.writeCommonWitness); err != nil {
213                 return errors.Wrap(err, "writing common witness")
214         }
215
216         if _, err := blockchain.WriteVarint31(w, uint64(len(tx.Inputs))); err != nil {
217                 return errors.Wrap(err, "writing tx input count")
218         }
219         for i, ti := range tx.Inputs {
220                 if err := ti.writeTo(w, serflags); err != nil {
221                         return errors.Wrapf(err, "writing tx input %d", i)
222                 }
223         }
224
225         if _, err := blockchain.WriteVarint31(w, uint64(len(tx.Outputs))); err != nil {
226                 return errors.Wrap(err, "writing tx output count")
227         }
228         for i, to := range tx.Outputs {
229                 if err := to.writeTo(w, serflags); err != nil {
230                         return errors.Wrapf(err, "writing tx output %d", i)
231                 }
232         }
233
234         return writeRefData(w, tx.ReferenceData, serflags)
235 }
236
237 // does not write the enclosing extensible string
238 func (tx *TxData) writeCommonWitness(w io.Writer) error {
239         // Future protocol versions may add fields here.
240         return nil
241 }
242
243 func writeRefData(w io.Writer, data []byte, serflags byte) error {
244         if serflags&SerMetadata != 0 {
245                 _, err := blockchain.WriteVarstr31(w, data)
246                 return err
247         }
248         return writeFastHash(w, data)
249 }
250
251 func writeFastHash(w io.Writer, d []byte) error {
252         if len(d) == 0 {
253                 _, err := blockchain.WriteVarstr31(w, nil)
254                 return err
255         }
256         var h [32]byte
257         sha3pool.Sum256(h[:], d)
258         _, err := blockchain.WriteVarstr31(w, h[:])
259         return err
260 }