Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 7 additions & 16 deletions data/transactions/logic/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -628,15 +628,9 @@ func (ep *EvalParams) RecordAD(gi int, ad transactions.ApplyData) {
}
ep.available.createdAsas[aid] = struct{}{}
}
if aid := ad.ApplicationID; aid != 0 {
if ep.available == nil {
ep.available = ep.computeAvailability()
}
if ep.available.createdApps == nil {
ep.available.createdApps = make(map[basics.AppIndex]struct{})
}
ep.available.createdApps[aid] = struct{}{}
}
// we don't need to add ad.ApplicationID to createdApps, because that is
Comment thread
gmalouf marked this conversation as resolved.
// done at the beginning of app execution now, so that newly created apps
// will already have their appID present.
}

type frame struct {
Expand Down Expand Up @@ -1152,12 +1146,10 @@ func EvalContract(program []byte, gi int, aid basics.AppIndex, params *EvalParam
}
}
// and add the appID to `createdApps`
if cx.EvalParams.Proto.LogicSigVersion >= sharedResourcesVersion {
if cx.EvalParams.available.createdApps == nil {
cx.EvalParams.available.createdApps = make(map[basics.AppIndex]struct{})
}
cx.EvalParams.available.createdApps[cx.appID] = struct{}{}
if cx.EvalParams.available.createdApps == nil {
cx.EvalParams.available.createdApps = make(map[basics.AppIndex]struct{})
}
cx.EvalParams.available.createdApps[cx.appID] = struct{}{}
Comment thread
jannotti marked this conversation as resolved.
}

// Check the I/O budget for reading if this is the first top-level app call
Expand Down Expand Up @@ -4285,8 +4277,7 @@ func (cx *EvalContext) availableAccount(addr basics.Address) bool {
// Allow an address for an app that was created in group
if cx.version >= createdResourcesVersion {
for appID := range cx.available.createdApps {
createdAddress := cx.GetApplicationAddress(appID)
if addr == createdAddress {
if addr == cx.GetApplicationAddress(appID) {
return true
}
}
Expand Down
23 changes: 7 additions & 16 deletions data/transactions/logic/resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,11 @@ func (cx *EvalContext) allows(tx *transactions.Transaction, calleeVer uint64) er
// if the caller is pre-sharing, it can't prepare transactions with
// resources that are not available, so `tx` is surely legal.
if cx.version < sharedResourcesVersion {
// this is important, not just an optimization, because a pre-sharing
// creation txn has access to the app and app account it is currently
// creating (and therefore can pass that access down), but cx.available
// doesn't track that properly until v9's protocol upgrade. See
// TestInnerAppCreateAndOptin for an example.
// this is just an optimization, from the perspective of properly
// evaluating transactions in "normal" mode. However, it is an
// important short-circuit for simulation. Simulation does not
// understand how to handle missing cross-products in non-sharing
// program versions.
return nil
}
switch tx.Type {
Expand Down Expand Up @@ -177,11 +177,7 @@ func (cx *EvalContext) allowsHolding(addr basics.Address, ai basics.AssetIndex)
return cx.availableAsset(ai)
}
}
// If the current txn is a creation, the new appID won't be in r.createdApps
// yet, but it should get the same special treatment.
if cx.txn.Txn.ApplicationID == 0 && cx.GetApplicationAddress(cx.appID) == addr {
return cx.availableAsset(ai)
}

if cx.UnnamedResources != nil {
// Ensure that the account and asset are available before consulting cx.UnnamedResources.AllowsHolding.
// This way cx.UnnamedResources.AllowsHolding only needs to make a decision about the asset holding
Expand All @@ -201,19 +197,14 @@ func (cx *EvalContext) allowsLocals(addr basics.Address, ai basics.AppIndex) boo
if _, ok := r.createdApps[ai]; ok {
return cx.availableAccount(addr)
}
if cx.txn.Txn.ApplicationID == 0 && cx.appID == ai {
Comment thread
gmalouf marked this conversation as resolved.
return cx.availableAccount(addr)
}

// All locals of created app accounts are available
for created := range r.createdApps {
if cx.GetApplicationAddress(created) == addr {
return cx.availableApp(ai)
}
}
if cx.txn.Txn.ApplicationID == 0 && cx.GetApplicationAddress(cx.appID) == addr {
return cx.availableApp(ai)
}

if cx.UnnamedResources != nil {
// Ensure that the account and app are available before consulting cx.UnnamedResources.AllowsLocal.
// This way cx.UnnamedResources.AllowsLocal only needs to make a decision about the app local
Expand Down
33 changes: 13 additions & 20 deletions ledger/apptxn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -984,21 +984,22 @@ func TestInnerAppCreateAndOptin(t *testing.T) {
// TestParentGlobals tests that a newly created app can call an inner app, and
// the inner app will have access to the parent globals, even if the originally
// created app ID isn't passed down, because the rule is that "pending" created
// apps are available, starting from v38
// apps are available. We added this rule in v38, but because it is more
// lenient, not more restrictive, we removed the consensus gated code. So it now
// works from v31 on.
func TestParentGlobals(t *testing.T) {
partitiontest.PartitionTest(t)
t.Parallel()

genBalances, addrs, _ := ledgertesting.NewTestGenesis()

// v38 allows parent access, but we start with v31 to make sure we don't mistakenly change it
ledgertesting.TestConsensusRange(t, 31, 0, func(t *testing.T, ver int, cv protocol.ConsensusVersion, cfg config.Local) {
dl := NewDoubleLedger(t, genBalances, cv, cfg)
defer dl.Close()

// helper app, is called during the creation of an app. this app tries
// to access its parent's globals, by using `global CallerApplicationID`
helper := dl.fundedApp(addrs[0], 1_000_000,
// checkParent is called during the creation of an app. It tries to
// access its parent's globals, by using `global CallerApplicationID`
checkParent := dl.fundedApp(addrs[0], 1_000_000,
main(`
global CallerApplicationID
byte "X"
Expand All @@ -1009,7 +1010,7 @@ func TestParentGlobals(t *testing.T) {
createProgram := `
itxn_begin
int appl; itxn_field TypeEnum
int ` + strconv.Itoa(int(helper)) + `; itxn_field ApplicationID
int ` + strconv.Itoa(int(checkParent)) + `; itxn_field ApplicationID
itxn_submit
int 1
`
Expand All @@ -1018,15 +1019,11 @@ func TestParentGlobals(t *testing.T) {
Sender: addrs[0],
Fee: 2 * 1000, // to pay for self and call to helper
ApprovalProgram: createProgram,
ForeignApps: []basics.AppIndex{helper},
ForeignApps: []basics.AppIndex{checkParent},
}
var creator basics.AppIndex
if ver >= 38 {
creator = dl.txn(&createapp).ApplyData.ApplicationID
require.NotZero(t, creator)
} else {
dl.txn(&createapp, "unavailable App")
}
creator = dl.txn(&createapp).ApplyData.ApplicationID
require.NotZero(t, creator)

// Now, test the same pattern, but do it all inside of yet another outer
// app, to show that the parent is available even if it was, itself
Expand All @@ -1041,25 +1038,21 @@ func TestParentGlobals(t *testing.T) {
ApprovalProgram: `
itxn_begin
int appl; itxn_field TypeEnum
txna Applications 1; itxn_field Applications; // We are checking some versions from before resource sharing
byte 0x` + hex.EncodeToString(createapp.SignedTxn().Txn.ApprovalProgram) + `; itxn_field ApprovalProgram
byte 0x` + hex.EncodeToString(createapp.SignedTxn().Txn.ClearStateProgram) + `; itxn_field ClearStateProgram
itxn_submit
int 1
`,
ForeignApps: []basics.AppIndex{creator, helper},
ForeignApps: []basics.AppIndex{checkParent, creator},
Comment thread
gmalouf marked this conversation as resolved.
}
fund := txntest.Txn{
Type: "pay",
Amount: 200_000,
Sender: addrs[0],
Receiver: outerAppAddress,
}
if ver >= 38 {
dl.txgroup("", &fund, &outer)
} else {
dl.txn(&createapp, "unavailable App")
}

dl.txgroup("", &fund, &outer)
})
}

Expand Down
Loading