Skip to content

Commit

Permalink
Test coverage for statedelta responses
Browse files Browse the repository at this point in the history
  • Loading branch information
jasonpaulos committed Aug 22, 2024
1 parent 361d1c6 commit 20bf166
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 17 deletions.
17 changes: 13 additions & 4 deletions daemon/algod/api/client/restClient.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"github.com/google/go-querystring/query"

"github.com/algorand/go-algorand/crypto"
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/daemon/algod/api/spec/common"
"github.com/algorand/go-algorand/data/basics"
Expand Down Expand Up @@ -229,7 +230,7 @@ func (client RestClient) submitForm(
}

if decodeJSON {
dec := json.NewDecoder(resp.Body)
dec := protocol.NewJSONDecoder(resp.Body)
return dec.Decode(&response)
}

Expand Down Expand Up @@ -767,19 +768,27 @@ func (client RestClient) GetSyncRound() (response model.GetSyncRoundResponse, er
}

// GetLedgerStateDelta retrieves the ledger state delta for the round
func (client RestClient) GetLedgerStateDelta(round uint64) (response model.LedgerStateDeltaResponse, err error) {
func (client RestClient) GetLedgerStateDelta(round uint64) (response v2.LedgerStateDeltaJSONSerializable, err error) {
// Note: this endpoint gets the StateDelta as JSON, meaning some string fields with non-UTF-8 data will lose
// information. Msgpack should be used instead if this becomes a problem.
err = client.get(&response, fmt.Sprintf("/v2/deltas/%d", round), nil)
return
}

// GetLedgerStateDeltaForTransactionGroup retrieves the ledger state delta for the txn group specified by the id
func (client RestClient) GetLedgerStateDeltaForTransactionGroup(id string) (response model.LedgerStateDeltaForTransactionGroupResponse, err error) {
func (client RestClient) GetLedgerStateDeltaForTransactionGroup(id string) (response v2.LedgerStateDeltaSubsetJSONSerializable, err error) {
// Note: this endpoint gets the StateDelta as JSON, meaning some string fields with non-UTF-8 data will lose
// information. Msgpack should be used instead if this becomes a problem.
err = client.get(&response, fmt.Sprintf("/v2/deltas/txn/group/%s", id), nil)
return
}

// GetTransactionGroupLedgerStateDeltasForRound retrieves the ledger state deltas for the txn groups in the specified round
func (client RestClient) GetTransactionGroupLedgerStateDeltasForRound(round uint64) (response model.TransactionGroupLedgerStateDeltasForRoundResponse, err error) {
func (client RestClient) GetTransactionGroupLedgerStateDeltasForRound(round uint64) (response struct {
Deltas []v2.TxnGroupDeltaWithIdsJSONSerializable
}, err error) {
// Note: this endpoint gets the StateDelta as JSON, meaning some string fields with non-UTF-8 data will lose
// information. Msgpack should be used instead if this becomes a problem.
err = client.get(&response, fmt.Sprintf("/v2/deltas/%d/txn/group", round), nil)
return
}
Expand Down
12 changes: 9 additions & 3 deletions daemon/algod/api/server/v2/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -839,7 +839,7 @@ func (v2 *Handlers) GetBlockHash(ctx echo.Context, round uint64) error {
// (GET /v2/blocks/{round}/transactions/{txid}/proof)
func (v2 *Handlers) GetTransactionProof(ctx echo.Context, round uint64, txid string, params model.GetTransactionProofParams) error {
var txID transactions.Txid
err := txID.UnmarshalText([]byte(txid))
err := txID.FromString(txid)
if err != nil {
return badRequest(ctx, err, errNoValidTxnSpecified, v2.Log)
}
Expand Down Expand Up @@ -1526,7 +1526,7 @@ func (v2 *Handlers) PendingTransactionInformation(ctx echo.Context, txid string,
}

txID := transactions.Txid{}
if err := txID.UnmarshalText([]byte(txid)); err != nil {
if err := txID.FromString(txid); err != nil {

Check failure on line 1529 in daemon/algod/api/server/v2/handlers.go

View workflow job for this annotation

GitHub Actions / reviewdog-errors

[Lint Errors] reported by reviewdog 🐶 shadow: declaration of "err" shadows declaration at line 1519 (govet) Raw Output: daemon/algod/api/server/v2/handlers.go:1529:5: shadow: declaration of "err" shadows declaration at line 1519 (govet) if err := txID.FromString(txid); err != nil { ^
return badRequest(ctx, err, errNoValidTxnSpecified, v2.Log)
}

Expand Down Expand Up @@ -2070,7 +2070,13 @@ func (v2 *Handlers) GetLedgerStateDeltaForTransactionGroup(ctx echo.Context, id
if err != nil {
return notFound(ctx, err, fmt.Sprintf(errFailedRetrievingStateDelta, err), v2.Log)
}
data, err := encode(handle, delta)
var response interface{}
if handle == protocol.JSONStrictHandle {
response = convertLedgerStateDeltaSubset(delta)
} else {
response = delta
}
data, err := encode(handle, response)
if err != nil {
return internalError(ctx, err, errFailedToEncodeResponse, v2.Log)
}
Expand Down
6 changes: 3 additions & 3 deletions data/transactions/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ func (txid Txid) String() string {
return fmt.Sprintf("%v", crypto.Digest(txid))
}

// UnmarshalText initializes the Address from an array of bytes.
func (txid *Txid) UnmarshalText(text []byte) error {
d, err := crypto.DigestFromString(string(text))
// FromString initializes the Txid from a string
func (txid *Txid) FromString(text string) error {
d, err := crypto.DigestFromString(text)
*txid = Txid(d)
return err
}
Expand Down
2 changes: 1 addition & 1 deletion libgoal/libgoal.go
Original file line number Diff line number Diff line change
Expand Up @@ -1341,7 +1341,7 @@ func (c *Client) GetSyncRound() (rep model.GetSyncRoundResponse, err error) {
}

// GetLedgerStateDelta gets the LedgerStateDelta on a node w/ EnableFollowMode
func (c *Client) GetLedgerStateDelta(round uint64) (rep model.LedgerStateDeltaResponse, err error) {
func (c *Client) GetLedgerStateDelta(round uint64) (rep v2.LedgerStateDeltaJSONSerializable, err error) {
algod, err := c.ensureAlgodClient()
if err == nil {
return algod.GetLedgerStateDelta(round)
Expand Down
32 changes: 28 additions & 4 deletions test/e2e-go/features/devmode/devmode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/stretchr/testify/require"

"github.com/algorand/go-algorand/crypto"
v2 "github.com/algorand/go-algorand/daemon/algod/api/server/v2"
"github.com/algorand/go-algorand/data/basics"
"github.com/algorand/go-algorand/netdeploy"
"github.com/algorand/go-algorand/protocol"
Expand Down Expand Up @@ -93,24 +94,47 @@ func testTxnGroupDeltasDevMode(t *testing.T, version protocol.ConsensusVersion)
require.NoError(t, err)
key := crypto.GenerateSignatureSecrets(crypto.Seed{})
receiver := basics.Address(key.SignatureVerifier)
txn := fixture.SendMoneyAndWait(0, 100000, 1000, sender.Address, receiver.String(), "")

status, err := fixture.AlgodClient.Status()
require.NoError(t, err)
curRound := status.LastRound

wh, err := fixture.LibGoalClient.GetUnencryptedWalletHandle()
require.NoError(t, err)

fundingTx, err := fixture.LibGoalClient.SendPaymentFromWalletWithLease(wh, nil, sender.Address, receiver.String(), 1000, 100000, nil, "", [32]byte{1, 2, 3}, basics.Round(curRound).SubSaturate(1), 0)
require.NoError(t, err)
txn, err := fixture.WaitForConfirmedTxn(curRound+uint64(5), fundingTx.ID().String())
require.NoError(t, err)
require.NotNil(t, txn.ConfirmedRound)
_, err = fixture.AlgodClient.Block(*txn.ConfirmedRound)
require.NoError(t, err)

// Test GetLedgerStateDeltaForTransactionGroup and verify the response contains a delta
txngroupResponse, err := fixture.AlgodClient.GetLedgerStateDeltaForTransactionGroup(txn.Txn.ID().String())
require.NoError(t, err)
require.True(t, len(txngroupResponse) > 0)
require.NotZero(t, txngroupResponse)

// Test GetTransactionGroupLedgerStateDeltasForRound and verify the response contains the delta for our txn
roundResponse, err := fixture.AlgodClient.GetTransactionGroupLedgerStateDeltasForRound(1)
require.NoError(t, err)
require.Equal(t, len(roundResponse.Deltas), 1)
groupDelta := roundResponse.Deltas[0]
require.Equal(t, 1, len(groupDelta.Ids))
require.Len(t, groupDelta.Ids, 1)
require.Equal(t, groupDelta.Ids[0], txn.Txn.ID().String())

// Assert that the TxIDs field across both endpoint responses is the same
require.Equal(t, txngroupResponse["Txids"], groupDelta.Delta["Txids"])
require.Equal(t, txngroupResponse.Txids, groupDelta.Delta.Txids)

// Verify Txleases field as well
require.Len(t, txngroupResponse.Txleases, 1)
senderAddress, err := basics.UnmarshalChecksumAddress(sender.Address)
require.NoError(t, err)
expectedLease := v2.TxleaseJSONSerializable{
Sender: senderAddress,
Lease: [32]byte{1, 2, 3},
Expiration: txn.Txn.Txn.LastValid,
}
require.Equal(t, expectedLease, txngroupResponse.Txleases[0])
require.Equal(t, txngroupResponse.Txleases, groupDelta.Delta.Txleases)
}
48 changes: 46 additions & 2 deletions test/e2e-go/features/followernode/syncDeltas_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ import (

"github.com/stretchr/testify/require"

v2 "github.com/algorand/go-algorand/daemon/algod/api/server/v2"
"github.com/algorand/go-algorand/data/basics"
"github.com/algorand/go-algorand/data/transactions"
"github.com/algorand/go-algorand/ledger/ledgercore"
"github.com/algorand/go-algorand/test/framework/fixtures"
"github.com/algorand/go-algorand/test/partitiontest"
)
Expand Down Expand Up @@ -52,6 +56,25 @@ func TestBasicSyncMode(t *testing.T) {
nc, err := fixture.GetNodeController("Primary")
a.NoError(err)

sender, err := fixture.GetRichestAccount()
require.NoError(t, err)
senderAddress, err := basics.UnmarshalChecksumAddress(sender.Address)
require.NoError(t, err)

status, err := fixture.AlgodClient.Status()
require.NoError(t, err)
curRound := status.LastRound

wh, err := fixture.LibGoalClient.GetUnencryptedWalletHandle()
require.NoError(t, err)

fundingTx, err := fixture.LibGoalClient.SendPaymentFromWalletWithLease(wh, nil, sender.Address, sender.Address, 0, 0, nil, "", [32]byte{1, 2, 3}, basics.Round(curRound).SubSaturate(1), 0)
require.NoError(t, err)
txn, err := fixture.WaitForConfirmedTxn(5, fundingTx.ID().String())
require.NoError(t, err)

require.LessOrEqual(t, *txn.ConfirmedRound, uint64(5), "Transaction should be confirmed in the first 5 rounds")

// Let the network make some progress
waitForRound := uint64(5)
err = fixture.ClientWaitForRoundWithTimeout(fixture.GetAlgodClientForController(nc), waitForRound)
Expand All @@ -71,10 +94,31 @@ func TestBasicSyncMode(t *testing.T) {
err = fixture.ClientWaitForRoundWithTimeout(followClient, round)
a.NoError(err)
// retrieve state delta
// TODO: expand this test to check the state delta JSON response
gResp, err := followClient.GetLedgerStateDelta(round)
a.NoError(err)
a.NotNil(gResp)
a.NotZero(gResp)

if round == *txn.ConfirmedRound {
// Verify that the transaction is in the state delta

expectedTxLeases := []v2.TxleaseJSONSerializable{
{
Sender: senderAddress,
Lease: [32]byte{1, 2, 3},
Expiration: txn.Txn.Txn.LastValid,
},
}
require.Equal(t, expectedTxLeases, gResp.Txleases)

expectedTxids := map[transactions.Txid]ledgercore.IncludedTransactions{
txn.Txn.ID(): {
LastValid: txn.Txn.Txn.LastValid,
Intra: 0,
},
}
require.Equal(t, expectedTxids, gResp.Txids)
}

// set sync round next
err = followClient.SetSyncRound(round + 1)
a.NoError(err)
Expand Down

0 comments on commit 20bf166

Please sign in to comment.