forked from febe19/bazo-miner
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathtx_verification.go
294 lines (234 loc) · 7.95 KB
/
tx_verification.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
package miner
import (
"crypto/ecdsa"
"crypto/elliptic"
"fmt"
"github.com/julwil/bazo-miner/crypto"
"github.com/julwil/bazo-miner/protocol"
"github.com/julwil/bazo-miner/storage"
"math/big"
"reflect"
)
//We can't use polymorphism, e.g. we can't use tx.verify() because the Transaction interface doesn't declare
//the verify method. This is because verification depends on the State (e.g., dynamic properties), which
//should only be of concern to the miner, not to the protocol package. However, this has the disadvantage
//that we have to do case distinction here.
func verify(tx protocol.Transaction) bool {
switch tx.(type) {
case *protocol.FundsTx:
return verifyFundsTx(tx.(*protocol.FundsTx))
case *protocol.AccTx:
return verifyAccTx(tx.(*protocol.AccTx))
case *protocol.ConfigTx:
return verifyConfigTx(tx.(*protocol.ConfigTx))
case *protocol.StakeTx:
return verifyStakeTx(tx.(*protocol.StakeTx))
case *protocol.AggTx:
return verifyAggTx(tx.(*protocol.AggTx))
case *protocol.UpdateTx:
return verifyUpdateTx(tx.(*protocol.UpdateTx))
default: // In case tx is nil or we encounter an unhandled transaction type
return false
}
}
func verifyFundsTx(tx *protocol.FundsTx) bool {
//fundsTx only makes sense if amount > 0
if tx.Amount == 0 || tx.Amount > MAX_MONEY {
logger.Printf("Invalid transaction amount: %v\n", tx.Amount)
return false
}
//Check if accounts are present in the actual state
accFrom := storage.State[tx.From]
accTo := storage.State[tx.To]
if accFrom == nil || accTo == nil {
logger.Printf("Account non existent. From: %v\nTo: %v\n", accFrom, accTo)
return false
}
// Check if From & To are different accounts
if reflect.DeepEqual(accFrom, accTo) {
logger.Printf("From account equals To account. From: %v\nTo: %v\n", accFrom, accTo)
}
txHash := tx.Hash()
var validSig1, validSig2 bool
// Validate Sig1
validSig1 = IsSigned(txHash, tx.Sig1, accFrom.Address)
// If no Sig2 is specified, we don't validate it.
if tx.Sig2 == [64]byte{} {
validSig2 = true
}
// Validate Sig2
if tx.Sig2 != [64]byte{} {
validSig2 = IsSigned(txHash, tx.Sig2, crypto.GetAddressFromPubKey(multisigPubKey))
}
return validSig1 && validSig2
}
func verifyAccTx(tx *protocol.AccTx) bool {
for _, rootAcc := range storage.RootKeys {
signature := tx.Sig
address := rootAcc.Address
txHash := tx.Hash()
if IsSigned(txHash, signature, address) {
return true
}
}
logger.Printf("No valid root account.")
return false
}
func verifyConfigTx(tx *protocol.ConfigTx) bool {
//account creation can only be done with a valid priv/pub key which is hard-coded
for _, rootAcc := range storage.RootKeys {
signature := tx.Sig
address := rootAcc.Address
txHash := tx.Hash()
if IsSigned(txHash, signature, address) {
return true
}
}
logger.Printf("No valid root account.")
return false
}
func verifyStakeTx(tx *protocol.StakeTx) bool {
//Check if account is present in the actual state
accFrom := storage.State[tx.Account]
//Account non existent
if accFrom == nil {
logger.Println("Account does not exist.")
return false
}
accFromHash := protocol.SerializeHashContent(accFrom.Address)
tx.Account = accFromHash
txHash := tx.Hash()
return IsSigned(txHash, tx.Sig, accFrom.Address)
}
//TODO Update this function
func verifyAggTx(tx *protocol.AggTx) bool {
//Check if accounts are existent
//accSender, err := storage.GetAccount(tx.From)
//if tx.From //!= protocol.SerializeHashContent(accSender.Address) || tx.To == nil || err != nil {
// logger.Printf("Account non existent. From: %v\nTo: %v\n%v", tx.From, tx.To, err)
// return false
//}
return true
}
func verifyUpdateTx(tx *protocol.UpdateTx) bool {
// First we make sure that the account of the tx issuer exists.
issuerAccount := storage.State[tx.Issuer]
if issuerAccount == nil {
logger.Printf("Account of tx issuer does not exist: %v", tx.Issuer)
return false
}
// Next we check if the tx to update actually exists.
var txToUpdate protocol.Transaction
switch true {
case storage.ReadOpenTx(tx.TxToUpdateHash) != nil:
txToUpdate = storage.ReadOpenTx(tx.TxToUpdateHash)
case storage.ReadClosedTx(tx.TxToUpdateHash) != nil:
txToUpdate = storage.ReadClosedTx(tx.TxToUpdateHash)
default: // If we don't find the tx to update in the storage, we also can't update it.
logger.Printf("Can't find TxToDelete: %x", tx.TxToUpdateHash)
return false
}
txHash := tx.Hash()
isTxSigned := IsSigned(txHash, tx.Sig, issuerAccount.Address)
if !isTxSigned {
logger.Printf("Tx: %x not signed correctly", txHash)
return false
}
// Lastly we check if the issuer of the update-tx also signed the tx to update.
// This makes sure that you only update your own txs.
// Get the hash
txToUpdateHash := txToUpdate.Hash()
var txToUpdateSig [64]byte
// Now we validate the txToUpdate and retrieve its signature.
switch txToUpdate.(type) {
case *protocol.FundsTx:
txToUpdateSig = txToUpdate.(*protocol.FundsTx).Sig1
case *protocol.AccTx:
txToUpdateSig = txToUpdate.(*protocol.AccTx).Sig
case *protocol.UpdateTx:
txToUpdateSig = txToUpdate.(*protocol.UpdateTx).Sig
case *protocol.ConfigTx:
logger.Printf("\nCan't update config tx")
return false
case *protocol.StakeTx:
logger.Printf("\nCan't update stake tx")
return false
case *protocol.AggTx:
logger.Printf("\nCan't update aggregate tx")
return false
default: // In case we can't cast the tx to a known type, abort
return false
}
if !IsSigned(txToUpdateHash, txToUpdateSig, issuerAccount.Address) {
logger.Printf("\nISSUER NOT ALLOWED TO UPDATE TX."+
"\nTxToUpdate was not signed by Issuer. (You can only update your own tx)"+
"\nIssuer: %x"+
"\nTxToUpdate: %x"+
"\nAbort update.", tx.Issuer, txToUpdateHash)
return false
}
return true
}
//Returns true if id is in the list of possible ids and rational value for payload parameter.
//Some values just don't make any sense and have to be restricted accordingly
func parameterBoundsChecking(id uint8, payload uint64) bool {
switch id {
case protocol.BLOCK_SIZE_ID:
if payload >= protocol.MIN_BLOCK_SIZE && payload <= protocol.MAX_BLOCK_SIZE {
return true
}
case protocol.DIFF_INTERVAL_ID:
if payload >= protocol.MIN_DIFF_INTERVAL && payload <= protocol.MAX_DIFF_INTERVAL {
return true
}
case protocol.FEE_MINIMUM_ID:
if payload >= protocol.MIN_FEE_MINIMUM && payload <= protocol.MAX_FEE_MINIMUM {
return true
}
case protocol.BLOCK_INTERVAL_ID:
if payload >= protocol.MIN_BLOCK_INTERVAL && payload <= protocol.MAX_BLOCK_INTERVAL {
return true
}
case protocol.BLOCK_REWARD_ID:
if payload >= protocol.MIN_BLOCK_REWARD && payload <= protocol.MAX_BLOCK_REWARD {
return true
}
case protocol.STAKING_MINIMUM_ID:
if payload >= protocol.MIN_STAKING_MINIMUM && payload <= protocol.MAX_STAKING_MINIMUM {
return true
}
case protocol.WAITING_MINIMUM_ID:
if payload >= protocol.MIN_WAITING_TIME && payload <= protocol.MAX_WAITING_TIME {
return true
}
case protocol.ACCEPTANCE_TIME_DIFF_ID:
if payload >= protocol.MIN_ACCEPTANCE_TIME_DIFF && payload <= protocol.MAX_ACCEPTANCE_TIME_DIFF {
return true
}
case protocol.SLASHING_WINDOW_SIZE_ID:
if payload >= protocol.MIN_SLASHING_WINDOW_SIZE && payload <= protocol.MAX_SLASHING_WINDOW_SIZE {
return true
}
case protocol.SLASHING_REWARD_ID:
if payload >= protocol.MIN_SLASHING_REWARD && payload <= protocol.MAX_SLASHING_REWARD {
return true
}
}
return false
}
// Checks if the hash is signed by the provided signature and address
func IsSigned(hash [32]byte, signature [64]byte, address [64]byte) bool {
pub1, pub2 := new(big.Int), new(big.Int)
r, s := new(big.Int), new(big.Int)
r.SetBytes(signature[:32])
s.SetBytes(signature[32:])
pub1.SetBytes(address[:32])
pub2.SetBytes(address[32:])
publicKey := ecdsa.PublicKey{elliptic.P256(), pub1, pub2}
//fmt.Printf("\nAddress: %x\nSignature: %x\nHash:%x\n", address, signature, hash)
isSigned := ecdsa.Verify(&publicKey, hash[:], r, s)
if !isSigned {
fmt.Printf("Could not verfiy signature.")
}
return isSigned
}