Skip to content

Commit b210374

Browse files
authored
AVM: Allow 0 as app in LocalRef, specifying current app (#6483)
1 parent 3b6cd2d commit b210374

File tree

9 files changed

+272
-101
lines changed

9 files changed

+272
-101
lines changed

config/consensus.go

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -539,11 +539,6 @@ type ConsensusParams struct {
539539
// EnableBoxRefNameError specifies that box ref names should be validated early
540540
EnableBoxRefNameError bool
541541

542-
// EnableUnnamedBoxAccessInNewApps allows newly created (in this group) apps to
543-
// create boxes that were not named in a box ref. Each empty box ref in the
544-
// group allows one such creation.
545-
EnableUnnamedBoxAccessInNewApps bool
546-
547542
// ExcludeExpiredCirculation excludes expired stake from the total online stake
548543
// used by agreement for Circulation, and updates the calculation of StateProofOnlineTotalWeight used
549544
// by state proofs to use the same method (rather than excluding stake from the top N stakeholders as before).
@@ -572,17 +567,16 @@ type ConsensusParams struct {
572567
// EnableSha512BlockHash adds an additional SHA-512 hash to the block header.
573568
EnableSha512BlockHash bool
574569

575-
// EnableInnerClawbackWithoutSenderHolding allows an inner clawback (axfer
576-
// w/ AssetSender) even if the Sender holding of the asset is not
577-
// available. This parameters can be removed and assumed true after the
578-
// first consensus release in which it is set true.
579-
EnableInnerClawbackWithoutSenderHolding bool
580-
581570
// AppSizeUpdates allows application update transactions to change
582571
// the extra-program-pages and global schema sizes. Since it enables newly
583572
// legal transactions, this parameter can be removed and assumed true after
584573
// the first consensus release in which it is set true.
585574
AppSizeUpdates bool
575+
576+
// AllowZeroLocalAppRef allows for a 0 in a LocalRef of the access list to
577+
// specify the current app. This parameter can be removed and assumed true
578+
// after the first consensus release in which it is set true.
579+
AllowZeroLocalAppRef bool
586580
}
587581

588582
// ProposerPayoutRules puts several related consensus parameters in one place. The same
@@ -1444,13 +1438,10 @@ func initConsensusProtocols() {
14441438
v41.EnableAppVersioning = true
14451439
v41.EnableSha512BlockHash = true
14461440

1447-
v41.EnableUnnamedBoxAccessInNewApps = true
1448-
14491441
// txn.Access work
14501442
v41.MaxAppTxnAccounts = 8 // Accounts are no worse than others, they should be the same
14511443
v41.MaxAppAccess = 16 // Twice as many, though cross products are explicit
14521444
v41.BytesPerBoxReference = 2048 // Count is more important that bytes, loosen up
1453-
v41.EnableInnerClawbackWithoutSenderHolding = true
14541445
v41.LogicSigMsig = false
14551446
v41.LogicSigLMsig = true
14561447

@@ -1469,6 +1460,7 @@ func initConsensusProtocols() {
14691460
vFuture.LogicSigVersion = 13 // When moving this to a release, put a new higher LogicSigVersion here
14701461

14711462
vFuture.AppSizeUpdates = true
1463+
vFuture.AllowZeroLocalAppRef = true
14721464

14731465
Consensus[protocol.ConsensusFuture] = vFuture
14741466

daemon/algod/api/server/v2/handlers.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1318,7 +1318,7 @@ func (v2 *Handlers) SimulateTransaction(ctx echo.Context, params model.SimulateT
13181318
}
13191319
}
13201320

1321-
response := convertSimulationResult(simulationResult, proto.EnableUnnamedBoxAccessInNewApps)
1321+
response := convertSimulationResult(simulationResult)
13221322

13231323
handle, contentType, err := getCodecHandle((*string)(params.Format))
13241324
if err != nil {

daemon/algod/api/server/v2/utils.go

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -473,13 +473,13 @@ func convertTxnTrace(txnTrace *simulation.TransactionTrace) *model.SimulationTra
473473
}
474474
}
475475

476-
func convertTxnResult(txnResult simulation.TxnResult, simplify bool) PreEncodedSimulateTxnResult {
476+
func convertTxnResult(txnResult simulation.TxnResult) PreEncodedSimulateTxnResult {
477477
result := PreEncodedSimulateTxnResult{
478478
Txn: ConvertInnerTxn(&txnResult.Txn),
479479
AppBudgetConsumed: omitEmpty(txnResult.AppBudgetConsumed),
480480
LogicSigBudgetConsumed: omitEmpty(txnResult.LogicSigBudgetConsumed),
481481
TransactionTrace: convertTxnTrace(txnResult.Trace),
482-
UnnamedResourcesAccessed: convertUnnamedResourcesAccessed(txnResult.UnnamedResourcesAccessed, simplify),
482+
UnnamedResourcesAccessed: convertUnnamedResourcesAccessed(txnResult.UnnamedResourcesAccessed),
483483
}
484484

485485
if !txnResult.FixedSigner.IsZero() {
@@ -490,13 +490,11 @@ func convertTxnResult(txnResult simulation.TxnResult, simplify bool) PreEncodedS
490490
return result
491491
}
492492

493-
func convertUnnamedResourcesAccessed(resources *simulation.ResourceTracker, simplify bool) *model.SimulateUnnamedResourcesAccessed {
493+
func convertUnnamedResourcesAccessed(resources *simulation.ResourceTracker) *model.SimulateUnnamedResourcesAccessed {
494494
if resources == nil {
495495
return nil
496496
}
497-
if simplify {
498-
resources.Simplify()
499-
}
497+
resources.Simplify()
500498
return &model.SimulateUnnamedResourcesAccessed{
501499
Accounts: sliceOrNil(stringSlice(slices.Collect(maps.Keys(resources.Accounts)))),
502500
Assets: sliceOrNil(slices.Collect(maps.Keys(resources.Assets))),
@@ -568,18 +566,15 @@ func convertSimulateInitialStates(initialStates *simulation.ResourcesInitialStat
568566
}
569567
}
570568

571-
func convertTxnGroupResult(txnGroupResult simulation.TxnGroupResult, simplify bool) PreEncodedSimulateTxnGroupResult {
572-
txnResults := make([]PreEncodedSimulateTxnResult, len(txnGroupResult.Txns))
573-
for i, txnResult := range txnGroupResult.Txns {
574-
txnResults[i] = convertTxnResult(txnResult, simplify)
575-
}
569+
func convertTxnGroupResult(txnGroupResult simulation.TxnGroupResult) PreEncodedSimulateTxnGroupResult {
570+
txnResults := util.Map(txnGroupResult.Txns, convertTxnResult)
576571

577572
encoded := PreEncodedSimulateTxnGroupResult{
578573
Txns: txnResults,
579574
FailureMessage: omitEmpty(txnGroupResult.FailureMessage),
580575
AppBudgetAdded: omitEmpty(txnGroupResult.AppBudgetAdded),
581576
AppBudgetConsumed: omitEmpty(txnGroupResult.AppBudgetConsumed),
582-
UnnamedResourcesAccessed: convertUnnamedResourcesAccessed(txnGroupResult.UnnamedResourcesAccessed, simplify),
577+
UnnamedResourcesAccessed: convertUnnamedResourcesAccessed(txnGroupResult.UnnamedResourcesAccessed),
583578
}
584579

585580
if len(txnGroupResult.FailedAt) > 0 {
@@ -590,7 +585,7 @@ func convertTxnGroupResult(txnGroupResult simulation.TxnGroupResult, simplify bo
590585
return encoded
591586
}
592587

593-
func convertSimulationResult(result simulation.Result, simplify bool) PreEncodedSimulateResponse {
588+
func convertSimulationResult(result simulation.Result) PreEncodedSimulateResponse {
594589
var evalOverrides *model.SimulationEvalOverrides
595590
if result.EvalOverrides != (simulation.ResultEvalOverrides{}) {
596591
evalOverrides = &model.SimulationEvalOverrides{
@@ -604,11 +599,9 @@ func convertSimulationResult(result simulation.Result, simplify bool) PreEncoded
604599
}
605600

606601
return PreEncodedSimulateResponse{
607-
Version: result.Version,
608-
LastRound: result.LastRound,
609-
TxnGroups: util.Map(result.TxnGroups, func(tg simulation.TxnGroupResult) PreEncodedSimulateTxnGroupResult {
610-
return convertTxnGroupResult(tg, simplify)
611-
}),
602+
Version: result.Version,
603+
LastRound: result.LastRound,
604+
TxnGroups: util.Map(result.TxnGroups, convertTxnGroupResult),
612605
EvalOverrides: evalOverrides,
613606
ExecTraceConfig: result.TraceConfig,
614607
InitialStates: convertSimulateInitialStates(result.InitialStates),

data/transactions/application.go

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ func (rr ResourceRef) Empty() bool {
210210
// wellFormed checks that a ResourceRef is a proper member of `access. `rr` is
211211
// either empty a single kind of resource. Any internal indices point to proper
212212
// locations inside `access`.
213-
func (rr ResourceRef) wellFormed(access []ResourceRef, proto config.ConsensusParams) error {
213+
func (rr ResourceRef) wellFormed(access []ResourceRef, inCreate bool, proto config.ConsensusParams) error {
214214
// Count the number of non-empty fields
215215
count := 0
216216
// The "basic" resources are inherently wellFormed
@@ -231,7 +231,13 @@ func (rr ResourceRef) wellFormed(access []ResourceRef, proto config.ConsensusPar
231231
count++
232232
}
233233
if !rr.Locals.Empty() {
234-
if _, _, err := rr.Locals.Resolve(access, basics.Address{}); err != nil {
234+
if !proto.AllowZeroLocalAppRef && rr.Locals.App == 0 {
235+
return errors.New("0 App in LocalsRef is not supported")
236+
}
237+
if inCreate && rr.Locals.App == 0 {
238+
return errors.New("0 App in LocalsRef during app create is not allowed or necessary")
239+
}
240+
if _, _, err := rr.Locals.Resolve(access, basics.Address{}, 0); err != nil {
235241
return err
236242
}
237243
count++
@@ -309,9 +315,9 @@ func (lr LocalsRef) Empty() bool {
309315
return lr == LocalsRef{}
310316
}
311317

312-
// Resolve looks up the referenced address and app in the access list. 0 is
313-
// returned if the App index is 0, meaning "current app".
314-
func (lr LocalsRef) Resolve(access []ResourceRef, sender basics.Address) (basics.Address, basics.AppIndex, error) {
318+
// Resolve looks up the referenced address and app in the access list. Zero
319+
// values are translated to the supplied sender or current app.
320+
func (lr LocalsRef) Resolve(access []ResourceRef, sender basics.Address, current basics.AppIndex) (basics.Address, basics.AppIndex, error) {
315321
address := sender // Returned when lr.Address == 0
316322
if lr.Address != 0 {
317323
if lr.Address > uint64(len(access)) { // recall that Access is 1-based
@@ -322,12 +328,15 @@ func (lr LocalsRef) Resolve(access []ResourceRef, sender basics.Address) (basics
322328
return basics.Address{}, 0, fmt.Errorf("locals Address reference %d is not an Address", lr.Address)
323329
}
324330
}
325-
if lr.App == 0 || lr.App > uint64(len(access)) { // 1-based
326-
return basics.Address{}, 0, fmt.Errorf("locals App reference %d outside tx.Access", lr.App)
327-
}
328-
app := access[lr.App-1].App
329-
if app == 0 {
330-
return basics.Address{}, 0, fmt.Errorf("locals App reference %d is not an App", lr.App)
331+
app := current // Returned when lr.App == 0
332+
if lr.App != 0 {
333+
if lr.App > uint64(len(access)) { // 1-based
334+
return basics.Address{}, 0, fmt.Errorf("locals App reference %d outside tx.Access", lr.App)
335+
}
336+
app = access[lr.App-1].App
337+
if app == 0 {
338+
return basics.Address{}, 0, fmt.Errorf("locals App reference %d is not an App", lr.App)
339+
}
331340
}
332341
return address, app, nil
333342
}
@@ -512,7 +521,7 @@ func (ac ApplicationCallTxnFields) wellFormed(proto config.ConsensusParams) erro
512521
}
513522

514523
for _, rr := range ac.Access {
515-
if err := rr.wellFormed(ac.Access, proto); err != nil {
524+
if err := rr.wellFormed(ac.Access, ac.ApplicationID == 0, proto); err != nil {
516525
return err
517526
}
518527
}

data/transactions/application_test.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,14 +203,37 @@ func TestAppCallAccessWellFormed(t *testing.T) {
203203
},
204204
},
205205
{
206-
expectedError: "locals App reference 0 outside tx.Access",
206+
// eliminate this test after AllowZeroAppInLocalsRef is removed
207+
expectedError: "0 App in LocalsRef is not supported",
207208
ac: ApplicationCallTxnFields{
208209
ApplicationID: 1,
209210
Access: []ResourceRef{
210211
{Address: basics.Address{0xaa}},
211212
{Locals: LocalsRef{Address: 1}},
212213
},
213214
},
215+
cv: protocol.ConsensusV41,
216+
},
217+
{
218+
ac: ApplicationCallTxnFields{
219+
ApplicationID: 1,
220+
Access: []ResourceRef{
221+
{Address: basics.Address{0xaa}},
222+
{Locals: LocalsRef{Address: 1}},
223+
},
224+
},
225+
},
226+
{
227+
expectedError: "0 App in LocalsRef during app create is not allowed or necessary",
228+
ac: ApplicationCallTxnFields{
229+
ApplicationID: 0,
230+
ApprovalProgram: []byte{0x05},
231+
ClearStateProgram: []byte{0x05},
232+
Access: []ResourceRef{
233+
{Address: basics.Address{0xaa}},
234+
{Locals: LocalsRef{Address: 1}},
235+
},
236+
},
214237
},
215238
{
216239
ac: ApplicationCallTxnFields{

data/transactions/logic/box.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ func (cx *EvalContext) availableBox(name string, operation BoxOperation, createS
5252
// we don't have to go to the disk. but we only allow one such access for
5353
// each spare (empty) box ref. that way, we can't end up needing to write
5454
// many separate newly created boxes.
55-
if !ok && cx.Proto.EnableUnnamedBoxAccessInNewApps {
55+
if !ok {
5656
if _, newAppAccess = cx.available.createdApps[cx.appID]; newAppAccess {
5757
if cx.available.unnamedAccess > 0 {
5858
ok = true // allow it

data/transactions/logic/resources.go

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -252,11 +252,9 @@ func (cx *EvalContext) requireLocals(acct basics.Address, id basics.AppIndex) er
252252
}
253253

254254
func (cx *EvalContext) allowsAssetTransfer(hdr *transactions.Header, tx *transactions.AssetTransferTxnFields) error {
255-
// After EnableInnerClawbackWithoutSenderHolding appears in a consensus
256-
// update, we should remove it from consensus params and assume it's true in
257-
// the next release. It only needs to be in there so that it gates the
258-
// behavior change in the release it first appears.
259-
if !cx.Proto.EnableInnerClawbackWithoutSenderHolding || tx.AssetSender.IsZero() {
255+
// When AssetSender is set (we're doing a clawback) we don't need the
256+
// Sender's holding. The Sender/ClawbackAddress may not even have the asset.
257+
if tx.AssetSender.IsZero() {
260258
err := cx.requireHolding(hdr.Sender, tx.XferAsset)
261259
if err != nil {
262260
return fmt.Errorf("axfer Sender: %w", err)
@@ -323,17 +321,14 @@ func (r *resources) fillApplicationCallAccess(ep *EvalParams, hdr *transactions.
323321
r.shareHolding(address, asset)
324322
case !rr.Locals.Empty():
325323
// ApplicationCallTxnFields.wellFormed ensures no error here.
326-
address, app, _ := rr.Locals.Resolve(tx.Access, hdr.Sender)
324+
address, app, _ := rr.Locals.Resolve(tx.Access, hdr.Sender, tx.ApplicationID)
327325
r.shareLocal(address, app)
328326
case !rr.Box.Empty():
329327
// ApplicationCallTxnFields.wellFormed ensures no error here.
330328
app, name, _ := rr.Box.Resolve(tx.Access)
331329
r.shareBox(basics.BoxRef{App: app, Name: name}, tx.ApplicationID)
332330
default:
333-
// all empty equals an "empty boxref" which allows one unnamed access
334-
if ep.Proto.EnableUnnamedBoxAccessInNewApps {
335-
r.unnamedAccess++
336-
}
331+
r.unnamedAccess++
337332
}
338333
}
339334
}
@@ -374,7 +369,7 @@ func (r *resources) fillApplicationCallForeign(ep *EvalParams, hdr *transactions
374369
}
375370

376371
for _, br := range tx.Boxes {
377-
if ep.Proto.EnableUnnamedBoxAccessInNewApps && br.Empty() {
372+
if br.Empty() {
378373
r.unnamedAccess++
379374
}
380375
var app basics.AppIndex

0 commit comments

Comments
 (0)