2015-05-01 19:10:23 +02:00
|
|
|
// Copyright (c) 2015 The btcsuite developers
|
2015-12-01 19:44:58 +01:00
|
|
|
// Use of this source code is governed by an ISC
|
|
|
|
// license that can be found in the LICENSE file.
|
2015-04-06 21:18:04 +02:00
|
|
|
|
2018-11-08 03:15:03 +01:00
|
|
|
package wtxmgr
|
2015-04-06 21:18:04 +02:00
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
|
2018-05-15 07:11:11 +02:00
|
|
|
"github.com/btcsuite/btcd/chaincfg"
|
|
|
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
|
|
|
"github.com/btcsuite/btcd/wire"
|
|
|
|
"github.com/btcsuite/btcwallet/walletdb"
|
2015-04-06 21:18:04 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
// Spends: bogus
|
|
|
|
// Outputs: 10 BTC
|
2018-11-08 03:15:03 +01:00
|
|
|
exampleTxRecordA *TxRecord
|
2015-04-06 21:18:04 +02:00
|
|
|
|
|
|
|
// Spends: A:0
|
|
|
|
// Outputs: 5 BTC, 5 BTC
|
2018-11-08 03:15:03 +01:00
|
|
|
exampleTxRecordB *TxRecord
|
2015-04-06 21:18:04 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
func init() {
|
2016-08-08 21:49:09 +02:00
|
|
|
tx := spendOutput(&chainhash.Hash{}, 0, 10e8)
|
2018-11-08 03:15:03 +01:00
|
|
|
rec, err := NewTxRecordFromMsgTx(tx, timeNow())
|
2015-04-06 21:18:04 +02:00
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
exampleTxRecordA = rec
|
|
|
|
|
|
|
|
tx = spendOutput(&exampleTxRecordA.Hash, 0, 5e8, 5e8)
|
2018-11-08 03:15:03 +01:00
|
|
|
rec, err = NewTxRecordFromMsgTx(tx, timeNow())
|
2015-04-06 21:18:04 +02:00
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
exampleTxRecordB = rec
|
|
|
|
}
|
|
|
|
|
|
|
|
var exampleBlock100 = makeBlockMeta(100)
|
|
|
|
|
|
|
|
// This example demonstrates reporting the Store balance given an unmined and
|
|
|
|
// mined transaction given 0, 1, and 6 block confirmations.
|
|
|
|
func ExampleStore_Balance() {
|
2017-01-21 17:29:06 +01:00
|
|
|
s, db, teardown, err := testStore()
|
2015-04-06 21:18:04 +02:00
|
|
|
defer teardown()
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Prints balances for 0 block confirmations, 1 confirmation, and 6
|
|
|
|
// confirmations.
|
|
|
|
printBalances := func(syncHeight int32) {
|
2017-01-21 17:29:06 +01:00
|
|
|
dbtx, err := db.BeginReadTx()
|
2015-04-06 21:18:04 +02:00
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
return
|
|
|
|
}
|
2017-01-21 17:29:06 +01:00
|
|
|
defer dbtx.Rollback()
|
|
|
|
ns := dbtx.ReadBucket(namespaceKey)
|
|
|
|
zeroConfBal, err := s.Balance(ns, 0, syncHeight)
|
2015-04-06 21:18:04 +02:00
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
return
|
|
|
|
}
|
2017-01-21 17:29:06 +01:00
|
|
|
oneConfBal, err := s.Balance(ns, 1, syncHeight)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
sixConfBal, err := s.Balance(ns, 6, syncHeight)
|
2015-04-06 21:18:04 +02:00
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
fmt.Printf("%v, %v, %v\n", zeroConfBal, oneConfBal, sixConfBal)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Insert a transaction which outputs 10 BTC unmined and mark the output
|
|
|
|
// as a credit.
|
2017-01-21 17:29:06 +01:00
|
|
|
err = walletdb.Update(db, func(tx walletdb.ReadWriteTx) error {
|
|
|
|
ns := tx.ReadWriteBucket(namespaceKey)
|
|
|
|
err := s.InsertTx(ns, exampleTxRecordA, nil)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return s.AddCredit(ns, exampleTxRecordA, nil, 0, false)
|
|
|
|
})
|
2015-04-06 21:18:04 +02:00
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
printBalances(100)
|
|
|
|
|
|
|
|
// Mine the transaction in block 100 and print balances again with a
|
|
|
|
// sync height of 100 and 105 blocks.
|
2017-01-21 17:29:06 +01:00
|
|
|
err = walletdb.Update(db, func(tx walletdb.ReadWriteTx) error {
|
|
|
|
ns := tx.ReadWriteBucket(namespaceKey)
|
|
|
|
return s.InsertTx(ns, exampleTxRecordA, &exampleBlock100)
|
|
|
|
})
|
2015-04-06 21:18:04 +02:00
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
printBalances(100)
|
|
|
|
printBalances(105)
|
|
|
|
|
|
|
|
// Output:
|
|
|
|
// 10 BTC, 0 BTC, 0 BTC
|
|
|
|
// 10 BTC, 10 BTC, 0 BTC
|
|
|
|
// 10 BTC, 10 BTC, 10 BTC
|
|
|
|
}
|
|
|
|
|
|
|
|
func ExampleStore_Rollback() {
|
2017-01-21 17:29:06 +01:00
|
|
|
s, db, teardown, err := testStore()
|
2015-04-06 21:18:04 +02:00
|
|
|
defer teardown()
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-01-21 17:29:06 +01:00
|
|
|
err = walletdb.Update(db, func(tx walletdb.ReadWriteTx) error {
|
|
|
|
ns := tx.ReadWriteBucket(namespaceKey)
|
2015-04-06 21:18:04 +02:00
|
|
|
|
2017-01-21 17:29:06 +01:00
|
|
|
// Insert a transaction which outputs 10 BTC in a block at height 100.
|
|
|
|
err := s.InsertTx(ns, exampleTxRecordA, &exampleBlock100)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Rollback everything from block 100 onwards.
|
|
|
|
err = s.Rollback(ns, 100)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-04-06 21:18:04 +02:00
|
|
|
|
2017-01-21 17:29:06 +01:00
|
|
|
// Assert that the transaction is now unmined.
|
|
|
|
details, err := s.TxDetails(ns, &exampleTxRecordA.Hash)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if details == nil {
|
|
|
|
return fmt.Errorf("no details found")
|
|
|
|
}
|
|
|
|
fmt.Println(details.Block.Height)
|
|
|
|
return nil
|
|
|
|
})
|
2015-04-06 21:18:04 +02:00
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Output:
|
|
|
|
// -1
|
|
|
|
}
|
|
|
|
|
|
|
|
func Example_basicUsage() {
|
|
|
|
// Open the database.
|
|
|
|
db, dbTeardown, err := testDB()
|
|
|
|
defer dbTeardown()
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-01-21 17:29:06 +01:00
|
|
|
// Open a read-write transaction to operate on the database.
|
|
|
|
dbtx, err := db.BeginReadWriteTx()
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
defer dbtx.Commit()
|
|
|
|
|
|
|
|
// Create a bucket for the transaction store.
|
|
|
|
b, err := dbtx.CreateTopLevelBucket([]byte("txstore"))
|
2015-04-06 21:18:04 +02:00
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-01-21 17:29:06 +01:00
|
|
|
// Create and open the transaction store in the provided namespace.
|
2018-11-08 03:15:03 +01:00
|
|
|
err = Create(b)
|
2016-03-17 15:05:11 +01:00
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
return
|
|
|
|
}
|
2018-11-08 03:15:03 +01:00
|
|
|
s, err := Open(b, &chaincfg.TestNet3Params)
|
2015-04-06 21:18:04 +02:00
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Insert an unmined transaction that outputs 10 BTC to a wallet address
|
|
|
|
// at output 0.
|
2017-01-21 17:29:06 +01:00
|
|
|
err = s.InsertTx(b, exampleTxRecordA, nil)
|
2015-04-06 21:18:04 +02:00
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
return
|
|
|
|
}
|
2017-01-21 17:29:06 +01:00
|
|
|
err = s.AddCredit(b, exampleTxRecordA, nil, 0, false)
|
2015-04-06 21:18:04 +02:00
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Insert a second transaction which spends the output, and creates two
|
|
|
|
// outputs. Mark the second one (5 BTC) as wallet change.
|
2017-01-21 17:29:06 +01:00
|
|
|
err = s.InsertTx(b, exampleTxRecordB, nil)
|
2015-04-06 21:18:04 +02:00
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
return
|
|
|
|
}
|
2017-01-21 17:29:06 +01:00
|
|
|
err = s.AddCredit(b, exampleTxRecordB, nil, 1, true)
|
2015-04-06 21:18:04 +02:00
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Mine each transaction in a block at height 100.
|
2017-01-21 17:29:06 +01:00
|
|
|
err = s.InsertTx(b, exampleTxRecordA, &exampleBlock100)
|
2015-04-06 21:18:04 +02:00
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
return
|
|
|
|
}
|
2017-01-21 17:29:06 +01:00
|
|
|
err = s.InsertTx(b, exampleTxRecordB, &exampleBlock100)
|
2015-04-06 21:18:04 +02:00
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Print the one confirmation balance.
|
2017-01-21 17:29:06 +01:00
|
|
|
bal, err := s.Balance(b, 1, 100)
|
2015-04-06 21:18:04 +02:00
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
fmt.Println(bal)
|
|
|
|
|
|
|
|
// Fetch unspent outputs.
|
2017-01-21 17:29:06 +01:00
|
|
|
utxos, err := s.UnspentOutputs(b)
|
2015-04-06 21:18:04 +02:00
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
}
|
|
|
|
expectedOutPoint := wire.OutPoint{Hash: exampleTxRecordB.Hash, Index: 1}
|
|
|
|
for _, utxo := range utxos {
|
|
|
|
fmt.Println(utxo.OutPoint == expectedOutPoint)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Output:
|
|
|
|
// 5 BTC
|
|
|
|
// true
|
|
|
|
}
|