From 0d2d55f839477380f3d1aac3f9288f1d5b13a553 Mon Sep 17 00:00:00 2001 From: MubarizHaroon0 Date: Thu, 20 Jun 2024 22:26:54 +0500 Subject: [PATCH 1/2] Fixing Issues --- blockchain.go | 20 +++++----- cli_createblockchain.go | 4 +- cli_getbalance.go | 5 ++- cli_listaddress.go | 2 +- cli_printchain.go | 1 + cli_send.go | 14 ++++++- go.mod | 16 ++++++++ go.sum | 16 ++++++++ logic.txt | 0 proofofwork.go | 10 ++--- server.go | 5 ++- transaction.go | 25 ++++++++---- transaction_input.go | 2 +- transaction_output.go | 2 +- utxo_set.go | 35 ++++++++++++----- wallet.go | 86 ++++++++++++++++++++++++++++++++++++++++- wallets.go | 10 ++++- 17 files changed, 211 insertions(+), 42 deletions(-) create mode 100644 go.mod create mode 100644 go.sum create mode 100644 logic.txt diff --git a/blockchain.go b/blockchain.go index 8436035..a1e92a4 100644 --- a/blockchain.go +++ b/blockchain.go @@ -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 } diff --git a/cli_createblockchain.go b/cli_createblockchain.go index 7e69405..cc89cff 100644 --- a/cli_createblockchain.go +++ b/cli_createblockchain.go @@ -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() diff --git a/cli_getbalance.go b/cli_getbalance.go index a86e3dc..d8942c4 100644 --- a/cli_getbalance.go +++ b/cli_getbalance.go @@ -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() diff --git a/cli_listaddress.go b/cli_listaddress.go index 0d30563..c698e2c 100644 --- a/cli_listaddress.go +++ b/cli_listaddress.go @@ -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) } } diff --git a/cli_printchain.go b/cli_printchain.go index 81b7edf..1e10e71 100644 --- a/cli_printchain.go +++ b/cli_printchain.go @@ -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 { diff --git a/cli_send.go b/cli_send.go index 75a5930..df8d83d 100644 --- a/cli_send.go +++ b/cli_send.go @@ -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) } diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..e0f3ae9 --- /dev/null +++ b/go.mod @@ -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 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..ae618a3 --- /dev/null +++ b/go.sum @@ -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= diff --git a/logic.txt b/logic.txt new file mode 100644 index 0000000..e69de29 diff --git a/proofofwork.go b/proofofwork.go index cc10c1d..752a70f 100644 --- a/proofofwork.go +++ b/proofofwork.go @@ -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[:] } diff --git a/server.go b/server.go index b13f520..eaa80e8 100644 --- a/server.go +++ b/server.go @@ -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 { diff --git a/transaction.go b/transaction.go index 107f015..490c5a3 100644 --- a/transaction.go +++ b/transaction.go @@ -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 diff --git a/transaction_input.go b/transaction_input.go index 23beeba..a6cf6fb 100644 --- a/transaction_input.go +++ b/transaction_input.go @@ -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) } diff --git a/transaction_output.go b/transaction_output.go index 2ae68de..fc7c69e 100644 --- a/transaction_output.go +++ b/transaction_output.go @@ -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 diff --git a/utxo_set.go b/utxo_set.go index 180ce04..97e83f3 100644 --- a/utxo_set.go +++ b/utxo_set.go @@ -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) diff --git a/wallet.go b/wallet.go index 506b544..3b4c6b9 100644 --- a/wallet.go +++ b/wallet.go @@ -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 +} diff --git a/wallets.go b/wallets.go index 9f376f5..210acca 100644 --- a/wallets.go +++ b/wallets.go @@ -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) From 5cdca681b0474ffacbf4f371ccb521f05d35e791 Mon Sep 17 00:00:00 2001 From: MubarizHaroon0 Date: Thu, 20 Jun 2024 22:27:23 +0500 Subject: [PATCH 2/2] Fixing Issues --- logic.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 logic.txt diff --git a/logic.txt b/logic.txt deleted file mode 100644 index e69de29..0000000