-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtransaction.go
219 lines (182 loc) · 5.93 KB
/
transaction.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
package main
import (
"bytes"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/sha256"
"encoding/gob"
"encoding/hex"
"fmt"
"math/big"
)
// A quickly explanation of transmitting mechanism or transaction procedure in a Blockchain system:
//
// Transaction: is a transfer of data values that is broadcast to the network and collected into blocks.
// A transaction typically references previous transaction outputs as new transaction inputs
// and dedicates all data values to new outputs.
//
// We can imagine there is a transaction that has been committed between person A and person B.
// A typically used 20 Bitcoins to buy an item from B.
// A has an ID to distinguish him/herself from the others, neither do B.
// The basic structure for a qualified transaction.
type Transaction struct {
ID []byte // Bytes slice to identify the transaction ID itself.
TxIns []TxInput // TransactionInputs array.
TxOuts []TxOutput // TransactionOutputs array.
}
const (
// The subsidy value that had been given to new user the first time they were joining the network.
SUBSIDY = 1000
)
// Utility functions start from here.
// newCoinBaseTx creates a new coin-base transaction. The coin-base transaction can be
// understood as the first transaction that was added in the first block of the chain.
func newCoinBaseTx(toAddr string) *Transaction {
txIn := TxInput{[]byte{}, -1, nil, []byte{}}
txOut := newTxOut(SUBSIDY, toAddr)
coinbaseTX := Transaction{nil, []TxInput{txIn}, []TxOutput{*txOut}}
coinbaseTX.ID = coinbaseTX.HashTx()
return &coinbaseTX
}
// IsCoinbase checking if the transaction is the coinbase transaction
// or more specifically if this transaction stored in the first block.
func (tx Transaction) IsCoinbase() bool {
return len(tx.TxIns) == 1 &&
len(tx.TxIns[0].TxID) == 0 &&
tx.TxIns[0].TxOutIdx == -1
}
// HashTx hashing the whole transaction into a 32 bytes array.
func (tx *Transaction) HashTx() []byte {
var hash [32]byte
clonedTx := *tx
clonedTx.ID = []byte{}
hash = sha256.Sum256(clonedTx.Serialize())
return hash[:]
}
// Clone is the method that allows creating a new imitation/emulation
// transaction from the original one.
func (tx *Transaction) Clone() Transaction {
var txIns []TxInput
var txOuts []TxOutput
for _, valIn := range tx.TxIns {
txIns = append(txIns, TxInput{
TxID: valIn.TxID,
TxOutIdx: valIn.TxOutIdx,
Signature: nil,
PubKey: valIn.PubKey,
})
}
for _, valOut := range tx.TxOuts {
txOuts = append(txOuts, TxOutput{
Value: valOut.Value,
PubKeyHash: valOut.PubKeyHash,
})
}
clonedTx := Transaction{tx.ID, txIns, txOuts}
return clonedTx
}
// Sign is a utility function that was invented for the main purpose
// is to help the buyer sign his/her own private key into the exchange deal.
func (tx *Transaction) Sign(privKey ecdsa.PrivateKey) {
if tx.IsCoinbase() {
return
}
clonedTx := tx.Clone()
signData := fmt.Sprintf("%x", clonedTx)
// NOTE: Not yet fully understood!
// IDEA: a full signature was generated from a `rand` number, a buyer's `privKey`,
// and the corresponding data.
r, s, err := ecdsa.Sign(rand.Reader, &privKey, []byte(signData))
if err != nil {
Error.Panic(err)
}
// NOTE: the main point of this process represents cloning the data of a transaction from a block.
// Executing all of the necessary calculations on the cloned transaction,
// before returning the signature to the original one.
signature := append(r.Bytes(), s.Bytes()...)
for idx := range clonedTx.TxIns {
clonedTx.TxIns[idx].Signature = nil
tx.TxIns[idx].Signature = signature
}
}
// VerifySignature is a helper function that used to verify the
// reliability of a transaction's signature.
func (tx *Transaction) VerifySignature() bool {
clonedTx := tx.Clone()
curve := elliptic.P256()
for _, valIn := range clonedTx.TxIns {
pubKey := valIn.PubKey
// HACK: maybe we should use `big.Int{}` constructor instead?
// Expected values:
r := new(big.Int)
s := new(big.Int)
signLen := len(valIn.Signature)
r.SetBytes(valIn.Signature[:(signLen / 2)])
s.SetBytes(valIn.Signature[(signLen / 2):])
// Real signature values:
x := new(big.Int)
y := new(big.Int)
keyLen := len(pubKey) - 1
x.SetBytes(pubKey[1:(keyLen/2 + 1)])
y.SetBytes(pubKey[(keyLen/2 + 1):])
// Transaction data that needs to be verified.
verifyData := fmt.Sprintf("%x", clonedTx)
rawPubKey := ecdsa.PublicKey{
Curve: curve,
X: x,
Y: y,
}
if !ecdsa.Verify(&rawPubKey, []byte(verifyData), r, s) {
return false
}
}
return true
}
// VerifyValues have similarities in use with the signatures verification method.
// But instead of verifying the signature itself, it checks the balancing between
// the total amount of the stream inputs (TxIns) and outputs (TxOuts), from the start
// until the current transaction.
func (tx *Transaction) VerifyValues(prevTxs map[string]Transaction) bool {
totalIns, totalOuts := 0, 0
for _, valIn := range tx.TxIns {
prevTx := prevTxs[hex.EncodeToString(valIn.TxID)]
totalIns = prevTx.TxOuts[valIn.TxOutIdx].Value
}
for _, valOut := range tx.TxOuts {
totalOuts += valOut.Value
}
// Returns true if the total amount of inputs value is equal to
// the total amount of outputs value.
return totalIns == totalOuts
}
func (tx Transaction) Serialize() []byte {
var encoded bytes.Buffer
encode := gob.NewEncoder(&encoded)
err := encode.Encode(tx)
if err != nil {
Error.Panic(err)
}
return encoded.Bytes()
}
func DeserializeTx(data []byte) *Transaction {
var tx Transaction
decoder := gob.NewDecoder(bytes.NewReader(data))
err := decoder.Decode(&tx)
if err != nil {
Error.Panic(err)
}
return &tx
}
func (tx Transaction) Stringify() string {
strTx := fmt.Sprintf("\n\tID: %x", tx.ID)
strTx += "\n\tValIn: \n"
for idx, txIn := range tx.TxIns {
strTx += fmt.Sprintf("\t[%d]%v\n", idx, txIn)
}
strTx += "\n\tValOut: \n"
for idx, txOut := range tx.TxOuts {
strTx += fmt.Sprintf("\t[%d]%v\n", idx, txOut)
}
return strTx
}