OSDN Git Service

module claim
[bytom/vapor.git] / claim / bytom / protocolbc / types / transaction.go
1 package types
2
3 import (
4         "bytes"
5         "encoding/hex"
6         "fmt"
7         "io"
8
9         bytom "github.com/vapor/claim/bytom/protocolbc"
10         "github.com/vapor/encoding/blockchain"
11         "github.com/vapor/errors"
12         "github.com/vapor/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         *bytom.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         }
58 }
59
60 // TxData encodes a transaction in the blockchain.
61 type TxData struct {
62         Version        uint64
63         SerializedSize uint64
64         TimeRange      uint64
65         Inputs         []*TxInput
66         Outputs        []*TxOutput
67 }
68
69 // MarshalText fulfills the json.Marshaler interface.
70 func (tx *TxData) MarshalText() ([]byte, error) {
71         var buf bytes.Buffer
72         if _, err := tx.WriteTo(&buf); err != nil {
73                 return nil, err
74         }
75
76         b := make([]byte, hex.EncodedLen(buf.Len()))
77         hex.Encode(b, buf.Bytes())
78         return b, nil
79 }
80
81 // UnmarshalText fulfills the encoding.TextUnmarshaler interface.
82 func (tx *TxData) UnmarshalText(p []byte) error {
83         b := make([]byte, hex.DecodedLen(len(p)))
84         if _, err := hex.Decode(b, p); err != nil {
85                 return err
86         }
87
88         r := blockchain.NewReader(b)
89         if err := tx.readFrom(r); err != nil {
90                 return err
91         }
92
93         if trailing := r.Len(); trailing > 0 {
94                 return fmt.Errorf("trailing garbage (%d bytes)", trailing)
95         }
96         return nil
97 }
98
99 func (tx *TxData) readFrom(r *blockchain.Reader) (err error) {
100         startSerializedSize := r.Len()
101         var serflags [1]byte
102         if _, err = io.ReadFull(r, serflags[:]); err != nil {
103                 return errors.Wrap(err, "reading serialization flags")
104         }
105         if serflags[0] != serRequired {
106                 return fmt.Errorf("unsupported serflags %#x", serflags[0])
107         }
108
109         if tx.Version, err = blockchain.ReadVarint63(r); err != nil {
110                 return errors.Wrap(err, "reading transaction version")
111         }
112         if tx.TimeRange, err = blockchain.ReadVarint63(r); err != nil {
113                 return err
114         }
115
116         n, err := blockchain.ReadVarint31(r)
117         if err != nil {
118                 return errors.Wrap(err, "reading number of transaction inputs")
119         }
120
121         for ; n > 0; n-- {
122                 ti := new(TxInput)
123                 if err = ti.readFrom(r); err != nil {
124                         return errors.Wrapf(err, "reading input %d", len(tx.Inputs))
125                 }
126                 tx.Inputs = append(tx.Inputs, ti)
127         }
128
129         n, err = blockchain.ReadVarint31(r)
130         if err != nil {
131                 return errors.Wrap(err, "reading number of transaction outputs")
132         }
133
134         for ; n > 0; n-- {
135                 to := new(TxOutput)
136                 if err = to.readFrom(r); err != nil {
137                         return errors.Wrapf(err, "reading output %d", len(tx.Outputs))
138                 }
139                 tx.Outputs = append(tx.Outputs, to)
140         }
141         tx.SerializedSize = uint64(startSerializedSize - r.Len())
142         return nil
143 }
144
145 // WriteTo writes tx to w.
146 func (tx *TxData) WriteTo(w io.Writer) (int64, error) {
147         ew := errors.NewWriter(w)
148         if err := tx.writeTo(ew, serRequired); err != nil {
149                 return 0, err
150         }
151         return ew.Written(), ew.Err()
152 }
153
154 func (tx *TxData) writeTo(w io.Writer, serflags byte) error {
155         if _, err := w.Write([]byte{serflags}); err != nil {
156                 return errors.Wrap(err, "writing serialization flags")
157         }
158         if _, err := blockchain.WriteVarint63(w, tx.Version); err != nil {
159                 return errors.Wrap(err, "writing transaction version")
160         }
161         if _, err := blockchain.WriteVarint63(w, tx.TimeRange); err != nil {
162                 return errors.Wrap(err, "writing transaction maxtime")
163         }
164
165         if _, err := blockchain.WriteVarint31(w, uint64(len(tx.Inputs))); err != nil {
166                 return errors.Wrap(err, "writing tx input count")
167         }
168
169         for i, ti := range tx.Inputs {
170                 if err := ti.writeTo(w); err != nil {
171                         return errors.Wrapf(err, "writing tx input %d", i)
172                 }
173         }
174
175         if _, err := blockchain.WriteVarint31(w, uint64(len(tx.Outputs))); err != nil {
176                 return errors.Wrap(err, "writing tx output count")
177         }
178
179         for i, to := range tx.Outputs {
180                 if err := to.writeTo(w); err != nil {
181                         return errors.Wrapf(err, "writing tx output %d", i)
182                 }
183         }
184         return nil
185 }