Add support for running wallet on testnet3.

Websocket connections to btcd will be closed if btcd and btcwallet are
running on different networks.
This commit is contained in:
Josh Rickmar 2013-10-07 12:35:32 -04:00
parent 1d6741eb05
commit 3c4ff4b0f4
5 changed files with 98 additions and 17 deletions

View file

@ -22,6 +22,7 @@ import (
"fmt" "fmt"
"github.com/conformal/btcjson" "github.com/conformal/btcjson"
"github.com/conformal/btcwallet/wallet" "github.com/conformal/btcwallet/wallet"
"github.com/conformal/btcwire"
"sync" "sync"
"time" "time"
) )
@ -568,22 +569,37 @@ func SendMany(reply chan []byte, msg *btcjson.Message) {
// All three parameters are required, and must be of type string. If // All three parameters are required, and must be of type string. If
// the wallet specified by account already exists, an invalid account // the wallet specified by account already exists, an invalid account
// name error is returned to the client. // name error is returned to the client.
//
// Wallets will be created on MainNet, or TestNet3 if btcwallet is run with
// the --testnet option.
func CreateEncryptedWallet(reply chan []byte, msg *btcjson.Message) { func CreateEncryptedWallet(reply chan []byte, msg *btcjson.Message) {
params, ok := msg.Params.([]interface{}) params, ok := msg.Params.([]interface{})
e := &InvalidParams
if !ok { if !ok {
log.Error("CreateEncryptedWallet: Cannot parse parameters.") ReplyError(reply, msg.Id, e)
return return
} }
var wname string
if len(params) != 3 { if len(params) != 3 {
ReplyError(reply, msg.Id, &InvalidParams) e.Message = "Incorrect number of parameters"
ReplyError(reply, msg.Id, e)
return return
} }
wname, ok1 := params[0].(string) wname, ok := params[0].(string)
desc, ok2 := params[1].(string) if !ok {
pass, ok3 := params[2].(string) e.Message = "Account is not a string"
if !ok1 || !ok2 || !ok3 { ReplyError(reply, msg.Id, e)
ReplyError(reply, msg.Id, &InvalidParams) return
}
desc, ok := params[1].(string)
if !ok {
e.Message = "Description is not a string"
ReplyError(reply, msg.Id, e)
return
}
pass, ok := params[2].(string)
if !ok {
e.Message = "Passphrase is not a string"
ReplyError(reply, msg.Id, e)
return return
} }
@ -597,7 +613,13 @@ func CreateEncryptedWallet(reply chan []byte, msg *btcjson.Message) {
} }
wallets.RUnlock() wallets.RUnlock()
w, err := wallet.NewWallet(wname, desc, []byte(pass)) var net btcwire.BitcoinNet
if cfg.TestNet3 {
net = btcwire.TestNet3
} else {
net = btcwire.MainNet
}
w, err := wallet.NewWallet(wname, desc, []byte(pass), net)
if err != nil { if err != nil {
log.Error("Error creating wallet: " + err.Error()) log.Error("Error creating wallet: " + err.Error())
ReplyError(reply, msg.Id, &InternalError) ReplyError(reply, msg.Id, &InternalError)

View file

@ -45,6 +45,7 @@ type config struct {
DataDir string `short:"D" long:"datadir" description:"Directory to store wallets and transactions"` DataDir string `short:"D" long:"datadir" description:"Directory to store wallets and transactions"`
Username string `short:"u" long:"username" description:"Username for btcd authorization"` Username string `short:"u" long:"username" description:"Username for btcd authorization"`
Password string `short:"P" long:"password" description:"Password for btcd authorization"` Password string `short:"P" long:"password" description:"Password for btcd authorization"`
TestNet3 bool `long:"testnet" description:"Use the test network"`
} }
// btcwalletHomeDir returns an OS appropriate home directory for btcwallet. // btcwalletHomeDir returns an OS appropriate home directory for btcwallet.

View file

@ -61,7 +61,8 @@ var (
// replyHandlers maps between a sequence number (passed as part of // replyHandlers maps between a sequence number (passed as part of
// the JSON Id field) and a function to handle a reply or notification // the JSON Id field) and a function to handle a reply or notification
// from btcd. As requests are received, this map is checked for a // from btcd. As requests are received, this map is checked for a
// handler function to route the reply to. // handler function to route the reply to. If the function returns
// true, the handler is removed from the map.
replyHandlers = struct { replyHandlers = struct {
sync.Mutex sync.Mutex
m map[uint64]func(interface{}, *btcjson.Error) bool m map[uint64]func(interface{}, *btcjson.Error) bool
@ -178,8 +179,9 @@ func frontendReqsNotifications(ws *websocket.Conn) {
// websocket and sends messages that btcwallet does not understand to // websocket and sends messages that btcwallet does not understand to
// btcd. Unlike FrontendHandler, exactly one BtcdHandler goroutine runs. // btcd. Unlike FrontendHandler, exactly one BtcdHandler goroutine runs.
func BtcdHandler(ws *websocket.Conn) { func BtcdHandler(ws *websocket.Conn) {
// Notification channel to return from listener goroutine when
// btcd disconnects.
disconnected := make(chan int) disconnected := make(chan int)
defer func() { defer func() {
close(disconnected) close(disconnected)
}() }()
@ -214,6 +216,7 @@ func BtcdHandler(ws *websocket.Conn) {
case r := <-btcdMsgs: case r := <-btcdMsgs:
if err := websocket.Message.Send(ws, r); err != nil { if err := websocket.Message.Send(ws, r); err != nil {
// btcd disconnected. // btcd disconnected.
log.Error("Unable to send message to btcd: %v", err)
return return
} }
} }
@ -446,13 +449,67 @@ func BtcdConnect(reply chan error) {
close(handlerClosed) close(handlerClosed)
}() }()
BtcdHandshake(btcdws)
<-handlerClosed
reply <- ErrConnLost
}
// BtcdHandshake first checks that the websocket connection between
// btcwallet and btcd is valid, that is, that there are no mismatching
// settings between the two processes (such as running on different
// Bitcoin networks). If the sanity checks pass, all wallets are set to
// be tracked against chain notifications from this btcd connection.
func BtcdHandshake(ws *websocket.Conn) {
seq.Lock()
n := seq.n
seq.n++
seq.Unlock()
msg := btcjson.Message{
Method: "getcurrentnet",
Id: fmt.Sprintf("btcwallet(%v)", n),
}
m, _ := json.Marshal(&msg)
correctNetwork := make(chan bool)
replyHandlers.Lock()
replyHandlers.m[n] = func(result interface{}, err *btcjson.Error) bool {
fmt.Println("got reply")
fnet, ok := result.(float64)
if !ok {
log.Error("btcd handshake: result is not a number")
ws.Close()
correctNetwork <- false
return true
}
var walletNetwork btcwire.BitcoinNet
if cfg.TestNet3 {
walletNetwork = btcwire.TestNet3
} else {
walletNetwork = btcwire.MainNet
}
correctNetwork <- btcwire.BitcoinNet(fnet) == walletNetwork
// No additional replies expected, remove handler.
return true
}
replyHandlers.Unlock()
btcdMsgs <- m
if !<-correctNetwork {
log.Error("btcd and btcwallet running on different Bitcoin networks")
ws.Close()
return
}
// Begin tracking wallets against this btcd instance. // Begin tracking wallets against this btcd instance.
wallets.RLock() wallets.RLock()
for _, w := range wallets.m { for _, w := range wallets.m {
w.Track() w.Track()
} }
wallets.RUnlock() wallets.RUnlock()
<-handlerClosed
reply <- ErrConnLost
} }

View file

@ -353,7 +353,7 @@ type Wallet struct {
// desc's binary representation must not exceed 32 and 256 bytes, // desc's binary representation must not exceed 32 and 256 bytes,
// respectively. All address private keys are encrypted with passphrase. // respectively. All address private keys are encrypted with passphrase.
// The wallet is returned unlocked. // The wallet is returned unlocked.
func NewWallet(name, desc string, passphrase []byte) (*Wallet, error) { func NewWallet(name, desc string, passphrase []byte, net btcwire.BitcoinNet) (*Wallet, error) {
if binary.Size(name) > 32 { if binary.Size(name) > 32 {
return nil, errors.New("name exceeds 32 byte maximum size") return nil, errors.New("name exceeds 32 byte maximum size")
} }
@ -382,7 +382,7 @@ func NewWallet(name, desc string, passphrase []byte) (*Wallet, error) {
// compat with armory. // compat with armory.
w := &Wallet{ w := &Wallet{
version: 0, // TODO(jrick): implement versioning version: 0, // TODO(jrick): implement versioning
net: btcwire.MainNet, net: net,
flags: walletFlags{ flags: walletFlags{
useEncryption: true, useEncryption: true,
watchingOnly: false, watchingOnly: false,

View file

@ -18,6 +18,7 @@ package wallet
import ( import (
"crypto/rand" "crypto/rand"
"github.com/conformal/btcwire"
"github.com/davecgh/go-spew/spew" "github.com/davecgh/go-spew/spew"
"os" "os"
"reflect" "reflect"
@ -78,7 +79,7 @@ func TestBtcAddressSerializer(t *testing.T) {
} }
func TestWalletCreationSerialization(t *testing.T) { func TestWalletCreationSerialization(t *testing.T) {
w1, err := NewWallet("banana wallet", "A wallet for testing.", []byte("banana")) w1, err := NewWallet("banana wallet", "A wallet for testing.", []byte("banana"), btcwire.MainNet)
if err != nil { if err != nil {
t.Error("Error creating new wallet: " + err.Error()) t.Error("Error creating new wallet: " + err.Error())
} }