diff options
Diffstat (limited to 'daemon/algod/api/server/v2/dryrun.go')
-rw-r--r-- | daemon/algod/api/server/v2/dryrun.go | 104 |
1 files changed, 51 insertions, 53 deletions
diff --git a/daemon/algod/api/server/v2/dryrun.go b/daemon/algod/api/server/v2/dryrun.go index 5a76d6fd2..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) @@ -397,16 +365,33 @@ func doDryrunRequest(dr *DryrunRequest, response *generated.DryrunResponse) { return } proto := config.Consensus[protocol.ConsensusVersion(dr.ProtocolVersion)] + origEnableAppCostPooling := proto.EnableAppCostPooling + // Enable EnableAppCostPooling so that dryrun + // 1) can determine cost 2) reports actual cost for large programs that fail + proto.EnableAppCostPooling = true + + // allow a huge execution budget + maxCurrentBudget := uint64(proto.MaxAppProgramCost * 100) + pooledAppBudget := maxCurrentBudget + allowedBudget := uint64(0) + cumulativeCost := uint64(0) + for _, stxn := range dr.Txns { + if stxn.Txn.Type == protocol.ApplicationCallTx { + allowedBudget += uint64(proto.MaxAppProgramCost) + } + } response.Txns = make([]generated.DryrunTxnResult, len(dr.Txns)) for ti, stxn := range dr.Txns { pse := logic.MakePastSideEffects(len(dr.Txns)) ep := logic.EvalParams{ - Txn: &stxn, - Proto: &proto, - TxnGroup: dr.Txns, - GroupIndex: ti, - PastSideEffects: pse, + Txn: &stxn, + Proto: &proto, + TxnGroup: dr.Txns, + GroupIndex: uint64(ti), + PastSideEffects: pse, + PooledApplicationBudget: &pooledAppBudget, + Specials: &transactions.SpecialAddresses{}, } var result generated.DryrunTxnResult if len(stxn.Lsig.Logic) > 0 { @@ -522,8 +507,25 @@ func doDryrunRequest(dr *DryrunRequest, response *generated.DryrunResponse) { result.LocalDeltas = &localDeltas } + // ensure the program has not exceeded execution budget + cost := maxCurrentBudget - pooledAppBudget + if pass { + if !origEnableAppCostPooling { + if cost > uint64(proto.MaxAppProgramCost) { + pass = false + err = fmt.Errorf("cost budget exceeded: budget is %d but program cost was %d", proto.MaxAppProgramCost, cost) + } + } else if cumulativeCost+cost > allowedBudget { + pass = false + err = fmt.Errorf("cost budget exceeded: budget is %d but program cost was %d", allowedBudget-cumulativeCost, cost) + } + } + result.Cost = &cost + maxCurrentBudget = pooledAppBudget + cumulativeCost += cost + var err3 error - result.Logs, err3 = DeltaLogToLog(delta.Logs, appIdx) + result.Logs, err3 = DeltaLogToLog(delta.Logs) if err3 != nil { messages = append(messages, err3.Error()) } @@ -570,20 +572,16 @@ func StateDeltaToStateDelta(sd basics.StateDelta) *generated.StateDelta { return &gsd } -// DeltaLogToLog EvalDelta.Logs to generated.LogItem -func DeltaLogToLog(logs []basics.LogItem, appIdx basics.AppIndex) (*[]generated.LogItem, error) { +// DeltaLogToLog base64 encode the logs +func DeltaLogToLog(logs []string) (*[][]byte, error) { if len(logs) == 0 { return nil, nil } - encodedLogs := make([]generated.LogItem, 0, len(logs)) - for _, log := range logs { - if log.ID != 0 { - return nil, fmt.Errorf("logging for a foreign app is not supported") - } - msg := base64.StdEncoding.EncodeToString([]byte(log.Message)) - encodedLogs = append(encodedLogs, generated.LogItem{Id: uint64(appIdx), Value: msg}) + logsAsBytes := make([][]byte, len(logs)) + for i, log := range logs { + logsAsBytes[i] = []byte(log) } - return &encodedLogs, nil + return &logsAsBytes, nil } // MergeAppParams merges values, existing in "base" take priority over new in "update" |