From 49dd3c78a4c1adabd4d88fd782fee591f2d0a410 Mon Sep 17 00:00:00 2001 From: chadlagore Date: Sat, 3 Jun 2017 14:23:25 -0700 Subject: [PATCH 1/8] refactor; new validation files and test files --- blockchain/block_test.go | 8 ++ blockchain/blockchain.go | 85 +++--------------- blockchain/blockchain_test.go | 95 -------------------- blockchain/test_utils.go | 9 +- blockchain/transaction.go | 21 +++++ blockchain/validation.go | 106 +++++++++++++++++++++++ blockchain/validation_test.go | 157 ++++++++++++++++++++++++++++++++++ 7 files changed, 311 insertions(+), 170 deletions(-) create mode 100644 blockchain/validation.go create mode 100644 blockchain/validation_test.go diff --git a/blockchain/block_test.go b/blockchain/block_test.go index b420394..d11437f 100644 --- a/blockchain/block_test.go +++ b/blockchain/block_test.go @@ -17,3 +17,11 @@ func TestEncodeDecodeBlock(t *testing.T) { t.Fail() } } + +func TestContainsTransaction(t *testing.T) { + b := newBlock() + + if exists, _ := b.ContainsTransaction(b.Transactions[0]); !exists { + t.Fail() + } +} diff --git a/blockchain/blockchain.go b/blockchain/blockchain.go index 126aaa4..e0835f7 100644 --- a/blockchain/blockchain.go +++ b/blockchain/blockchain.go @@ -1,7 +1,6 @@ package blockchain import ( - "crypto/ecdsa" "encoding/gob" "io" ) @@ -42,77 +41,6 @@ func DecodeBlockChain(r io.Reader) *BlockChain { return &bc } -// ValidTransaction checks whether a transaction is valid, assuming the -// blockchain is valid. -func (bc *BlockChain) ValidTransaction(t *Transaction) bool { - - // Find the transaction input in the chain (by hash) - var input *Transaction - inputBlock := bc.Blocks[t.Input.BlockNumber] - for _, transaction := range inputBlock.Transactions { - if t.Input.Hash == HashSum(transaction) { - input = transaction - } - } - if input == nil { - return false - } - - // Check that output to sender in input is equal to outputs in t - var inAmount uint64 - for _, output := range input.Outputs { - if output.Recipient == t.Sender { - inAmount += output.Amount - } - } - var outAmount uint64 - for _, output := range t.Outputs { - outAmount += output.Amount - } - if inAmount != outAmount { - return false - } - - // Verify signature of t - hash := HashSum(t.TxBody) - if !ecdsa.Verify(t.Sender.Key(), hash.Marshal(), t.Sig.R, t.Sig.S) { - return false - } - - // Test if identical transaction already exists in chain. - endChain := uint32(len(bc.Blocks)) - for i := t.Input.BlockNumber; i < endChain; i++ { - if exists, _ := bc.Blocks[i].ContainsTransaction(t); exists { - return false - } - } - - return true -} - -// ValidBlock checks whether a block is valid -func (bc *BlockChain) ValidBlock(b *Block) bool { - // Check that block number is one greater than last block - lastBlock := bc.Blocks[b.BlockNumber-1] - if lastBlock.BlockNumber != b.BlockNumber-1 { - return false - } - - // Verify every Transaction in the block. - for _, t := range b.Transactions { - if !bc.ValidTransaction(t) { - return false - } - } - - // Check that hash of last block is correct - if HashSum(lastBlock) != b.LastBlock { - return false - } - - return true -} - // AppendBlock adds a block to the end of the block chain. func (bc *BlockChain) AppendBlock(b *Block, miner Address) { b.BlockNumber = uint32(len(bc.Blocks)) @@ -120,3 +48,16 @@ func (bc *BlockChain) AppendBlock(b *Block, miner Address) { b.Miner = miner bc.Blocks = append(bc.Blocks, b) } + +// GetInputTransaction returns the input Transaction to t. If the input does +// not exist, then GetInputTransaction returns nil. +func (bc *BlockChain) GetInputTransaction(t *Transaction) *Transaction { + if t.Input.BlockNumber > uint32(len(bc.Blocks)) { + return nil + } + b := bc.Blocks[t.Input.BlockNumber] + if t.Input.Index > uint32(len(b.Transactions)) { + return nil + } + return b.Transactions[t.Input.Index] +} diff --git a/blockchain/blockchain_test.go b/blockchain/blockchain_test.go index 584e3fe..6e704a4 100644 --- a/blockchain/blockchain_test.go +++ b/blockchain/blockchain_test.go @@ -2,7 +2,6 @@ package blockchain import ( "bytes" - crand "crypto/rand" "testing" log "github.com/Sirupsen/logrus" @@ -12,100 +11,6 @@ func TestMain(t *testing.T) { log.SetLevel(log.DebugLevel) } -func TestValidTransactionNotInBlock(t *testing.T) { - tr, _ := newTransactionValue(newWallet(), newWallet(), 1) - bc, _ := newValidBlockChainFixture() - - if bc.ValidTransaction(tr) { - t.Fail() - } -} - -func TestValidTransactionInputsFail(t *testing.T) { - // 2 + 2 = 5 ? - bc, _ := newValidBlockChainFixture() - tr := bc.Blocks[1].Transactions[0] - tr.Outputs[0].Amount = 5 - - if bc.ValidTransaction(tr) { - t.Fail() - } -} - -func TestValidTransactionSignatureFail(t *testing.T) { - bc, _ := newValidBlockChainFixture() - tr := bc.Blocks[1].Transactions[0] - - fakeSender := newWallet() - tr, _ = tr.TxBody.Sign(fakeSender, crand.Reader) - bc.Blocks[1].Transactions[0] = tr - - if bc.ValidTransaction(tr) { - t.Fail() - } -} - -func TestValidTransactionPass(t *testing.T) { - bc, b := newValidChainAndBlock() - tr := b.Transactions[0] - - if !bc.ValidTransaction(tr) { - t.Fail() - } -} - -func TestTransactionRespend(t *testing.T) { - bc, _ := newValidBlockChainFixture() - trC := bc.Blocks[1].Transactions[0] - b := newOutputBlock([]*Transaction{trC}, bc.Blocks[1]) - bc.AppendBlock(b, newWallet().Public()) - - if bc.ValidTransaction(trC) { - t.Fail() - } -} - -func TestValidBlockTransactionInvalid(t *testing.T) { - bc, _ := newValidBlockChainFixture() - tr := bc.Blocks[1].Transactions[0] - tr.Outputs[0].Amount = 5 - - if bc.ValidBlock(bc.Blocks[1]) { - t.Fail() - } -} - -func TestValidBlockNumberWrong(t *testing.T) { - bc, _ := newValidBlockChainFixture() - bc.Blocks[1].BlockNumber = 2 - - if bc.ValidBlock(bc.Blocks[1]) { - t.Fail() - } -} - -func TestValidBlockHashWrong(t *testing.T) { - bc, b := newValidChainAndBlock() - b.BlockHeader.LastBlock = newHash() - - if bc.ValidBlock(b) { - t.Fail() - } -} - -func TestValidBlock(t *testing.T) { - bc, b := newValidChainAndBlock() - - if !bc.ValidBlock(b) { - t.Fail() - } -} - -func TestBlockTwoInputs(t *testing.T) { - // block should fail to be valid if there exists two transactions - // referencing the same input, but output > input (double spend attack) -} - func TestEncodeDecodeBlockChain(t *testing.T) { b1 := newBlockChain() diff --git a/blockchain/test_utils.go b/blockchain/test_utils.go index c72dee5..25552e7 100644 --- a/blockchain/test_utils.go +++ b/blockchain/test_utils.go @@ -22,6 +22,7 @@ func newTxHashPointer() TxHashPointer { return TxHashPointer{ BlockNumber: mrand.Uint32(), Hash: newHash(), + Index: mrand.Uint32(), } } @@ -107,12 +108,13 @@ func newOutputBlock(t []*Transaction, input *Block) *Block { } } -func newTransactionValue(s, r Wallet, a uint64) (*Transaction, error) { +func newTransactionValue(s, r Wallet, a uint64, i uint32) (*Transaction, error) { tbody := TxBody{ Sender: s.Public(), Input: TxHashPointer{ BlockNumber: 0, Hash: newHash(), + Index: i, }, Outputs: make([]TxOutput, 1), } @@ -129,13 +131,13 @@ func newValidBlockChainFixture() (*BlockChain, Wallet) { sender := newWallet() recipient := newWallet() - trA, _ := newTransactionValue(original, sender, 2) + trA, _ := newTransactionValue(original, sender, 2, 1) trA.Outputs = append(trA.Outputs, TxOutput{ Amount: 2, Recipient: sender.Public(), }) - trB, _ := newTransactionValue(sender, recipient, 4) + trB, _ := newTransactionValue(sender, recipient, 4, 0) trB.Input.Hash = HashSum(trA) trB, _ = trB.TxBody.Sign(sender, crand.Reader) @@ -166,6 +168,7 @@ func newValidChainAndBlock() (*BlockChain, *Block) { Input: TxHashPointer{ BlockNumber: 1, Hash: HashSum(inputTransaction), + Index: 0, }, Outputs: make([]TxOutput, 1), } diff --git a/blockchain/transaction.go b/blockchain/transaction.go index 3688072..a27ded7 100644 --- a/blockchain/transaction.go +++ b/blockchain/transaction.go @@ -16,6 +16,7 @@ const ( type TxHashPointer struct { BlockNumber uint32 Hash Hash + Index uint32 } // Marshal converts a TxHashPointer to a byte slice @@ -88,3 +89,23 @@ func (t *Transaction) Marshal() []byte { buf = append(buf, t.Sig.Marshal()...) return buf } + +// InputsEqualOutputs returns true if t.Inputs == other.Outputs, as well +// as the difference between the two (outputs - inputs). +func (t *Transaction) InputsEqualOutputs(other ...*Transaction) (bool, int) { + var inAmount uint64 + for _, otherTransaction := range other { + for _, output := range otherTransaction.Outputs { + inAmount += output.Amount + } + } + + var outAmount uint64 + for _, output := range t.Outputs { + outAmount += output.Amount + } + + diff := int(outAmount) - int(inAmount) + + return diff != 0, diff +} diff --git a/blockchain/validation.go b/blockchain/validation.go new file mode 100644 index 0000000..ee11c98 --- /dev/null +++ b/blockchain/validation.go @@ -0,0 +1,106 @@ +package blockchain + +// ValidTransaction checks whether a transaction is valid, assuming the +import "crypto/ecdsa" + +// TransactionCode is returned from ValidTransaction. +type TransactionCode uint32 + +// BlockCode is returned from ValidBlock. +type BlockCode uint32 + +const ( + // ValidTransaction is returned when transaction is valid. + ValidTransaction TransactionCode = 0 + // NoInputTransaction is returned when transaction has no valid input transaction. + NoInputTransaction TransactionCode = 1 + // Overspend is returned when transaction outputs exceed transaction inputs. + Overspend TransactionCode = 2 + // BadSig is returned when the signature verification fails. + BadSig TransactionCode = 3 + // Respend is returned when inputs have been spent elsewhere in the chain. + Respend TransactionCode = 4 +) + +const ( + // ValidBlock is returned when the block is valid. + ValidBlock BlockCode = 0 + // BadTransaction is returned when the block contains an invalid + // transaction. + BadTransaction BlockCode = 1 + // BadBlockNumber is returned when block number is not one greater than + // previous block. + BadBlockNumber BlockCode = 2 + // BadHash is returned when the block contains incorrect hash. + BadHash BlockCode = 3 + // DoubleSpend is returned when two transactions in the block share inputs, + // but outputs > inputs. + DoubleSpend BlockCode = 4 +) + +// ValidTransaction tests whether a transaction valid. +func (bc *BlockChain) ValidTransaction(t *Transaction) (bool, TransactionCode) { + + // Find the transaction input in the chain (by hash) + var input *Transaction + input = bc.GetInputTransaction(t) + if input == nil || HashSum(input) != t.Input.Hash { + return false, NoInputTransaction + } + + // Check that output to sender in input is equal to outputs in t + if _, diff := input.InputsEqualOutputs(t); diff != 0 { + return false, Overspend + } + + // Verify signature of t + hash := HashSum(t.TxBody) + if !ecdsa.Verify(t.Sender.Key(), hash.Marshal(), t.Sig.R, t.Sig.S) { + return false, BadSig + } + + // Test if identical transaction already exists in chain. + endChain := uint32(len(bc.Blocks)) + for i := t.Input.BlockNumber; i < endChain; i++ { + if exists, _ := bc.Blocks[i].ContainsTransaction(t); exists { + return false, Respend + } + } + + return true, ValidTransaction +} + +// ValidBlock checks whether a block is valid. +func (bc *BlockChain) ValidBlock(b *Block) (bool, BlockCode) { + // Check that block number is one greater than last block + lastBlock := bc.Blocks[b.BlockNumber-1] + if lastBlock.BlockNumber != b.BlockNumber-1 { + return false, BadBlockNumber + } + + // Verify every Transaction in the block. + for _, t := range b.Transactions { + if valid, _ := bc.ValidTransaction(t); !valid { + return false, BadTransaction + } + } + + // Check that hash of last block is correct + if HashSum(lastBlock) != b.LastBlock { + return false, BadHash + } + + // Check for multiple transactions referencing same input transaction. + for i, trA := range b.Transactions { + for j, trB := range b.Transactions { + if (i != j) && (trA.Input.Hash == trB.Input.Hash) { + inputTr := bc.GetInputTransaction(trA) + if _, diff := inputTr.InputsEqualOutputs(trA, trB); diff < 0 { + return false, DoubleSpend + } + } + } + } + + return true, ValidBlock +} diff --git a/blockchain/validation_test.go b/blockchain/validation_test.go new file mode 100644 index 0000000..0c9e5c3 --- /dev/null +++ b/blockchain/validation_test.go @@ -0,0 +1,157 @@ +package blockchain + +import ( + crand "crypto/rand" + "fmt" + "testing" +) + +func TestValidTransactionNoInputTransaction(t *testing.T) { + tr, _ := newTransactionValue(newWallet(), newWallet(), 1, 0) + bc, _ := newValidBlockChainFixture() + + valid, code := bc.ValidTransaction(tr) + + if valid { + t.Fail() + } + if code != NoInputTransaction { + t.Fail() + } +} + +func TestValidTransactionOverspend(t *testing.T) { + // 2 + 2 = 5 ? + bc, _ := newValidBlockChainFixture() + tr := bc.Blocks[1].Transactions[0] + tr.Outputs[0].Amount = 5 + + valid, code := bc.ValidTransaction(tr) + + if valid { + t.Fail() + } + if code != Overspend { + fmt.Println(code) + t.Fail() + } +} + +func TestValidTransactionSignatureFail(t *testing.T) { + bc, _ := newValidBlockChainFixture() + tr := bc.Blocks[1].Transactions[0] + + fakeSender := newWallet() + tr, _ = tr.TxBody.Sign(fakeSender, crand.Reader) + bc.Blocks[1].Transactions[0] = tr + + valid, code := bc.ValidTransaction(tr) + if valid { + t.Fail() + } + if code != BadSig { + t.Fail() + } +} + +func TestValidTransactionPass(t *testing.T) { + bc, b := newValidChainAndBlock() + tr := b.Transactions[0] + + valid, code := bc.ValidTransaction(tr) + + if !valid { + t.Fail() + } + if code != ValidTransaction { + t.Fail() + } +} + +func TestTransactionRespend(t *testing.T) { + bc, _ := newValidBlockChainFixture() + trC := bc.Blocks[1].Transactions[0] + b := newOutputBlock([]*Transaction{trC}, bc.Blocks[1]) + bc.AppendBlock(b, newWallet().Public()) + + valid, code := bc.ValidTransaction(trC) + + if valid { + t.Fail() + } + if code != Respend { + t.Fail() + } +} + +func TestValidBlockBadTransactoion(t *testing.T) { + bc, _ := newValidBlockChainFixture() + tr := bc.Blocks[1].Transactions[0] + tr.Outputs[0].Amount = 5 + + valid, code := bc.ValidBlock(bc.Blocks[1]) + + if valid { + t.Fail() + } + if code != BadTransaction { + t.Fail() + } +} + +func TestValidBlocBadBlockNumber(t *testing.T) { + bc, _ := newValidBlockChainFixture() + bc.Blocks[1].BlockNumber = 2 + + valid, code := bc.ValidBlock(bc.Blocks[1]) + + if valid { + t.Fail() + } + if code != BadBlockNumber { + t.Fail() + } +} + +func TestValidBlockBadHash(t *testing.T) { + bc, b := newValidChainAndBlock() + b.BlockHeader.LastBlock = newHash() + + valid, code := bc.ValidBlock(b) + + if valid { + t.Fail() + } + if code != BadHash { + t.Fail() + } +} + +func TestValidBlock(t *testing.T) { + bc, b := newValidChainAndBlock() + + valid, code := bc.ValidBlock(b) + + if !valid { + t.Fail() + } + if code != ValidBlock { + t.Fail() + } +} + +func TestBlockDoubleSpend(t *testing.T) { + // block should fail to be valid if there exists two transactions + // referencing the same input, but output > input (double spend attack) + bc, b := newValidChainAndBlock() + b.Transactions = append(b.Transactions, b.Transactions[0]) + + valid, code := bc.ValidBlock(b) + + if valid { + t.Fail() + } + if code != DoubleSpend { + t.Fail() + } +} From 58ad84180454d5cbda5c47a870ea916b3186ed25 Mon Sep 17 00:00:00 2001 From: chadlagore Date: Sat, 3 Jun 2017 15:20:30 -0700 Subject: [PATCH 2/8] more methods --- blockchain/block.go | 8 ++++---- blockchain/blockchain.go | 11 +++++++++++ blockchain/validation.go | 9 ++++----- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/blockchain/block.go b/blockchain/block.go index 40d5162..0498248 100644 --- a/blockchain/block.go +++ b/blockchain/block.go @@ -73,11 +73,11 @@ func DecodeBlock(r io.Reader) *Block { // ContainsTransaction returns true and the transaction itself if the Block // contains the transaction. -func (b *Block) ContainsTransaction(t *Transaction) (bool, *Transaction) { - for _, tr := range b.Transactions { +func (b *Block) ContainsTransaction(t *Transaction) (bool, uint32) { + for i, tr := range b.Transactions { if HashSum(t) == HashSum(tr) { - return true, tr + return true, uint32(i) } } - return false, nil + return false, 0 } diff --git a/blockchain/blockchain.go b/blockchain/blockchain.go index e0835f7..6c3a058 100644 --- a/blockchain/blockchain.go +++ b/blockchain/blockchain.go @@ -61,3 +61,14 @@ func (bc *BlockChain) GetInputTransaction(t *Transaction) *Transaction { } return b.Transactions[t.Input.Index] } + +// ContainsTransaction returns true if the BlockChain contains the transaction +// in a block between start and stop as indexes. +func (bc *BlockChain) ContainsTransaction(t *Transaction, start, stop uint32) (bool, uint32, uint32) { + for i := start; i < stop; i++ { + if exists, j := bc.Blocks[i].ContainsTransaction(t); exists { + return true, i, j + } + } + return false, 0, 0 +} diff --git a/blockchain/validation.go b/blockchain/validation.go index ee11c98..4262f5a 100644 --- a/blockchain/validation.go +++ b/blockchain/validation.go @@ -60,11 +60,10 @@ func (bc *BlockChain) ValidTransaction(t *Transaction) (bool, TransactionCode) { } // Test if identical transaction already exists in chain. - endChain := uint32(len(bc.Blocks)) - for i := t.Input.BlockNumber; i < endChain; i++ { - if exists, _ := bc.Blocks[i].ContainsTransaction(t); exists { - return false, Respend - } + end := uint32(len(bc.Blocks)) + start := t.Input.BlockNumber + if exists, _, _ := bc.ContainsTransaction(t, start, end); exists { + return false, Respend } return true, ValidTransaction From 0e6e5bbf7bab48f3ff64eaab733442273054ed6a Mon Sep 17 00:00:00 2001 From: chadlagore Date: Sat, 3 Jun 2017 15:54:01 -0700 Subject: [PATCH 3/8] iota --- blockchain/validation.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/blockchain/validation.go b/blockchain/validation.go index 4262f5a..ac41b3f 100644 --- a/blockchain/validation.go +++ b/blockchain/validation.go @@ -11,31 +11,31 @@ type BlockCode uint32 const ( // ValidTransaction is returned when transaction is valid. - ValidTransaction TransactionCode = 0 + ValidTransaction TransactionCode = iota // NoInputTransaction is returned when transaction has no valid input transaction. - NoInputTransaction TransactionCode = 1 + NoInputTransaction TransactionCode = iota // Overspend is returned when transaction outputs exceed transaction inputs. - Overspend TransactionCode = 2 + Overspend TransactionCode = iota // BadSig is returned when the signature verification fails. - BadSig TransactionCode = 3 + BadSig TransactionCode = iota // Respend is returned when inputs have been spent elsewhere in the chain. - Respend TransactionCode = 4 + Respend TransactionCode = iota ) const ( // ValidBlock is returned when the block is valid. - ValidBlock BlockCode = 0 + ValidBlock BlockCode = iota // BadTransaction is returned when the block contains an invalid // transaction. - BadTransaction BlockCode = 1 + BadTransaction BlockCode = iota // BadBlockNumber is returned when block number is not one greater than // previous block. - BadBlockNumber BlockCode = 2 + BadBlockNumber BlockCode = iota // BadHash is returned when the block contains incorrect hash. - BadHash BlockCode = 3 + BadHash BlockCode = iota // DoubleSpend is returned when two transactions in the block share inputs, // but outputs > inputs. - DoubleSpend BlockCode = 4 + DoubleSpend BlockCode = iota ) // ValidTransaction tests whether a transaction valid. From c0f998c8577f16354c7fd1d1d7d51c992ddb086d Mon Sep 17 00:00:00 2001 From: chadlagore Date: Sat, 3 Jun 2017 15:57:50 -0700 Subject: [PATCH 4/8] remove diff --- blockchain/transaction.go | 6 ++---- blockchain/validation.go | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/blockchain/transaction.go b/blockchain/transaction.go index a27ded7..b813868 100644 --- a/blockchain/transaction.go +++ b/blockchain/transaction.go @@ -92,7 +92,7 @@ func (t *Transaction) Marshal() []byte { // InputsEqualOutputs returns true if t.Inputs == other.Outputs, as well // as the difference between the two (outputs - inputs). -func (t *Transaction) InputsEqualOutputs(other ...*Transaction) (bool, int) { +func (t *Transaction) InputsEqualOutputs(other ...*Transaction) bool { var inAmount uint64 for _, otherTransaction := range other { for _, output := range otherTransaction.Outputs { @@ -105,7 +105,5 @@ func (t *Transaction) InputsEqualOutputs(other ...*Transaction) (bool, int) { outAmount += output.Amount } - diff := int(outAmount) - int(inAmount) - - return diff != 0, diff + return (int(outAmount) - int(inAmount)) != 0 } diff --git a/blockchain/validation.go b/blockchain/validation.go index ac41b3f..8668949 100644 --- a/blockchain/validation.go +++ b/blockchain/validation.go @@ -49,7 +49,7 @@ func (bc *BlockChain) ValidTransaction(t *Transaction) (bool, TransactionCode) { } // Check that output to sender in input is equal to outputs in t - if _, diff := input.InputsEqualOutputs(t); diff != 0 { + if input.InputsEqualOutputs(t) { return false, Overspend } @@ -94,7 +94,7 @@ func (bc *BlockChain) ValidBlock(b *Block) (bool, BlockCode) { for j, trB := range b.Transactions { if (i != j) && (trA.Input.Hash == trB.Input.Hash) { inputTr := bc.GetInputTransaction(trA) - if _, diff := inputTr.InputsEqualOutputs(trA, trB); diff < 0 { + if inputTr.InputsEqualOutputs(trA, trB) { return false, DoubleSpend } } From a23b0f56b63bc40138faf2489ff61442ac8d1ee3 Mon Sep 17 00:00:00 2001 From: chadlagore Date: Sat, 3 Jun 2017 16:00:49 -0700 Subject: [PATCH 5/8] crand --- blockchain/validation_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blockchain/validation_test.go b/blockchain/validation_test.go index 0c9e5c3..ffbaeda 100644 --- a/blockchain/validation_test.go +++ b/blockchain/validation_test.go @@ -1,7 +1,7 @@ package blockchain import ( - crand "crypto/rand" + "crypto/rand" "fmt" "testing" ) @@ -42,7 +42,7 @@ func TestValidTransactionSignatureFail(t *testing.T) { tr := bc.Blocks[1].Transactions[0] fakeSender := newWallet() - tr, _ = tr.TxBody.Sign(fakeSender, crand.Reader) + tr, _ = tr.TxBody.Sign(fakeSender, rand.Reader) bc.Blocks[1].Transactions[0] = tr valid, code := bc.ValidTransaction(tr) From 3c1b3ab1c20c7c0ca026eaf6c46f4719c8b82eea Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Sat, 3 Jun 2017 16:20:18 -0700 Subject: [PATCH 6/8] add connection listener and dialer (#53) * add connection listener and dialer * make test consistent --- conn/conn.go | 25 ++++++++++++++++++++++ conn/conn_test.go | 53 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 conn/conn.go create mode 100644 conn/conn_test.go diff --git a/conn/conn.go b/conn/conn.go new file mode 100644 index 0000000..c6d7975 --- /dev/null +++ b/conn/conn.go @@ -0,0 +1,25 @@ +package conn + +import "net" + +// Dial opens a connection to a remote host. `host` should be a string +// in the format |: +func Dial(host string) (net.Conn, error) { + return net.Dial("tcp", host) +} + +// Listen binds to a TCP port and waits for incoming connections. +// When a connection is accepted, dispatches to the handler. +func Listen(iface string, handler func(net.Conn)) error { + listener, err := net.Listen("tcp", iface) + if err != nil { + return err + } + for { + c, err := listener.Accept() + if err != nil { + return err + } + go handler(c) + } +} diff --git a/conn/conn_test.go b/conn/conn_test.go new file mode 100644 index 0000000..32f30fc --- /dev/null +++ b/conn/conn_test.go @@ -0,0 +1,53 @@ +package conn + +import ( + "fmt" + "net" + "sync" + "testing" + "time" +) + +func TestConnect(t *testing.T) { + _, err := Dial("www.google.ca:80") + if err != nil { + fmt.Println(err.Error()) + t.Fail() + } +} + +func TestListen(t *testing.T) { + wg := sync.WaitGroup{} + wg.Add(5) + + handler := func(c net.Conn) { + defer c.Close() + buf := make([]byte, 1) + n, err := c.Read(buf) + if err != nil { + t.Fail() + } + if n != 1 { + t.Fail() + } + wg.Done() + } + + go Listen(":8080", handler) + // Sleep to guarantee that our listener is ready when we start making connections + time.Sleep(time.Millisecond) + + for i := 0; i < 5; i++ { + go func() { + c, err := Dial(":8080") + if err != nil { + t.Fail() + return + } + c.Write([]byte{byte(i)}) + }() + } + + wg.Wait() + fmt.Println("ending") +} From 7e25a1026f73232ac91f0826c8221efa4c89fa18 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Sat, 3 Jun 2017 16:24:03 -0700 Subject: [PATCH 7/8] 45 cobra cli (#46) * install cobra app * add cobra scaffolding * cobra app * add lockfile * add send command skeleton * remvoe licence and add cmd description --- cmd/root.go | 58 ++++++++++++++++++++++++++++++++++++ cmd/run.go | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++ cmd/send.go | 33 +++++++++++++++++++++ glide.lock | 46 +++++++++++++++++++++++++++-- glide.yaml | 2 ++ main.go | 61 ++------------------------------------ 6 files changed, 223 insertions(+), 62 deletions(-) create mode 100644 cmd/root.go create mode 100644 cmd/run.go create mode 100644 cmd/send.go diff --git a/cmd/root.go b/cmd/root.go new file mode 100644 index 0000000..4bbabea --- /dev/null +++ b/cmd/root.go @@ -0,0 +1,58 @@ +package cmd + +import ( + "fmt" + "os" + + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +var cfgFile string + +// RootCmd represents the base command when called without any subcommands +var RootCmd = &cobra.Command{ + Use: "cumulus", + Short: "Cumulus is an interface to interacting with the Cumulus network", + Long: `Cumulus is used to create mining or verification nodes on the Cumulus network, + create transactions, and check the balance of wallets.`, +} + +// Execute adds all child commands to the root command sets flags appropriately. +// This is called by main.main(). It only needs to happen once to the rootCmd. +func Execute() { + if err := RootCmd.Execute(); err != nil { + fmt.Println(err) + os.Exit(-1) + } +} + +func init() { + cobra.OnInitialize(initConfig) + + // Here you will define your flags and configuration settings. + // Cobra supports Persistent Flags, which, if defined here, + // will be global for your application. + + RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.cumulus.yaml)") + // Cobra also supports local flags, which will only run + // when this action is called directly. + // RootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") +} + +// initConfig reads in config file and ENV variables if set. +func initConfig() { + if cfgFile != "" { // enable ability to specify config file via flag + viper.SetConfigFile(cfgFile) + } + + viper.SetConfigName(".cumulus") // name of config file (without extension) + viper.AddConfigPath(".") // add cwd as first search path + viper.AddConfigPath("$HOME") // adding home directory as second search path + viper.AutomaticEnv() // read in environment variables that match + + // If a config file is found, read it in. + if err := viper.ReadInConfig(); err == nil { + fmt.Println("Using config file:", viper.ConfigFileUsed()) + } +} diff --git a/cmd/run.go b/cmd/run.go new file mode 100644 index 0000000..22783d1 --- /dev/null +++ b/cmd/run.go @@ -0,0 +1,85 @@ +package cmd + +import ( + "io/ioutil" + + log "github.com/Sirupsen/logrus" + "github.com/spf13/cobra" + "github.com/ubclaunchpad/cumulus/peer" +) + +// runCmd represents the run command +var runCmd = &cobra.Command{ + Use: "run", + Short: "Run creates and runs a node on the Cumulus network", + Long: `Run creates a new Cumulus node and connects to the specified target node. + If a target is not provided, listen for incoming connections.`, + Run: func(cmd *cobra.Command, args []string) { + port, _ := cmd.Flags().GetInt("port") + ip, _ := cmd.Flags().GetString("interface") + target, _ := cmd.Flags().GetString("target") + verbose, _ := cmd.Flags().GetBool("verbose") + run(port, ip, target, verbose) + }, +} + +func init() { + RootCmd.AddCommand(runCmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // runCmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + runCmd.Flags().IntP("port", "p", peer.DefaultPort, "Port to bind to") + runCmd.Flags().StringP("interface", "i", peer.DefaultIP, "IP address to listen on") + runCmd.Flags().StringP("target", "t", "", "Multiaddress of peer to connect to") + runCmd.Flags().BoolP("verbose", "v", false, "Enable verbose logging") +} + +func run(port int, ip, target string, verbose bool) { + log.Info("Starting Cumulus Peer") + + if verbose { + log.SetLevel(log.DebugLevel) + } + + // Set up a new host on the Cumulus network + host, err := peer.New(ip, port) + if err != nil { + log.Fatal(err) + } + + // Set the host StreamHandler for the Cumulus Protocol and use + // BasicStreamHandler as its StreamHandler. + host.SetStreamHandler(peer.CumulusProtocol, host.Receive) + if target == "" { + // No target was specified, wait for incoming connections + log.Info("No target provided. Listening for incoming connections...") + select {} // Hang until someone connects to us + } + + stream, err := host.Connect(target) + if err != nil { + log.WithError(err).Fatal("Error connecting to target: ", target) + } + + // Send a message to the peer + _, err = stream.Write([]byte("Hello, world!")) + if err != nil { + log.WithError(err).Fatal("Error sending a message to the peer") + } + + // Read the reply from the peer + reply, err := ioutil.ReadAll(stream) + if err != nil { + log.WithError(err).Fatal("Error reading a message from the peer") + } + + log.Debugf("Peer %s read reply: %s", host.ID(), string(reply)) + + host.Close() +} diff --git a/cmd/send.go b/cmd/send.go new file mode 100644 index 0000000..2c24b6b --- /dev/null +++ b/cmd/send.go @@ -0,0 +1,33 @@ +package cmd + +import ( + "fmt" + + "github.com/spf13/cobra" +) + +// sendCmd represents the send command +var sendCmd = &cobra.Command{ + Use: "send", + Short: "Send creates a transaction", + Long: `Send creates a transaction and broadcasts it to the network.`, + Run: func(cmd *cobra.Command, args []string) { + // TODO: Work your own magic here + fmt.Println("send called") + }, +} + +func init() { + RootCmd.AddCommand(sendCmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // sendCmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + // sendCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") + +} diff --git a/glide.lock b/glide.lock index 9522746..9dee4c9 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ -hash: 474d608bbb499683dec7db95f6a10f5df61bd5d447651e04567c0d10b816cfe3 -updated: 2017-05-13T15:17:47.793598435-07:00 +hash: 416d5c866bf090f534cd568c92cf41d6cedb8ac5245a33e4074000ce61e72f3b +updated: 2017-06-03T10:30:04.32905091-07:00 imports: - name: github.com/agl/ed25519 version: 5312a61534124124185d41f09206b9fef1d88403 @@ -20,6 +20,8 @@ imports: - spdy - name: github.com/fd/go-nat version: dcaf50131e4810440bed2cbb6f7f32c4f4cc95dd +- name: github.com/fsnotify/fsnotify + version: 4da3e2cfbabc9f751898f250b49f2439785783a1 - name: github.com/gogo/protobuf version: 30433562cfbf487fe1df7cd26c7bab168d2f14d0 subpackages: @@ -29,6 +31,17 @@ imports: version: 80a92cca79a8041496ccc9dd773fcb52a57ec6f9 - name: github.com/gxed/GoEndian version: 0f5c6873267e5abf306ffcdfcfa4bf77517ef4a7 +- name: github.com/hashicorp/hcl + version: 392dba7d905ed5d04a5794ba89f558b27e2ba1ca + subpackages: + - hcl/ast + - hcl/parser + - hcl/token + - json/parser + - hcl/scanner + - hcl/strconv + - json/scanner + - json/token - name: github.com/hashicorp/yamux version: d1caa6c97c9fc1cc9e83bbe34d0603f9ff0ce8bd - name: github.com/huin/goupnp @@ -40,6 +53,8 @@ imports: - scpd - soap - ssdp +- name: github.com/inconshreveable/mousetrap + version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75 - name: github.com/ipfs/go-ipfs-util version: f25fcc891281327394bb48000ef0970d11baff2b - name: github.com/ipfs/go-log @@ -125,6 +140,10 @@ imports: version: 0b4003a6faf41b1dfeb766799eed376301e168e7 - name: github.com/libp2p/go-tcp-transport version: 9be1dfb53993f98f70abd55dba6175d83961d330 +- name: github.com/magiconair/properties + version: 51463bfca2576e06c62a8504b5c0f06d61312647 +- name: github.com/mitchellh/mapstructure + version: d0303fe809921458f417bcf828397a65db30a7e4 - name: github.com/multiformats/go-multiaddr version: 33741da7b3f5773a599d4a03c333704fc560ef34 - name: github.com/multiformats/go-multiaddr-net @@ -133,12 +152,30 @@ imports: version: 7aa9f26a231c6f34f4e9fad52bf580fd36627285 - name: github.com/multiformats/go-multistream version: b8f1996688ab586031517919b49b1967fca8d5d9 +- name: github.com/pelletier/go-buffruneio + version: c37440a7cf42ac63b919c752ca73a85067e05992 +- name: github.com/pelletier/go-toml + version: fe7536c3dee2596cdd23ee9976a17c22bdaae286 - name: github.com/satori/go.uuid version: 5bf94b69c6b68ee1b541973bb8e1144db23a194b - name: github.com/Sirupsen/logrus version: acfabf31db8f45a9174f54a0d48ea4d15627af4d - name: github.com/spaolacci/murmur3 version: 0d12bf811670bf6a1a63828dfbd003eded177fce +- name: github.com/spf13/afero + version: 9be650865eab0c12963d8753212f4f9c66cdcf12 + subpackages: + - mem +- name: github.com/spf13/cast + version: acbeb36b902d72a7a4c18e8f3241075e7ab763e4 +- name: github.com/spf13/cobra + version: 8d4ce3549a0bf0e3569df3aae7423b7743cd05a9 +- name: github.com/spf13/jwalterweatherman + version: 0efa5202c04663c757d84f90f5219c1250baf94f +- name: github.com/spf13/pflag + version: e57e3eeb33f795204c1ca35f56c44f83227c6e66 +- name: github.com/spf13/viper + version: 0967fc9aceab2ce9da34061253ac10fb99bba5b2 - name: github.com/whyrusleeping/go-logging version: 0a5b4a6decf577ce8293eca85ec733d7ab92d742 - name: github.com/whyrusleeping/go-metrics @@ -181,10 +218,11 @@ imports: - name: golang.org/x/text version: 19e51611da83d6be54ddafce4a4af510cb3e9ea4 subpackages: + - transform + - unicode/norm - encoding - encoding/charmap - encoding/htmlindex - - transform - encoding/internal/identifier - encoding/internal - encoding/japanese @@ -196,6 +234,8 @@ imports: - internal/utf8internal - runes - internal/tag +- name: gopkg.in/yaml.v2 + version: cd8b52f8269e0feb286dfeef29f8fe4d5b397e0b - name: leb.io/hashland version: e13accbe55f7fa03c73c74ace4cca4c425e47260 subpackages: diff --git a/glide.yaml b/glide.yaml index 6b0b13f..603b3e0 100644 --- a/glide.yaml +++ b/glide.yaml @@ -11,3 +11,5 @@ import: - p2p/host/basic - package: github.com/multiformats/go-multiaddr - package: github.com/Sirupsen/logrus +- package: github.com/spf13/cobra +- package: github.com/spf13/pflag diff --git a/main.go b/main.go index 70c9ca0..527d16e 100644 --- a/main.go +++ b/main.go @@ -1,64 +1,7 @@ package main -import ( - "flag" - "io/ioutil" - - log "github.com/Sirupsen/logrus" - "github.com/ubclaunchpad/cumulus/peer" -) +import "github.com/ubclaunchpad/cumulus/cmd" func main() { - log.Info("Starting Cumulus Peer") - - // Get and parse command line arguments - // targetPeer is a Multiaddr representing the target peer to connect to - // when joining the Cumulus Network. - // port is the port to communicate over (defaults to peer.DefaultPort). - // ip is the public IP address of the this host. - targetPeer := flag.String("t", "", "target peer to connect to") - port := flag.Int("p", peer.DefaultPort, "TCP port to use for this host") - ip := flag.String("i", peer.DefaultIP, "IP address to use for this host") - debug := flag.Bool("d", false, "Enable debug logging") - flag.Parse() - - if *debug { - log.SetLevel(log.DebugLevel) - } - - // Set up a new host on the Cumulus network - host, err := peer.New(*ip, *port) - if err != nil { - log.Fatal(err) - } - - // Set the host StreamHandler for the Cumulus Protocol and use - // BasicStreamHandler as its StreamHandler. - host.SetStreamHandler(peer.CumulusProtocol, host.Receive) - if *targetPeer == "" { - // No target was specified, wait for incoming connections - log.Info("No target provided. Listening for incoming connections...") - select {} // Hang until someone connects to us - } - - stream, err := host.Connect(*targetPeer) - if err != nil { - log.Fatal(err) - } - - // Send a message to the peer - _, err = stream.Write([]byte("Hello, world!")) - if err != nil { - log.Fatal(err) - } - - // Read the reply from the peer - reply, err := ioutil.ReadAll(stream) - if err != nil { - log.Fatal(err) - } - - log.Debugf("Peer %s read reply: %s", host.ID(), string(reply)) - - host.Close() + cmd.Execute() } From 92e22a6b1e3d5ebfd68761a58d5388e3c3eb6338 Mon Sep 17 00:00:00 2001 From: chadlagore Date: Sat, 3 Jun 2017 18:32:07 -0700 Subject: [PATCH 8/8] ive made a terrible mistake --- blockchain/transaction.go | 2 +- blockchain/validation.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/blockchain/transaction.go b/blockchain/transaction.go index b813868..c3977d5 100644 --- a/blockchain/transaction.go +++ b/blockchain/transaction.go @@ -105,5 +105,5 @@ func (t *Transaction) InputsEqualOutputs(other ...*Transaction) bool { outAmount += output.Amount } - return (int(outAmount) - int(inAmount)) != 0 + return (int(outAmount) - int(inAmount)) == 0 } diff --git a/blockchain/validation.go b/blockchain/validation.go index 8668949..f7db8d2 100644 --- a/blockchain/validation.go +++ b/blockchain/validation.go @@ -49,7 +49,7 @@ func (bc *BlockChain) ValidTransaction(t *Transaction) (bool, TransactionCode) { } // Check that output to sender in input is equal to outputs in t - if input.InputsEqualOutputs(t) { + if !input.InputsEqualOutputs(t) { return false, Overspend } @@ -94,7 +94,7 @@ func (bc *BlockChain) ValidBlock(b *Block) (bool, BlockCode) { for j, trB := range b.Transactions { if (i != j) && (trA.Input.Hash == trB.Input.Hash) { inputTr := bc.GetInputTransaction(trA) - if inputTr.InputsEqualOutputs(trA, trB) { + if !inputTr.InputsEqualOutputs(trA, trB) { return false, DoubleSpend } }