Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/wallet preparetx #24

Merged
merged 28 commits into from
Nov 5, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
b92abc0
init port of code from dlcdemo
akekeke Oct 30, 2018
d5cf21f
WIP ported btcwallet code
akekeke Oct 30, 2018
dbff721
added Open func, added txstore to wallet
akekeke Oct 31, 2018
d45c01a
refactored wallet init, seperated wallet and db creation
akekeke Oct 31, 2018
aebabaf
added listunspent func
akekeke Nov 1, 2018
9d0f19f
fixed lint issues
akekeke Nov 1, 2018
c948351
added comments and fixed lint
akekeke Nov 1, 2018
0e63022
fixed one xommment
akekeke Nov 1, 2018
860a882
init port of code from dlcdemo
akekeke Oct 30, 2018
4eb72f8
WIP ported btcwallet code
akekeke Oct 30, 2018
efcceb8
added listunspent func
akekeke Nov 1, 2018
8cc819d
fixed lint issues
akekeke Nov 1, 2018
aba2632
added comments and fixed lint
akekeke Nov 1, 2018
9fc81ce
added one more commmetn
akekeke Nov 1, 2018
cbfea64
merge WIP
akekeke Nov 2, 2018
2551333
merge WIP part 2
akekeke Nov 2, 2018
88dcdc2
refactored methods to return structs instead of interfaces
akekeke Nov 2, 2018
e0ebfbd
cleaned up
akekeke Nov 2, 2018
2cad023
more cleaning
akekeke Nov 2, 2018
c20b49c
added privpass to wallet struct
akekeke Nov 2, 2018
eeb6fbe
moved some func to test_util
akekeke Nov 2, 2018
74077b0
refactored Wallet interface
akekeke Nov 5, 2018
42cfd05
added back deleted stuff
akekeke Nov 5, 2018
d2d5285
fixed typo
akekeke Nov 5, 2018
8b77ef5
fix wallet creation (#49)
Jwata Nov 5, 2018
87c24db
utxo is not pointer anymore
akekeke Nov 5, 2018
98a9c41
cleaned up
akekeke Nov 5, 2018
c005e89
fix lint errors
Jwata Nov 5, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 18 additions & 5 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 11 additions & 22 deletions internal/wallet/address.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ import (
)

func (w *wallet) NewPubkey() (pub *btcec.PublicKey, err error) {
// TODO: generate new pubkey and address using newAddress
_, err = w.newAddress(waddrmgr.KeyScopeBIP0084, []byte{}, uint32(1), uint32(1))
mAddr, err := w.newAddress()
if err != nil {
return
return nil, err
}
return
pub = (mAddr.(waddrmgr.ManagedPubKeyAddress)).PubKey()
return pub, err
}

func (w *wallet) NewWitnessPubkeyScript() (pkScript []byte, err error) {
Expand All @@ -25,36 +25,25 @@ func (w *wallet) NewWitnessPubkeyScript() (pkScript []byte, err error) {
return script.P2WPKHpkScript(pub)
}

// NewAddress returns a new ManagedAddress for a given scope and account number.
// NOTE: this function callsNextExternalAddresses to generate a ManagadAdddress.
func (w *wallet) newAddress(scope waddrmgr.KeyScope, privPass []byte,
account uint32, numAddresses uint32) ([]waddrmgr.ManagedAddress, error) {
// unlock Manager
err := walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
e := w.manager.Unlock(ns, privPass)
return e
})
if err != nil {
return nil, err
}

// get ScopedKeyManager
scopedMgr, err := w.manager.FetchScopedKeyManager(scope)
// NewAddress returns a new ManagedAddress
// NOTE: this function calls NextExternalAddresses to generate a ManagadAdddress.
func (w *wallet) newAddress() (waddrmgr.ManagedAddress, error) {
scopedMgr, err := w.manager.FetchScopedKeyManager(waddrmgrKeyScope)
if err != nil {
return nil, err
}

var numAddresses uint32 = 1
var addrs []waddrmgr.ManagedAddress
err = walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
var e error
addrs, e = scopedMgr.NextExternalAddresses(ns, account, numAddresses)
addrs, e = scopedMgr.NextExternalAddresses(ns, w.account, numAddresses)
return e
})
if err != nil {
return nil, err
}

return addrs, nil
return addrs[0], nil
}
18 changes: 17 additions & 1 deletion internal/wallet/address_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,26 @@ package wallet

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestNewPubkey(t *testing.T) {
wallet, tearDownFunc := setupWallet(t)
defer tearDownFunc()

pub, err := wallet.NewPubkey()

assert.Nil(t, err)
assert.NotNil(t, pub)
}

func TestWitnessNewPubkeyScript(t *testing.T) {
func TestNewWitnessPubkeyScript(t *testing.T) {
wallet, tearDownFunc := setupWallet(t)
defer tearDownFunc()

pkScript, err := wallet.NewWitnessPubkeyScript()

assert.Nil(t, err)
assert.NotEmpty(t, pkScript)
}
25 changes: 8 additions & 17 deletions internal/wallet/test_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package wallet
import (
"io/ioutil"
"os"
"path/filepath"
"testing"

"github.com/btcsuite/btcd/chaincfg"
Expand All @@ -19,10 +18,9 @@ var (
0xb6, 0xb8, 0x39, 0xbe, 0xd9, 0xfd, 0x21, 0x6a, 0x6c,
0x03, 0xce, 0xe2, 0x2c, 0x84,
}
testPubPass = []byte("_DJr{fL4H0O}*-0\n:V1izc)(6BomK")
testPrivPass = []byte("81lUHXnOMZ@?XXd7O9xyDIWIbXX-lj")
testWalletName = "testwallet"
testAccountName = "testy"
testPubPass = []byte("_DJr{fL4H0O}*-0\n:V1izc)(6BomK")
testPrivPass = []byte("81lUHXnOMZ@?XXd7O9xyDIWIbXX-lj")
testWalletName = "testwallet.db"
)

func setupDB(t *testing.T) (db walletdb.DB, tearDownFunc func()) {
Expand All @@ -31,11 +29,7 @@ func setupDB(t *testing.T) (db walletdb.DB, tearDownFunc func()) {
dbDirPath, err := ioutil.TempDir("", "testdb")
assert.Nil(err)

dbPath := filepath.Join(dbDirPath, testWalletName+".db")
err = os.MkdirAll(dbDirPath, 0700)
assert.Nil(err)

db, err = walletdb.Create("bdb", dbPath)
db, err = createDB(dbDirPath, testWalletName)
assert.Nil(err)

tearDownFunc = func() {
Expand All @@ -48,21 +42,18 @@ func setupDB(t *testing.T) (db walletdb.DB, tearDownFunc func()) {
return
}

func setupWallet(t *testing.T) (tearDownFunc func(), w Wallet) {
func setupWallet(t *testing.T) (*wallet, func()) {
assert := assert.New(t)
db, deleteDB := setupDB(t)

err := Create(db, testNetParams, testSeed, testPubPass, testPrivPass)
assert.Nil(err)

w, err = Open(db, testPubPass, testNetParams)
w, err := create(db, testNetParams, testSeed, testPubPass, testPrivPass)
assert.Nil(err)

tearDownFunc = func() {
tearDownFunc := func() {
err = w.Close()
assert.Nil(err)
deleteDB()
}

return
return w, tearDownFunc
}
140 changes: 137 additions & 3 deletions internal/wallet/utxo.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,147 @@
package wallet

import (
"encoding/hex"

"github.com/btcsuite/btcd/btcjson"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcutil"
"github.com/btcsuite/btcwallet/waddrmgr"
"github.com/btcsuite/btcwallet/walletdb"
"github.com/btcsuite/btcwallet/wtxmgr"
)

// Utxo is a unspend transaction output
type Utxo btcjson.ListUnspentResult
type Utxo = btcjson.ListUnspentResult

// ListUnspent returns unspent transactions
// ListUnspent returns unspent transactions.
// TODO: add filter
// Only utxos with address contained the param addresses will be considered.
// If param addresses is empty, all addresses are considered and there is no
// filter
func (w *wallet) ListUnspent() (utxos []Utxo, err error) {
return
var results []btcjson.ListUnspentResult
err = walletdb.View(w.db, func(tx walletdb.ReadTx) error {
addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
txmgrNs := tx.ReadBucket(wtxmgrNamespaceKey)

// filter := len(addresses) != 0

unspent, e := w.txStore.UnspentOutputs(txmgrNs)
if e != nil {
return e
}

// utxos = make([]*btcjson.ListUnspentResult, 0, len(unspent))
for i := range unspent {
output := unspent[i]
result := w.credit2ListUnspentResult(output, addrmgrNs)
// TODO: result might return nil... catch that nil?
results = append(results, *result)
}
return nil
})
utxos = results
return utxos, err
}

func (w *wallet) credit2ListUnspentResult(
c wtxmgr.Credit,
addrmgrNs walletdb.ReadBucket) *btcjson.ListUnspentResult {

syncBlock := w.manager.SyncedTo()

// TODO: add minconf, maxconf params
confs := confirms(c.Height, syncBlock.Height)
// // Outputs with fewer confirmations than the minimum or more
// // confs than the maximum are excluded.
// confs := confirms(output.Height, syncBlock.Height)
// if confs < minconf || confs > maxconf {
// continue
// }

// Only mature coinbase outputs are included.
if c.FromCoinBase {
target := int32(w.params.CoinbaseMaturity) // make param
if !confirmed(target, c.Height, syncBlock.Height) {
// continue
return nil // maybe?

}
}

acctName := accountName

result := &btcjson.ListUnspentResult{
TxID: c.OutPoint.Hash.String(),
Vout: c.OutPoint.Index,
Account: acctName,
ScriptPubKey: hex.EncodeToString(c.PkScript),
Amount: c.Amount.ToBTC(),
Confirmations: int64(confs),
Spendable: true,
}

return result
}

// isSpendable determines if given ScriptClass is spendable or not.
// Does NOT support watch-only addresses. This func will need to be rewritten
// to support watch-only addresses
func (w *wallet) isSpendable(sc txscript.ScriptClass, addrs []btcutil.Address,
addrmgrNs walletdb.ReadBucket) (spendable bool) {
// At the moment watch-only addresses are not supported, so all
// recorded outputs that are not multisig are "spendable".
// Multisig outputs are only "spendable" if all keys are
// controlled by this wallet.
//
// TODO: Each case will need updates when watch-only addrs
// is added. For P2PK, P2PKH, and P2SH, the address must be
// looked up and not be watching-only. For multisig, all
// pubkeys must belong to the manager with the associated
// private key (currently it only checks whether the pubkey
// exists, since the private key is required at the moment).
scSwitch:
switch sc {
case txscript.PubKeyHashTy:
spendable = true
case txscript.PubKeyTy:
spendable = true
case txscript.WitnessV0ScriptHashTy:
spendable = true
case txscript.WitnessV0PubKeyHashTy:
spendable = true
case txscript.MultiSigTy:
for _, a := range addrs {
_, err := w.manager.Address(addrmgrNs, a)
if err == nil {
continue
}
if waddrmgr.IsError(err, waddrmgr.ErrAddressNotFound) {
break scSwitch
}
// return err TODO: figure out what to replace the return error
}
spendable = true
}

return spendable
}

// confirms returns the number of confirmations for a transaction in a block at
// height txHeight (or -1 for an unconfirmed tx) given the chain height
// curHeight.
func confirms(txHeight, curHeight int32) int32 {
switch {
case txHeight == -1, txHeight > curHeight:
return 0
default:
return curHeight - txHeight + 1
}
}

// confirmed checks whether a transaction at height txHeight has met minconf
// confirmations for a blockchain at height curHeight.
func confirmed(minconf, txHeight, curHeight int32) bool {
return confirms(txHeight, curHeight) >= minconf
}
Loading