-
Notifications
You must be signed in to change notification settings - Fork 1
/
blockchain.go
779 lines (660 loc) · 28 KB
/
blockchain.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
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
package core
import (
"bytes"
"crypto/ed25519"
"database/sql"
"encoding/base64"
"encoding/gob"
"encoding/json"
"errors"
"fmt"
"log"
"strconv"
"sync"
"time"
thrylos "github.com/thrylos-labs/thrylos"
"github.com/thrylos-labs/thrylos/shared"
"github.com/thrylos-labs/thrylos/database"
// other necessary imports
)
// Blockchain represents the entire blockchain structure, encapsulating all blocks, stakeholders,
// and transactions within the network. It serves as the central ledger of the system, tracking
// the state of the blockchain, including ownership of assets through UTXOs (Unspent Transaction Outputs),
// and the resolution of forks, ensuring the integrity and continuity of the chain.
type Blockchain struct {
// Blocks holds the sequence of blocks that constitute the blockchain. Each block contains
// a set of transactions and is linked to the previous block, forming the chain.
Blocks []*Block
// Genesis points to the first block in the blockchain, known as the Genesis block. This block
// is the foundation of the blockchain, with no preceding block.
Genesis *Block
// Adding transactions to the pending transactions pool
PendingTransactions []*thrylos.Transaction
// Stakeholders maps validator addresses to their respective stakes in the network. This is
// used in proof-of-stake (PoS) consensus mechanisms to determine validators' rights to create
// new blocks based on the size of their stake
Stakeholders map[string]int
// UTXOs tracks unspent transaction outputs, which represent the current state of ownership
// of the blockchain's assets. It is a key component in preventing double spending.
UTXOs map[string][]*thrylos.UTXO
// Forks captures any divergences in the blockchain, where two or more blocks are found to
// have the same predecessor. Forks are resolved through mechanisms that ensure consensus
// on a single chain.
Forks []*Fork
// Mu provides concurrency control to ensure that operations on the blockchain are thread-safe,
// preventing race conditions and ensuring data integrity.
Mu sync.RWMutex
// lastTimestamp records the timestamp of the last added block. This is used to ensure that
// blocks are added in chronological order, preserving the integrity of the blockchain's timeline.
lastTimestamp int64
// SmartContracts lists all smart contracts deployed on the blockchain. Smart contracts are
// self-executing contracts with the terms of the agreement directly written into code
// SmartContracts []SmartContract // New field for storing smart contracts
// Database provides an abstraction over the underlying database technology used to persist
// blockchain data, facilitating operations like adding blocks and retrieving blockchain state
Database shared.BlockchainDBInterface // Updated the type to interface
PublicKeyMap map[string]ed25519.PublicKey // To store public keys
}
// NewTransaction creates a new transaction
type Stakeholder struct {
Address string
Stake int
}
// Fork structure representing a fork in the blockchain
type Fork struct {
Index int
Blocks []*Block
}
// NewBlockchain initializes and returns a new instance of a Blockchain. It sets up the necessary
// infrastructure, including the genesis block and the database connection for persisting the blockchain state.
func NewBlockchain(dataDir string, aesKey []byte) (*Blockchain, error) {
// Initialize the database
db, err := database.InitializeDatabase(dataDir)
if err != nil {
return nil, fmt.Errorf("failed to initialize the blockchain database: %v", err)
}
bdb := database.NewBlockchainDB(db, aesKey)
// Create the genesis block
genesis := NewGenesisBlock()
// Initialize the map for public keys
publicKeyMap := make(map[string]ed25519.PublicKey)
// Simulate several stakeholders
stakeholders := []struct {
Address string
Balance int64
}{
{"6ab5fbf652da1467169cd68dd5dc9e82331d2cf17eb64e9a5b8b644dcb0e3d19", 10000},
{"8bcd8b1c3e3487743ed7caf19b688f83d6f86cf7d246bc71d5f7d322a64189f7", 20000},
}
// Initialize Stakeholders map
stakeholdersMap := make(map[string]int)
// Precompute genesis transactions and UTXOs
genesisTransactions := make([]*thrylos.Transaction, 0, len(stakeholders))
utxoMap := make(map[string][]*thrylos.UTXO, len(stakeholders))
for _, stakeholder := range stakeholders {
stakeholdersMap[stakeholder.Address] = int(stakeholder.Balance)
genesisTx := &thrylos.Transaction{
Id: "genesis_tx_" + stakeholder.Address,
Timestamp: time.Now().Unix(),
Outputs: []*thrylos.UTXO{{
OwnerAddress: stakeholder.Address,
Amount: stakeholder.Balance,
}},
Signature: []byte("genesis_signature"),
}
genesisTransactions = append(genesisTransactions, genesisTx)
utxoKey := fmt.Sprintf("%s:%d", genesisTx.Id, 0)
utxoMap[utxoKey] = []*thrylos.UTXO{genesisTx.Outputs[0]}
}
genesis.Transactions = genesisTransactions
blockchain := &Blockchain{
Blocks: []*Block{genesis},
Genesis: genesis,
Stakeholders: stakeholdersMap,
Database: bdb,
PublicKeyMap: publicKeyMap, // Initialize the public key map
UTXOs: utxoMap,
Forks: make([]*Fork, 0),
}
// Serialize the genesis block and insert into the database
var buf bytes.Buffer
encoder := gob.NewEncoder(&buf)
if err := encoder.Encode(genesis); err != nil {
return nil, fmt.Errorf("failed to serialize genesis block: %v", err)
}
serializedGenesis := buf.Bytes()
if err := bdb.InsertBlock(serializedGenesis, 0); err != nil {
return nil, fmt.Errorf("failed to add genesis block to the database: %v", err)
}
return blockchain, nil
}
// When reading or processing transactions that have been deserialized from Protobuf, you'll use ConvertProtoUTXOToShared to convert the Protobuf-generated UTXOs back into the format your application uses internally.
// ConvertProtoUTXOToShared converts a Protobuf-generated UTXO to your shared UTXO type.
func ConvertProtoUTXOToShared(protoUTXO *thrylos.UTXO) shared.UTXO {
return shared.UTXO{
ID: protoUTXO.GetTransactionId(), // Assuming you have corresponding fields
TransactionID: protoUTXO.GetTransactionId(),
Index: int(protoUTXO.GetIndex()), // Convert from int32 to int if necessary
OwnerAddress: protoUTXO.GetOwnerAddress(),
Amount: int(protoUTXO.GetAmount()), // Convert from int64 to int if necessary
}
}
func (bc *Blockchain) Status() string {
// Example status: return the number of blocks in the blockchain
return fmt.Sprintf("Current blockchain length: %d blocks", len(bc.Blocks))
}
// In this updated method, you're retrieving a slice of *thrylos.UTXO from the UTXOs map using the provided address. Then, you iterate over this slice, converting each *thrylos.UTXO to shared.UTXO using the ConvertProtoUTXOToShared function, and build a slice of shared.UTXO to return.
// GetUTXOsForAddress returns all UTXOs for a given address.
func (bc *Blockchain) GetUTXOsForAddress(address string) []shared.UTXO {
protoUTXOs := bc.UTXOs[address] // This retrieves a slice of *thrylos.UTXO
sharedUTXOs := make([]shared.UTXO, len(protoUTXOs))
for i, protoUTXO := range protoUTXOs {
sharedUTXOs[i] = ConvertProtoUTXOToShared(protoUTXO)
}
return sharedUTXOs
}
func (bc *Blockchain) GetBalance(address string) (int, error) {
var balance int
// Track spent outputs to avoid counting coins that have been spent.
spentOutputs := make(map[string]bool)
for _, block := range bc.Blocks {
for _, tx := range block.Transactions {
// Check outputs first - add to the balance if this address received coins
for i, output := range tx.Outputs {
outputKey := fmt.Sprintf("%s:%d", tx.Id, i)
if output.OwnerAddress == address {
if !spentOutputs[outputKey] {
balance += int(output.Amount)
}
}
}
// Check inputs - subtract from balance if this address spent coins and the outputs had been added to the balance
for _, input := range tx.Inputs {
if input.OwnerAddress == address {
spentKey := fmt.Sprintf("%s:%d", input.TransactionId, input.Index)
if !spentOutputs[spentKey] { // Check if this input was already marked as spent
spentOutputs[spentKey] = true
balance -= int(input.Amount)
}
}
}
}
}
return balance, nil
}
func (bc *Blockchain) RegisterPublicKey(pubKey string) error {
// Convert the public key string to bytes if necessary, assuming pubKey is base64 encoded
pubKeyBytes, err := base64.StdEncoding.DecodeString(pubKey)
if err != nil {
return fmt.Errorf("error decoding public key: %v", err)
}
// Assuming "publicKeyAddress" should be dynamically determined or correctly provided
return bc.Database.InsertOrUpdateEd25519PublicKey("publicKeyAddress", pubKeyBytes)
}
// In blockchain.go, within your Blockchain struct definition
func (bc *Blockchain) RetrievePublicKey(ownerAddress string) (ed25519.PublicKey, error) {
log.Printf("Attempting to retrieve public key from database for address: %s", ownerAddress)
publicKeyBytes, err := bc.Database.RetrieveEd25519PublicKey(ownerAddress)
if err != nil {
log.Printf("Database error retrieving public key: %v", err)
return nil, err
}
if len(publicKeyBytes) != ed25519.PublicKeySize {
errorMsg := fmt.Sprintf("retrieved public key size is incorrect for address: %s", ownerAddress)
log.Printf(errorMsg)
return nil, fmt.Errorf(errorMsg)
}
publicKey := ed25519.PublicKey(publicKeyBytes)
log.Printf("Successfully retrieved and validated public key for address: %s", ownerAddress)
return publicKey, nil
}
// CreateBlock generates a new block with the given transactions, validator, previous hash, and timestamp.
// This method encapsulates the logic for building a block to be added to the blockchain.
func (bc *Blockchain) CreateBlock(transactions []*thrylos.Transaction, validator string, prevHash string, timestamp int64) *Block {
// Log the incoming transactions
log.Printf("Creating block with %d transactions", len(transactions))
for i, tx := range transactions {
log.Printf("Transaction %d: ID=%s, Outputs=%+v", i, tx.Id, tx.Outputs)
}
// Create a new block with Protobuf transactions
newBlock := &Block{
Index: int32(len(bc.Blocks)), // Convert len to int32
Transactions: transactions, // Directly use the Protobuf transactions
Timestamp: timestamp,
Validator: validator,
PrevHash: prevHash,
}
// Log the newly created block details before returning
log.Printf("New block created: Index=%d, Hash=%s, Transactions=%d, Timestamp=%d, Validator=%s, PrevHash=%s",
newBlock.Index, newBlock.Hash, len(newBlock.Transactions), newBlock.Timestamp, newBlock.Validator, newBlock.PrevHash)
// Assuming ComputeHash() is adapted to work with the new Transactions type
newBlock.Hash = newBlock.ComputeHash()
return newBlock
}
func (bc *Blockchain) SlashMaliciousValidator(validatorAddress string, slashAmount int) {
if _, ok := bc.Stakeholders[validatorAddress]; ok {
// Deduct the slashAmount from the stake
bc.Stakeholders[validatorAddress] -= slashAmount
if bc.Stakeholders[validatorAddress] <= 0 {
// Remove validator if their stake goes to zero or negative
delete(bc.Stakeholders, validatorAddress)
}
}
}
func (bc *Blockchain) GetChainID() string {
return "0x1" // Mainnet (adjust as per your chain)
}
func (bc *Blockchain) ResolveForks() {
var longestFork *Fork
longestLength := len(bc.Blocks)
for _, fork := range bc.Forks {
if len(fork.Blocks)+fork.Index > longestLength {
longestLength = len(fork.Blocks) + fork.Index
longestFork = fork
}
}
if longestFork != nil {
// Switch to the longest fork
bc.Blocks = append(bc.Blocks[:longestFork.Index], longestFork.Blocks...)
}
// Clear forks as the longest chain is now the main chain
bc.Forks = nil
}
// In Blockchain
func (bc *Blockchain) InsertOrUpdatePublicKey(address string, publicKey []byte, keyType string) error {
log.Printf("InsertOrUpdatePublicKey called with address: %s, keyType: %s", address, keyType)
log.Printf("PublicKey: %x", publicKey)
switch keyType {
case "Ed25519":
return bc.Database.InsertOrUpdateEd25519PublicKey(address, publicKey)
default:
return fmt.Errorf("unsupported key type: %s", keyType)
}
}
// ValidateBlock checks if the block is valid
func (bc *Blockchain) ValidateBlock(newBlock *Block, prevBlock *Block) bool {
// Debugging: Print the timestamps for debugging
fmt.Printf("Validating block with timestamp: %d against previous block with timestamp: %d\n", newBlock.Timestamp, prevBlock.Timestamp)
// Check if PrevHash matches the hash of the previous block
if newBlock.Timestamp <= prevBlock.Timestamp {
fmt.Printf("Invalid timestamp in block %d: should be greater than %d, got %d\n", newBlock.Index, prevBlock.Timestamp, newBlock.Timestamp)
return false
}
// Validate the block's proof of stake
if !bc.VerifyPoSRules(*newBlock) {
fmt.Printf("Invalid block %d due to PoS rules: validator was %s\n", newBlock.Index, newBlock.Validator)
fmt.Printf("Full block: %+v\n", newBlock)
return false
}
// Validate the block's hash
computedHash := newBlock.ComputeHash()
if newBlock.Hash != computedHash {
fmt.Printf("Invalid hash in block %d: expected %s, got %s\n", newBlock.Index, computedHash, newBlock.Hash)
return false
}
return true
}
func (bc *Blockchain) GetLastBlock() (*Block, int, error) {
// Query the last block data and index
blockData, lastIndex, err := bc.Database.GetLastBlockData()
if err != nil {
if err == sql.ErrNoRows {
// Handle no rows returned, which means the blockchain is empty
return nil, 0, nil
}
return nil, 0, err
}
// Deserialize the block
var lastBlock Block
buffer := bytes.NewBuffer(blockData)
decoder := gob.NewDecoder(buffer)
err = decoder.Decode(&lastBlock)
if err != nil {
return nil, 0, err
}
// Return the block along with its index
return &lastBlock, lastIndex, nil
}
// addUTXO adds a new UTXO to the blockchain's UTXO set.
func (bc *Blockchain) addUTXO(utxo shared.UTXO) {
utxoKey := fmt.Sprintf("%s:%d", utxo.TransactionID, utxo.Index)
// Ensure the UTXO list for the key is initialized
if _, exists := bc.UTXOs[utxoKey]; !exists {
bc.UTXOs[utxoKey] = []*thrylos.UTXO{}
}
// Convert shared UTXO to thrylos UTXO and add it
thrylosUtxo := shared.ConvertSharedUTXOToProto(utxo)
bc.UTXOs[utxoKey] = append(bc.UTXOs[utxoKey], thrylosUtxo)
log.Printf("UTXO added: %s", utxoKey)
}
// removeUTXO removes a UTXO from the blockchain's UTXO set based on transaction ID and index.
func (bc *Blockchain) removeUTXO(transactionID string, index int32) bool {
utxoKey := fmt.Sprintf("%s:%d", transactionID, index)
if _, exists := bc.UTXOs[utxoKey]; exists {
delete(bc.UTXOs, utxoKey)
return true
}
return false
}
// VerifyTransaction checks the validity of a transaction against the current state of the blockchain,
// including signature verification and double spending checks. It's essential for maintaining the
// Example snippet for VerifyTransaction method adjustment
func (bc *Blockchain) VerifyTransaction(tx *thrylos.Transaction) (bool, error) {
// Function to retrieve Ed25519 public key from the address
getEd25519PublicKeyFunc := func(address string) (ed25519.PublicKey, error) {
pubKey, err := bc.Database.RetrievePublicKeyFromAddress(address)
if err != nil {
return ed25519.PublicKey{}, err // Return the zero value for ed25519.PublicKey in case of error
}
return pubKey, nil
}
// Assuming you've made necessary adjustments to the rest of your code to handle the protobuf and shared.UTXO types correctly
protoUTXOs := make(map[string][]*thrylos.UTXO)
for key, utxos := range bc.UTXOs {
protoUTXOs[key] = utxos // Adjust according to your actual type conversion if necessary
}
// Verify the transaction using the converted UTXOs and both public key types
isValid, err := shared.VerifyTransaction(tx, protoUTXOs, getEd25519PublicKeyFunc)
if err != nil {
fmt.Printf("Error during transaction verification: %v\n", err)
return false, err
}
if !isValid {
fmt.Println("Signature verification failed or transaction is invalid")
return false, nil
}
return true, nil
}
// AddPendingTransaction adds a new transaction to the pool of pending transactions.
func (bc *Blockchain) AddPendingTransaction(tx *thrylos.Transaction) {
bc.Mu.Lock()
defer bc.Mu.Unlock()
bc.PendingTransactions = append(bc.PendingTransactions, tx)
}
// ProcessPendingTransactions processes all pending transactions, attempting to form a new block.
// ProcessPendingTransactions processes all pending transactions, attempting to form a new block.
func (bc *Blockchain) ProcessPendingTransactions(validator string) (*Block, error) {
bc.Mu.Lock()
defer bc.Mu.Unlock()
for _, tx := range bc.PendingTransactions {
log.Printf("Processing transaction %s", tx.Id)
for _, input := range tx.Inputs {
if removed := bc.removeUTXO(input.TransactionId, input.Index); !removed {
log.Printf("Failed to remove input UTXO: TransactionID: %s, Index: %d", input.TransactionId, input.Index)
continue // Skip this transaction if the input UTXO cannot be removed (not found or already spent)
}
log.Printf("Input UTXO removed: TransactionID: %s, Index: %d", input.TransactionId, input.Index)
}
for _, output := range tx.Outputs {
newUTXO := shared.CreateUTXO(tx.Id, tx.Id, int(output.Index), output.OwnerAddress, int(output.Amount))
if err := newUTXO.ValidateUTXO(); err != nil {
log.Printf("Validation failed for UTXO: %v", err)
continue // Skip adding this UTXO if validation fails
}
bc.addUTXO(newUTXO)
log.Printf("Output UTXO added: Transaction ID: %s, Owner: %s, Amount: %d", tx.Id, output.OwnerAddress, output.Amount)
}
}
selectedValidator := bc.SelectValidator()
if validator != selectedValidator {
return nil, fmt.Errorf("selected validator does not match")
}
rewardTransaction := &thrylos.Transaction{
// Implementation for reward transaction
}
bc.PendingTransactions = append(bc.PendingTransactions, rewardTransaction)
newBlock := bc.CreateBlock(bc.PendingTransactions, validator, bc.Blocks[len(bc.Blocks)-1].Hash, time.Now().Unix())
if newBlock == nil {
return nil, fmt.Errorf("failed to create a new block")
}
bc.Blocks = append(bc.Blocks, newBlock)
bc.PendingTransactions = nil
return newBlock, nil
}
// Get the block and see how many transactions are in each block
func (bc *Blockchain) GetBlockByID(id string) (*Block, error) {
// iterate over blocks and find by ID
for _, block := range bc.Blocks {
if block.Hash == id || strconv.Itoa(int(block.Index)) == id { // Convert int32 to int before converting to string
log.Printf("Block found: Index=%d, Transactions=%v", block.Index, block.Transactions)
return block, nil
}
}
log.Println("Block not found with ID:", id)
return nil, errors.New("block not found")
}
func (bc *Blockchain) GetTransactionByID(id string) (*thrylos.Transaction, error) {
// iterate over blocks and transactions to find by ID
for _, block := range bc.Blocks {
for _, tx := range block.Transactions {
if tx.Id == id {
return tx, nil
}
}
}
return nil, errors.New("transaction not found")
}
// This function should return the number of blocks in the blockchain.
func (bc *Blockchain) GetBlockCount() int {
bc.Mu.RLock()
defer bc.Mu.RUnlock()
return len(bc.Blocks)
}
// This function should return the number of transactions for a given address, which is often referred to as the "nonce."
func (bc *Blockchain) GetTransactionCount(address string) int {
bc.Mu.RLock()
defer bc.Mu.RUnlock()
count := 0
for _, block := range bc.Blocks {
for _, transaction := range block.Transactions {
if transaction.Sender == address {
count++
}
}
}
return count
}
func (bc *Blockchain) GetBlock(blockNumber int) (*Block, error) {
blockData, err := bc.Database.RetrieveBlock(blockNumber)
if err != nil {
return nil, fmt.Errorf("failed to retrieve block data: %v", err)
}
var block Block
if err := json.Unmarshal(blockData, &block); err != nil { // Deserialize here
return nil, fmt.Errorf("failed to deserialize block: %v", err)
}
return &block, nil
}
// If the stake adjustment leads to a non-positive value, the stakeholder is removed from the map.
func (bc *Blockchain) UpdateStake(address string, amount int) error {
bc.Mu.Lock()
defer bc.Mu.Unlock()
// Calculate the new stake amount
currentStake, exists := bc.Stakeholders[address]
newStake := currentStake + amount
// Check if the new stake is positive
if newStake <= 0 {
if exists {
// Remove the stakeholder if the stake is zero or negative
delete(bc.Stakeholders, address)
} // If not exists and amount is negative, we cannot set a negative stake
return fmt.Errorf("invalid stake amount; stake cannot be negative or zero")
}
// Set or update the stake
bc.Stakeholders[address] = newStake
return nil
}
// RegisterValidator registers or updates a validator's information in the blockchain.
func (bc *Blockchain) RegisterValidator(address string, pubKey string) error {
bc.Mu.Lock()
defer bc.Mu.Unlock()
// Convert the public key string to bytes, assuming pubKey is base64 encoded
pubKeyBytes, err := base64.StdEncoding.DecodeString(pubKey)
if err != nil {
return fmt.Errorf("error decoding public key: %v", err)
}
// Ensure the public key size matches expected size for Ed25519
if len(pubKeyBytes) != ed25519.PublicKeySize {
return fmt.Errorf("public key has incorrect size")
}
// Validate that the address has a minimum stake required to be a validator, if needed
stake, exists := bc.Stakeholders[address]
if !exists || stake < minStakeRequirement {
return fmt.Errorf("insufficient stake or not found")
}
// Register or update the public key in a map, might also store additional validator metadata
bc.PublicKeyMap[address] = ed25519.PublicKey(pubKeyBytes)
return nil
}
// This method will adjust the stake between two addresses, which represents delegating stake from one user (the delegator) to another (the delegatee or validator).
func (bc *Blockchain) DelegateStake(from, to string, amount int) error {
bc.Mu.Lock()
defer bc.Mu.Unlock()
// Check if the 'from' address has enough stake to delegate
if stake, exists := bc.Stakeholders[from]; !exists || stake < amount {
return fmt.Errorf("insufficient stake to delegate: has %d, needs %d", stake, amount)
}
// Reduce stake from the 'from' address
bc.Stakeholders[from] -= amount
// Add stake to the 'to' address
if _, exists := bc.Stakeholders[to]; exists {
bc.Stakeholders[to] += amount
} else {
bc.Stakeholders[to] = amount
}
return nil
}
// AddBlock adds a new block to the blockchain, with an optional timestamp.
// If the timestamp is 0, the current system time is used as the block's timestamp.
func (bc *Blockchain) AddBlock(transactions []*thrylos.Transaction, validator string, prevHash string, optionalTimestamp ...int64) (bool, error) {
bc.Mu.Lock()
defer bc.Mu.Unlock()
var timestamp int64
if len(optionalTimestamp) > 0 && optionalTimestamp[0] > 0 {
timestamp = optionalTimestamp[0]
} else {
timestamp = time.Now().Unix()
}
// Handle potential forks.
if len(bc.Blocks) > 0 && prevHash != bc.Blocks[len(bc.Blocks)-1].Hash {
var selectedFork *Fork
for _, fork := range bc.Forks {
if fork.Blocks[len(fork.Blocks)-1].Hash == prevHash {
selectedFork = fork
break
}
}
newBlock := bc.CreateBlock(transactions, validator, prevHash, timestamp)
if newBlock == nil {
return false, fmt.Errorf("failed to create a new block")
}
blockData, err := json.Marshal(newBlock) // Serialize here
if err != nil {
return false, fmt.Errorf("failed to serialize new block: %v", err)
}
blockNumber := len(bc.Blocks) // This should be after block validation
if selectedFork != nil {
selectedFork.Blocks = append(selectedFork.Blocks, newBlock)
blockNumber = len(selectedFork.Blocks) - 1
} else {
bc.Blocks = append(bc.Blocks, newBlock)
blockNumber = len(bc.Blocks) - 1 // Use the index of the newly appended block
}
if err := bc.Database.StoreBlock(blockData, blockNumber); err != nil {
return false, fmt.Errorf("failed to store block in database: %v", err)
}
return true, nil
}
// Verify transactions.
for _, tx := range transactions {
isValid, err := bc.VerifyTransaction(tx) // Ensure VerifyTransaction accepts *thrylos.Transaction
if err != nil || !isValid {
return false, fmt.Errorf("transaction verification failed: %s, error: %v", tx.GetId(), err)
}
}
// Handle UTXOs: updating UTXO set with new transactions.
for _, tx := range transactions {
for _, input := range tx.GetInputs() {
utxoKey := fmt.Sprintf("%s:%d", input.GetTransactionId(), input.GetIndex())
delete(bc.UTXOs, utxoKey)
}
for index, output := range tx.GetOutputs() {
utxoKey := fmt.Sprintf("%s:%d", tx.GetId(), index)
// Append output to the slice for this utxoKey
bc.UTXOs[utxoKey] = append(bc.UTXOs[utxoKey], output)
}
}
// Create and validate the new block.
prevBlock := bc.Blocks[len(bc.Blocks)-1] // Ensure there is at least one block before doing this
newBlock := bc.CreateBlock(transactions, validator, prevHash, timestamp)
if newBlock == nil || !bc.ValidateBlock(newBlock, prevBlock) {
return false, fmt.Errorf("failed to create or validate a new block")
}
var buf bytes.Buffer
encoder := gob.NewEncoder(&buf)
if err := encoder.Encode(newBlock); err != nil {
return false, fmt.Errorf("failed to serialize new block: %v", err)
}
blockData := buf.Bytes()
// Use the length of the blockchain as the new block number
blockNumber := len(bc.Blocks) // This should be calculated appropriately
if err := bc.Database.InsertBlock(blockData, blockNumber); err != nil {
return false, fmt.Errorf("failed to insert block into database: %v", err)
}
// Update the blockchain with the new block
bc.Blocks = append(bc.Blocks, newBlock)
bc.lastTimestamp = timestamp
return true, nil
}
// RewardValidator rewards the validator with new tokens
func (bc *Blockchain) RewardValidator(validator string, reward int) {
bc.Mu.Lock() // Lock
bc.Stakeholders[validator] += reward
bc.Mu.Unlock() // Unlock
}
// VerifyPoSRules verifies the PoS rules for the given block
func (bc *Blockchain) VerifyPoSRules(block Block) bool {
// Check if the validator had a stake at the time of block creation
_, exists := bc.Stakeholders[block.Validator]
return exists
}
// CheckChainIntegrity verifies the entire blockchain for hash integrity and chronological order,
// ensuring that no blocks have been altered or inserted maliciously. It's a safeguard against tampering
// and a key component in the blockchain's security mechanisms.
func (bc *Blockchain) CheckChainIntegrity() bool {
for i := 1; i < len(bc.Blocks); i++ {
prevBlock := bc.Blocks[i-1]
currentBlock := bc.Blocks[i]
if currentBlock.PrevHash != prevBlock.Hash {
fmt.Println("Invalid previous hash in block:", currentBlock.Index)
return false
}
if currentBlock.Hash != currentBlock.ComputeHash() {
fmt.Println("Invalid hash in block:", currentBlock.Index)
return false
}
}
return true
}
// Blockchain Initialization:
// Initialize the blockchain database and genesis block upon starting the server.
// Load or create stakeholders, UTXOs, and transactions for the genesis block.
// Transaction Handling and Block Management:
// Receive transactions from clients, add to the pending transaction pool, and process them periodically.
// Create new blocks from pending transactions, ensuring transactions are valid, updating the UTXO set, and managing block links.
// Fork Resolution and Integrity Checks:
// Check for forks in the blockchain and resolve by selecting the longest chain.
// Perform regular integrity checks on the blockchain to ensure no tampering or inconsistencies.
// Blockchain Operations (Detailed Server-Side)
// Transaction Verification:
// Verify each transaction for double spending and proper signature before adding to a block.
// Manage UTXOs to reflect current ownership states.
// Block Creation:
// On achieving a sufficient number of transactions or a time limit, attempt to create a new block.
// Validate the new block against the previous block and the blockchain's proof-of-stake rules.
// Consensus and Blockchain Updates:
// If a new block is validated successfully, append it to the blockchain.
// Update the blockchain state, including UTXOs and potentially resolving forks.
// Blockchain Maintenance Tasks:
// Regularly check and ensure the blockchain's integrity using hash checks and timestamp validations.
// Optionally, handle rewards for validators and manage the stakeholder map based on proof-of-stake consensus.