func NewReadableTransaction(t *coin.Transaction) ReadableTransaction { sigs := make([]string, len(t.Sigs)) for i, _ := range t.Sigs { sigs[i] = t.Sigs[i].Hex() } in := make([]string, len(t.In)) for i, _ := range t.In { in[i] = t.In[i].Hex() } out := make([]ReadableTransactionOutput, len(t.Out)) for i, _ := range t.Out { out[i] = NewReadableTransactionOutput(&t.Out[i]) } return ReadableTransaction{ Length: t.Length, Type: t.Type, Hash: t.Hash().Hex(), InnerHash: t.InnerHash.Hex(), Sigs: sigs, In: in, Out: out, } }
// Adds a coin.Transaction to the pool, or updates an existing one's timestamps // Returns an error if txn is invalid, and whether the transaction already // existed in the pool. func (self *UnconfirmedTxnPool) RecordTxn(bc *coin.Blockchain, t coin.Transaction, addrs map[coin.Address]byte, maxSize int, burnFactor uint64) (error, bool) { if err := VerifyTransaction(bc, &t, maxSize, burnFactor); err != nil { return err, false } if err := bc.VerifyTransaction(t); err != nil { return err, false } // Update if we already have this txn ut, ok := self.Txns[t.Hash()] if ok { now := util.Now() ut.Received = now ut.Checked = now self.Txns[ut.Txn.Hash()] = ut return nil, true } // Add txn to index self.Txns[t.Hash()] = self.createUnconfirmedTxn(&bc.Unspent, t, addrs) // Add predicted unspents uxs := coin.CreateExpectedUnspents(t) for i, _ := range uxs { self.Unspent.Add(uxs[i]) } return nil, false }
// Adds a coin.Transaction to the pool, or updates an existing one's timestamps // Returns an error if txn is invalid, and whether the transaction already // existed in the pool. func (self *UnconfirmedTxnPool) RecordTxn(bc *coin.Blockchain, t coin.Transaction, addrs map[cipher.Address]byte, maxSize int, burnFactor uint64) (error, bool) { if err := VerifyTransaction(bc, &t, maxSize, burnFactor); err != nil { return err, false } if err := bc.VerifyTransaction(t); err != nil { return err, false } // Update if we already have this txn h := t.Hash() ut, ok := self.Txns[h] if ok { now := util.Now() ut.Received = now ut.Checked = now self.Txns[h] = ut return nil, true } // Add txn to index self.Txns[h] = self.createUnconfirmedTxn(&bc.Unspent, t, addrs) // Add predicted unspents self.Unspent[h] = coin.CreateUnspents(bc.Head().Head, t) return nil, false }
// InjectTxn adds a coin.Transaction to the pool, or updates an existing one's timestamps // Returns an error if txn is invalid, and whether the transaction already // existed in the pool. func (utp *UnconfirmedTxnPool) InjectTxn(bc *Blockchain, t coin.Transaction) (error, bool) { if err := t.Verify(); err != nil { return err, false } if err := VerifyTransactionFee(bc, &t); err != nil { return err, false } if err := bc.VerifyTransaction(t); err != nil { return err, false } // Update if we already have this txn h := t.Hash() ut, ok := utp.Txns[h] if ok { now := util.Now() ut.Received = now ut.Checked = now utp.Txns[h] = ut return nil, true } // Add txn to index unspent := bc.GetUnspent() utp.Txns[h] = utp.createUnconfirmedTxn(unspent, t) // Add predicted unspents utp.Unspent[h] = coin.CreateUnspents(bc.Head().Head, t) return nil, false }
func (fg fakeGateway) InjectTransaction(txn coin.Transaction) (coin.Transaction, error) { if _, v := fg.injectRawTxMap[txn.Hash().Hex()]; v { return txn, nil } return txn, errors.New("inject transaction failed") }
func assertValidUnconfirmed(t *testing.T, txns map[cipher.SHA256]UnconfirmedTxn, txn coin.Transaction) { ut, ok := txns[txn.Hash()] assert.True(t, ok) assert.Equal(t, ut.Txn, txn) assert.True(t, ut.Announced.IsZero()) assert.False(t, ut.Received.IsZero()) assert.False(t, ut.Checked.IsZero()) }
func assertValidUnconfirmed(t *testing.T, txns map[coin.SHA256]UnconfirmedTxn, txn coin.Transaction, didAnnounce, isOurReceive, isOurSpend bool) { ut, ok := txns[txn.Hash()] assert.True(t, ok) assert.Equal(t, ut.Txn, txn) assert.Equal(t, ut.IsOurReceive, isOurReceive) assert.Equal(t, ut.IsOurSpend, isOurSpend) assert.Equal(t, ut.Announced.IsZero(), !didAnnounce) assert.False(t, ut.Received.IsZero()) assert.False(t, ut.Checked.IsZero()) }
func assertValidUnspents(t *testing.T, bh coin.BlockHeader, tx coin.Transaction, uxo coin.UxArray) { assert.Equal(t, len(tx.Out), len(uxo)) for i, ux := range uxo { assert.Equal(t, bh.Time, ux.Head.Time) assert.Equal(t, bh.BkSeq, ux.Head.BkSeq) assert.Equal(t, tx.Hash(), ux.Body.SrcTransaction) assert.Equal(t, tx.Out[i].Address, ux.Body.Address) assert.Equal(t, tx.Out[i].Coins, ux.Body.Coins) assert.Equal(t, tx.Out[i].Hours, ux.Body.Hours) } }
// Adds a coin.Transaction to the pool //func (self *UnconfirmedTxnPool) InjectTxn(bc *coin.Blockchain, // t coin.Transaction, addrs map[cipher.Address]byte, didAnnounce bool) error { func (self *UnconfirmedTxnPool) InjectTxn(t coin.Transaction) error { now := time.Now().Unix() //announcedAt := util.ZeroTime() ut := UnconfirmedTxn{ Txn: t, Received: now, Checked: now, Announced: 0, //set to 0 until announced } self.Txns[t.Hash()] = ut return nil }
func assertValidUnspent(t *testing.T, bc *coin.Blockchain, unspent TxnUnspents, tx coin.Transaction) { expect := coin.CreateUnspents(bc.Head().Head, tx) assert.NotEqual(t, len(expect), 0) sum := 0 for _, uxs := range unspent { sum += len(uxs) } assert.Equal(t, len(expect), sum) uxs := unspent[tx.Hash()] for _, ux := range expect { found := false for _, u := range uxs { if u.Hash() == ux.Hash() { found = true break } } assert.True(t, found) } }
func TransactionToJSON(tx coin.Transaction) string { var o TransactionJSON if err := tx.Verify(); err != nil { log.Panic("Input Transaction Invalid: Cannot serialize to JSON, fails verify") } o.Hash = tx.Hash().Hex() o.InnerHash = tx.InnerHash.Hex() if tx.InnerHash != tx.HashInner() { log.Panic("TransactionToJSON called with invalid transaction, inner hash mising") } o.Sigs = make([]string, len(tx.Sigs)) o.In = make([]string, len(tx.In)) o.Out = make([]TransactionOutputJSON, len(tx.Out)) for i, sig := range tx.Sigs { o.Sigs[i] = sig.Hex() } for i, x := range tx.In { o.In[i] = x.Hex() //hash to hex } for i, y := range tx.Out { o.Out[i] = NewTransactionOutputJSON(y, tx.InnerHash) } b, err := json.MarshalIndent(o, "", " ") if err != nil { log.Panic("Cannot serialize transaction as JSON") } return string(b) }
func TransactionFromJSON(str string) (coin.Transaction, error) { var TxIn TransactionJSON err := json.Unmarshal([]byte(str), TxIn) if err != nil { errors.New("cannot deserialize") } var tx coin.Transaction tx.Sigs = make([]cipher.Sig, len(TxIn.Sigs)) tx.In = make([]cipher.SHA256, len(TxIn.In)) tx.Out = make([]coin.TransactionOutput, len(TxIn.Out)) for i, _ := range tx.Sigs { sig2, err := cipher.SigFromHex(TxIn.Sigs[i]) if err != nil { return coin.Transaction{}, errors.New("invalid signature") } tx.Sigs[i] = sig2 } for i, _ := range tx.In { hash, err := cipher.SHA256FromHex(TxIn.In[i]) if err != nil { return coin.Transaction{}, errors.New("invalid signature") } tx.In[i] = hash } for i, _ := range tx.Out { out, err := TransactionOutputFromJSON(TxIn.Out[i]) if err != nil { return coin.Transaction{}, errors.New("invalid output") } tx.Out[i] = out } tx.Length = uint32(tx.Size()) tx.Type = 0 hash, err := cipher.SHA256FromHex(TxIn.Hash) if err != nil { return coin.Transaction{}, errors.New("invalid hash") } if hash != tx.Hash() { } InnerHash, err := cipher.SHA256FromHex(TxIn.Hash) if InnerHash != tx.InnerHash { return coin.Transaction{}, errors.New("inner hash") } err = tx.Verify() if err != nil { return coin.Transaction{}, errors.New("transaction failed verification") } return tx, nil }
func TestProcessTransactions(t *testing.T) { ft := FakeTree{} bc := NewBlockchain(&ft, nil) bc.CreateGenesisBlock(genAddress, _genCoins, _genTime) fmt.Println("genesis time:", bc.GetGenesisBlock().Time()) assert.Equal(t, bc.Len(), uint64(1)) _, ux := addBlockToBlockchain(t, bc) assert.Equal(t, bc.Len(), uint64(3)) // Invalid, no transactions in block // arbitrating=false txns, err := bc.processTransactions(coin.Transactions{}, false) assert.Nil(t, txns) assertError(t, err, "No transactions") // arbitrating=true txns, err = bc.processTransactions(coin.Transactions{}, true) assert.Equal(t, len(txns), 0) assert.Nil(t, err) // Invalid, txn.Verify() fails // TODO -- combine all txn.Verify() failures into one test // method, and call it from here, from ExecuteBlock(), from // Verify(), from VerifyTransaction() txns = coin.Transactions{} txn := coin.Transaction{} txn.PushInput(ux.Hash()) txn.PushOutput(genAddress, 777, 100) txn.SignInputs([]cipher.SecKey{genSecret}) txn.UpdateHeader() txns = append(txns, txn) // arbitrating=false txns2, err := bc.processTransactions(txns, false) assert.Nil(t, txns2) assertError(t, err, "Transaction outputs must be multiple of 1e6 base units") // arbitrating=true txns2, err = bc.processTransactions(txns, true) assert.NotNil(t, txns2) assert.Nil(t, err) assert.Equal(t, len(txns2), 0) // Invalid, duplicate unspent will be created by these txns txn, _ = makeTransactionForChainWithHoursFee(t, bc, ux, genSecret, 100, 100) txns = coin.Transactions{txn, txn} // arbitrating=false txns2, err = bc.processTransactions(txns, false) assertError(t, err, "Duplicate unspent output across transactions") assert.Nil(t, txns2) // arbitrating=true. One of the offending transactions should be removed txns2, err = bc.processTransactions(txns, true) assert.Nil(t, err) assert.Equal(t, len(txns2), 1) assert.Equal(t, txns2[0], txn) // Check that a new output will not collide with the existing pool txn, _ = makeTransactionForChainWithHoursFee(t, bc, ux, genSecret, 100, 100) txns = coin.Transactions{txn} uxb := coin.UxBody{ SrcTransaction: txn.Hash(), Coins: txn.Out[0].Coins, Hours: txn.Out[0].Hours, Address: txn.Out[0].Address, } bc.GetUnspent().Add(coin.UxOut{Body: uxb}) // arbitrating=false txns2, err = bc.processTransactions(txns, false) assertError(t, err, "New unspent collides with existing unspent") assert.Nil(t, txns2) // arbitrating=true txns2, err = bc.processTransactions(txns, true) assert.Equal(t, len(txns2), 0) assert.NotNil(t, txns2) assert.Nil(t, err) // Spending of duplicate inputs being spent across txns txn, _ = makeTransactionForChainWithHoursFee(t, bc, ux, genSecret, 100, 100) txn2, _ := makeTransactionForChainWithHoursFee(t, bc, ux, genSecret, 100, 100) txn2.Out = nil txn2.PushOutput(makeAddress(), 1e6, 100) txn2.PushOutput(makeAddress(), ux.Body.Coins-1e6, 100) txn2.Sigs = nil txn2.SignInputs([]cipher.SecKey{genSecret}) txn2.UpdateHeader() txns = coin.SortTransactions(coin.Transactions{txn, txn2}, bc.TransactionFee) // arbitrating=false txns2, err = bc.processTransactions(txns, false) assertError(t, err, "Cannot spend output twice in the same block") assert.Nil(t, txns2) // arbitrating=true txns2, err = bc.processTransactions(txns, true) assert.Nil(t, err) assert.Equal(t, len(txns2), 1) assert.Equal(t, txns2[0], txns[0]) }
func TestExecuteBlock(t *testing.T) { ft := FakeTree{} bc := NewBlockchain(&ft, nil) bc.CreateGenesisBlock(genAddress, _genCoins, _genTime) assert.Equal(t, bc.Len(), uint64(1)) _, ux := addBlockToBlockchain(t, bc) assert.Equal(t, bc.Len(), uint64(3)) // Invalid block returns error b := coin.Block{} uxs, err := bc.ExecuteBlock(&b) assert.NotNil(t, err) assert.Nil(t, uxs) // Valid block, spends are removed from the unspent pool, new ones are // added. Blocks is updated, and new unspents are returns assert.Equal(t, bc.Len(), uint64(3)) assert.Equal(t, len(bc.GetUnspent().Pool), 2) spuxs := splitUnspent(t, bc, ux) tx := coin.Transaction{} tx.PushInput(spuxs[0].Hash()) coins := spuxs[0].Body.Coins extra := coins % 4e6 coins = (coins - extra) / 4 tx.PushOutput(genAddress, coins+extra, spuxs[0].Body.Hours/5) tx.PushOutput(genAddress, coins, spuxs[0].Body.Hours/6) tx.PushOutput(genAddress, coins, spuxs[0].Body.Hours/7) tx.PushOutput(genAddress, coins, spuxs[0].Body.Hours/8) tx.SignInputs([]cipher.SecKey{genSecret}) tx.UpdateHeader() tx2 := coin.Transaction{} tx2.PushInput(spuxs[1].Hash()) tx2.PushOutput(genAddress, spuxs[1].Body.Coins, spuxs[1].Body.Hours/10) tx2.SignInputs([]cipher.SecKey{genSecret}) tx2.UpdateHeader() txns := coin.Transactions{tx, tx2} sTxns := coin.NewSortableTransactions(txns, bc.TransactionFee) unswapped := sTxns.IsSorted() txns = coin.SortTransactions(txns, bc.TransactionFee) assert.Nil(t, bc.verifyTransactions(txns)) seq := bc.Head().Head.BkSeq b, err = bc.NewBlockFromTransactions(txns, bc.Time()+_incTime) assert.Equal(t, b.Head.BkSeq, seq+1) assert.Nil(t, err) assert.Equal(t, len(b.Body.Transactions), 2) assert.Equal(t, b.Body.Transactions, txns) uxs, err = bc.ExecuteBlock(&b) assert.Nil(t, err) assert.Equal(t, len(uxs), 5) // Check that all unspents look correct and are in the unspent pool txOuts := []coin.TransactionOutput{} if unswapped { txOuts = append(txOuts, tx.Out...) txOuts = append(txOuts, tx2.Out...) } else { txOuts = append(txOuts, tx2.Out...) txOuts = append(txOuts, tx.Out...) } for i, ux := range uxs { if unswapped { if i < len(tx.Out) { assert.Equal(t, ux.Body.SrcTransaction, tx.Hash()) } else { assert.Equal(t, ux.Body.SrcTransaction, tx2.Hash()) } } else { if i < len(tx2.Out) { assert.Equal(t, ux.Body.SrcTransaction, tx2.Hash()) } else { assert.Equal(t, ux.Body.SrcTransaction, tx.Hash()) } } assert.Equal(t, ux.Body.Address, txOuts[i].Address) assert.Equal(t, ux.Body.Coins, txOuts[i].Coins) assert.Equal(t, ux.Body.Hours, txOuts[i].Hours) assert.Equal(t, ux.Head.BkSeq, b.Head.BkSeq) assert.Equal(t, ux.Head.Time, b.Head.Time) assert.True(t, bc.GetUnspent().Has(ux.Hash())) } // Check that all spends are no longer in the pool txIns := []cipher.SHA256{} txIns = append(txIns, tx.In...) txIns = append(txIns, tx2.In...) for _, ux := range txIns { assert.False(t, bc.GetUnspent().Has(ux)) } }