diff options
-rw-r--r-- | buildnumber.dat | 2 | ||||
-rw-r--r-- | cmd/tealdbg/local.go | 6 | ||||
-rw-r--r-- | cmd/tealdbg/localLedger.go | 5 | ||||
-rw-r--r-- | cmd/tealdbg/local_test.go | 74 | ||||
-rw-r--r-- | daemon/algod/api/server/v2/account.go | 2 | ||||
-rw-r--r-- | daemon/algod/api/server/v2/dryrun.go | 43 | ||||
-rw-r--r-- | daemon/algod/api/server/v2/dryrun_test.go | 156 | ||||
-rw-r--r-- | test/e2e-go/cli/tealdbg/expect/tealdbgTest.exp | 2 |
8 files changed, 246 insertions, 44 deletions
diff --git a/buildnumber.dat b/buildnumber.dat index 573541ac9..d00491fd7 100644 --- a/buildnumber.dat +++ b/buildnumber.dat @@ -1 +1 @@ -0 +1 diff --git a/cmd/tealdbg/local.go b/cmd/tealdbg/local.go index 814afae3e..6fe2d006a 100644 --- a/cmd/tealdbg/local.go +++ b/cmd/tealdbg/local.go @@ -38,7 +38,7 @@ func protoFromString(protoString string) (name string, proto config.ConsensusPar var ok bool proto, ok = config.Consensus[protocol.ConsensusVersion(protoString)] if !ok { - err = fmt.Errorf("Unknown protocol %s", protoString) + err = fmt.Errorf("unknown protocol %s", protoString) return } name = protoString @@ -369,7 +369,7 @@ func (r *LocalRunner) Setup(dp *DebugParams) (err error) { source := string(data) ops, err := logic.AssembleString(source) if ops.Version > r.proto.LogicSigVersion { - return fmt.Errorf("Program version (%d) is beyond the maximum supported protocol version (%d)", ops.Version, r.proto.LogicSigVersion) + return fmt.Errorf("program version (%d) is beyond the maximum supported protocol version (%d)", ops.Version, r.proto.LogicSigVersion) } if err != nil { errorLines := "" @@ -534,6 +534,7 @@ func (r *LocalRunner) RunAll() error { TxnGroup: r.txnGroup, GroupIndex: run.groupIndex, PastSideEffects: run.pastSideEffects, + Specials: &transactions.SpecialAddresses{}, } run.result.pass, run.result.err = run.eval(ep) @@ -562,6 +563,7 @@ func (r *LocalRunner) Run() (bool, error) { TxnGroup: r.txnGroup, GroupIndex: run.groupIndex, PastSideEffects: run.pastSideEffects, + Specials: &transactions.SpecialAddresses{}, } // Workaround for Go's nil/empty interfaces nil check after nil assignment, i.e. diff --git a/cmd/tealdbg/localLedger.go b/cmd/tealdbg/localLedger.go index 6f4dcfeaa..23e58e3f3 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/cmd/tealdbg/local_test.go b/cmd/tealdbg/local_test.go index c0b26b145..d4d917a40 100644 --- a/cmd/tealdbg/local_test.go +++ b/cmd/tealdbg/local_test.go @@ -1064,3 +1064,77 @@ func TestLocalBalanceAdapterIndexer(t *testing.T) { ba := l.runs[0].ba checkBalanceAdapter(a, ba, sender, payTxn.Txn.Receiver, assetIdx, appIdx) } + +func TestDebugTxSubmit(t *testing.T) { + partitiontest.PartitionTest(t) + a := require.New(t) + + sender, err := basics.UnmarshalChecksumAddress("47YPQTIGQEO7T4Y4RWDYWEKV6RTR2UNBQXBABEEGM72ESWDQNCQ52OPASU") + a.NoError(err) + app, err := basics.UnmarshalChecksumAddress("6BPQU5WNZMTO4X72A2THZCGNJNTTE7YL6AWCYSUUTZEIYMJSEPJCQQ6DQI") + a.NoError(err) + + // make balance records + assetIdx := basics.AssetIndex(50) + appIdx := basics.AppIndex(100) + brs := makeSampleBalanceRecord(sender, assetIdx, appIdx) + bra := makeSampleBalanceRecord(app, assetIdx, appIdx) + balanceBlob := protocol.EncodeMsgp(&brs) + balanceBlob = append(balanceBlob, protocol.EncodeMsgp(&bra)...) + + txn := transactions.SignedTxn{ + Txn: transactions.Transaction{ + Type: protocol.ApplicationCallTx, + Header: transactions.Header{ + Sender: sender, + Fee: basics.MicroAlgos{Raw: 100}, + Note: []byte{1, 2, 3}, + }, + ApplicationCallTxnFields: transactions.ApplicationCallTxnFields{ + ApplicationID: appIdx, + }, + }, + } + + txnEnc := protocol.EncodeJSON(&txn) + txnBlob := []byte("[" + strings.Join([]string{string(txnEnc), txnSample}, ",") + "]") + + source := `#pragma version 5 +itxn_begin +int acfg +itxn_field TypeEnum +int 1000000 +itxn_field ConfigAssetTotal +int 3 +itxn_field ConfigAssetDecimals +byte "oz" +itxn_field ConfigAssetUnitName +byte "Gold" +itxn_field ConfigAssetName +byte "https://gold.rush/" +itxn_field ConfigAssetURL +byte 0x67f0cd61653bd34316160bc3f5cd3763c85b114d50d38e1f4e72c3b994411e7b +itxn_field ConfigAssetMetadataHash +itxn_submit +int 1` + + ds := DebugParams{ + ProgramNames: []string{"test"}, + ProgramBlobs: [][]byte{[]byte(source)}, + BalanceBlob: balanceBlob, + TxnBlob: txnBlob, + Proto: string(protocol.ConsensusCurrentVersion), + Round: 222, + LatestTimestamp: 333, + GroupIndex: 0, + RunMode: "application", + } + + local := MakeLocalRunner(nil) // no debugger + err = local.Setup(&ds) + a.NoError(err) + + pass, err := local.Run() + a.NoError(err) + a.True(pass) +} diff --git a/daemon/algod/api/server/v2/account.go b/daemon/algod/api/server/v2/account.go index 018618265..2187a1068 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 fed86ce62..3677c4b28 100644 --- a/daemon/algod/api/server/v2/dryrun.go +++ b/daemon/algod/api/server/v2/dryrun.go @@ -87,7 +87,7 @@ func (dr *DryrunRequest) ExpandSources() error { for i, s := range dr.Sources { ops, err := logic.AssembleString(s.Source) if err != nil { - return fmt.Errorf("Dryrun Source[%d]: %v", i, err) + return fmt.Errorf("dryrun Source[%d]: %v", i, err) } switch s.FieldName { case "lsig": @@ -104,7 +104,7 @@ func (dr *DryrunRequest) ExpandSources() error { } } default: - return fmt.Errorf("Dryrun Source[%d]: bad field name %#v", i, s.FieldName) + return fmt.Errorf("dryrun Source[%d]: bad field name %#v", i, s.FieldName) } } return nil @@ -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 { @@ -335,41 +338,6 @@ func (dl *dryrunLedger) GetCreatorForRound(rnd basics.Round, cidx basics.Creatab return basics.Address{}, false, fmt.Errorf("unknown creatable type %d", ctype) } -func (dl *dryrunLedger) getAppParams(addr basics.Address, aidx basics.AppIndex) (params basics.AppParams, err error) { - idx, ok := dl.accountApps[addr] - if !ok { - err = fmt.Errorf("addr %s is not know to dryrun", addr.String()) - return - } - if aidx != basics.AppIndex(dl.dr.Apps[idx].Id) { - err = fmt.Errorf("creator addr %s does not match to app id %d", addr.String(), aidx) - return - } - if params, err = ApplicationParamsToAppParams(&dl.dr.Apps[idx].Params); err != nil { - return - } - return -} - -func (dl *dryrunLedger) getLocalKV(addr basics.Address, aidx basics.AppIndex) (kv basics.TealKeyValue, err error) { - idx, ok := dl.accountsIn[addr] - if !ok { - err = fmt.Errorf("addr %s is not know to dryrun", addr.String()) - return - } - var ad basics.AccountData - if ad, err = AccountToAccountData(&dl.dr.Accounts[idx]); err != nil { - return - } - loc, ok := ad.AppLocalStates[aidx] - if !ok { - err = fmt.Errorf("addr %s not opted in to app %d, cannot fetch state", addr.String(), aidx) - return - } - kv = loc.KeyValue - return -} - func makeBalancesAdapter(dl *dryrunLedger, txn *transactions.Transaction, appIdx basics.AppIndex) (ba apply.Balances, err error) { ba = ledger.MakeDebugBalances(dl, basics.Round(dl.dr.Round), protocol.ConsensusVersion(dl.dr.ProtocolVersion), dl.dr.LatestTimestamp) @@ -423,6 +391,7 @@ func doDryrunRequest(dr *DryrunRequest, response *generated.DryrunResponse) { GroupIndex: uint64(ti), PastSideEffects: pse, PooledApplicationBudget: &pooledAppBudget, + Specials: &transactions.SpecialAddresses{}, } var result generated.DryrunTxnResult if len(stxn.Lsig.Logic) > 0 { diff --git a/daemon/algod/api/server/v2/dryrun_test.go b/daemon/algod/api/server/v2/dryrun_test.go index 10c758d4e..abb28143d 100644 --- a/daemon/algod/api/server/v2/dryrun_test.go +++ b/daemon/algod/api/server/v2/dryrun_test.go @@ -830,6 +830,7 @@ func TestDryrunEncodeDecode(t *testing.T) { dr1, err := DryrunRequestFromGenerated(&gdr) require.NoError(t, err) encoded, err = encode(protocol.CodecHandle, &dr) + require.NoError(t, err) encoded2 := protocol.EncodeReflect(&dr) require.Equal(t, encoded, encoded2) @@ -840,7 +841,6 @@ func TestDryrunEncodeDecode(t *testing.T) { require.NoError(t, err) require.Equal(t, dr1, dr2) - dec = protocol.NewDecoder(buf) dr2 = DryrunRequest{} err = decode(protocol.CodecHandle, encoded, &dr2) require.NoError(t, err) @@ -1136,6 +1136,7 @@ return require.NoError(t, err) approval := ops.Program ops, err = logic.AssembleString("int 1") + require.NoError(t, err) clst := ops.Program ops, err = logic.AssembleString("#pragma version 5 \nint 1") approv := ops.Program @@ -1331,3 +1332,156 @@ func TestDryrunCost(t *testing.T) { }) } } + +func TestDebugTxSubmit(t *testing.T) { + partitiontest.PartitionTest(t) + a := require.New(t) + source := `#pragma version 5 +itxn_begin +int acfg +itxn_field TypeEnum +int 1000000 +itxn_field ConfigAssetTotal +int 3 +itxn_field ConfigAssetDecimals +byte "oz" +itxn_field ConfigAssetUnitName +byte "Gold" +itxn_field ConfigAssetName +byte "https://gold.rush/" +itxn_field ConfigAssetURL +byte 0x67f0cd61653bd34316160bc3f5cd3763c85b114d50d38e1f4e72c3b994411e7b +itxn_field ConfigAssetMetadataHash +itxn_submit +int 1` + + ops, err := logic.AssembleString(source) + require.NoError(t, err) + approval := ops.Program + + ops, err = logic.AssembleString("int 1") + clst := ops.Program + require.NoError(t, err) + + sender, err := basics.UnmarshalChecksumAddress("47YPQTIGQEO7T4Y4RWDYWEKV6RTR2UNBQXBABEEGM72ESWDQNCQ52OPASU") + a.NoError(err) + app, err := basics.UnmarshalChecksumAddress("6BPQU5WNZMTO4X72A2THZCGNJNTTE7YL6AWCYSUUTZEIYMJSEPJCQQ6DQI") + a.NoError(err) + + // make balance records + appIdx := basics.AppIndex(100) + dr := DryrunRequest{ + Txns: []transactions.SignedTxn{{ + Txn: transactions.Transaction{ + Type: protocol.ApplicationCallTx, + Header: transactions.Header{ + Sender: sender, + Fee: basics.MicroAlgos{Raw: 100}, + Note: []byte{1, 2, 3}, + }, + ApplicationCallTxnFields: transactions.ApplicationCallTxnFields{ + ApplicationID: appIdx, + }, + }, + }}, + Apps: []generated.Application{ + { + Id: uint64(appIdx), + Params: generated.ApplicationParams{ + Creator: sender.String(), + ApprovalProgram: approval, + ClearStateProgram: clst, + }, + }, + }, + Accounts: []generated.Account{ + { + Address: sender.String(), + Status: "Online", + Amount: 10000000, + AmountWithoutPendingRewards: 10000000, + }, + { + Address: app.String(), + Status: "Offline", + Amount: 10000000, + AmountWithoutPendingRewards: 10000000, + }, + { + Address: basics.Address{}.String(), + Status: "Offline", + }, + }, + } + + dr.ProtocolVersion = string(dryrunProtoVersion) + + var response generated.DryrunResponse + doDryrunRequest(&dr, &response) + require.NoError(t, err) + checkAppCallPass(t, &response) + if t.Failed() { + logResponse(t, &response) + } +} + +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) + } +} diff --git a/test/e2e-go/cli/tealdbg/expect/tealdbgTest.exp b/test/e2e-go/cli/tealdbg/expect/tealdbgTest.exp index a4fc773eb..7d7b84ab4 100644 --- a/test/e2e-go/cli/tealdbg/expect/tealdbgTest.exp +++ b/test/e2e-go/cli/tealdbg/expect/tealdbgTest.exp @@ -81,7 +81,7 @@ if { [catch { spawn tealdbg debug -v $TEAL_PROG_FILE -p $PROTOCOL_VERSION_2 --remote-debugging-port 9392 --listen 127.0.0.1 expect { timeout { puts "tealdbg debug timed out"; exit 1 } - -re {Debug error: Program version \([0-9]+\) is beyond the maximum supported protocol version \([0-9]+\)} { set FAILED 1; close } + -re {Debug error: program version \([0-9]+\) is beyond the maximum supported protocol version \([0-9]+\)} { set FAILED 1; close } } if { $FAILED == 0 } { puts "ERROR: the command should have failed" |