From 2ce04f8f595ad4442a54cbeb7fcd026f657d2934 Mon Sep 17 00:00:00 2001 From: Ivan Kuznetsov Date: Sun, 10 Sep 2017 10:34:39 +0700 Subject: [PATCH] Implement transactions signing and verification --- blockchain.go | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++ transaction.go | 12 ++++++--- 2 files changed, 74 insertions(+), 4 deletions(-) diff --git a/blockchain.go b/blockchain.go index d644fe3..48709ae 100644 --- a/blockchain.go +++ b/blockchain.go @@ -1,7 +1,10 @@ package main import ( + "bytes" + "crypto/ecdsa" "encoding/hex" + "errors" "fmt" "log" "os" @@ -29,6 +32,12 @@ type BlockchainIterator struct { func (bc *Blockchain) MineBlock(transactions []*Transaction) { var lastHash []byte + for _, tx := range transactions { + if bc.VerifyTransaction(tx) != true { + log.Panic("ERROR: Invalid transaction") + } + } + err := bc.db.View(func(tx *bolt.Tx) error { b := tx.Bucket([]byte(blocksBucket)) lastHash = b.Get([]byte("l")) @@ -60,6 +69,63 @@ func (bc *Blockchain) MineBlock(transactions []*Transaction) { }) } +// FindTransaction finds a transaction by its ID +func (bc *Blockchain) FindTransaction(ID []byte) (Transaction, error) { + bci := bc.Iterator() + + for { + block := bci.Next() + + for _, tx := range block.Transactions { + if bytes.Compare(tx.ID, ID) == 0 { + return *tx, nil + } + } + + if len(block.PrevBlockHash) == 0 { + break + } + } + + return Transaction{}, errors.New("Transaction is not found") +} + +// SignTransaction signs a Transaction +func (bc *Blockchain) SignTransaction(tx *Transaction, privKey ecdsa.PrivateKey) { + prevTXs := make(map[string]Transaction) + + for _, vin := range tx.Vin { + prevTX, err := bc.FindTransaction(vin.Txid) + if err != nil { + log.Panic(err) + } + prevTXs[hex.EncodeToString(prevTX.ID)] = prevTX + } + + tx.Sign(privKey, prevTXs) +} + +// VerifyTransaction verifies transaction +func (bc *Blockchain) VerifyTransaction(tx *Transaction) bool { + prevTXs := make(map[string]Transaction) + + for _, vin := range tx.Vin { + prevTX, err := bc.FindTransaction(vin.Txid) + if err != nil { + log.Panic(err) + } + prevTXs[hex.EncodeToString(prevTX.ID)] = prevTX + } + + for _, tx := range prevTXs { + fmt.Println(tx) + } + // fmt.Println() + // fmt.Println(tx) + + return tx.Verify(prevTXs) +} + // FindUnspentTransactions returns a list of transactions containing unspent outputs func (bc *Blockchain) FindUnspentTransactions(pubKeyHash []byte) []Transaction { var unspentTXs []Transaction diff --git a/transaction.go b/transaction.go index 81bbf46..b7463f1 100644 --- a/transaction.go +++ b/transaction.go @@ -36,6 +36,7 @@ func (tx Transaction) String() string { lines = append(lines, fmt.Sprintf("Transaction %x:", tx.ID)) for i, input := range tx.Vin { + lines = append(lines, fmt.Sprintf(" Input %d:", i)) lines = append(lines, fmt.Sprintf(" TXID: %x", input.Txid)) lines = append(lines, fmt.Sprintf(" Out: %d", input.Vout)) @@ -84,13 +85,13 @@ func (tx *Transaction) TrimmedCopy() Transaction { } // Sign signs each input of a Transaction -func (tx *Transaction) Sign(privKey ecdsa.PrivateKey, prevTx *Transaction) { +func (tx *Transaction) Sign(privKey ecdsa.PrivateKey, prevTXs map[string]Transaction) { if tx.IsCoinbase() { return } for _, vin := range tx.Vin { - if bytes.Compare(vin.Txid, prevTx.ID) != 0 { + if prevTXs[hex.EncodeToString(vin.Txid)].ID == nil { log.Panic("ERROR: Previous transaction is not correct") } } @@ -98,6 +99,7 @@ func (tx *Transaction) Sign(privKey ecdsa.PrivateKey, prevTx *Transaction) { txCopy := tx.TrimmedCopy() for inID, vin := range txCopy.Vin { + prevTx := prevTXs[hex.EncodeToString(vin.Txid)] txCopy.Vin[inID].ScriptSig = prevTx.Vout[vin.Vout].ScriptPubKey txCopy.SetID() txCopy.Vin[inID].ScriptSig = []byte{} @@ -113,7 +115,7 @@ func (tx *Transaction) Sign(privKey ecdsa.PrivateKey, prevTx *Transaction) { } // Verify verifies signatures of Transaction inputs -func (tx *Transaction) Verify(prevTx *Transaction) bool { +func (tx *Transaction) Verify(prevTXs map[string]Transaction) bool { sigLen := 64 if tx.IsCoinbase() { @@ -121,7 +123,7 @@ func (tx *Transaction) Verify(prevTx *Transaction) bool { } for _, vin := range tx.Vin { - if bytes.Compare(vin.Txid, prevTx.ID) != 0 { + if prevTXs[hex.EncodeToString(vin.Txid)].ID == nil { log.Panic("ERROR: Previous transaction is not correct") } } @@ -130,6 +132,7 @@ func (tx *Transaction) Verify(prevTx *Transaction) bool { curve := elliptic.P256() for inID, vin := range tx.Vin { + prevTx := prevTXs[hex.EncodeToString(vin.Txid)] txCopy.Vin[inID].ScriptSig = prevTx.Vout[vin.Vout].ScriptPubKey txCopy.SetID() txCopy.Vin[inID].ScriptSig = []byte{} @@ -249,6 +252,7 @@ func NewUTXOTransaction(from, to string, amount int, bc *Blockchain) *Transactio tx := Transaction{nil, inputs, outputs} tx.SetID() + bc.SignTransaction(&tx, wallet.PrivateKey) return &tx }