Fixing Issues
This commit is contained in:
parent
ff408351a2
commit
0d2d55f839
|
@ -71,7 +71,7 @@ func CreateBlockchain(address, nodeID string) *Blockchain {
|
||||||
// NewBlockchain creates a new Blockchain with genesis Block
|
// NewBlockchain creates a new Blockchain with genesis Block
|
||||||
func NewBlockchain(nodeID string) *Blockchain {
|
func NewBlockchain(nodeID string) *Blockchain {
|
||||||
dbFile := fmt.Sprintf(dbFile, nodeID)
|
dbFile := fmt.Sprintf(dbFile, nodeID)
|
||||||
if dbExists(dbFile) == false {
|
if !dbExists(dbFile) {
|
||||||
fmt.Println("No existing blockchain found. Create one first.")
|
fmt.Println("No existing blockchain found. Create one first.")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
@ -81,7 +81,6 @@ func NewBlockchain(nodeID string) *Blockchain {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panic(err)
|
log.Panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = db.Update(func(tx *bolt.Tx) error {
|
err = db.Update(func(tx *bolt.Tx) error {
|
||||||
b := tx.Bucket([]byte(blocksBucket))
|
b := tx.Bucket([]byte(blocksBucket))
|
||||||
tip = b.Get([]byte("l"))
|
tip = b.Get([]byte("l"))
|
||||||
|
@ -140,7 +139,7 @@ func (bc *Blockchain) FindTransaction(ID []byte) (Transaction, error) {
|
||||||
block := bci.Next()
|
block := bci.Next()
|
||||||
|
|
||||||
for _, tx := range block.Transactions {
|
for _, tx := range block.Transactions {
|
||||||
if bytes.Compare(tx.ID, ID) == 0 {
|
if bytes.Equal(tx.ID, ID) {
|
||||||
return *tx, nil
|
return *tx, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -181,7 +180,7 @@ func (bc *Blockchain) FindUTXO() map[string]TXOutputs {
|
||||||
UTXO[txID] = outs
|
UTXO[txID] = outs
|
||||||
}
|
}
|
||||||
|
|
||||||
if tx.IsCoinbase() == false {
|
if !tx.IsCoinbase() {
|
||||||
for _, in := range tx.Vin {
|
for _, in := range tx.Vin {
|
||||||
inTxID := hex.EncodeToString(in.Txid)
|
inTxID := hex.EncodeToString(in.Txid)
|
||||||
spentTXOs[inTxID] = append(spentTXOs[inTxID], in.Vout)
|
spentTXOs[inTxID] = append(spentTXOs[inTxID], in.Vout)
|
||||||
|
@ -233,7 +232,7 @@ func (bc *Blockchain) GetBlock(blockHash []byte) (Block, error) {
|
||||||
blockData := b.Get(blockHash)
|
blockData := b.Get(blockHash)
|
||||||
|
|
||||||
if blockData == nil {
|
if blockData == nil {
|
||||||
return errors.New("Block is not found.")
|
return errors.New("Block not found ... ")
|
||||||
}
|
}
|
||||||
|
|
||||||
block = *DeserializeBlock(blockData)
|
block = *DeserializeBlock(blockData)
|
||||||
|
@ -272,8 +271,9 @@ func (bc *Blockchain) MineBlock(transactions []*Transaction) *Block {
|
||||||
|
|
||||||
for _, tx := range transactions {
|
for _, tx := range transactions {
|
||||||
// TODO: ignore transaction if it's not valid
|
// TODO: ignore transaction if it's not valid
|
||||||
if bc.VerifyTransaction(tx) != true {
|
if !bc.VerifyTransaction(tx) {
|
||||||
log.Panic("ERROR: Invalid transaction")
|
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 {
|
for _, vin := range tx.Vin {
|
||||||
prevTX, err := bc.FindTransaction(vin.Txid)
|
prevTX, err := bc.FindTransaction(vin.Txid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panic(err)
|
log.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
prevTXs[hex.EncodeToString(prevTX.ID)] = prevTX
|
prevTXs[hex.EncodeToString(prevTX.ID)] = prevTX
|
||||||
}
|
}
|
||||||
|
@ -343,7 +344,8 @@ func (bc *Blockchain) VerifyTransaction(tx *Transaction) bool {
|
||||||
for _, vin := range tx.Vin {
|
for _, vin := range tx.Vin {
|
||||||
prevTX, err := bc.FindTransaction(vin.Txid)
|
prevTX, err := bc.FindTransaction(vin.Txid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panic(err)
|
log.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
prevTXs[hex.EncodeToString(prevTX.ID)] = prevTX
|
prevTXs[hex.EncodeToString(prevTX.ID)] = prevTX
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,11 +3,13 @@ package main
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (cli *CLI) createBlockchain(address, nodeID string) {
|
func (cli *CLI) createBlockchain(address, nodeID string) {
|
||||||
if !ValidateAddress(address) {
|
if !ValidateAddress(address) {
|
||||||
log.Panic("ERROR: Address is not valid")
|
log.Println("ERROR: Address is not valid")
|
||||||
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
bc := CreateBlockchain(address, nodeID)
|
bc := CreateBlockchain(address, nodeID)
|
||||||
defer bc.db.Close()
|
defer bc.db.Close()
|
||||||
|
|
|
@ -3,12 +3,15 @@ package main
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (cli *CLI) getBalance(address, nodeID string) {
|
func (cli *CLI) getBalance(address, nodeID string) {
|
||||||
if !ValidateAddress(address) {
|
if !ValidateAddress(address) {
|
||||||
log.Panic("ERROR: Address is not valid")
|
log.Println("ERROR: Address is not valid")
|
||||||
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
bc := NewBlockchain(nodeID)
|
bc := NewBlockchain(nodeID)
|
||||||
UTXOSet := UTXOSet{bc}
|
UTXOSet := UTXOSet{bc}
|
||||||
defer bc.db.Close()
|
defer bc.db.Close()
|
||||||
|
|
|
@ -13,6 +13,6 @@ func (cli *CLI) listAddresses(nodeID string) {
|
||||||
addresses := wallets.GetAddresses()
|
addresses := wallets.GetAddresses()
|
||||||
|
|
||||||
for _, address := range addresses {
|
for _, address := range addresses {
|
||||||
fmt.Println(address)
|
fmt.Printf("Address : %s\n", address)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ func (cli *CLI) printChain(nodeID string) {
|
||||||
fmt.Printf("============ Block %x ============\n", block.Hash)
|
fmt.Printf("============ Block %x ============\n", block.Hash)
|
||||||
fmt.Printf("Height: %d\n", block.Height)
|
fmt.Printf("Height: %d\n", block.Height)
|
||||||
fmt.Printf("Prev. block: %x\n", block.PrevBlockHash)
|
fmt.Printf("Prev. block: %x\n", block.PrevBlockHash)
|
||||||
|
fmt.Printf("Merkle Root: %x\n", block.HashTransactions())
|
||||||
pow := NewProofOfWork(block)
|
pow := NewProofOfWork(block)
|
||||||
fmt.Printf("PoW: %s\n\n", strconv.FormatBool(pow.Validate()))
|
fmt.Printf("PoW: %s\n\n", strconv.FormatBool(pow.Validate()))
|
||||||
for _, tx := range block.Transactions {
|
for _, tx := range block.Transactions {
|
||||||
|
|
14
cli_send.go
14
cli_send.go
|
@ -3,16 +3,23 @@ package main
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (cli *CLI) send(from, to string, amount int, nodeID string, mineNow bool) {
|
func (cli *CLI) send(from, to string, amount int, nodeID string, mineNow bool) {
|
||||||
if !ValidateAddress(from) {
|
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) {
|
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)
|
bc := NewBlockchain(nodeID)
|
||||||
UTXOSet := UTXOSet{bc}
|
UTXOSet := UTXOSet{bc}
|
||||||
defer bc.db.Close()
|
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)
|
tx := NewUTXOTransaction(&wallet, to, amount, &UTXOSet)
|
||||||
|
|
||||||
if mineNow {
|
if mineNow {
|
||||||
|
|
||||||
cbTx := NewCoinbaseTX(from, "")
|
cbTx := NewCoinbaseTX(from, "")
|
||||||
txs := []*Transaction{cbTx, tx}
|
txs := []*Transaction{cbTx, tx}
|
||||||
|
|
||||||
newBlock := bc.MineBlock(txs)
|
newBlock := bc.MineBlock(txs)
|
||||||
UTXOSet.Update(newBlock)
|
UTXOSet.Update(newBlock)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
sendTx(knownNodes[0], tx)
|
sendTx(knownNodes[0], tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
)
|
|
@ -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=
|
|
@ -51,14 +51,12 @@ func (pow *ProofOfWork) Run() (int, []byte) {
|
||||||
var hash [32]byte
|
var hash [32]byte
|
||||||
nonce := 0
|
nonce := 0
|
||||||
|
|
||||||
fmt.Printf("Mining a new block")
|
fmt.Printf("Mining a new block ")
|
||||||
|
|
||||||
for nonce < maxNonce {
|
for nonce < maxNonce {
|
||||||
data := pow.prepareData(nonce)
|
data := pow.prepareData(nonce)
|
||||||
|
|
||||||
hash = sha256.Sum256(data)
|
hash = sha256.Sum256(data)
|
||||||
if math.Remainder(float64(nonce), 100000) == 0 {
|
|
||||||
fmt.Printf("\r%x", hash)
|
|
||||||
}
|
|
||||||
hashInt.SetBytes(hash[:])
|
hashInt.SetBytes(hash[:])
|
||||||
|
|
||||||
if hashInt.Cmp(pow.target) == -1 {
|
if hashInt.Cmp(pow.target) == -1 {
|
||||||
|
@ -67,8 +65,8 @@ func (pow *ProofOfWork) Run() (int, []byte) {
|
||||||
nonce++
|
nonce++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fmt.Print("\n\n")
|
|
||||||
|
|
||||||
|
fmt.Printf("Nonce found: %d\n", nonce)
|
||||||
return nonce, hash[:]
|
return nonce, hash[:]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -203,7 +203,7 @@ func handleBlock(request []byte, bc *Blockchain) {
|
||||||
bc.AddBlock(block)
|
bc.AddBlock(block)
|
||||||
|
|
||||||
fmt.Printf("Added block %x\n", block.Hash)
|
fmt.Printf("Added block %x\n", block.Hash)
|
||||||
|
fmt.Printf("Merkle root %x\n", block.HashTransactions())
|
||||||
if len(blocksInTransit) > 0 {
|
if len(blocksInTransit) > 0 {
|
||||||
blockHash := blocksInTransit[0]
|
blockHash := blocksInTransit[0]
|
||||||
sendGetData(payload.AddrFrom, "block", blockHash)
|
sendGetData(payload.AddrFrom, "block", blockHash)
|
||||||
|
@ -421,7 +421,9 @@ func handleConnection(conn net.Conn, bc *Blockchain) {
|
||||||
func StartServer(nodeID, minerAddress string) {
|
func StartServer(nodeID, minerAddress string) {
|
||||||
nodeAddress = fmt.Sprintf("localhost:%s", nodeID)
|
nodeAddress = fmt.Sprintf("localhost:%s", nodeID)
|
||||||
miningAddress = minerAddress
|
miningAddress = minerAddress
|
||||||
|
|
||||||
ln, err := net.Listen(protocol, nodeAddress)
|
ln, err := net.Listen(protocol, nodeAddress)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panic(err)
|
log.Panic(err)
|
||||||
}
|
}
|
||||||
|
@ -440,6 +442,7 @@ func StartServer(nodeID, minerAddress string) {
|
||||||
}
|
}
|
||||||
go handleConnection(conn, bc)
|
go handleConnection(conn, bc)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func gobEncode(data interface{}) []byte {
|
func gobEncode(data interface{}) []byte {
|
||||||
|
|
|
@ -163,7 +163,7 @@ func (tx *Transaction) Verify(prevTXs map[string]Transaction) bool {
|
||||||
dataToVerify := fmt.Sprintf("%x\n", txCopy)
|
dataToVerify := fmt.Sprintf("%x\n", txCopy)
|
||||||
|
|
||||||
rawPubKey := ecdsa.PublicKey{Curve: curve, X: &x, Y: &y}
|
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
|
return false
|
||||||
}
|
}
|
||||||
txCopy.Vin[inID].PubKey = nil
|
txCopy.Vin[inID].PubKey = nil
|
||||||
|
@ -173,6 +173,7 @@ func (tx *Transaction) Verify(prevTXs map[string]Transaction) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCoinbaseTX creates a new coinbase transaction
|
// NewCoinbaseTX creates a new coinbase transaction
|
||||||
|
|
||||||
func NewCoinbaseTX(to, data string) *Transaction {
|
func NewCoinbaseTX(to, data string) *Transaction {
|
||||||
if data == "" {
|
if data == "" {
|
||||||
randData := make([]byte, 20)
|
randData := make([]byte, 20)
|
||||||
|
@ -197,35 +198,43 @@ func NewUTXOTransaction(wallet *Wallet, to string, amount int, UTXOSet *UTXOSet)
|
||||||
var inputs []TXInput
|
var inputs []TXInput
|
||||||
var outputs []TXOutput
|
var outputs []TXOutput
|
||||||
|
|
||||||
|
// Get the public key hash of the wallet
|
||||||
pubKeyHash := HashPubKey(wallet.PublicKey)
|
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 {
|
if acc < amount {
|
||||||
log.Panic("ERROR: Not enough funds")
|
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 {
|
for txid, outs := range validOutputs {
|
||||||
txID, err := hex.DecodeString(txid)
|
txID, err := hex.DecodeString(txid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panic(err)
|
log.Panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, out := range outs {
|
for _, outIndex := range outs {
|
||||||
input := TXInput{txID, out, nil, wallet.PublicKey}
|
input := TXInput{txID, outIndex, nil, wallet.PublicKey}
|
||||||
inputs = append(inputs, input)
|
inputs = append(inputs, input)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build a list of outputs
|
// Create an output for the recipient
|
||||||
from := fmt.Sprintf("%s", wallet.GetAddress())
|
|
||||||
outputs = append(outputs, *NewTXOutput(amount, to))
|
outputs = append(outputs, *NewTXOutput(amount, to))
|
||||||
|
|
||||||
|
// Create a change output if there is any change
|
||||||
|
|
||||||
if acc > amount {
|
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 := Transaction{nil, inputs, outputs}
|
||||||
tx.ID = tx.Hash()
|
tx.ID = tx.Hash()
|
||||||
|
|
||||||
|
// Sign the transaction
|
||||||
UTXOSet.Blockchain.SignTransaction(&tx, wallet.PrivateKey)
|
UTXOSet.Blockchain.SignTransaction(&tx, wallet.PrivateKey)
|
||||||
|
|
||||||
return &tx
|
return &tx
|
||||||
|
|
|
@ -14,5 +14,5 @@ type TXInput struct {
|
||||||
func (in *TXInput) UsesKey(pubKeyHash []byte) bool {
|
func (in *TXInput) UsesKey(pubKeyHash []byte) bool {
|
||||||
lockingHash := HashPubKey(in.PubKey)
|
lockingHash := HashPubKey(in.PubKey)
|
||||||
|
|
||||||
return bytes.Compare(lockingHash, pubKeyHash) == 0
|
return bytes.Equal(lockingHash, pubKeyHash)
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ func (out *TXOutput) Lock(address []byte) {
|
||||||
|
|
||||||
// IsLockedWithKey checks if the output can be used by the owner of the pubkey
|
// IsLockedWithKey checks if the output can be used by the owner of the pubkey
|
||||||
func (out *TXOutput) IsLockedWithKey(pubKeyHash []byte) bool {
|
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
|
// NewTXOutput create a new TXOutput
|
||||||
|
|
35
utxo_set.go
35
utxo_set.go
|
@ -2,6 +2,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
"github.com/boltdb/bolt"
|
"github.com/boltdb/bolt"
|
||||||
|
@ -29,9 +30,16 @@ func (u UTXOSet) FindSpendableOutputs(pubkeyHash []byte, amount int) (int, map[s
|
||||||
outs := DeserializeOutputs(v)
|
outs := DeserializeOutputs(v)
|
||||||
|
|
||||||
for outIdx, out := range outs.Outputs {
|
for outIdx, out := range outs.Outputs {
|
||||||
if out.IsLockedWithKey(pubkeyHash) && accumulated < amount {
|
if out.IsLockedWithKey(pubkeyHash) {
|
||||||
accumulated += out.Value
|
if accumulated < amount {
|
||||||
unspentOutputs[txID] = append(unspentOutputs[txID], outIdx)
|
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)
|
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
|
return accumulated, unspentOutputs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,13 +63,20 @@ func (u UTXOSet) FindUTXO(pubKeyHash []byte) []TXOutput {
|
||||||
var UTXOs []TXOutput
|
var UTXOs []TXOutput
|
||||||
db := u.Blockchain.db
|
db := u.Blockchain.db
|
||||||
|
|
||||||
|
// Read-only transaction to view the database
|
||||||
err := db.View(func(tx *bolt.Tx) error {
|
err := db.View(func(tx *bolt.Tx) error {
|
||||||
b := tx.Bucket([]byte(utxoBucket))
|
b := tx.Bucket([]byte(utxoBucket))
|
||||||
|
if b == nil {
|
||||||
|
return fmt.Errorf("Bucket %s not found!", utxoBucket)
|
||||||
|
}
|
||||||
|
|
||||||
c := b.Cursor()
|
c := b.Cursor()
|
||||||
|
|
||||||
|
// Iterate through all keys in the bucket
|
||||||
for k, v := c.First(); k != nil; k, v = c.Next() {
|
for k, v := c.First(); k != nil; k, v = c.Next() {
|
||||||
outs := DeserializeOutputs(v)
|
outs := DeserializeOutputs(v)
|
||||||
|
|
||||||
|
// Filter outputs that are locked with the given public key hash
|
||||||
for _, out := range outs.Outputs {
|
for _, out := range outs.Outputs {
|
||||||
if out.IsLockedWithKey(pubKeyHash) {
|
if out.IsLockedWithKey(pubKeyHash) {
|
||||||
UTXOs = append(UTXOs, out)
|
UTXOs = append(UTXOs, out)
|
||||||
|
@ -119,7 +139,7 @@ func (u UTXOSet) Reindex() {
|
||||||
|
|
||||||
UTXO := u.Blockchain.FindUTXO()
|
UTXO := u.Blockchain.FindUTXO()
|
||||||
|
|
||||||
err = db.Update(func(tx *bolt.Tx) error {
|
_ = db.Update(func(tx *bolt.Tx) error {
|
||||||
b := tx.Bucket(bucketName)
|
b := tx.Bucket(bucketName)
|
||||||
|
|
||||||
for txID, outs := range UTXO {
|
for txID, outs := range UTXO {
|
||||||
|
@ -147,7 +167,7 @@ func (u UTXOSet) Update(block *Block) {
|
||||||
b := tx.Bucket([]byte(utxoBucket))
|
b := tx.Bucket([]byte(utxoBucket))
|
||||||
|
|
||||||
for _, tx := range block.Transactions {
|
for _, tx := range block.Transactions {
|
||||||
if tx.IsCoinbase() == false {
|
if !tx.IsCoinbase() {
|
||||||
for _, vin := range tx.Vin {
|
for _, vin := range tx.Vin {
|
||||||
updatedOuts := TXOutputs{}
|
updatedOuts := TXOutputs{}
|
||||||
outsBytes := b.Get(vin.Txid)
|
outsBytes := b.Get(vin.Txid)
|
||||||
|
@ -175,10 +195,7 @@ func (u UTXOSet) Update(block *Block) {
|
||||||
}
|
}
|
||||||
|
|
||||||
newOutputs := TXOutputs{}
|
newOutputs := TXOutputs{}
|
||||||
for _, out := range tx.Vout {
|
newOutputs.Outputs = append(newOutputs.Outputs, tx.Vout...)
|
||||||
newOutputs.Outputs = append(newOutputs.Outputs, out)
|
|
||||||
}
|
|
||||||
|
|
||||||
err := b.Put(tx.ID, newOutputs.Serialize())
|
err := b.Put(tx.ID, newOutputs.Serialize())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panic(err)
|
log.Panic(err)
|
||||||
|
|
86
wallet.go
86
wallet.go
|
@ -6,7 +6,10 @@ import (
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
|
"encoding/gob"
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
"golang.org/x/crypto/ripemd160"
|
"golang.org/x/crypto/ripemd160"
|
||||||
)
|
)
|
||||||
|
@ -63,7 +66,7 @@ func ValidateAddress(address string) bool {
|
||||||
pubKeyHash = pubKeyHash[1 : len(pubKeyHash)-addressChecksumLen]
|
pubKeyHash = pubKeyHash[1 : len(pubKeyHash)-addressChecksumLen]
|
||||||
targetChecksum := checksum(append([]byte{version}, pubKeyHash...))
|
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
|
// Checksum generates a checksum for a public key
|
||||||
|
@ -84,3 +87,84 @@ func newKeyPair() (ecdsa.PrivateKey, []byte) {
|
||||||
|
|
||||||
return *private, pubKey
|
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
|
||||||
|
}
|
||||||
|
|
10
wallets.go
10
wallets.go
|
@ -66,7 +66,7 @@ func (ws *Wallets) LoadFromFile(nodeID string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
var wallets Wallets
|
var wallets Wallets
|
||||||
gob.Register(elliptic.P256())
|
//gob.Register(elliptic.P256())
|
||||||
decoder := gob.NewDecoder(bytes.NewReader(fileContent))
|
decoder := gob.NewDecoder(bytes.NewReader(fileContent))
|
||||||
err = decoder.Decode(&wallets)
|
err = decoder.Decode(&wallets)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -80,17 +80,25 @@ func (ws *Wallets) LoadFromFile(nodeID string) error {
|
||||||
|
|
||||||
// SaveToFile saves wallets to a file
|
// SaveToFile saves wallets to a file
|
||||||
func (ws Wallets) SaveToFile(nodeID string) {
|
func (ws Wallets) SaveToFile(nodeID string) {
|
||||||
|
// Create a buffer to hold the serialized data
|
||||||
var content bytes.Buffer
|
var content bytes.Buffer
|
||||||
|
|
||||||
|
// Format the filename using the nodeID
|
||||||
walletFile := fmt.Sprintf(walletFile, nodeID)
|
walletFile := fmt.Sprintf(walletFile, nodeID)
|
||||||
|
|
||||||
|
// Register the elliptic curve with the gob package
|
||||||
gob.Register(elliptic.P256())
|
gob.Register(elliptic.P256())
|
||||||
|
|
||||||
|
// Create a new encoder that writes to the buffer
|
||||||
encoder := gob.NewEncoder(&content)
|
encoder := gob.NewEncoder(&content)
|
||||||
|
|
||||||
|
// Encode the Wallets object into the buffer
|
||||||
err := encoder.Encode(ws)
|
err := encoder.Encode(ws)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panic(err)
|
log.Panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Write the buffer's content to the file
|
||||||
err = ioutil.WriteFile(walletFile, content.Bytes(), 0644)
|
err = ioutil.WriteFile(walletFile, content.Bytes(), 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panic(err)
|
log.Panic(err)
|
||||||
|
|
Loading…
Reference in New Issue