/
blockchain.go
163 lines (137 loc) · 3.61 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
// blockchain.go
package blockchain
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"encoding/hex"
"encoding/json"
"fmt"
"strings"
"time"
"github.com/go-redis/redis/v8"
"golang.org/x/net/context"
)
var redisClient *redis.Client
const miningDifficulty = 3
type Block struct {
Index int
PrevHash string
Hash string
TimeStamp string
Data string
Transactions []*Transaction
}
type Transaction struct {
From *ecdsa.PublicKey
To *ecdsa.PublicKey
Amount int
// Add more fields as needed
}
type Blockchain struct {
Blocks []*Block
}
type Wallet struct {
PrivateKey *ecdsa.PrivateKey
PublicKey *ecdsa.PublicKey
}
// InitializeRedis initializes the Redis client.
func InitializeRedis() {
redisClient = redis.NewClient(&redis.Options{
Addr: "localhost:6379", // Adjust the address based on your Redis server configuration
DB: 0,
})
}
func generateKeyPair() (*ecdsa.PrivateKey, *ecdsa.PublicKey, error) {
privateKey, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
if err != nil {
return nil, nil, err
}
publicKey := &privateKey.PublicKey
return privateKey, publicKey, nil
}
func CreateWallet() (*Wallet, error) {
privateKey, publicKey, err := generateKeyPair()
if err != nil {
return nil, err
}
return &Wallet{
PrivateKey: privateKey,
PublicKey: publicKey,
}, nil
}
func calculateHash(block *Block) string {
for {
hash := fmt.Sprintf("%d%s%s%s", block.Index, block.PrevHash, block.TimeStamp, block.Data)
if strings.HasPrefix(hash, strings.Repeat("0", miningDifficulty)) {
return hash
}
// If the hash doesn't meet the difficulty, try again with a different timestamp
block.TimeStamp = time.Now().String()
}
}
func NewBlockchain() *Blockchain {
return &Blockchain{
Blocks: []*Block{CreateGenesisBlock()},
}
}
func CreateGenesisBlock() *Block {
return &Block{
Index: 0,
PrevHash: "0",
Hash: "genesis-hash",
TimeStamp: "genesis-timestamp",
Data: "genesis-data",
}
}
func NewBlockchainWithGenesis(genesis *Block) *Blockchain {
return &Blockchain{
Blocks: []*Block{genesis},
}
}
func (bc *Blockchain) AddBlock(data string, sender *Wallet, receiver *Wallet, amount int) {
prevBlock := bc.Blocks[len(bc.Blocks)-1]
newBlock := &Block{
Index: prevBlock.Index + 1,
PrevHash: prevBlock.Hash,
TimeStamp: time.Now().String(),
Data: data,
Transactions: []*Transaction{{From: sender.PublicKey, To: receiver.PublicKey, Amount: amount}},
}
// Calculate hash and set it
newBlock.Hash = calculateHash(newBlock)
// Append block to the chain
bc.Blocks = append(bc.Blocks, newBlock)
// Broadcast the new block to Redis channel
broadcastBlockToRedis(newBlock)
}
func broadcastBlockToRedis(block *Block) error {
// Encode block data to JSON
blockData, err := json.Marshal(block)
if err != nil {
return err
}
// Publish block data to the Redis channel
err = redisClient.Publish(context.Background(), "blockchain_channel", string(blockData)).Err()
if err != nil {
return err
}
return nil
}
func (bc *Blockchain) PrintBlockchain() {
for _, block := range bc.Blocks {
fmt.Printf("Index: %d, PrevHash: %s, Hash: %s, TimeStamp: %s, Data: %s\n",
block.Index, block.PrevHash, block.Hash, block.TimeStamp, block.Data)
}
}
func FormatAddress(publicKey *ecdsa.PublicKey) string {
// Convert the public key to a hexadecimal string
pubKeyHex := hex.EncodeToString(elliptic.Marshal(publicKey.Curve, publicKey.X, publicKey.Y))
// Truncate or pad the string to 42 characters
if len(pubKeyHex) > 42 {
pubKeyHex = pubKeyHex[:42]
} else {
pubKeyHex = fmt.Sprintf("%-42s", pubKeyHex)
}
return pubKeyHex
}