From d0962b42a3e0ab9bb097c48d142af89640b1325c Mon Sep 17 00:00:00 2001 From: Pavel Zbitskiy Date: Wed, 15 Sep 2021 12:38:50 -0400 Subject: [PATCH] Fix dryrun crash on rewards calculation --- cmd/tealdbg/localLedger.go | 5 +- daemon/algod/api/server/v2/account.go | 2 +- daemon/algod/api/server/v2/dryrun.go | 3 ++ daemon/algod/api/server/v2/dryrun_test.go | 61 +++++++++++++++++++++++ 4 files changed, 69 insertions(+), 2 deletions(-) diff --git a/cmd/tealdbg/localLedger.go b/cmd/tealdbg/localLedger.go index 6f4dcfeaa3..23e58e3f36 100644 --- a/cmd/tealdbg/localLedger.go +++ b/cmd/tealdbg/localLedger.go @@ -285,7 +285,10 @@ func (l *localLedger) CheckDup(config.ConsensusParams, basics.Round, basics.Roun } func (l *localLedger) LookupWithoutRewards(rnd basics.Round, addr basics.Address) (basics.AccountData, basics.Round, error) { - return l.balances[addr], rnd, nil + ad := l.balances[addr] + // Clear RewardsBase since tealdbg has no idea about rewards level so the underlying calculation with reward will fail. + ad.RewardsBase = 0 + return ad, rnd, nil } func (l *localLedger) GetCreatorForRound(rnd basics.Round, cidx basics.CreatableIndex, ctype basics.CreatableType) (basics.Address, bool, error) { diff --git a/daemon/algod/api/server/v2/account.go b/daemon/algod/api/server/v2/account.go index 0186182653..2187a1068b 100644 --- a/daemon/algod/api/server/v2/account.go +++ b/daemon/algod/api/server/v2/account.go @@ -105,7 +105,7 @@ func AccountDataToAccount( amount := record.MicroAlgos pendingRewards, overflowed := basics.OSubA(amount, amountWithoutPendingRewards) if overflowed { - return generated.Account{}, errors.New("overflow on pending reward calcuation") + return generated.Account{}, errors.New("overflow on pending reward calculation") } return generated.Account{ diff --git a/daemon/algod/api/server/v2/dryrun.go b/daemon/algod/api/server/v2/dryrun.go index fed86ce627..9e5eadc004 100644 --- a/daemon/algod/api/server/v2/dryrun.go +++ b/daemon/algod/api/server/v2/dryrun.go @@ -273,6 +273,9 @@ func (dl *dryrunLedger) LookupWithoutRewards(rnd basics.Round, addr basics.Addre return basics.AccountData{}, 0, err } out.MicroAlgos.Raw = acct.AmountWithoutPendingRewards + // Clear RewardsBase since dryrun has no idea about rewards level so the underlying calculation with reward will fail. + // The amount needed is known as acct.Amount but this method must return AmountWithoutPendingRewards + out.RewardsBase = 0 } appi, ok := dl.accountApps[addr] if ok { diff --git a/daemon/algod/api/server/v2/dryrun_test.go b/daemon/algod/api/server/v2/dryrun_test.go index 10c758d4e5..6daac2033e 100644 --- a/daemon/algod/api/server/v2/dryrun_test.go +++ b/daemon/algod/api/server/v2/dryrun_test.go @@ -1331,3 +1331,64 @@ func TestDryrunCost(t *testing.T) { }) } } + +func TestDryrunBalanceWithReward(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + ops, err := logic.AssembleString(`#pragma version 5 +int 0 +balance +int 0 +>`) + require.NoError(t, err) + approval := ops.Program + ops, err = logic.AssembleString("int 1") + clst := ops.Program + require.NoError(t, err) + var appIdx basics.AppIndex = 1 + creator := randomAddress() + rewardBase := uint64(10000000) + dr := DryrunRequest{ + Txns: []transactions.SignedTxn{ + { + Txn: transactions.Transaction{ + Header: transactions.Header{Sender: creator}, + Type: protocol.ApplicationCallTx, + ApplicationCallTxnFields: transactions.ApplicationCallTxnFields{ + ApplicationID: appIdx, + }, + }, + }, + }, + Apps: []generated.Application{ + { + Id: uint64(appIdx), + Params: generated.ApplicationParams{ + Creator: creator.String(), + ApprovalProgram: approval, + ClearStateProgram: clst, + LocalStateSchema: &generated.ApplicationStateSchema{NumByteSlice: 1}, + }, + }, + }, + Accounts: []generated.Account{ + { + Address: creator.String(), + Status: "Online", + Amount: 10000000, + AmountWithoutPendingRewards: 10000000, + RewardBase: &rewardBase, + }, + }, + } + dr.ProtocolVersion = string(dryrunProtoVersion) + + var response generated.DryrunResponse + doDryrunRequest(&dr, &response) + require.NoError(t, err) + checkAppCallPass(t, &response) + if t.Failed() { + logResponse(t, &response) + } +}