// 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 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 }
func InitTransaction() coin.Transaction { var tx coin.Transaction output := cipher.MustSHA256FromHex("043836eb6f29aaeb8b9bfce847e07c159c72b25ae17d291f32125e7f1912e2a0") tx.PushInput(output) for i := 0; i < 100; i++ { addr := cipher.MustDecodeBase58Address(AddrList[i]) tx.PushOutput(addr, 1e12, 1) // 10e6*10e6 } /* seckeys := make([]cipher.SecKey, 1) seckey := "" seckeys[0] = cipher.MustSecKeyFromHex(seckey) tx.SignInputs(seckeys) */ txs := make([]cipher.Sig, 1) sig := "ed9bd7a31fe30b9e2d53b35154233dfdf48aaaceb694a07142f84cdf4f5263d21b723f631817ae1c1f735bea13f0ff2a816e24a53ccb92afae685fdfc06724de01" txs[0] = cipher.MustSigFromHex(sig) tx.Sigs = txs tx.UpdateHeader() err := tx.Verify() if err != nil { log.Panic(err) } log.Printf("signature= %s", tx.Sigs[0].Hex()) return tx }
// 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 }
func (fbc *fakeBlockchain) CreateGenesisBlock(genesisAddr cipher.Address, genesisCoins, timestamp uint64) coin.Block { txn := coin.Transaction{} txn.PushOutput(genesisAddr, genesisCoins, genesisCoins) body := coin.BlockBody{coin.Transactions{txn}} prevHash := cipher.SHA256{} head := coin.BlockHeader{ Time: timestamp, BodyHash: body.Hash(), PrevHash: prevHash, BkSeq: 0, Version: 0, Fee: 0, UxHash: coin.NewUnspentPool().GetUxHash(), } b := coin.Block{ Head: head, Body: body, } // b.Body.Transactions[0].UpdateHeader() fbc.blocks = append(fbc.blocks, b) ux := coin.UxOut{ Head: coin.UxHead{ Time: timestamp, BkSeq: 0, }, Body: coin.UxBody{ SrcTransaction: txn.InnerHash, //user inner hash Address: genesisAddr, Coins: genesisCoins, Hours: genesisCoins, // Allocate 1 coin hour per coin }, } fbc.unspent.Add(ux) return b }
// 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 }
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 TestBlockchainVerifyBlock(t *testing.T) { ft := FakeTree{} bc := NewBlockchain(&ft, nil) gb := bc.CreateGenesisBlock(genAddress, _genCoins, _genTime) // Genesis block not valid after the fact assert.NotNil(t, bc.verifyBlock(gb)) assert.Equal(t, bc.Len(), uint64(1)) _, ux := addBlockToBlockchain(t, bc) assert.Equal(t, bc.Len(), uint64(3)) // Valid block tx := coin.Transaction{} tx.PushInput(ux.Hash()) tx.PushOutput(genAddress, ux.Body.Coins, ux.CoinHours(bc.Time())) tx.SignInputs([]cipher.SecKey{genSecret}) tx.UpdateHeader() b, err := bc.NewBlockFromTransactions(coin.Transactions{tx}, bc.Time()+_incTime) assert.Equal(t, len(b.Body.Transactions), 1) assert.Equal(t, len(b.Body.Transactions[0].Out), 1) assert.Nil(t, err) assert.Nil(t, bc.verifyBlock(b)) // Invalid block header b.Head.BkSeq = gb.Head.BkSeq assert.Equal(t, len(b.Body.Transactions), 1) assert.Equal(t, len(b.Body.Transactions[0].Out), 1) assertError(t, bc.verifyBlock(b), "BkSeq invalid") // Invalid transactions, makes duplicate outputs b.Head.BkSeq = bc.Head().Head.BkSeq + 1 b.Body.Transactions = append(b.Body.Transactions, b.Body.Transactions[0]) b.Head.BodyHash = b.HashBody() assertError(t, bc.verifyBlock(b), "Duplicate unspent output across transactions") }
func splitUnspent(t *testing.T, bc *Blockchain, ux coin.UxOut) coin.UxArray { tx := coin.Transaction{} hrs := ux.CoinHours(bc.Time()) if hrs < 2 { log.Panic("Not enough hours, would generate duplicate output") } assert.Equal(t, ux.Body.Address, genAddress) tx.PushInput(ux.Hash()) coinsA := ux.Body.Coins / 2 coinsB := coinsA if (ux.Body.Coins/1e6)%2 == 1 { coinsA = (ux.Body.Coins - 1e6) / 2 coinsB = coinsA + 1e6 } tx.PushOutput(genAddress, coinsA, hrs/4) tx.PushOutput(genAddress, coinsB, hrs/2) 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) assert.Equal(t, len(uxs), 2) return uxs }
func TestCreateUnspents(t *testing.T) { ft := FakeTree{} bc := NewBlockchain(&ft, nil) bc.CreateGenesisBlock(genAddress, _genCoins, _genTime) // 1 out tx := coin.Transaction{} tx.PushOutput(genAddress, 11e6, 255) bh := coin.BlockHeader{ Time: tNow(), BkSeq: uint64(1), } uxout := coin.CreateUnspents(bh, tx) assert.Equal(t, len(uxout), 1) assertValidUnspents(t, bh, tx, uxout) // Multiple outs. Should work regardless of validity tx = coin.Transaction{} ux := makeUxOut(t) tx.PushInput(ux.Hash()) tx.PushOutput(genAddress, 100, 150) tx.PushOutput(genAddress, 200, 77) bh.BkSeq++ uxout = coin.CreateUnspents(bh, tx) assert.Equal(t, len(uxout), 2) assertValidUnspents(t, bh, tx, uxout) // No outs tx = coin.Transaction{} uxout = coin.CreateUnspents(bh, tx) assertValidUnspents(t, bh, tx, uxout) }
//InjectTransaction makes the blockchain server aware of raw transactions //InjectTransaction inserts the transaction into the unconfirmed set // TODO: lock for thread safety func (self *Blockchain) InjectTransaction(txn coin.Transaction) error { //strict filter would disallow transactions that cant be executed from unspent output set if txn.Size() > MaxTransactionSize { //16 KB/max size return errors.New("transaction over size limit") } if err := self.blockchain.VerifyTransaction(txn); err != nil { return err } self.Unconfirmed.RecordTxn(txn) return nil }
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) } }
// Performs additional transaction verification at the unconfirmed pool level. // This checks tunable parameters that should prevent the transaction from // entering the blockchain, but cannot be done at the blockchain level because // they may be changed. func VerifyTransaction(bc *coin.Blockchain, t *coin.Transaction, maxSize int, burnFactor uint64) error { if t.Size() > maxSize { return errors.New("Transaction too large") } if fee, err := bc.TransactionFee(t); err != nil { return err } else if burnFactor != 0 && t.OutputHours()/burnFactor > fee { return errors.New("Transaction fee minimum not met") } return nil }
func makeTransactionWithSecret(t *testing.T) (coin.Transaction, cipher.SecKey) { tx := coin.Transaction{} ux, s := makeUxOutWithSecret(t) tx.PushInput(ux.Hash()) tx.SignInputs([]cipher.SecKey{s}) tx.PushOutput(makeAddress(), 10e6, 100) tx.UpdateHeader() return tx, s }
//move into visor //DEPRECATE func (self *Visor) InjectTransaction(txn coin.Transaction, pool *Pool) (coin.Transaction, error) { err := txn.Verify() if err != nil { return txn, errors.New("Transaction Verification Failed") } err, _ = self.Visor.InjectTxn(txn) if err == nil { self.BroadcastTransaction(txn, pool) } return txn, err }
// VerifyTransactionFee performs additional transaction verification at the unconfirmed pool level. // This checks tunable parameters that should prevent the transaction from // entering the blockchain, but cannot be done at the blockchain level because // they may be changed. func VerifyTransactionFee(bc *Blockchain, t *coin.Transaction) error { fee, err := bc.TransactionFee(t) if err != nil { return err } //calculate total number of coinhours var total = t.OutputHours() + fee //make sure at least half the coin hours are destroyed if fee < total/BurnFactor { return errors.New("Transaction coinhour fee minimum not met") } return nil }
func TestSpendsForAddresses(t *testing.T) { up := NewUnconfirmedTxnPool() unspent := coin.NewUnspentPool() addrs := make(map[cipher.Address]byte, 0) n := 4 useAddrs := make([]cipher.Address, n) for i, _ := range useAddrs { useAddrs[i] = makeAddress() } useAddrs[1] = useAddrs[0] for _, a := range useAddrs { addrs[a] = byte(1) } // Make confirmed transactions to add to unspent pool uxs := make(coin.UxArray, 0) for i := 0; i < n; i++ { txn := coin.Transaction{} txn.PushInput(randSHA256()) txn.PushOutput(useAddrs[i], 10e6, 1000) uxa := coin.CreateUnspents(coin.BlockHeader{}, txn) for _, ux := range uxa { unspent.Add(ux) } uxs = append(uxs, uxa...) } assert.Equal(t, len(uxs), 4) // Make unconfirmed txns that spend those unspents for i := 0; i < n; i++ { txn := coin.Transaction{} txn.PushInput(uxs[i].Hash()) txn.PushOutput(makeAddress(), 10e6, 1000) ut := UnconfirmedTxn{ Txn: txn, } up.Txns[ut.Hash()] = ut } // Now look them up assert.Equal(t, len(addrs), 3) assert.Equal(t, len(up.Txns), 4) auxs := up.SpendsForAddresses(&unspent, addrs) assert.Equal(t, len(auxs), 3) assert.Equal(t, len(auxs[useAddrs[0]]), 2) assert.Equal(t, len(auxs[useAddrs[2]]), 1) assert.Equal(t, len(auxs[useAddrs[3]]), 1) assert.Equal(t, auxs[useAddrs[0]], coin.UxArray{uxs[0], uxs[1]}) assert.Equal(t, auxs[useAddrs[2]], coin.UxArray{uxs[2]}) assert.Equal(t, auxs[useAddrs[3]], coin.UxArray{uxs[3]}) }
// 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 }
//move into visor //DEPRECATE func (self *Visor) InjectTransaction(txn coin.Transaction, pool *Pool) (coin.Transaction, error) { if err := visor.VerifyTransactionFee(self.Visor.Blockchain, &txn); err != nil { return txn, err } if err := txn.Verify(); err != nil { return txn, fmt.Errorf("Transaction Verification Failed, %v", err) } err, _ := self.Visor.InjectTxn(txn) if err == nil { self.BroadcastTransaction(txn, pool) } return txn, err }
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) } }
// NewTransaction create skycoin transaction. func newTransaction(utxos []unspentOut, keys []cipher.SecKey, outs []coin.TransactionOutput) (*coin.Transaction, error) { tx := coin.Transaction{} // keys := make([]cipher.SecKey, len(utxos)) for _, u := range utxos { tx.PushInput(cipher.MustSHA256FromHex(u.Hash)) } for _, o := range outs { if (o.Coins % 1e6) != 0 { return nil, errors.New("skycoin coins must be multiple of 1e6") } tx.PushOutput(o.Address, o.Coins, o.Hours) } // tx.Verify() tx.SignInputs(keys) tx.UpdateHeader() return &tx, nil }
func addBlock(bc historydb.Blockchainer, td testData, tm uint64) (*coin.Block, *coin.Transaction, error) { tx := coin.Transaction{} // get unspent output ux, err := getUx(bc, td.Vin.BlockSeq, td.Vin.TxID, td.Vin.Addr) if err != nil { return nil, nil, err } if ux == nil { return nil, nil, errors.New("no unspent output") } tx.PushInput(ux.Hash()) for _, o := range td.Vouts { addr, err := cipher.DecodeBase58Address(o.ToAddr) if err != nil { return nil, nil, err } tx.PushOutput(addr, o.Coins, o.Hours) } sigKey := cipher.MustSecKeyFromHex(td.Vin.SigKey) tx.SignInputs([]cipher.SecKey{sigKey}) tx.UpdateHeader() if err := bc.VerifyTransaction(tx); err != nil { return nil, nil, err } preBlock := bc.GetBlock(td.PreBlockHash) b := newBlock(*preBlock, tm, *bc.GetUnspent(), coin.Transactions{tx}, _feeCalc) // uxs, err := bc.ExecuteBlock(&b) _, err = bc.ExecuteBlock(&b) if err != nil { return nil, nil, err } return &b, &tx, nil }
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 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]) }
// Creates a Transaction spending coins and hours from our coins func CreateSpendingTransaction(wallet Wallet, unconfirmed *UnconfirmedTxnPool, unspent *coin.UnspentPool, headTime uint64, amt Balance, fee, burnFactor uint64, dest coin.Address) (coin.Transaction, error) { txn := coin.Transaction{} auxs := unspent.AllForAddresses(wallet.GetAddresses()) // Subtract pending spends from available puxs := unconfirmed.SpendsForAddresses(unspent, wallet.GetAddressSet()) auxs = auxs.Sub(puxs) // Determine which unspents to spend spends, err := createSpends(headTime, auxs.Flatten(), amt, fee, burnFactor) if err != nil { return txn, err } // Add these unspents as tx inputs toSign := make([]coin.SecKey, len(spends)) spending := Balance{0, 0} for i, au := range spends { entry, exists := wallet.GetEntry(au.Body.Address) if !exists { log.Panic("On second thought, the wallet entry does not exist") } txn.PushInput(au.Hash()) toSign[i] = entry.Secret spending.Coins += au.Body.Coins spending.Hours += au.CoinHours(headTime) } // Determine how much change we get back, if any _, changeHours, err := calculateBurnAndChange(spending.Hours, amt.Hours, fee, burnFactor) if err != nil { // This should not occur, else createSpends is broken return txn, err } change := NewBalance(spending.Coins-amt.Coins, changeHours) // TODO -- send change to a new address changeAddr := spends[0].Body.Address if change.Coins == 0 { if change.Hours > 0 { msg := ("Have enough coins, but not enough to send coin hours " + "change back. Would spend %d more hours than requested.") return txn, fmt.Errorf(msg, change.Hours) } } else { txn.PushOutput(changeAddr, change.Coins, change.Hours) } // Finalize the the transaction txn.PushOutput(dest, amt.Coins, amt.Hours) txn.SignInputs(toSign) txn.UpdateHeader() return txn, nil }
//DEPRECATE //deprecate dependency on wallet // Creates a Transaction spending coins and hours from our coins //MOVE SOMEWHERE ELSE //Move to wallet or move to ??? func CreateSpendingTransaction(wlt wallet.Wallet, unconfirmed *UnconfirmedTxnPool, unspent *coin.UnspentPool, headTime uint64, amt wallet.Balance, dest cipher.Address) (coin.Transaction, error) { txn := coin.Transaction{} auxs := unspent.AllForAddresses(wlt.GetAddresses()) // Subtract pending spends from available puxs := unconfirmed.SpendsForAddresses(unspent, wlt.GetAddressSet()) auxs = auxs.Sub(puxs) // Determine which unspents to spend spends, err := createSpends(headTime, auxs.Flatten(), amt) if err != nil { return txn, err } // Add these unspents as tx inputs toSign := make([]cipher.SecKey, len(spends)) spending := wallet.Balance{0, 0} for i, au := range spends { entry, exists := wlt.GetEntry(au.Body.Address) if !exists { log.Panic("On second thought, the wallet entry does not exist") } txn.PushInput(au.Hash()) toSign[i] = entry.Secret spending.Coins += au.Body.Coins spending.Hours += au.CoinHours(headTime) } //keep 1/4th of hours as change //send half to each address var changeHours uint64 = spending.Hours / 4 if amt.Coins == spending.Coins { txn.PushOutput(dest, amt.Coins, changeHours/2) txn.SignInputs(toSign) txn.UpdateHeader() return txn, nil } change := wallet.NewBalance(spending.Coins-amt.Coins, changeHours/2) // TODO -- send change to a new address changeAddr := spends[0].Body.Address //create transaction txn.PushOutput(changeAddr, change.Coins, change.Hours) txn.PushOutput(dest, amt.Coins, changeHours/2) txn.SignInputs(toSign) txn.UpdateHeader() return txn, nil }
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 }