Пример #1
0
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,
	}
}
Пример #2
0
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())
}
Пример #3
0
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())
}
Пример #4
0
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)
}
Пример #5
0
// 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
}