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.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 }
// 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 }
// 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 }
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{}) }
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") }
// 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 }
// 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 }