Fixing Issues

This commit is contained in:
MubarizHaroon0 2024-06-20 22:26:54 +05:00
parent ff408351a2
commit 0d2d55f839
17 changed files with 211 additions and 42 deletions

View File

@ -71,7 +71,7 @@ func CreateBlockchain(address, nodeID string) *Blockchain {
// NewBlockchain creates a new Blockchain with genesis Block
func NewBlockchain(nodeID string) *Blockchain {
dbFile := fmt.Sprintf(dbFile, nodeID)
if dbExists(dbFile) == false {
if !dbExists(dbFile) {
fmt.Println("No existing blockchain found. Create one first.")
os.Exit(1)
}
@ -81,7 +81,6 @@ func NewBlockchain(nodeID string) *Blockchain {
if err != nil {
log.Panic(err)
}
err = db.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(blocksBucket))
tip = b.Get([]byte("l"))
@ -140,7 +139,7 @@ func (bc *Blockchain) FindTransaction(ID []byte) (Transaction, error) {
block := bci.Next()
for _, tx := range block.Transactions {
if bytes.Compare(tx.ID, ID) == 0 {
if bytes.Equal(tx.ID, ID) {
return *tx, nil
}
}
@ -181,7 +180,7 @@ func (bc *Blockchain) FindUTXO() map[string]TXOutputs {
UTXO[txID] = outs
}
if tx.IsCoinbase() == false {
if !tx.IsCoinbase() {
for _, in := range tx.Vin {
inTxID := hex.EncodeToString(in.Txid)
spentTXOs[inTxID] = append(spentTXOs[inTxID], in.Vout)
@ -233,7 +232,7 @@ func (bc *Blockchain) GetBlock(blockHash []byte) (Block, error) {
blockData := b.Get(blockHash)
if blockData == nil {
return errors.New("Block is not found.")
return errors.New("Block not found ... ")
}
block = *DeserializeBlock(blockData)
@ -272,8 +271,9 @@ func (bc *Blockchain) MineBlock(transactions []*Transaction) *Block {
for _, tx := range transactions {
// TODO: ignore transaction if it's not valid
if bc.VerifyTransaction(tx) != true {
log.Panic("ERROR: Invalid transaction")
if !bc.VerifyTransaction(tx) {
log.Println("ERROR: Invalid transaction passed to 'MineBlock'")
os.Exit(1)
}
}
@ -324,7 +324,8 @@ func (bc *Blockchain) SignTransaction(tx *Transaction, privKey ecdsa.PrivateKey)
for _, vin := range tx.Vin {
prevTX, err := bc.FindTransaction(vin.Txid)
if err != nil {
log.Panic(err)
log.Println(err)
os.Exit(1)
}
prevTXs[hex.EncodeToString(prevTX.ID)] = prevTX
}
@ -343,7 +344,8 @@ func (bc *Blockchain) VerifyTransaction(tx *Transaction) bool {
for _, vin := range tx.Vin {
prevTX, err := bc.FindTransaction(vin.Txid)
if err != nil {
log.Panic(err)
log.Println(err)
os.Exit(1)
}
prevTXs[hex.EncodeToString(prevTX.ID)] = prevTX
}

View File

@ -3,11 +3,13 @@ package main
import (
"fmt"
"log"
"os"
)
func (cli *CLI) createBlockchain(address, nodeID string) {
if !ValidateAddress(address) {
log.Panic("ERROR: Address is not valid")
log.Println("ERROR: Address is not valid")
os.Exit(1)
}
bc := CreateBlockchain(address, nodeID)
defer bc.db.Close()

View File

@ -3,12 +3,15 @@ package main
import (
"fmt"
"log"
"os"
)
func (cli *CLI) getBalance(address, nodeID string) {
if !ValidateAddress(address) {
log.Panic("ERROR: Address is not valid")
log.Println("ERROR: Address is not valid")
os.Exit(1)
}
bc := NewBlockchain(nodeID)
UTXOSet := UTXOSet{bc}
defer bc.db.Close()

View File

@ -13,6 +13,6 @@ func (cli *CLI) listAddresses(nodeID string) {
addresses := wallets.GetAddresses()
for _, address := range addresses {
fmt.Println(address)
fmt.Printf("Address : %s\n", address)
}
}

View File

@ -17,6 +17,7 @@ func (cli *CLI) printChain(nodeID string) {
fmt.Printf("============ Block %x ============\n", block.Hash)
fmt.Printf("Height: %d\n", block.Height)
fmt.Printf("Prev. block: %x\n", block.PrevBlockHash)
fmt.Printf("Merkle Root: %x\n", block.HashTransactions())
pow := NewProofOfWork(block)
fmt.Printf("PoW: %s\n\n", strconv.FormatBool(pow.Validate()))
for _, tx := range block.Transactions {

View File

@ -3,16 +3,23 @@ package main
import (
"fmt"
"log"
"os"
)
func (cli *CLI) send(from, to string, amount int, nodeID string, mineNow bool) {
if !ValidateAddress(from) {
log.Panic("ERROR: Sender address is not valid")
log.Println("ERROR: Sender address is not valid")
os.Exit(1)
}
if !ValidateAddress(to) {
log.Panic("ERROR: Recipient address is not valid")
log.Println("ERROR: Recipient address is not valid")
os.Exit(1)
}
if from == to {
log.Println("ERROR: Sender and Recipient cannot be same")
os.Exit(1)
}
bc := NewBlockchain(nodeID)
UTXOSet := UTXOSet{bc}
defer bc.db.Close()
@ -26,12 +33,15 @@ func (cli *CLI) send(from, to string, amount int, nodeID string, mineNow bool) {
tx := NewUTXOTransaction(&wallet, to, amount, &UTXOSet)
if mineNow {
cbTx := NewCoinbaseTX(from, "")
txs := []*Transaction{cbTx, tx}
newBlock := bc.MineBlock(txs)
UTXOSet.Update(newBlock)
} else {
sendTx(knownNodes[0], tx)
}

16
go.mod Normal file
View File

@ -0,0 +1,16 @@
module blockchain_go
go 1.22.4
require (
github.com/boltdb/bolt v1.3.1
github.com/stretchr/testify v1.9.0
golang.org/x/crypto v0.24.0
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/sys v0.21.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

16
go.sum Normal file
View File

@ -0,0 +1,16 @@
github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4=
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

0
logic.txt Normal file
View File

View File

@ -51,14 +51,12 @@ func (pow *ProofOfWork) Run() (int, []byte) {
var hash [32]byte
nonce := 0
fmt.Printf("Mining a new block")
fmt.Printf("Mining a new block ")
for nonce < maxNonce {
data := pow.prepareData(nonce)
hash = sha256.Sum256(data)
if math.Remainder(float64(nonce), 100000) == 0 {
fmt.Printf("\r%x", hash)
}
hashInt.SetBytes(hash[:])
if hashInt.Cmp(pow.target) == -1 {
@ -67,8 +65,8 @@ func (pow *ProofOfWork) Run() (int, []byte) {
nonce++
}
}
fmt.Print("\n\n")
fmt.Printf("Nonce found: %d\n", nonce)
return nonce, hash[:]
}

View File

@ -203,7 +203,7 @@ func handleBlock(request []byte, bc *Blockchain) {
bc.AddBlock(block)
fmt.Printf("Added block %x\n", block.Hash)
fmt.Printf("Merkle root %x\n", block.HashTransactions())
if len(blocksInTransit) > 0 {
blockHash := blocksInTransit[0]
sendGetData(payload.AddrFrom, "block", blockHash)
@ -421,7 +421,9 @@ func handleConnection(conn net.Conn, bc *Blockchain) {
func StartServer(nodeID, minerAddress string) {
nodeAddress = fmt.Sprintf("localhost:%s", nodeID)
miningAddress = minerAddress
ln, err := net.Listen(protocol, nodeAddress)
if err != nil {
log.Panic(err)
}
@ -440,6 +442,7 @@ func StartServer(nodeID, minerAddress string) {
}
go handleConnection(conn, bc)
}
}
func gobEncode(data interface{}) []byte {

View File

@ -163,7 +163,7 @@ func (tx *Transaction) Verify(prevTXs map[string]Transaction) bool {
dataToVerify := fmt.Sprintf("%x\n", txCopy)
rawPubKey := ecdsa.PublicKey{Curve: curve, X: &x, Y: &y}
if ecdsa.Verify(&rawPubKey, []byte(dataToVerify), &r, &s) == false {
if !ecdsa.Verify(&rawPubKey, []byte(dataToVerify), &r, &s) {
return false
}
txCopy.Vin[inID].PubKey = nil
@ -173,6 +173,7 @@ func (tx *Transaction) Verify(prevTXs map[string]Transaction) bool {
}
// NewCoinbaseTX creates a new coinbase transaction
func NewCoinbaseTX(to, data string) *Transaction {
if data == "" {
randData := make([]byte, 20)
@ -197,35 +198,43 @@ func NewUTXOTransaction(wallet *Wallet, to string, amount int, UTXOSet *UTXOSet)
var inputs []TXInput
var outputs []TXOutput
// Get the public key hash of the wallet
pubKeyHash := HashPubKey(wallet.PublicKey)
acc, validOutputs := UTXOSet.FindSpendableOutputs(pubKeyHash, amount)
// Find spendable outputs for the given amount
acc, validOutputs := UTXOSet.FindSpendableOutputs(pubKeyHash, amount)
if acc < amount {
log.Panic("ERROR: Not enough funds")
}
// Build a list of inputs
// Build a list of inputs from the valid outputs
for txid, outs := range validOutputs {
txID, err := hex.DecodeString(txid)
if err != nil {
log.Panic(err)
}
for _, out := range outs {
input := TXInput{txID, out, nil, wallet.PublicKey}
for _, outIndex := range outs {
input := TXInput{txID, outIndex, nil, wallet.PublicKey}
inputs = append(inputs, input)
}
}
// Build a list of outputs
from := fmt.Sprintf("%s", wallet.GetAddress())
// Create an output for the recipient
outputs = append(outputs, *NewTXOutput(amount, to))
// Create a change output if there is any change
if acc > amount {
outputs = append(outputs, *NewTXOutput(acc-amount, from)) // a change
changeAddress := fmt.Sprintf("%s", wallet.GetAddress())
outputs = append(outputs, *NewTXOutput(acc-amount, changeAddress)) // change output
}
// Create the transaction
tx := Transaction{nil, inputs, outputs}
tx.ID = tx.Hash()
// Sign the transaction
UTXOSet.Blockchain.SignTransaction(&tx, wallet.PrivateKey)
return &tx

View File

@ -14,5 +14,5 @@ type TXInput struct {
func (in *TXInput) UsesKey(pubKeyHash []byte) bool {
lockingHash := HashPubKey(in.PubKey)
return bytes.Compare(lockingHash, pubKeyHash) == 0
return bytes.Equal(lockingHash, pubKeyHash)
}

View File

@ -21,7 +21,7 @@ func (out *TXOutput) Lock(address []byte) {
// IsLockedWithKey checks if the output can be used by the owner of the pubkey
func (out *TXOutput) IsLockedWithKey(pubKeyHash []byte) bool {
return bytes.Compare(out.PubKeyHash, pubKeyHash) == 0
return bytes.Equal(out.PubKeyHash, pubKeyHash)
}
// NewTXOutput create a new TXOutput

View File

@ -2,6 +2,7 @@ package main
import (
"encoding/hex"
"fmt"
"log"
"github.com/boltdb/bolt"
@ -29,9 +30,16 @@ func (u UTXOSet) FindSpendableOutputs(pubkeyHash []byte, amount int) (int, map[s
outs := DeserializeOutputs(v)
for outIdx, out := range outs.Outputs {
if out.IsLockedWithKey(pubkeyHash) && accumulated < amount {
accumulated += out.Value
unspentOutputs[txID] = append(unspentOutputs[txID], outIdx)
if out.IsLockedWithKey(pubkeyHash) {
if accumulated < amount {
accumulated += out.Value
unspentOutputs[txID] = append(unspentOutputs[txID], outIdx)
// Early exit if the required amount is accumulated
if accumulated >= amount {
return nil
}
}
}
}
}
@ -42,6 +50,11 @@ func (u UTXOSet) FindSpendableOutputs(pubkeyHash []byte, amount int) (int, map[s
log.Panic(err)
}
// Check if we were able to accumulate the required amount
if accumulated < amount {
log.Printf("Warning: Unable to find enough spendable outputs. Needed %d, found %d", amount, accumulated)
}
return accumulated, unspentOutputs
}
@ -50,13 +63,20 @@ func (u UTXOSet) FindUTXO(pubKeyHash []byte) []TXOutput {
var UTXOs []TXOutput
db := u.Blockchain.db
// Read-only transaction to view the database
err := db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(utxoBucket))
if b == nil {
return fmt.Errorf("Bucket %s not found!", utxoBucket)
}
c := b.Cursor()
// Iterate through all keys in the bucket
for k, v := c.First(); k != nil; k, v = c.Next() {
outs := DeserializeOutputs(v)
// Filter outputs that are locked with the given public key hash
for _, out := range outs.Outputs {
if out.IsLockedWithKey(pubKeyHash) {
UTXOs = append(UTXOs, out)
@ -119,7 +139,7 @@ func (u UTXOSet) Reindex() {
UTXO := u.Blockchain.FindUTXO()
err = db.Update(func(tx *bolt.Tx) error {
_ = db.Update(func(tx *bolt.Tx) error {
b := tx.Bucket(bucketName)
for txID, outs := range UTXO {
@ -147,7 +167,7 @@ func (u UTXOSet) Update(block *Block) {
b := tx.Bucket([]byte(utxoBucket))
for _, tx := range block.Transactions {
if tx.IsCoinbase() == false {
if !tx.IsCoinbase() {
for _, vin := range tx.Vin {
updatedOuts := TXOutputs{}
outsBytes := b.Get(vin.Txid)
@ -175,10 +195,7 @@ func (u UTXOSet) Update(block *Block) {
}
newOutputs := TXOutputs{}
for _, out := range tx.Vout {
newOutputs.Outputs = append(newOutputs.Outputs, out)
}
newOutputs.Outputs = append(newOutputs.Outputs, tx.Vout...)
err := b.Put(tx.ID, newOutputs.Serialize())
if err != nil {
log.Panic(err)

View File

@ -6,7 +6,10 @@ import (
"crypto/elliptic"
"crypto/rand"
"crypto/sha256"
"encoding/gob"
"fmt"
"log"
"math/big"
"golang.org/x/crypto/ripemd160"
)
@ -63,7 +66,7 @@ func ValidateAddress(address string) bool {
pubKeyHash = pubKeyHash[1 : len(pubKeyHash)-addressChecksumLen]
targetChecksum := checksum(append([]byte{version}, pubKeyHash...))
return bytes.Compare(actualChecksum, targetChecksum) == 0
return bytes.Equal(actualChecksum, targetChecksum)
}
// Checksum generates a checksum for a public key
@ -84,3 +87,84 @@ func newKeyPair() (ecdsa.PrivateKey, []byte) {
return *private, pubKey
}
func (w *Wallet) GobEncode() ([]byte, error) {
var buffer bytes.Buffer
encoder := gob.NewEncoder(&buffer)
// Encode the curve name
curveName := "P256"
if err := encoder.Encode(curveName); err != nil {
return nil, err
}
// Encode the private key components
privateKeyBytes := w.PrivateKey.D.Bytes()
if err := encoder.Encode(privateKeyBytes); err != nil {
return nil, err
}
// Encode the public key components
xBytes := w.PrivateKey.PublicKey.X.Bytes()
yBytes := w.PrivateKey.PublicKey.Y.Bytes()
if err := encoder.Encode(xBytes); err != nil {
return nil, err
}
if err := encoder.Encode(yBytes); err != nil {
return nil, err
}
// Encode the public key
if err := encoder.Encode(w.PublicKey); err != nil {
return nil, err
}
return buffer.Bytes(), nil
}
func (w *Wallet) GobDecode(data []byte) error {
buffer := bytes.NewBuffer(data)
decoder := gob.NewDecoder(buffer)
// Decode the curve name
var curveName string
if err := decoder.Decode(&curveName); err != nil {
return err
}
// Set the curve based on the name
var curve elliptic.Curve
switch curveName {
case "P256":
curve = elliptic.P256()
default:
return fmt.Errorf("unsupported curve: %s", curveName)
}
// Decode the private key components
var privateKeyBytes []byte
if err := decoder.Decode(&privateKeyBytes); err != nil {
return err
}
w.PrivateKey.PublicKey.Curve = curve
w.PrivateKey.D = new(big.Int).SetBytes(privateKeyBytes)
// Decode the public key components
var xBytes, yBytes []byte
if err := decoder.Decode(&xBytes); err != nil {
return err
}
if err := decoder.Decode(&yBytes); err != nil {
return err
}
w.PrivateKey.PublicKey.X = new(big.Int).SetBytes(xBytes)
w.PrivateKey.PublicKey.Y = new(big.Int).SetBytes(yBytes)
// Decode the public key
if err := decoder.Decode(&w.PublicKey); err != nil {
return err
}
return nil
}

View File

@ -66,7 +66,7 @@ func (ws *Wallets) LoadFromFile(nodeID string) error {
}
var wallets Wallets
gob.Register(elliptic.P256())
//gob.Register(elliptic.P256())
decoder := gob.NewDecoder(bytes.NewReader(fileContent))
err = decoder.Decode(&wallets)
if err != nil {
@ -80,17 +80,25 @@ func (ws *Wallets) LoadFromFile(nodeID string) error {
// SaveToFile saves wallets to a file
func (ws Wallets) SaveToFile(nodeID string) {
// Create a buffer to hold the serialized data
var content bytes.Buffer
// Format the filename using the nodeID
walletFile := fmt.Sprintf(walletFile, nodeID)
// Register the elliptic curve with the gob package
gob.Register(elliptic.P256())
// Create a new encoder that writes to the buffer
encoder := gob.NewEncoder(&content)
// Encode the Wallets object into the buffer
err := encoder.Encode(ws)
if err != nil {
log.Panic(err)
}
// Write the buffer's content to the file
err = ioutil.WriteFile(walletFile, content.Bytes(), 0644)
if err != nil {
log.Panic(err)