forked from btcsuite/btcd
-
Notifications
You must be signed in to change notification settings - Fork 0
/
msgtx.go
294 lines (260 loc) · 9.11 KB
/
msgtx.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
// Copyright (c) 2013-2015 The qchain developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"bytes"
"encoding/binary"
"errors"
"io"
)
const (
// TxVersion is the current latest supported transaction version.
TxVersion = 1
// minTxPayload is the minimum payload size for a transaction. Note
// that any realistically usable transaction must have at least one
// input or output, but that is a rule enforced at a higher layer, so
// it is intentionally not included here.
// Version 4 bytes + Varint number of transaction inputs 1 byte + Varint
// number of transaction outputs 1 byte + LockTime 4 bytes + min input
// payload + min output payload.
minTxPayload = 10
)
// List of transaction types and their numerical equivalent.
const (
TxTypeUnknown = -1
TxTypeData = 1
TxTypeFile = 2
TxTypeSignFile = 3
)
// All current and future transactions should adherere to this interface.
// This is to allow for simple convertions from and to specialized go structs
// and the more generic MsgTX struct.
//
// Serialize should return a byte-slice representation of the
// transaction. The internal byte format should be the same as used by the
// Deserialize method below.
//
// Deserialize will take byte-slice representation of the transaction and populate
// the underlying data struct based on the format used by Serialize above.
// The underlying struct will be overwritten with the new data. However the
// underlying struct should only be changed if error returns nil.
type TxInterface interface {
Serialize(w io.Writer) error
Deserialize(r io.Reader) error
SerializeSize() int
GetVersion() int32
GetType() int32
}
// MsgTx implements the Message interface and represents a generic tx message.
// It is used to deliver transaction information in response to a getdata
// message (MsgGetData) for a given transaction. Type stores value assosiated
// with a certain transaction type.
type MsgTx struct {
Type int32
LockTime uint32
Data []byte
}
const (
// The number of bytes the MsgTx struct adds to the inner trasaction.
// TODO: Make the MsgTxHeader a part of TxHeader when serialized.
MsgTxHeader = 4 + 4
)
// SetData sets data to the transaction message.
func (msg *MsgTx) SetData(data []byte) {
msg.Data = data
}
// AppendData appends data to the transaction message.
func (msg *MsgTx) AppendData(data []byte) {
msg.Data = append(msg.Data, data...)
}
// TxSha generates the ShaHash name for the transaction.
func (msg *MsgTx) TxSha() ShaHash {
// Serialize the transaction and calculate double sha256 on the result.
// Ignore the error returns since the only way the encode could fail
// is being out of memory or due to nil pointers, both of which would
// cause a run-time panic.
buf := bytes.NewBuffer(make([]byte, 0, msg.SerializeSize()))
_ = msg.Serialize(buf)
return DoubleSha256SH(buf.Bytes())
}
// Copy creates a deep copy of a transaction so that the original does not get
// modified when the copy is manipulated.
func (msg *MsgTx) Copy() *MsgTx {
newTx := MsgTx{
Type: msg.Type,
Data: make([]byte, len(msg.Data)),
LockTime: msg.LockTime,
}
copy(newTx.Data, msg.Data)
return &newTx
}
// MsgDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation.
// See Deserialize for decoding transactions stored to disk, such as in a
// database, as opposed to decoding transactions from the wire.
func (msg *MsgTx) MsgDecode(r io.Reader, pver uint32) error {
var buf [4]byte
_, err := io.ReadFull(r, buf[:])
if err != nil {
return err
}
msg.Type = int32(binary.LittleEndian.Uint32(buf[:]))
_, err = io.ReadFull(r, buf[:])
if err != nil {
return err
}
msg.LockTime = binary.LittleEndian.Uint32(buf[:])
databuf := make([]byte, 1024)
data := make([]byte, 0)
for { // Stuck in infinite loop
n, err := r.Read(databuf)
data = append(data, databuf[:n]...)
if err == io.EOF {
break
}
if err != nil {
return err
}
}
msg.Data = data
return nil
}
// This function is similar to MsgDecode. It differs in that it takes a size paramter
// and as such does not read the reader until some error happens including EOF.
// It will try to read up to size bytes before returning and those will make up the
// transaction.
func (msg *MsgTx) MsgDecodeFixed(r io.Reader, pver uint32, size uint32) error {
var buf [4]byte
_, err := io.ReadFull(r, buf[:])
if err != nil {
return err
}
msg.Type = int32(binary.LittleEndian.Uint32(buf[:]))
_, err = io.ReadFull(r, buf[:])
if err != nil {
return err
}
msg.LockTime = binary.LittleEndian.Uint32(buf[:])
databufSize := 1024
databuf := make([]byte, databufSize)
dataSize := size - MsgTxHeader
data := make([]byte, 0, dataSize)
remaninigBytes := size - MsgTxHeader
for uint32(len(data)) < dataSize {
var n int
var err error
if remaninigBytes < uint32(databufSize) {
n, err = r.Read(databuf[:remaninigBytes])
} else {
n, err = r.Read(databuf)
}
remaninigBytes -= uint32(n)
data = append(data, databuf[:n]...)
if err == io.EOF {
if uint32(len(data)) < size {
return errors.New("unexpected end of file while decoding transaction " +
"in MsgDecodeFixed")
} else {
return nil
}
}
if err != nil {
return err
}
}
msg.Data = data
return nil
}
// Deserialize decodes a transaction from r into the receiver using a format
// that is suitable for long-term storage such as a database while respecting
// the Type field in the transaction. This function differs from MsgDecode
// in that MsgDecode decodes from the bitcoin wire protocol as it was sent
// across the network. The wire encoding can technically differ depending on
// the protocol Type and doesn't even really need to match the format of a
// stored transaction at all. As of the time this comment was written, the
// encoded transaction is the same in both instances, but there is a distinct
// difference and separating the two allows the API to be flexible enough to
// deal with changes.
func (msg *MsgTx) Deserialize(r io.Reader) error {
// At the current time, there is no difference between the wire encoding
// at protocol Type 0 and the stable long-term storage format. As
// a result, make use of MsgDecode.
return msg.MsgDecode(r, 0)
}
// MsgEncode encodes the receiver to w using the bitcoin protocol encoding.
// This is part of the Message interface implementation.
// See Serialize for encoding transactions to be stored to disk, such as in a
// database, as opposed to encoding transactions for the wire.
func (msg *MsgTx) MsgEncode(w io.Writer, pver uint32) error {
var buf [4]byte
binary.LittleEndian.PutUint32(buf[:], uint32(msg.Type))
_, err := w.Write(buf[:])
if err != nil {
return err
}
binary.LittleEndian.PutUint32(buf[:], msg.LockTime)
_, err = w.Write(buf[:])
if err != nil {
return err
}
_, err = w.Write(msg.Data)
if err != nil {
return err
}
return nil
}
// Serialize encodes the transaction to w using a format that suitable for
// long-term storage such as a database while respecting the Type field in
// the transaction. This function differs from MsgEncode in that MsgEncode
// encodes the transaction to the bitcoin wire protocol in order to be sent
// across the network. The wire encoding can technically differ depending on
// the protocol Type and doesn't even really need to match the format of a
// stored transaction at all. As of the time this comment was written, the
// encoded transaction is the same in both instances, but there is a distinct
// difference and separating the two allows the API to be flexible enough to
// deal with changes.
func (msg *MsgTx) Serialize(w io.Writer) error {
// At the current time, there is no difference between the wire encoding
// at protocol Type 0 and the stable long-term storage format. As
// a result, make use of MsgEncode.
return msg.MsgEncode(w, 0)
}
// SerializeSize returns the number of bytes it would take to serialize the
// the transaction.
func (msg *MsgTx) SerializeSize() int {
// Type 4 bytes + LockTime 4 bytes + data
return 4 + 4 + len(msg.Data)
}
// Command returns the protocol command string for the message. This is part
// of the Message interface implementation.
func (msg *MsgTx) Command() string {
return CmdTx
}
// MaxPayloadLength returns the maximum length the payload can be for the
// receiver. This is part of the Message interface implementation.
func (msg *MsgTx) MaxPayloadLength(pver uint32) uint32 {
return MaxBlockPayload
}
// NewMsgTx returns a new generic tx message that conforms to the Message
// interface. The lock time is set to zero to indicate the transaction is
// valid immediately as opposed to some time in future.
func NewMsgTx() *MsgTx {
return &MsgTx{
Type: -1,
Data: make([]byte, 0),
LockTime: uint32(0),
}
}
// Surrounds the specific transaction with generic message transaction to
// be sent over the wire and other non-transaction specific operations.
func WrapMsgTx(tx TxInterface) *MsgTx {
buf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize()))
_ = tx.Serialize(buf)
data := buf.Bytes()
return &MsgTx{
Type: tx.GetType(),
Data: data,
LockTime: uint32(0),
}
}