예제 #1
0
func TestTransactionSignInputs(t *testing.T) {
	tx := &Transaction{}
	// Panics if txns already signed
	tx.Sigs = append(tx.Sigs, cipher.Sig{})
	assert.Panics(t, func() { tx.SignInputs([]cipher.SecKey{}) })
	// Panics if not enough keys
	tx = &Transaction{}
	ux, s := makeUxOutWithSecret(t)
	tx.PushInput(ux.Hash())
	ux2, s2 := makeUxOutWithSecret(t)
	tx.PushInput(ux2.Hash())
	tx.PushOutput(makeAddress(), 40, 80)
	assert.Equal(t, len(tx.Sigs), 0)
	assert.Panics(t, func() { tx.SignInputs([]cipher.SecKey{s}) })
	assert.Equal(t, len(tx.Sigs), 0)
	// Valid signing
	h := tx.HashInner()
	assert.NotPanics(t, func() { tx.SignInputs([]cipher.SecKey{s, s2}) })
	assert.Equal(t, len(tx.Sigs), 2)
	assert.Equal(t, tx.HashInner(), h)
	p := cipher.PubKeyFromSecKey(s)
	a := cipher.AddressFromPubKey(p)
	p = cipher.PubKeyFromSecKey(s2)
	a2 := cipher.AddressFromPubKey(p)
	assert.Nil(t, cipher.ChkSig(a, cipher.AddSHA256(h, tx.In[0]), tx.Sigs[0]))
	assert.Nil(t, cipher.ChkSig(a2, cipher.AddSHA256(h, tx.In[1]), tx.Sigs[1]))
	assert.NotNil(t, cipher.ChkSig(a, h, tx.Sigs[1]))
	assert.NotNil(t, cipher.ChkSig(a2, h, tx.Sigs[0]))
}
예제 #2
0
func makeTransactionForChainWithHoursFee(t *testing.T, bc *Blockchain,
	ux coin.UxOut, sec cipher.SecKey, hours, fee uint64) (coin.Transaction, cipher.SecKey) {
	chrs := ux.CoinHours(bc.Time())
	if chrs < hours+fee {
		log.Panicf("CoinHours underflow. Have %d, need at least %d", chrs,
			hours+fee)
	}
	assert.Equal(t, cipher.AddressFromPubKey(cipher.PubKeyFromSecKey(sec)), ux.Body.Address)
	knownUx, exists := bc.GetUnspent().Get(ux.Hash())
	assert.True(t, exists)
	assert.Equal(t, knownUx, ux)
	tx := coin.Transaction{}
	tx.PushInput(ux.Hash())
	p, newSec := cipher.GenerateKeyPair()
	addr := cipher.AddressFromPubKey(p)
	tx.PushOutput(addr, 1e6, hours)
	coinsOut := ux.Body.Coins - 1e6
	if coinsOut > 0 {
		tx.PushOutput(genAddress, coinsOut, chrs-hours-fee)
	}
	tx.SignInputs([]cipher.SecKey{sec})
	assert.Equal(t, len(tx.Sigs), 1)
	assert.Nil(t, cipher.ChkSig(ux.Body.Address, cipher.AddSHA256(tx.HashInner(), tx.In[0]), tx.Sigs[0]))
	tx.UpdateHeader()
	assert.Nil(t, tx.Verify())
	err := bc.VerifyTransaction(tx)
	assert.Nil(t, err)
	return tx, newSec
}
예제 #3
0
// Validates the inputs to a transaction by checking signatures. Assumes txn
// has valid number of signatures for inputs.
func verifyTransactionInputs(tx Transaction, uxIn UxArray) error {
	if DebugLevel2 {
		if len(tx.In) != len(tx.Sigs) || len(tx.In) != len(uxIn) {
			log.Panic("tx.In != tx.Sigs != uxIn")
		}
		if tx.InnerHash != tx.HashInner() {
			log.Panic("Invalid Tx Header Hash")
		}
	}

	// Check signatures against unspent address
	for i, _ := range tx.In {
		hash := cipher.AddSHA256(tx.InnerHash, tx.In[i]) //use inner hash, not outer hash
		err := cipher.ChkSig(uxIn[i].Body.Address, hash, tx.Sigs[i])
		if err != nil {
			return errors.New("Signature not valid for output being spent")
		}
	}
	if DebugLevel2 {
		// Check that hashes match.
		// This would imply a bug with UnspentPool.GetMultiple
		if len(tx.In) != len(uxIn) {
			log.Panic("tx.In does not match uxIn")
		}
		for i, _ := range tx.In {
			if tx.In[i] != uxIn[i].Hash() {
				log.Panic("impossible error: Ux hash mismatch")
			}
		}
	}
	return nil
}
예제 #4
0
// Returns unspent output checksum for the Block. Must be called after Block
// is fully initialized, and before its outputs are added to the unspent pool
func getSnapshotHash(unspent UnspentPool, prevHash cipher.SHA256) [4]byte {
	uxHash := cipher.AddSHA256(unspent.XorHash, prevHash)
	var snapshot [4]byte
	if copy(snapshot[:], uxHash[:]) != 4 {
		log.Panic("UxSnapshot copy is broken")
	}
	return snapshot
}
예제 #5
0
// Compares the state of the current UxSnapshot hash to state of unspent
// output pool.
func (self *Blockchain) verifyUxSnapshot(b Block) error {
	head := self.Head().Head
	uxHash := cipher.AddSHA256(self.Unspent.XorHash, head.Hash())
	if !bytes.Equal(b.Head.UxSnapshot[:], uxHash[:4]) {
		return errors.New("UxSnapshot does not match")
	}
	return nil
}
예제 #6
0
func TestGetUxSnapshot(t *testing.T) {
	unsp := NewUnspentPool()
	xor := randSHA256(t)
	unsp.XorHash = xor
	prev := randSHA256(t)
	sh := getSnapshotHash(unsp, prev)
	expect := cipher.AddSHA256(xor, prev)
	assert.True(t, bytes.Equal(expect[:4], sh[:]))
	assert.NotEqual(t, sh, [4]byte{})
}
예제 #7
0
func TestVerifyUxSnapshot(t *testing.T) {
	bc := NewBlockchain()
	gb := bc.CreateGenesisBlock(genAddress, _genTime, _genCoins)
	b := Block{Body: BlockBody{}, Head: BlockHeader{}}
	b.Body.Transactions = append(b.Body.Transactions, makeTransaction(t))
	bc.Unspent.XorHash = randSHA256(t)
	uxHash := cipher.AddSHA256(bc.Unspent.XorHash, gb.Head.Hash())
	copy(b.Head.UxSnapshot[:], uxHash[:])
	assert.Nil(t, bc.verifyUxSnapshot(b))
	b.Head.UxSnapshot = [4]byte{}
	assertError(t, bc.verifyUxSnapshot(b), "UxSnapshot does not match")
}
예제 #8
0
// Signs all inputs in the transaction
func (self *Transaction) SignInputs(keys []cipher.SecKey) {
	self.InnerHash = self.HashInner() //update hash

	if len(self.Sigs) != 0 {
		log.Panic("Transaction has been signed")
	}
	if len(keys) != len(self.In) {
		log.Panic("Invalid number of keys")
	}
	if len(keys) > math.MaxUint16 {
		log.Panic("Too many key")
	}
	if len(keys) == 0 {
		log.Panic("No keys")
	}
	sigs := make([]cipher.Sig, len(self.In))
	inner_hash := self.HashInner()
	for i, k := range keys {
		h := cipher.AddSHA256(inner_hash, self.In[i]) //hash to sign
		sigs[i] = cipher.SignHash(h, k)
	}
	self.Sigs = sigs
}
예제 #9
0
// Verify attempts to determine if the transaction is well formed
// Verify cannot check transaction signatures, it needs the address from unspents
// Verify cannot check if outputs being spent exist
// Verify cannot check if the transaction would create or destroy coins
// or if the inputs have the required coin base
func (self *Transaction) Verify() error {

	h := self.HashInner()
	if h != self.InnerHash {
		return errors.New("Invalid header hash")
	}

	if len(self.In) == 0 {
		return errors.New("No inputs")
	}
	if len(self.Out) == 0 {
		return errors.New("No outputs")
	}

	// Check signature index fields
	if len(self.Sigs) != len(self.In) {
		return errors.New("Invalid number of signatures")
	}
	if len(self.Sigs) >= math.MaxUint16 {
		return errors.New("Too many signatures and inputs")
	}

	// Check duplicate inputs
	uxOuts := make(map[cipher.SHA256]int, len(self.In))
	for i, _ := range self.In {
		uxOuts[self.In[i]] = 1
	}
	if len(uxOuts) != len(self.In) {
		return errors.New("Duplicate spend")
	}

	if self.Type != 0 {
		return errors.New("transaction type invalid")
	}
	if self.Length != uint32(self.Size()) {
		return errors.New("transaction size prefix invalid")
	}

	// Check for duplicate potential outputs
	outputs := make(map[cipher.SHA256]int, len(self.Out))
	uxb := UxBody{
		SrcTransaction: self.Hash(),
	}
	for _, to := range self.Out {
		uxb.Coins = to.Coins
		uxb.Hours = to.Hours
		uxb.Address = to.Address
		outputs[uxb.Hash()] = 1
	}
	if len(outputs) != len(self.Out) {
		return errors.New("Duplicate output in transaction")
	}

	// Validate signature
	for i, sig := range self.Sigs {
		hash := cipher.AddSHA256(self.InnerHash, self.In[i])
		if err := cipher.VerifySignedHash(sig, hash); err != nil {
			return err
		}
	}

	// Artificial restriction to prevent spam
	// Must spend only multiples of 1e6
	for _, txo := range self.Out {
		if txo.Coins == 0 {
			return errors.New("Zero coin output")
		}
		if txo.Coins%1e6 != 0 {
			return errors.New("Transaction outputs must be multiple of 1e6 " +
				"base units")
		}
	}

	return nil
}