Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
39 changes: 39 additions & 0 deletions daemon/algod/api/client/restClient.go
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,45 @@ func (client RestClient) WaitForRoundWithTimeout(roundToWaitFor uint64) error {
return nil
}

// WaitForConfirmedTxn waits until either the passed txid is confirmed
// or until the passed roundTimeout passes
// or until waiting for a round to pass times out
func (client RestClient) WaitForConfirmedTxn(roundTimeout uint64, txid string) (txn v2.PreEncodedTxInfo, err error) {
for {
// Get current round information
curStatus, statusErr := client.Status()
if err != nil {
return txn, statusErr
}
curRound := curStatus.LastRound

// Check if we know about the transaction yet
var resp []byte
resp, err = client.RawPendingTransactionInformation(txid)
if err == nil {
err = protocol.DecodeReflect(resp, &txn)
if err != nil {
return txn, err
}
}

// Check if transaction was confirmed
if txn.ConfirmedRound != nil && *txn.ConfirmedRound > 0 {
return
}
// Check if we should wait a round
if curRound > roundTimeout {
err = fmt.Errorf("failed to see confirmed transaction by round %v", roundTimeout)
return txn, err
}
// Wait a round
err = client.WaitForRoundWithTimeout(curRound + 1)
if err != nil {
return txn, err
}
}
}

// HealthCheck does a health check on the potentially running node,
// returning an error if the API is down
func (client RestClient) HealthCheck() error {
Expand Down
10 changes: 10 additions & 0 deletions libgoal/transactions.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/algorand/go-algorand/config"
"github.com/algorand/go-algorand/crypto"
"github.com/algorand/go-algorand/crypto/merklesignature"
v2 "github.com/algorand/go-algorand/daemon/algod/api/server/v2"
"github.com/algorand/go-algorand/daemon/algod/api/server/v2/generated/model"
"github.com/algorand/go-algorand/data/account"
"github.com/algorand/go-algorand/data/basics"
Expand Down Expand Up @@ -206,6 +207,15 @@ func (c *Client) SignAndBroadcastTransaction(walletHandle, pw []byte, utx transa
return c.BroadcastTransaction(stx)
}

// WaitForConfirmedTxn waits for a transaction to be confirmed, returing information about it.
func (c *Client) WaitForConfirmedTxn(roundTimeout uint64, txid string) (txn v2.PreEncodedTxInfo, err error) {
algod, err := c.ensureAlgodClient()
if err != nil {
return
}
return algod.WaitForConfirmedTxn(roundTimeout, txid)
}

// generateRegistrationTransaction returns a transaction object for registering a Participation with its parent this is
// similar to account.Participation.GenerateRegistrationTransaction.
func generateRegistrationTransaction(part model.ParticipationKey, fee basics.MicroAlgos, txnFirstValid, txnLastValid basics.Round, leaseBytes [32]byte) (transactions.Transaction, error) {
Expand Down
4 changes: 2 additions & 2 deletions test/e2e-go/features/incentives/challenge_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,11 @@ func testChallengesOnce(t *testing.T, a *require.Assertions) (retry bool) {

// eligible accounts1 will get challenged with node offline, and suspended
for _, account := range accounts1 {
rekeyreg(&fixture, a, c1, account.Address, eligible(account.Address))
rekeyreg(a, c1, account.Address, eligible(account.Address))
}
// eligible accounts2 will get challenged, but node2 will heartbeat for them
for _, account := range accounts2 {
rekeyreg(&fixture, a, c2, account.Address, eligible(account.Address))
rekeyreg(a, c2, account.Address, eligible(account.Address))
}

// turn off node 1, so it can't heartbeat
Expand Down
8 changes: 4 additions & 4 deletions test/e2e-go/features/incentives/payouts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ func TestBasicPayouts(t *testing.T) {
c01, account01 := clientAndAccount("Node01")
relay, _ := clientAndAccount("Relay")

data01 := rekeyreg(&fixture, a, c01, account01.Address, true)
data15 := rekeyreg(&fixture, a, c15, account15.Address, true)
data01 := rekeyreg(a, c01, account01.Address, true)
data15 := rekeyreg(a, c15, account15.Address, true)

// Wait a few rounds after rekeyreg, this means that `lookback` rounds after
// those rekeyregs, the nodes will be IncentiveEligible, but both will have
Expand Down Expand Up @@ -296,7 +296,7 @@ func getblock(client libgoal.Client, round uint64) (bookkeeping.Block, error) {
return client.BookkeepingBlock(round)
}

func rekeyreg(f *fixtures.RestClientFixture, a *require.Assertions, client libgoal.Client, address string, becomeEligible bool) basics.AccountData {
func rekeyreg(a *require.Assertions, client libgoal.Client, address string, becomeEligible bool) basics.AccountData {
// we start by making an _offline_ tx here, because we want to populate the
// key material ourself with a copy of the account's existing material. That
// makes it an _online_ keyreg. That allows the running node to chug along
Expand Down Expand Up @@ -329,7 +329,7 @@ func rekeyreg(f *fixtures.RestClientFixture, a *require.Assertions, client libgo
a.NoError(err)
onlineTxID, err := client.SignAndBroadcastTransaction(wh, nil, reReg)
a.NoError(err)
txn, err := f.WaitForConfirmedTxn(uint64(reReg.LastValid), onlineTxID)
txn, err := client.WaitForConfirmedTxn(uint64(reReg.LastValid), onlineTxID)
a.NoError(err)
// sync up with the network
_, err = client.WaitForRound(*txn.ConfirmedRound)
Expand Down
4 changes: 2 additions & 2 deletions test/e2e-go/features/incentives/suspension_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ func TestBasicSuspension(t *testing.T) {
c10, account10 := clientAndAccount("Node10")
c20, account20 := clientAndAccount("Node20")

rekeyreg(&fixture, a, c10, account10.Address, true)
rekeyreg(&fixture, a, c20, account20.Address, true)
rekeyreg(a, c10, account10.Address, true)
rekeyreg(a, c20, account20.Address, true)

// Accounts are now suspendable whether they have proposed yet or not
// because keyreg sets LastHeartbeat. Stop c20 which means account20 will be
Expand Down
44 changes: 21 additions & 23 deletions test/e2e-go/features/incentives/whalejoin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
package suspension

import (
"fmt"
"path/filepath"
"testing"
"time"
Expand Down Expand Up @@ -65,15 +64,15 @@ func TestWhaleJoin(t *testing.T) {
accounts, err := fixture.GetNodeWalletsSortedByBalance(c)
a.NoError(err)
a.Len(accounts, 1)
fmt.Printf("Client %s is %v\n", name, accounts[0].Address)
t.Logf("Client %s is %v\n", name, accounts[0].Address)
return c, accounts[0]
}

c15, account15 := clientAndAccount("Node15")
c01, account01 := clientAndAccount("Node01")

// 1. take wallet15 offline
keys := offline(&fixture, a, c15, account15.Address)
keys := offline(a, c15, account15.Address)

// 2. c01 starts with 100M, so burn 99.9M to get total online stake down
burn, err := c01.SendPaymentFromUnencryptedWallet(account01.Address, basics.Address{}.String(),
Expand All @@ -87,7 +86,7 @@ func TestWhaleJoin(t *testing.T) {
a.NoError(err)

// 4. rejoin, with 1.5B against the paltry 100k that's currently online
online(&fixture, a, c15, account15.Address, keys)
online(a, c15, account15.Address, keys)

// 5. wait for agreement balances to kick in (another lookback's worth, plus some slack)
_, err = c01.WaitForRound(*receipt.ConfirmedRound + 2*lookback + 5)
Expand Down Expand Up @@ -139,20 +138,20 @@ func TestBigJoin(t *testing.T) {
accounts, err := fixture.GetNodeWalletsSortedByBalance(c)
a.NoError(err)
a.Len(accounts, 1)
fmt.Printf("Client %s is %v\n", name, accounts[0].Address)
t.Logf("Client %s is %v\n", name, accounts[0].Address)
return c, accounts[0]
}

c01, account01 := clientAndAccount("Node01")

// 1. take wallet01 offline
keys := offline(&fixture, a, c01, account01.Address)
keys := offline(a, c01, account01.Address)

// 2. Wait lookback rounds
wait(&fixture, a, lookback)

// 4. rejoin, with 1/16 of total stake
onRound := online(&fixture, a, c01, account01.Address, keys)
onRound := online(a, c01, account01.Address, keys)

// 5. wait for enough rounds to pass, during which c01 can't vote, that is
// could get knocked off.
Expand All @@ -162,7 +161,7 @@ func TestBigJoin(t *testing.T) {
a.Equal(basics.Online, data.Status)

// 5a. just to be sure, do a zero pay to get it "noticed"
zeroPay(&fixture, a, c01, account01.Address)
zeroPay(a, c01, account01.Address)
data, err = c01.AccountData(account01.Address)
a.NoError(err)
a.Equal(basics.Online, data.Status)
Expand All @@ -175,7 +174,7 @@ func TestBigJoin(t *testing.T) {
a.NoError(err)
a.Equal(basics.Online, data.Status)

zeroPay(&fixture, a, c01, account01.Address)
zeroPay(a, c01, account01.Address)
data, err = c01.AccountData(account01.Address)
a.NoError(err)
a.Equal(basics.Online, data.Status)
Expand Down Expand Up @@ -215,7 +214,7 @@ func TestBigIncrease(t *testing.T) {
accounts, err := fixture.GetNodeWalletsSortedByBalance(c)
a.NoError(err)
a.Len(accounts, 1)
fmt.Printf("Client %s is %v\n", name, accounts[0].Address)
t.Logf("Client %s is %v\n", name, accounts[0].Address)
return c, accounts[0]
}

Expand All @@ -226,14 +225,14 @@ func TestBigIncrease(t *testing.T) {
// certainly will not have proposed by pure luck just before the critical
// round. If we don't do that, 1/16 of stake is enough that it will probably
// have a fairly recent proposal, and not get knocked off.
pay(&fixture, a, c1, account01.Address, account15.Address, 99*account01.Amount/100)
pay(a, c1, account01.Address, account15.Address, 99*account01.Amount/100)

rekeyreg(&fixture, a, c1, account01.Address, true)
rekeyreg(a, c1, account01.Address, true)

// 2. Wait lookback rounds
wait(&fixture, a, lookback)

tx := pay(&fixture, a, c15, account15.Address, account01.Address, 50*account15.Amount/100)
tx := pay(a, c15, account15.Address, account01.Address, 50*account15.Amount/100)
data, err := c15.AccountData(account01.Address)
a.NoError(err)
a.EqualValues(*tx.ConfirmedRound+lookback, data.LastHeartbeat)
Expand All @@ -252,22 +251,21 @@ func wait(f *fixtures.RestClientFixture, a *require.Assertions, count uint64) {
a.NoError(f.WaitForRoundWithTimeout(round))
}

func pay(f *fixtures.RestClientFixture, a *require.Assertions,
c libgoal.Client, from string, to string, amount uint64) v2.PreEncodedTxInfo {
func pay(a *require.Assertions, c libgoal.Client,
from string, to string, amount uint64) v2.PreEncodedTxInfo {
pay, err := c.SendPaymentFromUnencryptedWallet(from, to, 1000, amount, nil)
a.NoError(err)
tx, err := f.WaitForConfirmedTxn(uint64(pay.LastValid), pay.ID().String())
tx, err := c.WaitForConfirmedTxn(uint64(pay.LastValid), pay.ID().String())
a.NoError(err)
return tx
}

func zeroPay(f *fixtures.RestClientFixture, a *require.Assertions,
c libgoal.Client, address string) {
pay(f, a, c, address, address, 0)
func zeroPay(a *require.Assertions, c libgoal.Client, address string) {
pay(a, c, address, address, 0)
}

// Go offline, but return the key material so it's easy to go back online
func offline(f *fixtures.RestClientFixture, a *require.Assertions, client libgoal.Client, address string) transactions.KeyregTxnFields {
func offline(a *require.Assertions, client libgoal.Client, address string) transactions.KeyregTxnFields {
offTx, err := client.MakeUnsignedGoOfflineTx(address, 0, 0, 100_000, [32]byte{})
a.NoError(err)

Expand All @@ -286,7 +284,7 @@ func offline(f *fixtures.RestClientFixture, a *require.Assertions, client libgoa
a.NoError(err)
onlineTxID, err := client.SignAndBroadcastTransaction(wh, nil, offTx)
a.NoError(err)
txn, err := f.WaitForConfirmedTxn(uint64(offTx.LastValid), onlineTxID)
txn, err := client.WaitForConfirmedTxn(uint64(offTx.LastValid), onlineTxID)
a.NoError(err)
// sync up with the network
_, err = client.WaitForRound(*txn.ConfirmedRound)
Expand All @@ -298,7 +296,7 @@ func offline(f *fixtures.RestClientFixture, a *require.Assertions, client libgoa
}

// Go online with the supplied key material
func online(f *fixtures.RestClientFixture, a *require.Assertions, client libgoal.Client, address string, keys transactions.KeyregTxnFields) uint64 {
func online(a *require.Assertions, client libgoal.Client, address string, keys transactions.KeyregTxnFields) uint64 {
// sanity check that we start offline
data, err := client.AccountData(address)
a.NoError(err)
Expand All @@ -313,7 +311,7 @@ func online(f *fixtures.RestClientFixture, a *require.Assertions, client libgoal
a.NoError(err)
onlineTxID, err := client.SignAndBroadcastTransaction(wh, nil, onTx)
a.NoError(err)
receipt, err := f.WaitForConfirmedTxn(uint64(onTx.LastValid), onlineTxID)
receipt, err := client.WaitForConfirmedTxn(uint64(onTx.LastValid), onlineTxID)
a.NoError(err)
data, err = client.AccountData(address)
a.NoError(err)
Expand Down
34 changes: 3 additions & 31 deletions test/framework/fixtures/restClientFixture.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import (
"github.com/algorand/go-algorand/data/basics"
"github.com/algorand/go-algorand/data/bookkeeping"
"github.com/algorand/go-algorand/netdeploy"
"github.com/algorand/go-algorand/protocol"

"github.com/algorand/go-algorand/daemon/algod/api/client"
v2 "github.com/algorand/go-algorand/daemon/algod/api/server/v2"
Expand Down Expand Up @@ -192,34 +191,7 @@ func (f *RestClientFixture) WaitForTxnConfirmation(roundTimeout uint64, txid str
// or until the passed roundTimeout passes
// or until waiting for a round to pass times out
func (f *RestClientFixture) WaitForConfirmedTxn(roundTimeout uint64, txid string) (txn v2.PreEncodedTxInfo, err error) {
client := f.AlgodClient
for {
// Get current round information
curStatus, statusErr := client.Status()
require.NoError(f.t, statusErr, "fixture should be able to get node status")
curRound := curStatus.LastRound

// Check if we know about the transaction yet
var resp []byte
resp, err = client.RawPendingTransactionInformation(txid)
if err == nil {
err = protocol.DecodeReflect(resp, &txn)
require.NoError(f.t, err)
}

// Check if transaction was confirmed
if txn.ConfirmedRound != nil && *txn.ConfirmedRound > 0 {
return
}
// Check if we should wait a round
if curRound > roundTimeout {
err = fmt.Errorf("failed to see confirmed transaction by round %v", roundTimeout)
return
}
// Wait a round
err = f.WaitForRoundWithTimeout(curRound + 1)
require.NoError(f.t, err, "fixture should be able to wait for one round to pass")
}
return f.AlgodClient.WaitForConfirmedTxn(roundTimeout, txid)
}

// WaitForAllTxnsToConfirm is as WaitForTxnConfirmation,
Expand Down Expand Up @@ -295,7 +267,7 @@ func (f *RestClientFixture) WaitForAccountFunded(roundTimeout uint64, accountAdd
return fmt.Errorf("failed to see confirmed transaction by round %v", roundTimeout)
}
// Wait a round
err = f.WaitForRoundWithTimeout(curRound + 1)
err = client.WaitForRoundWithTimeout(curRound + 1)
require.NoError(f.t, err, "fixture should be able to wait for one round to pass")
}
}
Expand All @@ -318,7 +290,7 @@ func (f *RestClientFixture) SendMoneyAndWaitFromWallet(walletHandle, walletPassw
require.NoError(f.t, err, "client should be able to send money from rich to poor account")
require.NotEmpty(f.t, fundingTx.ID().String(), "transaction ID should not be empty")
waitingDeadline := curRound + uint64(5)
txn, err = f.WaitForConfirmedTxn(waitingDeadline, fundingTx.ID().String())
txn, err = client.WaitForConfirmedTxn(waitingDeadline, fundingTx.ID().String())
require.NoError(f.t, err)
return
}
Expand Down