func TestEncodeDecode(t *testing.T) { privateKey := generateKey(t) txn1, rand := trans.NewNameReservation("my-new-repository") txn1e := trans.NewEnvelope(types.EmptyHash(), txn1) txn2, _ := trans.NewNameAllocation("my-new-repository", rand) txn2e := trans.NewEnvelope(types.EmptyHash(), txn2) txn3, _ := trans.NewNameDeallocation("my-new-repository") txn3e := trans.NewEnvelope(types.EmptyHash(), txn3) txn1e.Sign(privateKey) txn2e.Sign(privateKey) txn3e.Sign(privateKey) transactions := []*trans.Envelope{txn1e, txn2e, txn3e} block, err := NewBlock(types.EmptyHash(), HIGHEST_TARGET, transactions) if err != nil { t.Errorf("can't create a block because of %v", err) } enc, err := block.Encode() if err != nil { t.Errorf("error while encoding block: %v", err) } block1, err := Decode(enc) if err != nil { t.Errorf("error while encoding block: %v", err) } assert.Equal(t, block, block1, "encoded and decoded block should be identical to the original one") }
func TestListRepository(t *testing.T) { db, err := NewDB("test.db") defer os.Remove("test.db") if err != nil { t.Errorf("error opening database: %v", err) } repo := repository.NewRepository("test", repository.PENDING, types.EmptyHash()) err = db.PutRepository(repo) if err != nil { t.Errorf("error putting repository: %v", err) } repo1 := repository.NewRepository("hello_world", repository.ACTIVE, types.EmptyHash()) err = db.PutRepository(repo1) if err != nil { t.Errorf("error putting repository: %v", err) } actualRepositories := db.ListRepositories() sort.Strings(actualRepositories) expectedRepositories := []string{"test", "hello_world"} sort.Strings(expectedRepositories) assert.Equal(t, actualRepositories, expectedRepositories) }
func TestNewBlockNoTx(t *testing.T) { transactions := []*trans.Envelope{} block1, err := NewBlock(types.EmptyHash(), HIGHEST_TARGET, transactions) if err != nil { t.Errorf("can't create a block because of %v", err) } assert.Equal(t, transactions, block1.Transactions) assert.Equal(t, block1.MerkleRootHash, types.EmptyHash()) }
func TestGetNextBlock(t *testing.T) { transactions, _ := fixtureSampleTransactions(t) blk, err := block.NewBlock(types.EmptyHash(), block.HIGHEST_TARGET, transactions) if err != nil { t.Errorf("can't create a block because of %v", err) } blk1, err := block.NewBlock(blk.Hash(), block.HIGHEST_TARGET, []*transaction.Envelope{}) if err != nil { t.Errorf("can't create a block because of %v", err) } db, err := NewDB("test.db") defer os.Remove("test.db") if err != nil { t.Errorf("error opening database: %v", err) } err = db.PutBlock(blk, false) if err != nil { t.Errorf("error putting block: %v", err) } blk_, err := db.GetNextBlock(types.EmptyHash()) if err != nil { t.Errorf("error getting next block: %v", err) } assert.Equal(t, blk, blk_) err = db.PutBlock(blk1, false) if err != nil { t.Errorf("error putting block: %v", err) } blk1_, err := db.GetNextBlock(blk.Hash()) if err != nil { t.Errorf("error getting next block: %v", err) } assert.Equal(t, blk1_, blk1) block0, err := db.GetBlock(types.EmptyHash()) if err == nil { t.Errorf("error getting zero block (no error thrown)") } if block0 != nil { t.Errorf("error getting zero block") } assert.True(t, nil == block0) }
func TestNewBlockSingleTx(t *testing.T) { privateKey := generateKey(t) txn1, _ := trans.NewNameReservation("my-new-repository", &privateKey.PublicKey) transactions := []trans.T{txn1} block1, err := NewBlock(types.EmptyHash(), HIGHEST_TARGET, transactions) if err != nil { t.Errorf("can't create a block because of %v", err) return } assert.Equal(t, transactions, block1.Transactions) assert.NotEqual(t, block1.MerkleRootHash, types.EmptyHash()) }
func TestListRefs(t *testing.T) { db, err := NewDB("test.db") defer os.Remove("test.db") if err != nil { t.Errorf("error opening database: %v", err) } repo := repository.NewRepository("myrepo", repository.ACTIVE, types.EmptyHash()) err = db.PutRepository(repo) if err != nil { t.Errorf("error putting repository: %v", err) } ref := util.SHA160([]byte("random")) err = db.PutRef("myrepo", "refs/heads/master", ref) if err != nil { t.Errorf("error putting repository ref: %v", err) } err = db.PutRef("myrepo", "refs/heads/next", ref) if err != nil { t.Errorf("error putting repository ref: %v", err) } refs, err := db.ListRefs("myrepo") if err != nil { t.Errorf("error listing repository refs: %v", err) } assert.Equal(t, refs, []string{"refs/heads/master", "refs/heads/next"}) }
func NewBlock(previousBlockHash types.Hash, bits uint32, transactions []*trans.Envelope) (*Block, error) { encodedTransactions := make([][]byte, len(transactions)) for i := range transactions { t, _ := transactions[i].Encode() encodedTransactions[i] = make([]byte, len(t)) copy(encodedTransactions[i], t) } var merkleRootHash types.Hash var err error if len(encodedTransactions) > 0 { merkleRootHash, err = merkleRoot(encodedTransactions) } else { merkleRootHash = types.EmptyHash() } if err != nil { return nil, err } return &Block{ Version: BLOCK_VERSION, PreviousBlockHash: previousBlockHash, MerkleRootHash: merkleRootHash, Timestamp: time.Now().UTC().Unix(), Bits: bits, Nonce: 0, Transactions: transactions}, nil }
func MiningFactory(srv *context.T) { log := srv.Log.New("cmp", "mining") var status MiningStatus n := 1 // TODO: in the future: srv.Config.Mining.Processes minedCh := make(chan *block.Block) for i := 0; i < n; i++ { ch := make(chan *block.Block) status.Miners = append(status.Miners, Miner{signallingChannel: ch}) go block.Miner(ch, minedCh) } ch := srv.Router.Sub("/transaction") transactionsPool := make([]*transaction.Envelope, 0) var previousBlockHash types.Hash // Setup previous block hash if blk, _ := srv.DB.GetLastBlock(); blk == nil { previousBlockHash = types.EmptyHash() } else { previousBlockHash = blk.Hash() } loop: select { case txni := <-ch: if txn, ok := txni.(*transaction.Envelope); ok { transactionsPool = append(transactionsPool, txn) stopMiners(status) mineBlock(status, srv, log, previousBlockHash, transactionsPool) } case blk := <-minedCh: if bytes.Compare(blk.PreviousBlockHash, previousBlockHash) == 0 { log.Debug("mined", "block", blk) for i := range blk.Transactions { for j := range transactionsPool { if bytes.Compare(transactionsPool[j].Hash(), blk.Transactions[i].Hash()) == 0 { transactionsPool = append(transactionsPool[0:j], transactionsPool[j+1:]...) } } } previousBlockHash = blk.Hash() stopMiners(status) err := srv.DB.PutBlock(blk, true) if err != nil { log.Error("error while serializing block", "block", blk.Hash(), "err", err) } else { srv.Router.Pub(blk, "/block", "/block/last") mineBlock(status, srv, log, previousBlockHash, transactionsPool) } } case reqi := <-miningFactoryRequests: if req, ok := reqi.(*MiningFactoryStatusRequest); ok { req.ResponseChannel <- status } case <-time.After(time.Second * 1): if key, _ := srv.DB.GetMainKey(); key != nil && len(transactionsPool) == 0 && status.AvailableMiners() == n { mineBlock(status, srv, log, previousBlockHash, make([]*transaction.Envelope, 0)) } } goto loop }
func TestGetTransactionBlock(t *testing.T) { transactions := fixtureSampleTransactions(t) block, err := block.NewBlock(types.EmptyHash(), block.HIGHEST_TARGET, transactions) if err != nil { t.Errorf("can't create a block because of %v", err) } db, err := NewDB("test.db") defer os.Remove("test.db") if err != nil { t.Errorf("error opening database: %v", err) } err = db.PutBlock(block, false) if err != nil { t.Errorf("error putting block: %v", err) } // Block<->transaction indexing for i := range transactions { block1, err := db.GetTransactionBlock(transactions[i].Hash()) if err != nil { t.Errorf("error getting transaction's block: %v", err) } assert.Equal(t, block, block1) } }
func merkleRoot(data [][]byte) (types.Hash, error) { tree := merkle.NewTree() err := tree.Generate(data, fastsha256.New()) if err != nil { return types.EmptyHash(), err } return tree.Root().Hash, err }
func merkleRoot(data [][]byte) (types.Hash, error) { if len(data) == 1 { // FIXME: a workaround for trees with one element data = append(data, []byte{}) } tree := merkle.NewTree() err := tree.Generate(data, fastsha256.New()) if err != nil { return types.EmptyHash(), err } return types.NewHash(tree.Root().Hash), err }
func TestPutGetBlock(t *testing.T) { privateKey := generateECDSAKey(t) txn1, rand := transaction.NewNameReservation("my-new-repository", &privateKey.PublicKey) txn2, _ := transaction.NewNameAllocation("my-new-repository", rand, privateKey) txn3, _ := transaction.NewNameDeallocation("my-new-repository", privateKey) transactions := []transaction.T{txn1, txn2, txn3} block, err := block.NewBlock(types.EmptyHash(), block.HIGHEST_TARGET, transactions) if err != nil { t.Errorf("can't create a block because of %v", err) } db, err := NewDB("test.db") defer os.Remove("test.db") if err != nil { t.Errorf("error opening database: %v", err) } err = db.PutBlock(block, false) if err != nil { t.Errorf("error putting block: %v", err) } block1, err := db.GetBlock(block.Hash()) if err != nil { t.Errorf("error getting block: %v", err) } if block1 == nil { t.Errorf("error getting block %v", block.Hash()) } assert.Equal(t, block, block1) // Attempt fetching the last one block1, err = db.GetLastBlock() if err != nil { t.Errorf("error getting block: %v", err) } if block1 != nil { t.Errorf("error getting block, there should be no last block") } // Set the last one err = db.PutBlock(block, true) if err != nil { t.Errorf("error putting block: %v", err) } block1, err = db.GetLastBlock() if err != nil { t.Errorf("error getting last block: %v", err) } if block1 == nil { t.Errorf("error getting block, there should be a last block") } assert.Equal(t, block, block1) }
func (db *T) GetNextTransactionHash(hash []byte) (h types.Hash, e error) { readable(&e, db, func(dbtx *bolt.Tx) { bucket := dbtx.Bucket([]byte("blocks")) if bucket != nil { h = bucket.Get(append([]byte(">"), hash...)) if h == nil { h = types.EmptyHash() } } }) return }
func TestGetNextTransactionHash(t *testing.T) { transactions, _ := fixtureSampleTransactions(t) block, err := block.NewBlock(types.EmptyHash(), block.HIGHEST_TARGET, transactions) if err != nil { t.Errorf("can't create a block because of %v", err) } db, err := NewDB("test.db") defer os.Remove("test.db") if err != nil { t.Errorf("error opening database: %v", err) } err = db.PutBlock(block, false) if err != nil { t.Errorf("error putting block: %v", err) } tx, err := db.GetNextTransactionHash(transactions[0].Hash()) if err != nil { t.Errorf("error getting next transaction: %v", err) } assert.True(t, bytes.Compare(tx, transactions[1].Hash()) == 0) tx, err = db.GetNextTransactionHash(transactions[1].Hash()) if err != nil { t.Errorf("error getting next transaction: %v", err) } assert.True(t, bytes.Compare(tx, transactions[2].Hash()) == 0) tx, err = db.GetNextTransactionHash(transactions[2].Hash()) if err != nil { t.Errorf("error getting next transaction: %v", err) } assert.True(t, bytes.Compare(tx, types.EmptyHash()) == 0) }
func TestNewBlock(t *testing.T) { privateKey := generateKey(t) txn1, rand := trans.NewNameReservation("my-new-repository") txn1e := trans.NewEnvelope(types.EmptyHash(), txn1) txn2, _ := trans.NewNameAllocation("my-new-repository", rand) txn2e := trans.NewEnvelope(types.EmptyHash(), txn2) txn3, _ := trans.NewNameDeallocation("my-new-repository") txn3e := trans.NewEnvelope(types.EmptyHash(), txn3) txn1e.Sign(privateKey) txn2e.Sign(privateKey) txn3e.Sign(privateKey) transactions := []*trans.Envelope{txn1e, txn2e, txn3e} block1, err := NewBlock(types.EmptyHash(), HIGHEST_TARGET, transactions) if err != nil { t.Errorf("can't create a block because of %v", err) } assert.Equal(t, transactions, block1.Transactions) assert.NotEqual(t, block1.MerkleRootHash, types.EmptyHash()) }
func fixtureSampleTransactions(t *testing.T) ([]*transaction.Envelope, *ecdsa.PrivateKey) { privateKey := generateECDSAKey(t) txn1, rand := transaction.NewNameReservation("my-new-repository") txn1e := transaction.NewEnvelope(types.EmptyHash(), txn1) txn1e.Sign(privateKey) txn2, _ := transaction.NewNameAllocation("my-new-repository", rand) txn2e := transaction.NewEnvelope(txn1e.Hash(), txn2) txn2e.Sign(privateKey) txn3, _ := transaction.NewNameDeallocation("my-new-repository") txn3e := transaction.NewEnvelope(txn2e.Hash(), txn3) txn3e.Sign(privateKey) return []*transaction.Envelope{txn1e, txn2e, txn3e}, privateKey }
func TestPutGetBlock(t *testing.T) { transactions, _ := fixtureSampleTransactions(t) blk, err := block.NewBlock(types.EmptyHash(), block.HIGHEST_TARGET, transactions) if err != nil { t.Errorf("can't create a block because of %v", err) } db, err := NewDB("test.db") defer os.Remove("test.db") if err != nil { t.Errorf("error opening database: %v", err) } err = db.PutBlock(blk, false) if err != nil { t.Errorf("error putting block: %v", err) } block1, err := db.GetBlock(blk.Hash()) if err != nil { t.Errorf("error getting block: %v", err) } if block1 == nil { t.Errorf("error getting block %v", blk.Hash()) } assert.Equal(t, blk, block1) // Attempt fetching the last one block1, err = db.GetLastBlock() if err != nil { t.Errorf("error getting block: %v", err) } if block1 != nil { t.Errorf("error getting block, there should be no last block") } // Set the last one err = db.PutBlock(blk, true) if err != nil { t.Errorf("error putting block: %v", err) } block1, err = db.GetLastBlock() if err != nil { t.Errorf("error getting last block: %v", err) } if block1 == nil { t.Errorf("error getting block, there should be a last block") } assert.Equal(t, blk, block1) }
func TestEncodeDecodeEmptyBlock(t *testing.T) { transactions := []*trans.Envelope{} block, err := NewBlock(types.EmptyHash(), HIGHEST_TARGET, transactions) if err != nil { t.Errorf("can't create a block because of %v", err) } enc, err := block.Encode() if err != nil { t.Errorf("error while encoding block: %v", err) } block1, err := Decode(enc) if err != nil { t.Errorf("error while encoding block: %v", err) } assert.Equal(t, block, block1, "encoded and decoded block should be identical to the original one") }
func TestTransactionConfirmations(t *testing.T) { db, err := NewDB("test.db") defer os.Remove("test.db") if err != nil { t.Errorf("error opening database: %v", err) } transactions := fixtureSampleTransactions(t) confirmationsTest := func(count int, note string) { for i := range transactions { confirmations, err := db.GetTransactionConfirmations(transactions[i].Hash()) if err != nil { t.Errorf("error getting transaction's confirmations: %v", err) } assert.Equal(t, confirmations, count, note) } } blk, err := block.NewBlock(types.EmptyHash(), block.HIGHEST_TARGET, transactions) if err != nil { t.Errorf("can't create a block because of %v", err) } confirmationsTest(0, "no transaction was confirmed yet") err = db.PutBlock(blk, true) if err != nil { t.Errorf("error putting block: %v", err) } confirmationsTest(1, "there should be one confirmation") anotherSampleOfTransactions := fixtureSampleTransactions(t) blk, err = block.NewBlock(blk.Hash(), block.HIGHEST_TARGET, anotherSampleOfTransactions) err = db.PutBlock(blk, true) if err != nil { t.Errorf("error putting block: %v", err) } }
func TestEnvelopeSignVerify(t *testing.T) { privateKey := generateKey(t) txn, _ := NewNameReservation("my-new-repository") e := NewEnvelope(types.EmptyHash(), txn) err := e.Sign(privateKey) if err != nil { t.Errorf("Can't sign the envelope: %v", err) } v, err := e.Verify() if err != nil { t.Errorf("Can't verify the envelope: %v", err) } assert.True(t, v) }
func TestPutGetDeleteTransaction(t *testing.T) { privateKey := generateECDSAKey(t) txn1, _ := transaction.NewNameReservation("my-new-repository") txn1e := transaction.NewEnvelope(types.EmptyHash(), txn1) txn1e.Sign(privateKey) db, err := NewDB("test.db") defer os.Remove("test.db") if err != nil { t.Errorf("error opening database: %v", err) } err = db.PutTransaction(txn1e) if err != nil { t.Errorf("error putting transaction: %v", err) } tx, err := db.GetTransaction(txn1e.Hash()) if err != nil { t.Errorf("error getting transaction: %v", err) } assert.Equal(t, tx, txn1e) err = db.DeleteTransaction(txn1e.Hash()) if err != nil { t.Errorf("error getting transaction: %v", err) } tx, err = db.GetTransaction(txn1e.Hash()) if err != nil { t.Errorf("error getting transaction: %v", err) } assert.Nil(t, tx) }
func TestGetPreviousEnvelopeHashForPublicKey(t *testing.T) { transactions, _ := fixtureSampleTransactions(t) block, err := block.NewBlock(types.EmptyHash(), block.HIGHEST_TARGET, transactions) if err != nil { t.Errorf("can't create a block because of %v", err) } db, err := NewDB("test.db") defer os.Remove("test.db") if err != nil { t.Errorf("error opening database: %v", err) } err = db.PutBlock(block, false) if err != nil { t.Errorf("error putting block: %v", err) } dec, err := keys.DecodeECDSAPublicKey(transactions[2].NextPublicKey) if err != nil { t.Errorf("error decoding ECDSA pubkey: %v", err) } tx, err := db.GetPreviousEnvelopeHashForPublicKey(dec) if err != nil { t.Errorf("error getting previous transaction's for a pubkey: %v", err) } assert.True(t, bytes.Compare(tx, transactions[2].Hash()) == 0) privateKey := generateECDSAKey(t) tx, err = db.GetPreviousEnvelopeHashForPublicKey(&privateKey.PublicKey) if err != nil { t.Errorf("error getting previous transaction's for a pubkey: %v", err) } assert.Nil(t, tx) }
func TestEnvelopeEncodeDecode(t *testing.T) { privateKey := generateKey(t) txn, _ := NewNameReservation("my-new-repository") e := NewEnvelope(types.EmptyHash(), txn) err := e.Sign(privateKey) if err != nil { t.Errorf("Can't sign the envelope: %v", err) } enc, err := e.Encode() if err != nil { t.Errorf("Can't encode the envelope: %v", err) } e1, err := DecodeEnvelope(enc) if err != nil { t.Errorf("Can't decode the envelope: %v", err) } assert.Equal(t, e, e1) }
func TestPutGetRepository(t *testing.T) { db, err := NewDB("test.db") defer os.Remove("test.db") if err != nil { t.Errorf("error opening database: %v", err) } repo := repository.NewRepository("test", repository.PENDING, types.EmptyHash()) err = db.PutRepository(repo) if err != nil { t.Errorf("error putting repository: %v", err) } repo1, err := db.GetRepository("test") if err != nil { t.Errorf("error getting repository: %v", err) } if repo1 == nil { t.Errorf("error getting repository `test'") } assert.Equal(t, repo, repo1) }
func TestPutGetRef(t *testing.T) { db, err := NewDB("test.db") defer os.Remove("test.db") if err != nil { t.Errorf("error opening database: %v", err) } repo := repository.NewRepository("myrepo", repository.ACTIVE, types.EmptyHash()) err = db.PutRepository(repo) if err != nil { t.Errorf("error putting repository: %v", err) } // before the ref is set... ref0, err := db.GetRef("myrepo", "refs/heads/master") if err != nil { t.Errorf("error getting repository ref: %v", err) } assert.True(t, ref0.Equals(repository.EmptyRef())) ref := util.SHA160([]byte("random")) err = db.PutRef("myrepo", "refs/heads/master", ref) if err != nil { t.Errorf("error putting repository ref: %v", err) } ref1, err := db.GetRef("myrepo", "refs/heads/master") if err != nil { t.Errorf("error getting repository ref: %v", err) } if ref1 == nil { t.Errorf("error getting repository ref `refs/heads/master'") } assert.True(t, bytes.Compare(ref, ref1) == 0) }
func TransactionListener() { var msg transaction.T var blk *block.Block blockChannel := make(chan *block.Block) var transactionsPool []transaction.T var previousBlockHash types.Hash var ch chan transaction.T = make(chan transaction.T) router.PermanentSubscribe("/transaction", ch) miningEmpty := false initPool: transactionsPool = make([]transaction.T, 0) loop: select { case msg = <-ch: miningEmpty = false env.DB.PutTransaction(msg) transactionsPool = append(transactionsPool, msg) if blk, _ = env.DB.GetLastBlock(); blk == nil { previousBlockHash = types.EmptyHash() } else { previousBlockHash = blk.Hash() } if bat := prepareBAT(); bat != nil { transactionsPool = append(transactionsPool, bat) } blk, err := block.NewBlock(previousBlockHash, targetBits(), transactionsPool) if err != nil { log.Printf("Error while creating a new block: %v", err) } else { miningFactoryRequests <- MiningFactoryInstantiationRequest{Block: blk, ResponseChannel: blockChannel} } case blk = <-blockChannel: miningEmpty = false if lastBlk, _ := env.DB.GetLastBlock(); lastBlk == nil { previousBlockHash = types.EmptyHash() } else { previousBlockHash = blk.Hash() } isLastBlock := bytes.Compare(blk.PreviousBlockHash, previousBlockHash) == 0 env.DB.PutBlock(blk, isLastBlock) goto initPool default: if len(transactionsPool) == 0 && !miningEmpty { // if there are no transactions to be included into a block, try mining an empty/BAT-only block if blk, _ = env.DB.GetLastBlock(); blk == nil { previousBlockHash = types.EmptyHash() } else { previousBlockHash = blk.Hash() } if bat := prepareBAT(); bat != nil { transactionsPool = append(transactionsPool, bat) } blk, err := block.NewBlock(previousBlockHash, targetBits(), transactionsPool) if err != nil { log.Printf("Error while creating a new block: %v", err) } else { miningFactoryRequests <- MiningFactoryInstantiationRequest{Block: blk, ResponseChannel: blockChannel} miningEmpty = true } } } goto loop }