func newBlock(prev coin.Block, currentTime uint64, unspent coin.UnspentPool, txns coin.Transactions, calc coin.FeeCalculator) coin.Block { if len(txns) == 0 { log.Panic("Refusing to create block with no transactions") } fee, err := txns.Fees(calc) if err != nil { // This should have been caught earlier log.Panicf("Invalid transaction fees: %v", err) } body := coin.BlockBody{txns} return coin.Block{ Head: newBlockHeader(prev.Head, unspent, currentTime, fee, body), Body: body, } }
func TestDaemonLoopSendResults(t *testing.T) { d, quit := setupDaemonLoop() defer closeDaemon(d, quit) go d.Start(quit) c := gnetConnection(addr) d.Pool.Pool.Pool[1] = c vc, _ := setupVisor() v := NewVisor(vc) d.Visor = v txn := addUnconfirmedTxn(d.Visor) ut := d.Visor.Visor.Unconfirmed.Txns[txn.Hash()] assert.True(t, ut.Announced.IsZero()) txns := coin.Transactions{txn.Txn} m := NewAnnounceTxnsMessage(txns.Hashes()) sr := gnet.SendResult{Connection: c, Error: nil, Message: m} d.Pool.Pool.SendResults <- sr wait() ut = d.Visor.Visor.Unconfirmed.Txns[txn.Hash()] assert.False(t, ut.Announced.IsZero()) }
func TestHandleMessageSendResult(t *testing.T) { d := newDefaultDaemon() defer shutdown(d) // Nothing happens: Message successfully sent and isnt a SendingTxnsMessage m := NewGetBlocksMessage(6) sr := gnet.SendResult{ Message: m, Connection: nil, Error: nil, } assert.NotPanics(t, func() { d.handleMessageSendResult(sr) }) // Add a txn for txn announce update testing vc, _ := setupVisor() v := NewVisor(vc) tx := addUnconfirmedTxn(v) assert.Equal(t, len(v.Visor.Unconfirmed.Txns), 1) ut := v.Visor.Unconfirmed.Txns[tx.Hash()] assert.True(t, ut.Announced.IsZero()) txns := coin.Transactions{tx.Txn} m2 := NewAnnounceTxnsMessage(txns.Hashes()) // Logs a warning, and exits sr.Message = m2 sr.Error = errors.New("Failed") sr.Connection = gnetConnection(addr) assert.NotPanics(t, func() { d.handleMessageSendResult(sr) }) ut = v.Visor.Unconfirmed.Txns[tx.Hash()] assert.True(t, ut.Announced.IsZero()) // Updates announcement sr.Error = nil sr.Message = m2 d.Visor = v assert.NotPanics(t, func() { d.handleMessageSendResult(sr) }) ut = v.Visor.Unconfirmed.Txns[tx.Hash()] assert.False(t, ut.Announced.IsZero()) }
func TestSendingTxnsMessageInterface(t *testing.T) { hashes := []coin.SHA256{randSHA256(t), randSHA256(t)} // GetTxnsMessage should not be a SendingTxnsMessage, it is a request for // them getx := NewGetTxnsMessage(hashes) assertSendingTxnsMessageInterface(t, getx, hashes, false) // AnnounceTxnsMessage is a SendingTxnsMessage annx := NewAnnounceTxnsMessage(hashes) assertSendingTxnsMessageInterface(t, annx, hashes, true) // GiveTxnsMessage is a SendingTxnsMessage defer cleanupVisor() _, v := setupVisor() txns := coin.Transactions{ makeValidTxnNoError(t, v), makeValidTxnNoError(t, v), } givx := NewGiveTxnsMessage(txns) assertSendingTxnsMessageInterface(t, givx, txns.Hashes(), true) }
// Validates a set of Transactions, individually, against each other and // against the Blockchain. If firstFail is true, it will return an error // as soon as it encounters one. Else, it will return an array of // Transactions that are valid as a whole. It may return an error if // firstFalse is false, if there is no way to filter the txns into a valid // array, i.e. processTransactions(processTransactions(txn, false), true) // should not result in an error, unless all txns are invalid. // TODO: // - move arbitration to visor // - blockchain should have strict checking func (bc Blockchain) processTransactions(txns coin.Transactions, arbitrating bool) (coin.Transactions, error) { // Transactions need to be sorted by fee and hash before arbitrating if arbitrating { txns = coin.SortTransactions(txns, bc.TransactionFee) } //TODO: audit if len(txns) == 0 { if arbitrating { return txns, nil } // If there are no transactions, a block should not be made return nil, errors.New("No transactions") } skip := make(map[int]byte) uxHashes := make(coin.UxHashSet, len(txns)) for i, tx := range txns { // Check the transaction against itself. This covers the hash, // signature indices and duplicate spends within itself err := bc.VerifyTransaction(tx) if err != nil { if arbitrating { skip[i] = byte(1) continue } else { return nil, err } } // Check that each pending unspent will be unique uxb := coin.UxBody{ SrcTransaction: tx.Hash(), } for _, to := range tx.Out { uxb.Coins = to.Coins uxb.Hours = to.Hours uxb.Address = to.Address h := uxb.Hash() _, exists := uxHashes[h] if exists { if arbitrating { skip[i] = byte(1) continue } else { m := "Duplicate unspent output across transactions" return nil, errors.New(m) } } if DebugLevel1 { // Check that the expected unspent is not already in the pool. // This should never happen because its a hash collision if bc.unspent.Has(h) { if arbitrating { skip[i] = byte(1) continue } else { m := "Output hash is in the UnspentPool" return nil, errors.New(m) } } } uxHashes[h] = byte(1) } } // Filter invalid transactions before arbitrating between colliding ones if len(skip) > 0 { newtxns := make(coin.Transactions, len(txns)-len(skip)) j := 0 for i := range txns { if _, shouldSkip := skip[i]; !shouldSkip { newtxns[j] = txns[i] j++ } } txns = newtxns skip = make(map[int]byte) } // Check to ensure that there are no duplicate spends in the entire block, // and that we aren't creating duplicate outputs. Duplicate outputs // within a single Transaction are already checked by VerifyTransaction hashes := txns.Hashes() for i := 0; i < len(txns)-1; i++ { s := txns[i] for j := i + 1; j < len(txns); j++ { t := txns[j] if DebugLevel1 { if hashes[i] == hashes[j] { // This is a non-recoverable error for filtering, and // should never occur. It indicates a hash collision // amongst different txns. Duplicate transactions are // caught earlier, when duplicate expected outputs are // checked for, and will not trigger this. return nil, errors.New("Duplicate transaction") } } for a := range s.In { for b := range t.In { if s.In[a] == t.In[b] { if arbitrating { // The txn with the highest fee and lowest hash // is chosen when attempting a double spend. // Since the txns are sorted, we skip the 2nd // iterable skip[j] = byte(1) } else { m := "Cannot spend output twice in the same block" return nil, errors.New(m) } } } } } } // Filter the final results, if necessary if len(skip) > 0 { newtxns := make(coin.Transactions, len(txns)-len(skip)) j := 0 for i := range txns { if _, shouldSkip := skip[i]; !shouldSkip { newtxns[j] = txns[i] j++ } } return newtxns, nil } return txns, nil }