From b9dc914ff99feec27c80f0963a74439d7a257f45 Mon Sep 17 00:00:00 2001 From: stackdump Date: Thu, 6 Jun 2019 08:01:34 -0500 Subject: [PATCH] improve simWallet to allow waiting for min/max balance targets --- longTest/ChainedTransactions_test.go | 117 +++++++++++++++++++++++++++ simTest/EntriesBeforeChain_test.go | 6 +- simTest/HoldingRebound_test.go | 10 +-- simTest/SimWallet_test.go | 36 +++++++++ state/stateConsensus.go | 2 +- testHelper/simWallet.go | 109 ++++++++++++++++++++++--- 6 files changed, 260 insertions(+), 20 deletions(-) create mode 100644 longTest/ChainedTransactions_test.go create mode 100644 simTest/SimWallet_test.go diff --git a/longTest/ChainedTransactions_test.go b/longTest/ChainedTransactions_test.go new file mode 100644 index 0000000000..54a4111565 --- /dev/null +++ b/longTest/ChainedTransactions_test.go @@ -0,0 +1,117 @@ +package longtest + +import ( + "fmt" + "testing" + "time" + + "github.com/FactomProject/factom" + . "github.com/FactomProject/factomd/engine" + . "github.com/FactomProject/factomd/testHelper" +) + +// FIXME: test runs > 40 min try to tune down to 10 min +// TODO: refactor to use testAccount helpers +func TestChainedTransactions(t *testing.T) { + // a genesis block address w/ funding + bankSecret := "Fs3E9gV6DXsYzf7Fqx1fVBQPQXV695eP3k5XbmHEZVRLkMdD9qCK" + bankAddress := "FA2jK2HcLnRdS94dEcU27rF3meoJfpUcZPSinpb7AwQvPRY6RL1Q" + + var depositSecrets []string + var depositAddresses []string + + for i := 0; i < 120; i++ { + priv, addr := RandomFctAddressPair() + depositSecrets = append(depositSecrets, priv) + depositAddresses = append(depositAddresses, addr) + } + + var maxBlocks = 500 + state0 := SetupSim("LAF", map[string]string{"--debuglog": "."}, maxBlocks+1, 0, 0, t) + var ecPrice uint64 = state0.GetFactoshisPerEC() //10000 + var oneFct uint64 = factom.FactoidToFactoshi("1") + + waitForDeposit := func(i int, amt uint64) uint64 { + balance := GetBalance(state0, depositAddresses[i]) + TimeNow(state0) + fmt.Printf("%v waitForDeposit %v %v - %v = diff: %v \n", i, depositAddresses[i], balance, amt, balance-int64(amt)) + var waited bool + for balance != int64(amt) { + waited = true + balance = GetBalance(state0, depositAddresses[i]) + time.Sleep(time.Millisecond * 100) + } + if waited { + fmt.Printf("%v waitForDeposit %v %v - %v = diff: %v \n", i, depositAddresses[i], balance, amt, balance-int64(amt)) + TimeNow(state0) + } + return uint64(balance) + } + _ = waitForDeposit + + initialBalance := 10 * oneFct + fee := 12 * ecPrice + + prepareTransactions := func(bal uint64) ([]func(), uint64, int) { + + var transactions []func() + var i int + + for i = 0; i < len(depositAddresses)-1; i += 1 { + bal -= fee + + in := i + out := i + 1 + send := bal + + txn := func() { + SendTxn(state0, send, depositSecrets[in], depositAddresses[out], ecPrice) + } + transactions = append(transactions, txn) + } + return transactions, bal, i + } + + // offset to send initial blocking transaction + offset := 1 + + mkTransactions := func() { // txnGenerator + // fund the start address + SendTxn(state0, initialBalance, bankSecret, depositAddresses[0], ecPrice) + WaitMinutes(state0, 1) + waitForDeposit(0, initialBalance) + transactions, finalBalance, finalAddress := prepareTransactions(initialBalance) + + var sent []int + var unblocked bool = false + + for i := 1; i < len(transactions); i++ { + sent = append(sent, i) + //fmt.Printf("offset: %v <=> i:%v", offset, i) + if i == offset { + fmt.Printf("\n==>TXN offset%v\n", offset) + transactions[0]() // unblock the transactions + unblocked = true + } + transactions[i]() + } + if !unblocked { + transactions[0]() // unblock the transactions + } + offset++ // next time start further in the future + fmt.Printf("send chained transations") + waitForDeposit(finalAddress, finalBalance) + + // empty final address returning remaining funds to bank + SendTxn(state0, finalBalance-fee, depositSecrets[finalAddress], bankAddress, ecPrice) + waitForDeposit(finalAddress, 0) + } + + for x := 1; x <= 120; x++ { + mkTransactions() + WaitBlocks(state0, 1) + } + + WaitForAllNodes(state0) + ShutDownEverything(t) +} diff --git a/simTest/EntriesBeforeChain_test.go b/simTest/EntriesBeforeChain_test.go index 8b9df5590d..02877f5764 100644 --- a/simTest/EntriesBeforeChain_test.go +++ b/simTest/EntriesBeforeChain_test.go @@ -63,10 +63,10 @@ func TestEntriesBeforeChain(t *testing.T) { // REVIEW is this a good enough test for holding WaitMinutes(state0, 2) // ensure messages are reviewed in holding at least once - a.FundEC(numEntries + 11) // Chain costs 10 + 1 per k so our chain costs 11 - WaitForAnyDeposit(state0, a.EcPub()) + a.FundEC(uint64(numEntries + 11)) // Chain costs 10 + 1 per k so our chain costs 11 + WaitForAnyEcBalance(state0, a.EcPub()) - WaitForZero(state0, a.EcPub()) + WaitForZeroEC(state0, a.EcPub()) ShutDownEverything(t) WaitForAllNodes(state0) diff --git a/simTest/HoldingRebound_test.go b/simTest/HoldingRebound_test.go index 410ec5bd20..49fafc897e 100644 --- a/simTest/HoldingRebound_test.go +++ b/simTest/HoldingRebound_test.go @@ -47,11 +47,11 @@ func TestHoldingRebound(t *testing.T) { state0.APIQueue().Enqueue(reveal) a.FundEC(11) - WaitForAnyDeposit(state0, a.EcPub()) // EC deposit goes through - WaitForZero(state0, a.EcPub()) // Account should be drained + WaitForAnyEcBalance(state0, a.EcPub()) // EC deposit goes through + WaitForZeroEC(state0, a.EcPub()) // Account should be drained GenerateCommitsAndRevealsInBatches(t, state0) - WaitForZero(state0, a.EcPub()) + WaitForZeroEC(state0, a.EcPub()) ht := state0.GetDBHeightComplete() WaitBlocks(state0, 2) newHt := state0.GetDBHeightComplete() @@ -116,8 +116,8 @@ func GenerateCommitsAndRevealsInBatches(t *testing.T, state0 *state.State) { publish(x) } - a.FundEC(numEntries) - WaitForAnyDeposit(state0, a.EcPub()) + a.FundEC(uint64(numEntries)) + WaitForAnyEcBalance(state0, a.EcPub()) tend := WaitForEmptyHolding(state0, fmt.Sprintf("WAIT_HOLDING_END%v", BatchID)) diff --git a/simTest/SimWallet_test.go b/simTest/SimWallet_test.go new file mode 100644 index 0000000000..9f0ac4ec52 --- /dev/null +++ b/simTest/SimWallet_test.go @@ -0,0 +1,36 @@ +package simtest + +import ( + "fmt" + "github.com/FactomProject/factom" + . "github.com/FactomProject/factomd/testHelper" + "testing" +) + +func TestSimWallet(t *testing.T) { + + a := AccountFromFctSecret("Fs31kMMKBSCDwa7tSEzjQ4EfGeXARdUS22oBEJKNSJWbLAMTsEHr") + b := AccountFromFctSecret("Fs2BNvoDgSoGJpWg4PvRUxqvLE28CQexp5FZM9X5qU6QvzFBUn6D") + + fmt.Printf("A: %s", a) + fmt.Printf("B: %s", b) + + state0 := SetupSim("L", map[string]string{"--debuglog": ""}, 80, 0, 0, t) + + var oneFct uint64 = factom.FactoidToFactoshi("1") + a.FundFCT(10*oneFct) // fund fct from coinbase 'bank' + a.SendFCT(b, oneFct) // fund Account b from Acct a + WaitForAnyFctBalance(state0, a.FctPub()) // wait for non-zero + WaitForMaxFctBalance(state0, b.FctPub(), int64(oneFct)) // wait for at least 1 fct + + + a.FundEC(10) // fund EC from coinbase 'bank' + WaitForMaxEcBalance(state0, a.EcPub(), 1) // wait for at least 1 ec + + b.ConvertEC(10) // fund EC from account b + WaitForAnyEcBalance(state0, b.EcPub()) + + WaitBlocks(state0, 1) + ShutDownEverything(t) + +} diff --git a/state/stateConsensus.go b/state/stateConsensus.go index ff17c5a047..bcd92927a8 100644 --- a/state/stateConsensus.go +++ b/state/stateConsensus.go @@ -845,6 +845,7 @@ func (s *State) MoveStateToHeight(dbheight uint32, newMinute int) { s.LeaderPL.SortAuditServers() s.LeaderPL.SortFedServers() + s.Hold.Review() // check for resolved dependents and cleanup new messages } { // debug @@ -880,7 +881,6 @@ func (s *State) MoveStateToHeight(dbheight uint32, newMinute int) { s.EOMLimit = len(s.LeaderPL.FedServers) // We add or remove server only on block boundaries s.DBSigLimit = s.EOMLimit // We add or remove server only on block boundaries - s.Hold.Review() // cleanup expired messages from NewHolding s.LogPrintf("dbstateprocess", "MoveStateToHeight(%d-:-%d) leader=%v leaderPL=%p, leaderVMIndex=%d", dbheight, newMinute, s.Leader, s.LeaderPL, s.LeaderVMIndex) } diff --git a/testHelper/simWallet.go b/testHelper/simWallet.go index 39483a2478..5aaba57f62 100644 --- a/testHelper/simWallet.go +++ b/testHelper/simWallet.go @@ -74,11 +74,35 @@ func (d *testAccount) EcAddr() interfaces.IHash { return x } -func (d *testAccount) FundEC(amt int) { +// buy EC from coinbase 'bank' +func (d *testAccount) FundEC(amt uint64) { state0 := engine.GetFnodes()[0].State engine.FundECWallet(state0, GetBankAccount().FctPrivHash(), d.EcAddr(), uint64(amt)*state0.GetFactoshisPerEC()) } +// buy EC from account +func (d *testAccount) ConvertEC(amt uint64) { + state0 := engine.GetFnodes()[0].State + engine.FundECWallet(state0, d.FctPrivHash(), d.EcAddr(), uint64(amt)*state0.GetFactoshisPerEC()) +} + + +// get FCT from coinbase 'bank' +func (d *testAccount) FundFCT(amt uint64) { + state0 := engine.GetFnodes()[0].State + _, err := engine.SendTxn(state0, uint64(amt), GetBankAccount().FctPriv(), d.FctPub(), state0.GetFactoshisPerEC()) + if err != nil { + panic(err) + } +} + +// transfer FCT from account +func (d *testAccount) SendFCT(a *testAccount, amt uint64) { + state0 := engine.GetFnodes()[0].State + engine.SendTxn(state0, uint64(amt), d.FctPriv(), a.FctPub(), state0.GetFactoshisPerEC()) +} + +// check EC balance func (d *testAccount) GetECBalance() int64 { state0 := engine.GetFnodes()[0].State return engine.GetBalanceEC(state0, d.EcPub()) @@ -113,11 +137,12 @@ func AccountFromFctSecret(s string) *testAccount { return d } -// This account has a balance from inital coinbase +// This account has a balance from initial coinbase func GetBankAccount() *testAccount { return AccountFromFctSecret("Fs3E9gV6DXsYzf7Fqx1fVBQPQXV695eP3k5XbmHEZVRLkMdD9qCK") } +// build addresses from random key func GetRandomAccount() *testAccount { d := new(testAccount) d.Priv = primitives.RandomPrivateKey() @@ -270,27 +295,89 @@ func ComposeChainCommit(pkey *primitives.PrivateKey, c *factom.Chain) (*messages return m, nil } -func WaitForAnyDeposit(s *state.State, ecPub string) int64 { - s.LogPrintf(logName, "WaitForAnyDeposit %v", ecPub) - return WaitForEcBalance(s, ecPub, 1) +// wait for non-zero EC balance +func WaitForAnyEcBalance(s *state.State, ecPub string) int64 { + //s.LogPrintf(logName, "WaitForAnyEcBalance %v", ecPub) + return WaitForMinEcBalance(s, ecPub, 1) } -func WaitForZero(s *state.State, ecPub string) int64 { +// wait for non-zero FCT balance +func WaitForAnyFctBalance(s *state.State, fctPub string) int64 { + //s.LogPrintf(logName, "WaitForAnyEcBalance %v", fctPub) + return WaitForMinFctBalance(s, fctPub, 1) +} + +// wait for exactly Zero EC balance +func WaitForZeroEC(s *state.State, ecPub string) int64 { fmt.Println("Waiting for Zero Balance") - return WaitForEcBalance(s, ecPub, 0) + return WaitForMinEcBalance(s, ecPub, 0) } -func WaitForEcBalance(s *state.State, ecPub string, target int64) int64 { +const balanceWaitInterval = time.Millisecond * 20 + +// loop until balance is >= target +func WaitForMinEcBalance(s *state.State, ecPub string, target int64) int64 { - s.LogPrintf(logName, "WaitForBalance%v: %v", target, ecPub) + //s.LogPrintf(logName, "WaitForMinEcBalance%v: %v", target, ecPub) for { bal := engine.GetBalanceEC(s, ecPub) - time.Sleep(time.Millisecond * 20) + time.Sleep(balanceWaitInterval) //fmt.Printf("WaitForBalance: %v => %v\n", ecPub, bal) if (target == 0 && bal == 0) || (target > 0 && bal >= target) { - s.LogPrintf(logName, "FoundBalance%v: %v", target, bal) + //s.LogPrintf(logName, "FoundMinEcBalance%v: %v", target, bal) + return bal + } + } +} + +// loop until balance is <= target +func WaitForMaxEcBalance(s *state.State, ecPub string, target int64) int64 { + + //s.LogPrintf(logName, "WaitForMaxEcBalance%v: %v", target, ecPub) + + for { + bal := engine.GetBalanceEC(s, ecPub) + time.Sleep(balanceWaitInterval) + //fmt.Printf("WaitForBalance: %v => %v\n", ecPub, bal) + + if (target == 0 && bal == 0) || (target > 0 && bal <= target) { + //s.LogPrintf(logName, "FoundMaxEcBalance%v: %v", target, bal) + return bal + } + } +} + +// loop until balance is >= target +func WaitForMinFctBalance(s *state.State, fctPub string, target int64) int64 { + + //s.LogPrintf(logName, "WaitForMinFctBalance%v: %v", target, fctPub) + + for { + bal := engine.GetBalance(s, fctPub) + time.Sleep(balanceWaitInterval) + //s.LogPrintf(logName, "WaitForMinFctBalance: %v => %v\n", fctPub, bal) + + if (target == 0 && bal == 0) || (target > 0 && bal >= target) { + //s.LogPrintf(logName, "FoundBalance%v: %v", target, bal) + return bal + } + } +} + +// loop until balance is <= target +func WaitForMaxFctBalance(s *state.State, fctPub string, target int64) int64 { + + //s.LogPrintf(logName, "WaitForMaxFctBalance%v: %v", target, fctPub) + + for { + bal := engine.GetBalance(s, fctPub) + time.Sleep(balanceWaitInterval) + //s.LogPrintf(logName, "WaitForMaxFctBalance: %v => %v\n", fctPub, bal) + + if (target == 0 && bal == 0) || (target > 0 && bal <= target) { + //s.LogPrintf(logName, "FoundMaxFctBalance%v: %v", target, bal) return bal } }