Commit fd235573 authored by David Vorick's avatar David Vorick

bring back wallet tests

Some of them just got deleted because they were designed and written
using a different testing methodology. Though the tests got removed, the
functionality is already confirmed by the transaction pool testing.
parent e66b215c
......@@ -2,6 +2,22 @@
# and much faster mining and block constants.
all: install
# dependencies installs all of the dependencies that are required for building
# Sia.
dependencies:
go install -race std
go get -u code.google.com/p/gcfg
go get -u github.com/agl/ed25519
go get -u github.com/dchest/blake2b
go get -u github.com/inconshreveable/go-update
go get -u github.com/laher/goxc
go get -u github.com/mitchellh/go-homedir
go get -u github.com/spf13/cobra
go get -u github.com/stretchr/graceful
go get -u golang.org/x/crypto/twofish
go get -u golang.org/x/tools/cmd/cover
go get -u github.com/NebulousLabs/merkletree
# fmt calls go fmt on all packages.
fmt:
go fmt ./...
......@@ -16,6 +32,17 @@ REBUILD:
install: fmt REBUILD
go install -tags=dev ./...
# release builds and installs release binaries.
release: dependencies test-long REBUILD
go install ./...
# xc builds and packages release binaries for all systems by using goxc.
# Cross Compile - makes binaries for windows, linux, and mac, 32 and 64 bit.
xc: dependencies test test-long REBUILD
goxc -arch="amd64" -bc="linux windows darwin" -d=release -pv=0.3.0 \
-br=release -pr=beta -include=example-config,LICENSE*,README*,API* \
-tasks-=deb,deb-dev,deb-source,go-test
# clean removes all directories that get automatically created during
# development.
clean:
......@@ -35,8 +62,11 @@ test: clean fmt REBUILD
test-long: clean fmt REBUILD
go test -v -race -tags=test -timeout=180s ./...
# Testing for each package individually. Packages are added to this set as needed.
test-tpool: clean fmt REBUILD
go test -v -race -tags=test -timeout=8s github.com/NebulousLabs/Sia/modules/transactionpool
test-wallet: clean fmt REBUILD
go test -v -race -tags=test -timeout=8s github.com/NebulousLabs/Sia/modules/wallet
# cover runs the long tests and creats html files that show you which lines
# have been hit during testing and how many times each line has been hit.
......@@ -57,31 +87,4 @@ whitepaper:
@pdflatex whitepaper.tex > /dev/null
pdflatex whitepaper.tex
# dependencies installs all of the dependencies that are required for building
# Sia.
dependencies:
go install -race std
go get -u code.google.com/p/gcfg
go get -u github.com/agl/ed25519
go get -u github.com/dchest/blake2b
go get -u github.com/inconshreveable/go-update
go get -u github.com/laher/goxc
go get -u github.com/mitchellh/go-homedir
go get -u github.com/spf13/cobra
go get -u github.com/stretchr/graceful
go get -u golang.org/x/crypto/twofish
go get -u golang.org/x/tools/cmd/cover
go get -u github.com/NebulousLabs/merkletree
# release builds and installs release binaries.
release: dependencies test-long REBUILD
go install ./...
# xc builds and packages release binaries for all systems by using goxc.
# Cross Compile - makes binaries for windows, linux, and mac, 32 and 64 bit.
xc: dependencies test test-long REBUILD
goxc -arch="amd64" -bc="linux windows darwin" -d=release -pv=0.3.0 \
-br=release -pr=beta -include=example-config,LICENSE*,README*,API* \
-tasks-=deb,deb-dev,deb-source,go-test
.PHONY: all fmt install clean test test-long cover whitepaper dependencies release xc REBUILD
......@@ -148,17 +148,11 @@ func newTpoolTester(directory string, t *testing.T) (tpt *tpoolTester) {
// Mine blocks until there is money in the wallet.
for i := 0; i <= consensus.MaturityDelay; i++ {
for {
var found bool
_, found, err = tpt.miner.FindBlock()
if err != nil {
t.Fatal(err)
}
if found {
tpt.updateWait()
break
}
_, _, err = tpt.miner.FindBlock()
if err != nil {
t.Fatal(err)
}
tpt.updateWait()
}
return
......
......@@ -9,29 +9,19 @@ import (
// TestCoinAddress fetches a coin address from the wallet and then spends an
// output to the coin address to verify that the wallet is correctly
// recognizing coins sent to itself.
func (wt *WalletTester) testCoinAddress() {
func (wt *walletTester) testCoinAddress() {
// Get an address.
walletAddress, _, err := wt.CoinAddress()
walletAddress, _, err := wt.wallet.CoinAddress()
if err != nil {
wt.Fatal(err)
wt.t.Fatal(err)
}
// Send coins to the address, in a mined block.
siacoinInput, value := wt.FindSpendableSiacoinInput()
txn := wt.AddSiacoinInputToTransaction(consensus.Transaction{}, siacoinInput)
txn.SiacoinOutputs = append(txn.SiacoinOutputs, consensus.SiacoinOutput{
Value: value,
UnlockHash: walletAddress,
})
block := wt.MineCurrentBlock([]consensus.Transaction{txn})
err = wt.State.AcceptBlock(block)
if err != nil {
wt.Fatal(err)
}
// Send all of the wallets coins to itself.
wt.spendCoins(wt.wallet.Balance(false), walletAddress)
// Check that the wallet sees the coins.
if wt.Balance(false).Cmp(consensus.ZeroCurrency) == 0 {
wt.Error("wallet didn't get the coins sent to it.")
if wt.wallet.Balance(false).Cmp(consensus.ZeroCurrency) == 0 {
wt.t.Error("wallet didn't get the coins sent to it.")
}
}
......
......@@ -7,34 +7,30 @@ import (
// TestSaveLoad tests that saving and loading a wallet restores its data.
func TestSaveLoad(t *testing.T) {
wt := NewWalletTester("Wallet - TestSaveLoad", t)
// add an output to the wallet
wt.testCoinAddress()
// save wallet data
err := wt.save()
err := wt.wallet.save()
if err != nil {
wt.Fatal(err)
wt.t.Fatal(err)
}
// create a new wallet using the saved data
newWallet, err := New(wt.state, wt.tpool, wt.saveDir)
newWallet, err := New(wt.cs, wt.tpool, wt.wallet.saveDir)
if err != nil {
wt.Fatal(err)
wt.t.Fatal(err)
}
// check that the wallets match
for mapKey := range wt.keys {
for mapKey := range wt.wallet.keys {
if _, exists := newWallet.keys[mapKey]; !exists {
wt.Fatal("Loaded wallet is missing a key")
wt.t.Fatal("Loaded wallet is missing a key")
}
}
for mapKey := range wt.timelockedKeys {
for mapKey := range wt.wallet.timelockedKeys {
if _, exists := newWallet.timelockedKeys[mapKey]; !exists {
wt.Fatal("Loaded wallet is missing a time-locked key")
wt.t.Fatal("Loaded wallet is missing a time-locked key")
}
}
if wt.Balance(true).Cmp(newWallet.Balance(true)) != 0 {
wt.Fatal("Loaded wallet has wrong balance")
}
// TODO: I don't know how to synchronize the wallet.
}
package wallet
// notifySubscribers tells each subscriber that the wallet has received an
// update.
func (w *Wallet) notifySubscribers() {
for _, subscriber := range w.subscribers {
select {
case subscriber <- struct{}{}:
default:
}
}
}
// WalletSubscribe adds a subscriber to the wallet.
func (w *Wallet) WalletSubscribe() <-chan struct{} {
c := make(chan struct{}, 1)
id := w.mu.Lock()
w.subscribers = append(w.subscribers, c)
w.mu.Unlock(id)
return c
}
......@@ -9,75 +9,57 @@ import (
// testFundTransaction funds and completes a transaction using the
// build-your-own transaction functions, checking that a no-refund transaction
// is created that is valid.
func (wt *WalletTester) testFundTransaction() {
// Get a coin address for the wallet and fund the wallet using money from
// the
addr, _, err := wt.CoinAddress()
if err != nil {
wt.Fatal(err)
}
siacoinOutput, value := wt.FindSpendableSiacoinInput()
walletFunderTxn := wt.AddSiacoinInputToTransaction(consensus.Transaction{}, siacoinOutput)
walletFunderOutput := consensus.SiacoinOutput{
Value: value,
UnlockHash: addr,
}
walletFunderTxn.SiacoinOutputs = append(walletFunderTxn.SiacoinOutputs, walletFunderOutput)
block := wt.MineCurrentBlock([]consensus.Transaction{walletFunderTxn})
err = wt.State.AcceptBlock(block)
if err != nil {
wt.Fatal(err)
}
func (wt *walletTester) testFundTransaction() {
// Build a transaction that intentionally needs a refund.
id, err := wt.RegisterTransaction(consensus.Transaction{})
id, err := wt.wallet.RegisterTransaction(consensus.Transaction{})
fund := wt.wallet.Balance(false).Sub(consensus.NewCurrency64(1))
if err != nil {
wt.Fatal(err)
wt.t.Fatal(err)
}
_, err = wt.FundTransaction(id, value.Sub(consensus.NewCurrency64(1)))
_, err = wt.wallet.FundTransaction(id, fund)
if err != nil {
wt.Fatal(err)
wt.t.Fatal(err)
}
_, _, err = wt.AddMinerFee(id, value.Sub(consensus.NewCurrency64(1)))
wt.updateWait()
_, _, err = wt.wallet.AddMinerFee(id, fund)
if err != nil {
wt.Fatal(err)
wt.t.Fatal(err)
}
t, err := wt.SignTransaction(id, true)
t, err := wt.wallet.SignTransaction(id, true)
if err != nil {
wt.Fatal(err)
wt.t.Fatal(err)
}
err = wt.tpool.AcceptTransaction(t)
if err != nil {
wt.Fatal(err)
wt.t.Fatal(err)
}
wt.updateWait()
// Check that the length of the created transaction is 1 siacoin, and that
// the unconfirmed balance of the wallet is 1.
if len(t.SiacoinOutputs) != 0 {
wt.Error("more than zero siacoin outputs created in custom transaction")
wt.t.Error("more than zero siacoin outputs created in custom transaction")
}
if wt.Balance(true).Cmp(consensus.NewCurrency64(1)) != 0 {
wt.Error(wt.Balance(true).MarshalJSON())
wt.Error("wallet balance not reporting at one?")
if wt.wallet.Balance(true).Cmp(consensus.NewCurrency64(1)) != 0 {
wt.t.Error(wt.wallet.Balance(true).MarshalJSON())
wt.t.Error("wallet balance not reporting at one?")
}
// Dump the transaction pool into a block and see that the balance still
// registers correctly.
txns := wt.tpool.TransactionSet()
block = wt.MineCurrentBlock(txns)
err = wt.State.AcceptBlock(block)
_, _, err = wt.miner.FindBlock()
if err != nil {
wt.Error(err)
wt.t.Fatal(err)
}
// Check that the length of the created transaction is 1 siacoin, and that
// the unconfirmed balance of the wallet is 1.
if len(t.SiacoinOutputs) != 0 {
wt.Error("more than zero siacoin outputs created in custom transaction")
wt.t.Error("more than zero siacoin outputs created in custom transaction")
}
if wt.Balance(true).Cmp(consensus.NewCurrency64(1)) != 0 {
wt.Error(wt.Balance(true).MarshalJSON())
wt.Error("wallet balance not reporting at one?")
if wt.wallet.Balance(true).Cmp(consensus.NewCurrency64(1)) != 0 {
wt.t.Error(wt.wallet.Balance(true).MarshalJSON())
wt.t.Error("wallet balance not reporting at one?")
}
}
......
......@@ -94,4 +94,6 @@ func (w *Wallet) ReceiveTransactionPoolUpdate(revertedBlocks, appliedBlocks []co
for _, diff := range w.unconfirmedDiffs {
w.applyDiff(diff, consensus.DiffApply)
}
w.notifySubscribers()
}
package wallet
import (
"testing"
"github.com/NebulousLabs/Sia/consensus"
)
// testUnconfirmedTransaction has money sent to itself through an unconfirmed
// transaction in the transaction pool, and checks that 'update' adds the
// balance to the wallet's total funds.
func (wt *WalletTester) testUnconfirmedTransaction() {
// Get the initial balance, then get an address that can receive coins.
initialBal := wt.Balance(false)
address, _, err := wt.CoinAddress()
if err != nil {
wt.Fatal(err)
}
// Get a transaction with coins sent to the wallet's address.
input, value := wt.FindSpendableSiacoinInput()
txn := wt.AddSiacoinInputToTransaction(consensus.Transaction{}, input)
output := consensus.SiacoinOutput{
Value: value,
UnlockHash: address,
}
txn.SiacoinOutputs = append(txn.SiacoinOutputs, output)
err = wt.tpool.AcceptTransaction(txn)
if err != nil {
wt.Fatal(err)
}
// Update the wallet and check that the balance has increased by value.
expectedBal := initialBal.Add(value)
if expectedBal.Cmp(wt.Balance(false)) != 0 {
wt.Error("unexpected balance after adding unconfirmed transaction")
}
}
// TestUnconfirmedTransaction creates a wallet tester and uses it to call
// testUnconfirmedTransaction.
func TestUnconfirmedTransaction(t *testing.T) {
wt := NewWalletTester("Wallet - TestUnconfirmedTransaction", t)
wt.testUnconfirmedTransaction()
}
......@@ -70,6 +70,8 @@ type Wallet struct {
transactionCounter int
transactions map[string]*openTransaction
subscribers []chan struct{}
mu *sync.RWMutex
}
......
......@@ -6,6 +6,7 @@ import (
"github.com/NebulousLabs/Sia/consensus"
"github.com/NebulousLabs/Sia/modules"
"github.com/NebulousLabs/Sia/modules/gateway"
"github.com/NebulousLabs/Sia/modules/miner"
"github.com/NebulousLabs/Sia/modules/tester"
"github.com/NebulousLabs/Sia/modules/transactionpool"
)
......@@ -16,30 +17,108 @@ var (
// A Wallet tester contains a ConsensusTester and has a bunch of helpful
// functions for facilitating wallet integration testing.
type WalletTester struct {
*Wallet
*consensus.ConsensusTester
type walletTester struct {
cs *consensus.State
tpool modules.TransactionPool
miner modules.Miner
wallet *Wallet
minerChan <-chan struct{}
walletChan <-chan struct{}
t *testing.T
}
// spendCoins sends the desired amount of coins to the desired address, calling
// wait at all of the appropriate places to assist synchronization.
func (wt *walletTester) spendCoins(amount consensus.Currency, dest consensus.UnlockHash) (t consensus.Transaction, err error) {
output := consensus.SiacoinOutput{
Value: amount,
UnlockHash: dest,
}
id, err := wt.wallet.RegisterTransaction(t)
if err != nil {
return
}
_, err = wt.wallet.FundTransaction(id, amount)
if err != nil {
return
}
wt.updateWait()
_, _, err = wt.wallet.AddOutput(id, output)
if err != nil {
return
}
t, err = wt.wallet.SignTransaction(id, true)
if err != nil {
return
}
err = wt.tpool.AcceptTransaction(t)
if err != nil {
return
}
wt.updateWait()
return
}
// updateWait blocks while an update propagates through the modules.
func (wt *walletTester) updateWait() {
<-wt.minerChan
<-wt.walletChan
}
// NewWalletTester takes a testing.T and creates a WalletTester.
func NewWalletTester(directory string, t *testing.T) (wt *WalletTester) {
wt = new(WalletTester)
wt.ConsensusTester = consensus.NewTestingEnvironment(t)
func NewWalletTester(directory string, t *testing.T) (wt *walletTester) {
// Create the consensus set.
cs := consensus.CreateGenesisState()
// Create the gateway.
gDir := tester.TempDir(directory, modules.GatewayDir)
g, err := gateway.New(":0", wt.State, gDir)
g, err := gateway.New(":0", cs, gDir)
if err != nil {
t.Fatal(err)
}
tpool, err := transactionpool.New(wt.State, g)
// Create the transaction pool.
tp, err := transactionpool.New(cs, g)
if err != nil {
t.Fatal(err)
}
// Create the wallet.
wDir := tester.TempDir(directory, modules.WalletDir)
wt.Wallet, err = New(wt.State, tpool, wDir)
w, err := New(cs, tp, wDir)
if err != nil {
t.Fatal(err)
}
walletNum++
// Create the miner.
m, err := miner.New(cs, g, tp, w)
if err != nil {
t.Fatal(err)
}
// Assemble all componenets into a wallet tester.
wt = &walletTester{
cs: cs,
tpool: tp,
miner: m,
wallet: w,
minerChan: m.MinerSubscribe(),
walletChan: w.WalletSubscribe(),
t: t,
}
// Mine blocks until there is money in the wallet.
for i := 0; i <= consensus.MaturityDelay; i++ {
_, _, err = wt.miner.FindBlock()
if err != nil {
t.Fatal(err)
}
wt.updateWait()
}
return
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment