summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Jannotti <john.jannotti@algorand.com>2022-02-01 16:51:48 -0500
committerGitHub <noreply@github.com>2022-02-01 16:51:48 -0500
commit7c2329ce0a3405a62a841369c2aed3a0a81349e7 (patch)
tree49266ace2bd67d2e840ccee3f184fbca70f1c6c5
parent84b52e7567eec1641768fc9441adbfed3cc3a875 (diff)
Tsachi cr (#3538)
-rw-r--r--cmd/opdoc/opdoc.go48
-rw-r--r--cmd/tealdbg/cdtState.go3
-rw-r--r--data/transactions/blackbox_test.go82
-rw-r--r--data/transactions/logic/assembler.go9
-rw-r--r--data/transactions/logic/backwardCompat_test.go1
-rw-r--r--data/transactions/logic/blackbox_test.go2
-rw-r--r--data/transactions/logic/eval.go147
-rw-r--r--data/transactions/logic/evalStateful_test.go6
-rw-r--r--data/transactions/logic/eval_test.go25
-rw-r--r--data/transactions/logic/export_test.go4
-rw-r--r--data/transactions/logic/fields_test.go4
-rw-r--r--data/transactions/logic/ledger_test.go74
-rw-r--r--ledger/internal/applications.go18
-rw-r--r--ledger/internal/applications_test.go4
14 files changed, 248 insertions, 179 deletions
diff --git a/cmd/opdoc/opdoc.go b/cmd/opdoc/opdoc.go
index 7167583d3..9b6fff979 100644
--- a/cmd/opdoc/opdoc.go
+++ b/cmd/opdoc/opdoc.go
@@ -235,22 +235,25 @@ func opToMarkdown(out io.Writer, op *logic.OpSpec) (err error) {
if !op.Modes.Any() {
fmt.Fprintf(out, "- Mode: %s\n", op.Modes.String())
}
- if op.Name == "global" {
+ switch op.Name {
+ case "global":
globalFieldsMarkdown(out)
- } else if op.Name == "txn" {
+ case "txn":
transactionFieldsMarkdown(out)
fmt.Fprintf(out, "\nTypeEnum mapping:\n\n")
typeEnumTableMarkdown(out)
- } else if op.Name == "asset_holding_get" {
+ case "asset_holding_get":
assetHoldingFieldsMarkdown(out)
- } else if op.Name == "asset_params_get" {
+ case "asset_params_get":
assetParamsFieldsMarkdown(out)
- } else if op.Name == "app_params_get" {
+ case "app_params_get":
appParamsFieldsMarkdown(out)
- } else if op.Name == "acct_params_get" {
+ case "acct_params_get":
acctParamsFieldsMarkdown(out)
- } else if strings.HasPrefix(op.Name, "ecdsa") {
- ecDsaCurvesMarkdown(out)
+ default:
+ if strings.HasPrefix(op.Name, "ecdsa") {
+ ecDsaCurvesMarkdown(out)
+ }
}
ode := logic.OpDocExtra(op.Name)
if ode != "" {
@@ -369,42 +372,51 @@ func buildLanguageSpec(opGroups map[string][]string) *LanguageSpec {
}
}
+func create(file string) *os.File {
+ f, err := os.Create(file)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Unable to create '%s': %v", file, err)
+ os.Exit(1)
+ }
+ return f
+}
+
func main() {
- opcodesMd, _ := os.Create("TEAL_opcodes.md")
+ opcodesMd := create("TEAL_opcodes.md")
opsToMarkdown(opcodesMd)
opcodesMd.Close()
opGroups := make(map[string][]string, len(logic.OpSpecs))
for grp, names := range logic.OpGroups {
fname := fmt.Sprintf("%s.md", grp)
fname = strings.ReplaceAll(fname, " ", "_")
- fout, _ := os.Create(fname)
+ fout := create(fname)
opGroupMarkdownTable(names, fout)
fout.Close()
for _, opname := range names {
opGroups[opname] = append(opGroups[opname], grp)
}
}
- constants, _ := os.Create("named_integer_constants.md")
+ constants := create("named_integer_constants.md")
integerConstantsTableMarkdown(constants)
constants.Close()
- txnfields, _ := os.Create("txn_fields.md")
+ txnfields := create("txn_fields.md")
fieldSpecsMarkdown(txnfields, logic.TxnFieldNames, logic.TxnFieldSpecByName)
txnfields.Close()
- globalfields, _ := os.Create("global_fields.md")
+ globalfields := create("global_fields.md")
fieldSpecsMarkdown(globalfields, logic.GlobalFieldNames, logic.GlobalFieldSpecByName)
globalfields.Close()
- assetholding, _ := os.Create("asset_holding_fields.md")
+ assetholding := create("asset_holding_fields.md")
fieldSpecsMarkdown(assetholding, logic.AssetHoldingFieldNames, logic.AssetHoldingFieldSpecByName)
assetholding.Close()
- assetparams, _ := os.Create("asset_params_fields.md")
+ assetparams := create("asset_params_fields.md")
fieldSpecsMarkdown(assetparams, logic.AssetParamsFieldNames, logic.AssetParamsFieldSpecByName)
assetparams.Close()
- appparams, _ := os.Create("app_params_fields.md")
+ appparams := create("app_params_fields.md")
fieldSpecsMarkdown(appparams, logic.AppParamsFieldNames, logic.AppParamsFieldSpecByName)
appparams.Close()
@@ -412,12 +424,12 @@ func main() {
fieldSpecsMarkdown(acctparams, logic.AcctParamsFieldNames, logic.AcctParamsFieldSpecByName)
acctparams.Close()
- langspecjs, _ := os.Create("langspec.json")
+ langspecjs := create("langspec.json")
enc := json.NewEncoder(langspecjs)
enc.Encode(buildLanguageSpec(opGroups))
langspecjs.Close()
- tealtm, _ := os.Create("teal.tmLanguage.json")
+ tealtm := create("teal.tmLanguage.json")
enc = json.NewEncoder(tealtm)
enc.SetIndent("", " ")
enc.Encode(buildSyntaxHighlight())
diff --git a/cmd/tealdbg/cdtState.go b/cmd/tealdbg/cdtState.go
index 5e7ee9825..58ea184f0 100644
--- a/cmd/tealdbg/cdtState.go
+++ b/cmd/tealdbg/cdtState.go
@@ -368,7 +368,8 @@ func prepareTxn(txn *transactions.Transaction, groupIndex int) []fieldDesc {
field == int(logic.CreatedApplicationID) ||
field == int(logic.CreatedAssetID) ||
field == int(logic.Logs) ||
- field == int(logic.NumLogs) {
+ field == int(logic.NumLogs) ||
+ field == int(logic.LastLog) {
continue
}
var value string
diff --git a/data/transactions/blackbox_test.go b/data/transactions/blackbox_test.go
new file mode 100644
index 000000000..b8f35e9a8
--- /dev/null
+++ b/data/transactions/blackbox_test.go
@@ -0,0 +1,82 @@
+// Copyright (C) 2019-2022 Algorand, Inc.
+// This file is part of go-algorand
+//
+// go-algorand is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// go-algorand is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with go-algorand. If not, see <https://www.gnu.org/licenses/>.
+
+package transactions_test
+
+import (
+ "testing"
+
+ "github.com/algorand/go-algorand/data/transactions"
+ "github.com/algorand/go-algorand/data/txntest"
+ "github.com/algorand/go-algorand/protocol"
+ "github.com/algorand/go-algorand/test/partitiontest"
+ "github.com/stretchr/testify/require"
+)
+
+func TestFeeCredit(t *testing.T) {
+ partitiontest.PartitionTest(t)
+
+ c, err := transactions.FeeCredit([]transactions.SignedTxnWithAD{
+ txntest.Txn{Fee: 5}.SignedTxnWithAD(),
+ }, 5)
+ require.NoError(t, err)
+ require.Equal(t, c, uint64(0))
+
+ c, err = transactions.FeeCredit([]transactions.SignedTxnWithAD{
+ txntest.Txn{Fee: 4}.SignedTxnWithAD(),
+ }, 5)
+ require.Error(t, err)
+
+ c, err = transactions.FeeCredit([]transactions.SignedTxnWithAD{
+ txntest.Txn{Fee: 5}.SignedTxnWithAD(),
+ }, 4)
+ require.NoError(t, err)
+ require.Equal(t, c, uint64(1))
+
+ c, err = transactions.FeeCredit([]transactions.SignedTxnWithAD{
+ txntest.Txn{Fee: 5}.SignedTxnWithAD(),
+ txntest.Txn{Fee: 5}.SignedTxnWithAD(),
+ }, 5)
+ require.NoError(t, err)
+ require.Equal(t, c, uint64(0))
+
+ c, err = transactions.FeeCredit([]transactions.SignedTxnWithAD{
+ txntest.Txn{Fee: 5}.SignedTxnWithAD(),
+ txntest.Txn{Type: protocol.CompactCertTx, Fee: 0}.SignedTxnWithAD(),
+ }, 5)
+ require.NoError(t, err)
+ require.Equal(t, c, uint64(0))
+
+ c, err = transactions.FeeCredit([]transactions.SignedTxnWithAD{
+ txntest.Txn{Fee: 5}.SignedTxnWithAD(),
+ txntest.Txn{Fee: 5}.SignedTxnWithAD(),
+ txntest.Txn{Fee: 5}.SignedTxnWithAD(),
+ }, 5)
+ require.NoError(t, err)
+ require.Equal(t, c, uint64(0))
+
+ c, err = transactions.FeeCredit([]transactions.SignedTxnWithAD{}, 5)
+ require.NoError(t, err)
+ require.Equal(t, c, uint64(0))
+
+ c, err = transactions.FeeCredit([]transactions.SignedTxnWithAD{
+ txntest.Txn{Fee: 5}.SignedTxnWithAD(),
+ txntest.Txn{Fee: 25}.SignedTxnWithAD(),
+ txntest.Txn{Fee: 5}.SignedTxnWithAD(),
+ }, 5)
+ require.NoError(t, err)
+ require.Equal(t, c, uint64(20))
+}
diff --git a/data/transactions/logic/assembler.go b/data/transactions/logic/assembler.go
index 5d5af3dec..8202af0bc 100644
--- a/data/transactions/logic/assembler.go
+++ b/data/transactions/logic/assembler.go
@@ -823,14 +823,15 @@ func asmTxn(ops *OpStream, spec *OpSpec, args []string) error {
// asmTxn2 delegates to asmTxn or asmTxna depending on number of operands
func asmTxn2(ops *OpStream, spec *OpSpec, args []string) error {
- if len(args) == 1 {
+ switch len(args) {
+ case 1:
return asmTxn(ops, spec, args)
- }
- if len(args) == 2 {
+ case 2:
txna := OpsByName[ops.Version]["txna"]
return asmTxna(ops, &txna, args)
+ default:
+ return ops.error("txn expects one or two arguments")
}
- return ops.error("txn expects one or two arguments")
}
// asmTxna also assemble asmItxna
diff --git a/data/transactions/logic/backwardCompat_test.go b/data/transactions/logic/backwardCompat_test.go
index 21985d749..b5917219d 100644
--- a/data/transactions/logic/backwardCompat_test.go
+++ b/data/transactions/logic/backwardCompat_test.go
@@ -308,7 +308,6 @@ func TestBackwardCompatTEALv1(t *testing.T) {
// Costs for v2 should be higher because of hash opcode cost changes
ep2, tx, _ := makeSampleEnvWithVersion(2)
ep2.Proto.LogicSigMaxCost = 2307
- // ep2.TxnGroup[0].Lsig.Logic = opsV2.Program
ep2.TxnGroup[0].Lsig.Args = [][]byte{data[:], sig[:], pk[:], tx.Sender[:], tx.Note}
// Eval doesn't fail, but it would be ok (better?) if it did
testLogicBytes(t, opsV2.Program, ep2, "static cost", "")
diff --git a/data/transactions/logic/blackbox_test.go b/data/transactions/logic/blackbox_test.go
index d9a01f02e..e26f84a8f 100644
--- a/data/transactions/logic/blackbox_test.go
+++ b/data/transactions/logic/blackbox_test.go
@@ -84,7 +84,7 @@ func TestNewAppEvalParams(t *testing.T) {
require.NotNil(t, ep)
require.Equal(t, ep.TxnGroup, testCase.group)
require.Equal(t, *ep.Proto, param)
- if reflect.DeepEqual(param, config.Consensus[protocol.ConsensusV29]) {
+ if reflect.DeepEqual(param, config.Consensus[protocol.ConsensusV29]) || testCase.numAppCalls == 0 {
require.Nil(t, ep.PooledApplicationBudget)
} else if reflect.DeepEqual(param, config.Consensus[protocol.ConsensusFuture]) {
require.Equal(t, *ep.PooledApplicationBudget, param.MaxAppProgramCost*testCase.numAppCalls)
diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go
index d06d3f1d1..eb4d18f11 100644
--- a/data/transactions/logic/eval.go
+++ b/data/transactions/logic/eval.go
@@ -207,8 +207,7 @@ func (sv *stackValue) toTealValue() (tv basics.TealValue) {
// LedgerForLogic represents ledger API for Stateful TEAL program
type LedgerForLogic interface {
- Balance(addr basics.Address) (basics.MicroAlgos, error)
- MinBalance(addr basics.Address, proto *config.ConsensusParams) (basics.MicroAlgos, error)
+ AccountData(addr basics.Address) (basics.AccountData, error)
Authorizer(addr basics.Address) (basics.Address, error)
Round() basics.Round
LatestTimestamp() int64
@@ -314,12 +313,12 @@ func NewEvalParams(txgroup []transactions.SignedTxnWithAD, proto *config.Consens
credit, _ := transactions.FeeCredit(txgroup, proto.MinTxnFee)
- if proto.EnableAppCostPooling {
+ if proto.EnableAppCostPooling && apps > 0 {
pooledApplicationBudget = new(int)
*pooledApplicationBudget = apps * proto.MaxAppProgramCost
}
- if proto.EnableInnerTransactionPooling {
+ if proto.EnableInnerTransactionPooling && apps > 0 {
pooledAllowedInners = new(int)
*pooledAllowedInners = proto.MaxTxGroupSize * proto.MaxInnerTransactions
}
@@ -495,13 +494,13 @@ const (
StackNone StackType = iota
// StackAny in an OpSpec shows that the op pops or yield any type
- StackAny StackType = iota
+ StackAny
// StackUint64 in an OpSpec shows that the op pops or yields a uint64
- StackUint64 StackType = iota
+ StackUint64
// StackBytes in an OpSpec shows that the op pops or yields a []byte
- StackBytes StackType = iota
+ StackBytes
)
// StackTypes is an alias for a list of StackType with syntactic sugar
@@ -552,6 +551,9 @@ func EvalContract(program []byte, gi int, aid basics.AppIndex, params *EvalParam
if params.Ledger == nil {
return false, nil, errors.New("no ledger in contract eval")
}
+ if aid == 0 {
+ return false, nil, errors.New("0 appId in contract eval")
+ }
cx := EvalContext{
EvalParams: params,
runModeFlags: runModeApplication,
@@ -2023,12 +2025,10 @@ func TxnFieldToTealValue(txn *transactions.Transaction, groupIndex int, field Tx
if groupIndex < 0 {
return basics.TealValue{}, fmt.Errorf("negative groupIndex %d", groupIndex)
}
- cx := EvalContext{
- GroupIndex: groupIndex,
- Txn: &transactions.SignedTxnWithAD{SignedTxn: transactions.SignedTxn{Txn: *txn}},
- }
+ var cx EvalContext
+ stxnad := &transactions.SignedTxnWithAD{SignedTxn: transactions.SignedTxn{Txn: *txn}}
fs := txnFieldSpecByField[field]
- sv, err := cx.txnFieldToStack(cx.Txn, &fs, arrayFieldIdx, groupIndex, false)
+ sv, err := cx.txnFieldToStack(stxnad, &fs, arrayFieldIdx, groupIndex, false)
return sv.toTealValue(), err
}
@@ -2272,9 +2272,9 @@ func (cx *EvalContext) fetchField(field TxnField, expectArray bool) (*txnFieldSp
type txnSource int
const (
- srcGroup txnSource = iota
- srcInner = iota
- srcInnerGroup = iota
+ srcGroup txnSource = iota
+ srcInner
+ srcInnerGroup
)
// opTxnImpl implements all of the txn variants. Each form of txn opcode should
@@ -2566,35 +2566,38 @@ func opGitxnas(cx *EvalContext) {
cx.stack[last] = sv
}
-func opGaidImpl(cx *EvalContext, gi int, opName string) (sv stackValue, err error) {
- if gi >= len(cx.TxnGroup) {
- err = fmt.Errorf("%s lookup TxnGroup[%d] but it only has %d", opName, gi, len(cx.TxnGroup))
+func opGaidImpl(cx *EvalContext, giw uint64, opName string) (sv stackValue, err error) {
+ if giw >= uint64(len(cx.TxnGroup)) {
+ err = fmt.Errorf("%s lookup TxnGroup[%d] but it only has %d", opName, giw, len(cx.TxnGroup))
return
- } else if gi > cx.GroupIndex {
+ }
+ // Is now assured smalled than a len() so fits in int.
+ gi := int(giw)
+ if gi > cx.GroupIndex {
err = fmt.Errorf("%s can't get creatable ID of txn ahead of the current one (index %d) in the transaction group", opName, gi)
return
- } else if gi == cx.GroupIndex {
+ }
+ if gi == cx.GroupIndex {
err = fmt.Errorf("%s is only for accessing creatable IDs of previous txns, use `global CurrentApplicationID` instead to access the current app's creatable ID", opName)
return
- } else if txn := cx.TxnGroup[gi].Txn; !(txn.Type == protocol.ApplicationCallTx || txn.Type == protocol.AssetConfigTx) {
+ }
+ if txn := cx.TxnGroup[gi].Txn; !(txn.Type == protocol.ApplicationCallTx || txn.Type == protocol.AssetConfigTx) {
err = fmt.Errorf("can't use %s on txn that is not an app call nor an asset config txn with index %d", opName, gi)
return
}
- cid, err := cx.getCreatableID(gi)
- if cid == 0 {
- err = fmt.Errorf("%s can't read creatable ID from txn with group index %d because the txn did not create anything", opName, gi)
- return
+ if aid := cx.TxnGroup[gi].ApplyData.ConfigAsset; aid != 0 {
+ return stackValue{Uint: uint64(aid)}, nil
}
-
- sv = stackValue{
- Uint: cid,
+ if aid := cx.TxnGroup[gi].ApplyData.ApplicationID; aid != 0 {
+ return stackValue{Uint: uint64(aid)}, nil
}
+ err = fmt.Errorf("%s: index %d did not create anything", opName, gi)
return
}
func opGaid(cx *EvalContext) {
- gi := int(cx.program[cx.pc+1])
+ gi := uint64(cx.program[cx.pc+1])
sv, err := opGaidImpl(cx, gi, "gaid")
if err != nil {
cx.err = err
@@ -2606,14 +2609,9 @@ func opGaid(cx *EvalContext) {
func opGaids(cx *EvalContext) {
last := len(cx.stack) - 1
-
gi := cx.stack[last].Uint
- if gi >= uint64(len(cx.TxnGroup)) {
- cx.err = fmt.Errorf("gaids lookup TxnGroup[%d] but it only has %d", gi, len(cx.TxnGroup))
- return
- }
- sv, err := opGaidImpl(cx, int(gi), "gaids")
+ sv, err := opGaidImpl(cx, gi, "gaids")
if err != nil {
cx.err = err
return
@@ -2657,16 +2655,6 @@ func (cx *EvalContext) getApplicationAddress(app basics.AppIndex) basics.Address
return appAddr
}
-func (cx *EvalContext) getCreatableID(groupIndex int) (cid uint64, err error) {
- if aid := cx.TxnGroup[groupIndex].ApplyData.ConfigAsset; aid != 0 {
- return uint64(aid), nil
- }
- if aid := cx.TxnGroup[groupIndex].ApplyData.ApplicationID; aid != 0 {
- return uint64(aid), nil
- }
- return 0, fmt.Errorf("Index %d did not create anything", groupIndex)
-}
-
func (cx *EvalContext) getCreatorAddress() ([]byte, error) {
if cx.Ledger == nil {
return nil, fmt.Errorf("ledger not available")
@@ -3044,7 +3032,7 @@ func opGloadss(cx *EvalContext) {
gi := cx.stack[prev].Uint
if gi >= uint64(len(cx.TxnGroup)) {
- cx.err = fmt.Errorf("gloads lookup TxnGroup[%d] but it only has %d", gi, len(cx.TxnGroup))
+ cx.err = fmt.Errorf("gloadss lookup TxnGroup[%d] but it only has %d", gi, len(cx.TxnGroup))
return
}
scratchIdx := cx.stack[last].Uint
@@ -3363,49 +3351,50 @@ func (cx *EvalContext) mutableAccountReference(account stackValue) (basics.Addre
return addr, accountIdx, err
}
-type opQuery func(basics.Address, *config.ConsensusParams) (basics.MicroAlgos, error)
-
-func opBalanceQuery(cx *EvalContext, query opQuery, item string) error {
+func opBalance(cx *EvalContext) {
+ if cx.Ledger == nil {
+ cx.err = fmt.Errorf("ledger not available")
+ return
+ }
last := len(cx.stack) - 1 // account (index or actual address)
addr, _, err := cx.accountReference(cx.stack[last])
if err != nil {
- return err
+ cx.err = err
+ return
}
- microAlgos, err := query(addr, cx.Proto)
+ account, err := cx.Ledger.AccountData(addr)
if err != nil {
- return fmt.Errorf("failed to fetch %s of %v: %w", item, addr, err)
+ cx.err = err
+ return
}
cx.stack[last].Bytes = nil
- cx.stack[last].Uint = microAlgos.Raw
- return nil
+ cx.stack[last].Uint = account.MicroAlgos.Raw
}
-func opBalance(cx *EvalContext) {
+
+func opMinBalance(cx *EvalContext) {
if cx.Ledger == nil {
cx.err = fmt.Errorf("ledger not available")
return
}
+ last := len(cx.stack) - 1 // account (index or actual address)
- balanceQuery := func(addr basics.Address, _ *config.ConsensusParams) (basics.MicroAlgos, error) {
- return cx.Ledger.Balance(addr)
- }
- err := opBalanceQuery(cx, balanceQuery, "balance")
+ addr, _, err := cx.accountReference(cx.stack[last])
if err != nil {
cx.err = err
- }
-}
-func opMinBalance(cx *EvalContext) {
- if cx.Ledger == nil {
- cx.err = fmt.Errorf("ledger not available")
return
}
- err := opBalanceQuery(cx, cx.Ledger.MinBalance, "minimum balance")
+ account, err := cx.Ledger.AccountData(addr)
if err != nil {
cx.err = err
+ return
}
+
+ cx.stack[last].Bytes = nil
+ cx.stack[last].Uint = account.MinBalance(cx.Proto).Raw
}
func opAppOptedIn(cx *EvalContext) {
@@ -3929,36 +3918,23 @@ func opAcctParamsGet(cx *EvalContext) {
return
}
- bal, err := cx.Ledger.Balance(addr)
+ account, err := cx.Ledger.AccountData(addr)
if err != nil {
cx.err = err
return
}
- exist := boolToUint(bal.Raw > 0)
+
+ exist := boolToUint(account.MicroAlgos.Raw > 0)
var value stackValue
switch fs.field {
case AcctBalance:
- value.Uint = bal.Raw
+ value.Uint = account.MicroAlgos.Raw
case AcctMinBalance:
- mbal, err := cx.Ledger.MinBalance(addr, cx.Proto)
- if err != nil {
- cx.err = err
- return
- }
- value.Uint = mbal.Raw
+ value.Uint = account.MinBalance(cx.Proto).Raw
case AcctAuthAddr:
- auth, err := cx.Ledger.Authorizer(addr)
- if err != nil {
- cx.err = err
- return
- }
- if auth == addr {
- value.Bytes = zeroAddress[:]
- } else {
- value.Bytes = auth[:]
- }
+ value.Bytes = account.AuthAddr[:]
}
cx.stack[last] = value
cx.stack = append(cx.stack, stackValue{Uint: exist})
@@ -3967,7 +3943,7 @@ func opAcctParamsGet(cx *EvalContext) {
func opLog(cx *EvalContext) {
last := len(cx.stack) - 1
- if len(cx.Txn.EvalDelta.Logs) == MaxLogCalls {
+ if len(cx.Txn.EvalDelta.Logs) >= MaxLogCalls {
cx.err = fmt.Errorf("too many log calls in program. up to %d is allowed", MaxLogCalls)
return
}
@@ -4546,5 +4522,6 @@ func opBase64Decode(cx *EvalContext) {
if encodingField == StdEncoding {
encoding = base64.StdEncoding
}
+ encoding = encoding.Strict()
cx.stack[last].Bytes, cx.err = base64Decode(cx.stack[last].Bytes, encoding)
}
diff --git a/data/transactions/logic/evalStateful_test.go b/data/transactions/logic/evalStateful_test.go
index 9bacb5ae3..804e2ec77 100644
--- a/data/transactions/logic/evalStateful_test.go
+++ b/data/transactions/logic/evalStateful_test.go
@@ -462,7 +462,7 @@ func TestMinBalance(t *testing.T) {
ledger.NewLocals(tx.Sender, 77)
// create + optin + 10 schema base + 4 ints + 6 bytes (local
// and global count b/c NewLocals opts the creator in)
- minb := 2*1002 + 10*1003 + 4*1004 + 6*1005
+ minb := 1002 + 1006 + 10*1003 + 4*1004 + 6*1005
testApp(t, fmt.Sprintf("int 0; min_balance; int %d; ==", 2002+minb), ep)
// request extra program pages, min balance increase
withepp := makeApp(1, 2, 3, 4)
@@ -1010,7 +1010,7 @@ intc_2 // 1
ops := testProg(t, source, version)
require.Equal(t, OpsByName[now.Proto.LogicSigVersion]["asset_holding_get"].Opcode, ops.Program[8])
ops.Program[9] = 0x02
- _, err := EvalApp(ops.Program, 0, 0, now)
+ _, err := EvalApp(ops.Program, 0, 888, now)
require.Error(t, err)
require.Contains(t, err.Error(), "invalid asset_holding_get field 2")
@@ -1035,7 +1035,7 @@ intc_1
ops = testProg(t, source, version)
require.Equal(t, OpsByName[now.Proto.LogicSigVersion]["asset_params_get"].Opcode, ops.Program[6])
ops.Program[7] = 0x20
- _, err = EvalApp(ops.Program, 0, 0, now)
+ _, err = EvalApp(ops.Program, 0, 888, now)
require.Error(t, err)
require.Contains(t, err.Error(), "invalid asset_params_get field 32")
diff --git a/data/transactions/logic/eval_test.go b/data/transactions/logic/eval_test.go
index 5921e7511..ebf7ed9ba 100644
--- a/data/transactions/logic/eval_test.go
+++ b/data/transactions/logic/eval_test.go
@@ -62,6 +62,7 @@ func makeTestProtoV(version uint64) *config.ConsensusParams {
SchemaMinBalancePerEntry: 1003,
SchemaUintMinBalance: 1004,
SchemaBytesMinBalance: 1005,
+ AppFlatOptInMinBalance: 1006,
MaxInnerTransactions: 4,
MaxTxGroupSize: 8,
@@ -1649,12 +1650,12 @@ func TestGaid(t *testing.T) {
ep.Ledger = MakeLedger(nil)
// should fail when no creatable was created
- _, err := EvalApp(check0.Program, 1, 0, ep)
+ _, err := EvalApp(check0.Program, 1, 888, ep)
require.Error(t, err)
- require.Contains(t, err.Error(), "the txn did not create anything")
+ require.Contains(t, err.Error(), "did not create anything")
ep.TxnGroup[0].ApplyData.ConfigAsset = 100
- pass, err := EvalApp(check0.Program, 1, 0, ep)
+ pass, err := EvalApp(check0.Program, 1, 888, ep)
if !pass || err != nil {
t.Log(ep.Trace.String())
}
@@ -1663,18 +1664,18 @@ func TestGaid(t *testing.T) {
// should fail when accessing future transaction in group
check2 := testProg(t, "gaid 2; int 0; >", 4)
- _, err = EvalApp(check2.Program, 1, 0, ep)
+ _, err = EvalApp(check2.Program, 1, 888, ep)
require.Error(t, err)
require.Contains(t, err.Error(), "gaid can't get creatable ID of txn ahead of the current one")
// should fail when accessing self
- _, err = EvalApp(check0.Program, 0, 0, ep)
+ _, err = EvalApp(check0.Program, 0, 888, ep)
require.Error(t, err)
require.Contains(t, err.Error(), "gaid is only for accessing creatable IDs of previous txns")
// should fail on non-creatable
ep.TxnGroup[0].Txn.Type = protocol.PaymentTx
- _, err = EvalApp(check0.Program, 1, 0, ep)
+ _, err = EvalApp(check0.Program, 1, 888, ep)
require.Error(t, err)
require.Contains(t, err.Error(), "can't use gaid on txn that is not an app call nor an asset config txn")
ep.TxnGroup[0].Txn.Type = protocol.AssetConfigTx
@@ -4543,9 +4544,10 @@ func TestPcDetails(t *testing.T) {
ops := testProg(t, test.source, LogicVersion)
ep, _, _ := makeSampleEnv()
- pass, cx, err := EvalContract(ops.Program, 0, 0, ep)
+ pass, cx, err := EvalContract(ops.Program, 0, 888, ep)
require.Error(t, err)
require.False(t, pass)
+ require.NotNil(t, cx) // cx comes back nil if we couldn't even run
assert.Equal(t, test.pc, cx.pc, ep.Trace.String())
@@ -4646,6 +4648,15 @@ By Herman Melville`, "",
{"SQ=", "URLEncoding", "", "byte 3"},
{"SQ===", "StdEncoding", "", "byte 4"},
{"SQ===", "URLEncoding", "", "byte 4"},
+
+ // Strict decoding. "Y" is normally encoded as "WQ==", as confirmed by the first test
+ {"WQ==", "StdEncoding", "Y", ""},
+ // When encoding one byte, the Y (90) becomes a 6bit value (the W) and a
+ // 2bit value (the first 2 bits of the Q. Q is the 16th b64 digit, it is
+ // 0b010000. For encoding Y, only those first two bits matter. In
+ // Strict() mode, the rest must be 0s. So using R (0b010001) should
+ // fail.
+ {"WR==", "StdEncoding", "Y", "byte 2"},
}
template := `byte 0x%s; byte 0x%s; base64_decode %s; ==`
diff --git a/data/transactions/logic/export_test.go b/data/transactions/logic/export_test.go
index d0ca904b2..6575fa3bc 100644
--- a/data/transactions/logic/export_test.go
+++ b/data/transactions/logic/export_test.go
@@ -18,7 +18,9 @@ package logic
// Export for testing only. See
// https://medium.com/@robiplus/golang-trick-export-for-test-aa16cbd7b8cd for a
-// nice explanation.
+// nice explanation. tl;dr: Since some of our testing is in logic_test package,
+// we export some extra things to make testing easier there. But we do it in a
+// _test.go file, so they are only exported during testing.
func NewExpect(l int, s string) Expect {
return Expect{l, s}
diff --git a/data/transactions/logic/fields_test.go b/data/transactions/logic/fields_test.go
index c3fe020b3..b6d1a4989 100644
--- a/data/transactions/logic/fields_test.go
+++ b/data/transactions/logic/fields_test.go
@@ -64,7 +64,7 @@ func TestGlobalFieldsVersions(t *testing.T) {
ep.Ledger = ledger
// check failure with version check
- _, err := EvalApp(ops.Program, 0, 0, ep)
+ _, err := EvalApp(ops.Program, 0, 888, ep)
require.Error(t, err)
require.Contains(t, err.Error(), "greater than protocol supported version")
@@ -191,7 +191,7 @@ func TestTxnEffectsAvailable(t *testing.T) {
_, err := EvalSignature(1, ep)
require.Error(t, err)
ep.Ledger = MakeLedger(nil)
- _, err = EvalApp(ops.Program, 1, 0, ep)
+ _, err = EvalApp(ops.Program, 1, 888, ep)
if v < txnEffectsVersion {
require.Error(t, err, source)
} else {
diff --git a/data/transactions/logic/ledger_test.go b/data/transactions/logic/ledger_test.go
index 26b959bcc..7efdeebbd 100644
--- a/data/transactions/logic/ledger_test.go
+++ b/data/transactions/logic/ledger_test.go
@@ -21,7 +21,6 @@ import (
"fmt"
"math/rand"
- "github.com/algorand/go-algorand/config"
"github.com/algorand/go-algorand/data/basics"
"github.com/algorand/go-algorand/data/transactions"
"github.com/algorand/go-algorand/protocol"
@@ -95,10 +94,9 @@ func (l *Ledger) NewAccount(addr basics.Address, balance uint64) {
l.balances[addr] = makeBalanceRecord(addr, balance)
}
-// NewApp add a new AVM app to the Ledger, and arranges so that future
-// executions will act as though they are that app. In most uses, it only sets
-// up the id and schema but no code, as testing will want to try many different
-// code sequences.
+// NewApp add a new AVM app to the Ledger. In most uses, it only sets up the id
+// and schema but no code, as testing will want to try many different code
+// sequences.
func (l *Ledger) NewApp(creator basics.Address, appID basics.AppIndex, params basics.AppParams) {
params = params.Clone()
if params.GlobalState == nil {
@@ -198,48 +196,46 @@ func (l *Ledger) LatestTimestamp() int64 {
return int64(rand.Uint32() + 1)
}
-// Balance returns the value in an account, as MicroAlgos
-func (l *Ledger) Balance(addr basics.Address) (amount basics.MicroAlgos, err error) {
- br, ok := l.balances[addr]
- if !ok {
- return basics.MicroAlgos{Raw: 0}, nil
- }
- return basics.MicroAlgos{Raw: br.balance}, nil
-}
-
-// MinBalance computes the MinBalance requirement for an account,
-// under the given consensus parameters.
-func (l *Ledger) MinBalance(addr basics.Address, proto *config.ConsensusParams) (amount basics.MicroAlgos, err error) {
- br, ok := l.balances[addr]
- if !ok {
- br = makeBalanceRecord(addr, 0)
+// AccountData returns a version of the account that is good enough for
+// satisfiying AVM needs. (balance, calc minbalance, and authaddr)
+func (l *Ledger) AccountData(addr basics.Address) (basics.AccountData, error) {
+ br := l.balances[addr]
+ // br may come back empty if addr doesn't exist. That's fine for our needs.
+ assets := make(map[basics.AssetIndex]basics.AssetParams)
+ for a, p := range l.assets {
+ if p.Creator == addr {
+ assets[a] = p.AssetParams
+ }
}
- var min uint64
-
- // First, base MinBalance
- min = proto.MinBalance
+ schemaTotal := basics.StateSchema{}
+ pagesTotal := uint32(0)
- // MinBalance for each Asset
- assetCost := basics.MulSaturate(proto.MinBalance, uint64(len(br.holdings)))
- min = basics.AddSaturate(min, assetCost)
-
- // Base MinBalance + GlobalStateSchema.MinBalance + ExtraProgramPages MinBalance for each created application
- for _, params := range l.applications {
- if params.Creator == addr {
- min = basics.AddSaturate(min, proto.AppFlatParamsMinBalance)
- min = basics.AddSaturate(min, params.GlobalStateSchema.MinBalance(proto).Raw)
- min = basics.AddSaturate(min, basics.MulSaturate(proto.AppFlatParamsMinBalance, uint64(params.ExtraProgramPages)))
+ apps := make(map[basics.AppIndex]basics.AppParams)
+ for a, p := range l.applications {
+ if p.Creator == addr {
+ apps[a] = p.AppParams
+ schemaTotal = schemaTotal.AddSchema(p.GlobalStateSchema)
+ pagesTotal = p.ExtraProgramPages
}
}
- // Base MinBalance + LocalStateSchema.MinBalance for each opted in application
- for idx := range br.locals {
- min = basics.AddSaturate(min, proto.AppFlatParamsMinBalance)
- min = basics.AddSaturate(min, l.applications[idx].LocalStateSchema.MinBalance(proto).Raw)
+ locals := map[basics.AppIndex]basics.AppLocalState{}
+ for a := range br.locals {
+ locals[a] = basics.AppLocalState{} // No need to fill in
+ schemaTotal = schemaTotal.AddSchema(l.applications[a].LocalStateSchema)
}
- return basics.MicroAlgos{Raw: min}, nil
+ return basics.AccountData{
+ MicroAlgos: basics.MicroAlgos{Raw: br.balance},
+ AssetParams: assets,
+ Assets: br.holdings,
+ AuthAddr: br.auth,
+ AppLocalStates: locals,
+ AppParams: apps,
+ TotalAppSchema: schemaTotal,
+ TotalExtraAppPages: pagesTotal,
+ }, nil
}
// Authorizer returns the address that must authorize txns from a
diff --git a/ledger/internal/applications.go b/ledger/internal/applications.go
index 5fef80a21..1c9502c5a 100644
--- a/ledger/internal/applications.go
+++ b/ledger/internal/applications.go
@@ -19,7 +19,6 @@ package internal
import (
"fmt"
- "github.com/algorand/go-algorand/config"
"github.com/algorand/go-algorand/data/basics"
"github.com/algorand/go-algorand/data/transactions"
"github.com/algorand/go-algorand/data/transactions/logic"
@@ -53,23 +52,12 @@ func newLogicLedger(cow cowForLogicLedger) *logicLedger {
}
}
-func (al *logicLedger) Balance(addr basics.Address) (res basics.MicroAlgos, err error) {
- // Fetch record with pending rewards applied
+func (al *logicLedger) AccountData(addr basics.Address) (basics.AccountData, error) {
record, err := al.cow.Get(addr, true)
if err != nil {
- return
+ return basics.AccountData{}, err
}
-
- return record.MicroAlgos, nil
-}
-
-func (al *logicLedger) MinBalance(addr basics.Address, proto *config.ConsensusParams) (res basics.MicroAlgos, err error) {
- record, err := al.cow.Get(addr, false) // pending rewards unneeded
- if err != nil {
- return
- }
-
- return record.MinBalance(proto), nil
+ return record, nil
}
func (al *logicLedger) Authorizer(addr basics.Address) (basics.Address, error) {
diff --git a/ledger/internal/applications_test.go b/ledger/internal/applications_test.go
index c53989757..2ea442171 100644
--- a/ledger/internal/applications_test.go
+++ b/ledger/internal/applications_test.go
@@ -145,9 +145,9 @@ func TestLogicLedgerBalances(t *testing.T) {
addr1 := ledgertesting.RandomAddress()
ble := basics.MicroAlgos{Raw: 100}
c.brs = map[basics.Address]basics.AccountData{addr1: {MicroAlgos: ble}}
- bla, err := l.Balance(addr1)
+ acct, err := l.AccountData(addr1)
a.NoError(err)
- a.Equal(ble, bla)
+ a.Equal(ble, acct.MicroAlgos)
}
func TestLogicLedgerGetters(t *testing.T) {