Example #1
0
// VerifyTransaction checks that the inputs to the transaction exist,
// that the transaction does not create or destroy coins and that the
// signatures on the transaction are valid
func (bc Blockchain) VerifyTransaction(tx coin.Transaction) error {
	//CHECKLIST: DONE: check for duplicate ux inputs/double spending
	//CHECKLIST: DONE: check that inputs of transaction have not been spent
	//CHECKLIST: DONE: check there are no duplicate outputs

	// Q: why are coin hours based on last block time and not
	// current time?
	// A: no two computers will agree on system time. Need system clock
	// indepedent timing that everyone agrees on. fee values would depend on
	// local clock

	// Check transaction type and length
	// Check for duplicate outputs
	// Check for duplicate inputs
	// Check for invalid hash
	// Check for no inputs
	// Check for no outputs
	// Check for non 1e6 multiple coin outputs
	// Check for zero coin outputs
	// Check valid looking signatures
	if err := tx.Verify(); err != nil {
		return err
	}

	uxIn, err := bc.unspent.GetMultiple(tx.In)
	if err != nil {
		return err
	}
	// Checks whether ux inputs exist,
	// Check that signatures are allowed to spend inputs
	if err := tx.VerifyInput(uxIn); err != nil {
		return err
	}

	// Get the UxOuts we expect to have when the block is created.
	uxOut := coin.CreateUnspents(bc.Head().Head, tx)
	// Check that there are any duplicates within this set
	if uxOut.HasDupes() {
		return errors.New("Duplicate unspent outputs in transaction")
	}
	if DebugLevel1 {
		// Check that new unspents don't collide with existing.  This should
		// also be checked in verifyTransactions
		for i := range uxOut {
			if bc.unspent.Has(uxOut[i].Hash()) {
				return errors.New("New unspent collides with existing unspent")
			}
		}
	}

	// Check that no coins are lost, and sufficient coins and hours are spent
	err = coin.VerifyTransactionSpending(bc.Time(), uxIn, uxOut)
	if err != nil {
		return err
	}
	return nil
}
Example #2
0
func TestVerifyTransactionSpending(t *testing.T) {
	ft := FakeTree{}
	bc := NewBlockchain(&ft, nil)
	bc.CreateGenesisBlock(genAddress, _genCoins, _genTime)

	// Overspending hours

	tx := coin.Transaction{}
	uxs := bc.GetUnspent().Array()
	tx.PushInput(uxs[0].Hash())
	tx.PushOutput(genAddress, 1e6, uxs[0].Body.Hours)
	tx.PushOutput(genAddress, uxs[0].Body.Coins-1e6, 1)
	uxIn, err := bc.GetUnspent().GetMultiple(tx.In)
	assert.Nil(t, err)
	uxOut := coin.CreateUnspents(bc.Head().Head, tx)
	assertError(t, coin.VerifyTransactionSpending(bc.Time(), uxIn, uxOut),
		"Insufficient coin hours")

	// add block to blockchain.
	_, ux := addBlockToBlockchain(t, bc)
	// addBlockToBlockchain(t, bc)

	// Valid
	tx, _ = makeTransactionForChainWithHoursFee(t, bc, ux, genSecret, 100, 50)
	uxIn, err = bc.GetUnspent().GetMultiple(tx.In)
	assert.Nil(t, err)
	uxOut = coin.CreateUnspents(bc.Head().Head, tx)
	assert.Nil(t, coin.VerifyTransactionSpending(bc.Time(), uxIn, uxOut))

	// Destroying coins
	tx = coin.Transaction{}
	tx.PushInput(ux.Hash())
	tx.PushOutput(genAddress, 1e6, 100)
	tx.PushOutput(genAddress, 10e6, 100)
	uxIn, err = bc.GetUnspent().GetMultiple(tx.In)
	assert.Nil(t, err)
	uxOut = coin.CreateUnspents(bc.Head().Head, tx)
	err = coin.VerifyTransactionSpending(bc.Time(), uxIn, uxOut)
	assert.NotNil(t, err)
	assert.Equal(t, err.Error(),
		"Transactions may not create or destroy coins")
	assertError(t, coin.VerifyTransactionSpending(bc.Time(), uxIn, uxOut),
		"Transactions may not create or destroy coins")

	// Insufficient coins
	tx = coin.Transaction{}
	tx.PushInput(ux.Hash())
	p, s := cipher.GenerateKeyPair()
	a := cipher.AddressFromPubKey(p)
	coins := ux.Body.Coins
	assert.True(t, coins > 1e6)
	tx.PushOutput(a, 1e6, 100)
	tx.PushOutput(genAddress, coins-1e6, 100)
	tx.SignInputs([]cipher.SecKey{genSecret})
	tx.UpdateHeader()
	b, err := bc.NewBlockFromTransactions(coin.Transactions{tx}, bc.Time()+_incTime)
	assert.Nil(t, err)
	uxs, err = bc.ExecuteBlock(&b)
	assert.Nil(t, err)
	tx = coin.Transaction{}
	tx.PushInput(uxs[0].Hash())
	tx.PushOutput(a, 10e6, 1)
	tx.SignInputs([]cipher.SecKey{s})
	tx.UpdateHeader()
	uxIn, err = bc.GetUnspent().GetMultiple(tx.In)
	assert.Nil(t, err)
	uxOut = coin.CreateUnspents(bc.Head().Head, tx)
	assertError(t, coin.VerifyTransactionSpending(bc.Time(), uxIn, uxOut),
		"Insufficient coins")
}