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])) }
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 }
// 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.Head.Sigs) || len(tx.In) != len(uxIn) { log.Panic("tx.In != tx.Head.Sigs != uxIn") } if tx.Head.Hash != tx.hashInner() { log.Panic("Invalid Tx Header Hash") } } // Check signatures against unspent address for i, _ := range tx.In { err := cipher.ChkSig(uxIn[i].Body.Address, tx.Head.Hash, tx.Head.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 }
// addBlockToBlockchain test helper function // Adds 2 blocks to the blockchain and return an unspent that has >0 coin hours func addBlockToBlockchain(t *testing.T, bc *Blockchain) (coin.Block, coin.UxOut) { // Split the genesis block into two transactions assert.Equal(t, len(bc.GetUnspent().Array()), 1) ux := bc.GetUnspent().Array()[0] assert.Equal(t, ux.Body.Address, genAddress) pub := cipher.PubKeyFromSecKey(genSecret) assert.Equal(t, genAddress, cipher.AddressFromPubKey(pub)) sig := cipher.SignHash(ux.Hash(), genSecret) assert.Nil(t, cipher.ChkSig(ux.Body.Address, ux.Hash(), sig)) tx, sec := makeTransactionForChainWithHoursFee(t, bc, ux, genSecret, 0, 0) b, err := bc.NewBlockFromTransactions(coin.Transactions{tx}, _incTime) assert.Nil(t, err) assertExecuteBlock(t, bc, b, tx) assert.Equal(t, len(bc.GetUnspent().Array()), 2) // Spend one of them // The other will have hours now ux = coin.UxOut{} for _, u := range bc.GetUnspent().Pool { if u.Body.Address != genAddress { ux = u break } } assert.NotEqual(t, ux.Body.Address, cipher.Address{}) assert.NotEqual(t, ux.Body.Address, genAddress) pub = cipher.PubKeyFromSecKey(sec) addr := cipher.AddressFromPubKey(pub) assert.Equal(t, ux.Body.Address, addr) tx, _ = makeTransactionForChainWithHoursFee(t, bc, ux, sec, 0, 0) b, err = bc.NewBlockFromTransactions(coin.Transactions{tx}, bc.Time()+_incTime) assert.Nil(t, err) assertExecuteBlock(t, bc, b, tx) assert.Equal(t, len(bc.GetUnspent().Array()), 2) // Check that the output in the 2nd block is owned by genesis, // and has coin hours for _, u := range bc.GetUnspent().Pool { if u.Body.Address == genAddress { ux = u break } } assert.Equal(t, ux.Body.Address, genAddress) assert.Equal(t, ux.Head.BkSeq, uint64(1)) assert.True(t, ux.CoinHours(bc.Time()) > 0) return b, ux }