diff --git a/ledger/apply/application.go b/ledger/apply/application.go index ec1c5c5589..644c8e3960 100644 --- a/ledger/apply/application.go +++ b/ledger/apply/application.go @@ -193,10 +193,17 @@ func updateApplication(ac *transactions.ApplicationCallTxnFields, balances Balan proto := balances.ConsensusParams() // when proto.EnableExtraPageOnAppUpdate is false, WellFormed rejects all updates with a multiple-page program if proto.EnableExtraPagesOnAppUpdate { - allowed := int(1+params.ExtraProgramPages) * proto.MaxAppTotalProgramLen - actual := len(ac.ApprovalProgram) + len(ac.ClearStateProgram) - if actual > allowed { - return fmt.Errorf("updateApplication app programs too long, %d. max total len %d bytes", actual, allowed) + lap := len(ac.ApprovalProgram) + lcs := len(ac.ClearStateProgram) + pages := int(1 + params.ExtraProgramPages) + if lap > pages*proto.MaxAppProgramLen { + return fmt.Errorf("updateApplication approval program too long. max len %d bytes", pages*proto.MaxAppProgramLen) + } + if lcs > pages*proto.MaxAppProgramLen { + return fmt.Errorf("updateApplication clear state program too long. max len %d bytes", pages*proto.MaxAppProgramLen) + } + if lap+lcs > pages*proto.MaxAppTotalProgramLen { + return fmt.Errorf("updateApplication app programs too long, %d. max total len %d bytes", lap+lcs, pages*proto.MaxAppTotalProgramLen) } } diff --git a/ledger/apply/application_test.go b/ledger/apply/application_test.go index 70346fd7bb..90bd12320d 100644 --- a/ledger/apply/application_test.go +++ b/ledger/apply/application_test.go @@ -268,6 +268,10 @@ func (b *testBalances) SetProto(name protocol.ConsensusVersion) { b.proto = config.Consensus[name] } +func (b *testBalances) SetParams(params config.ConsensusParams) { + b.proto = params +} + type testEvaluator struct { pass bool delta basics.EvalDelta @@ -984,19 +988,12 @@ func TestAppCallApplyUpdate(t *testing.T) { a.Equal([]byte{2}, br.AppParams[appIdx].ClearStateProgram) a.Equal(basics.EvalDelta{}, ad.EvalDelta) - // check app program len - appr := make([]byte, 6050) + //check program len check happens in future consensus proto version + b.SetProto(protocol.ConsensusFuture) + proto = b.ConsensusParams() + ep.Proto = &proto - for i := range appr { - appr[i] = 2 - } - appr[0] = 4 - ac = transactions.ApplicationCallTxnFields{ - ApplicationID: appIdx, - OnCompletion: transactions.UpdateApplicationOC, - ApprovalProgram: appr, - ClearStateProgram: []byte{2}, - } + // check app program len params = basics.AppParams{ ApprovalProgram: []byte{1}, StateSchemas: basics.StateSchemas{ @@ -1018,22 +1015,34 @@ func TestAppCallApplyUpdate(t *testing.T) { b.balances[creator] = cp b.appCreators = map[basics.AppIndex]basics.Address{appIdx: creator} - //check program len check happens in future consensus proto version - b.SetProto(protocol.ConsensusFuture) - proto = b.ConsensusParams() - ep.Proto = &proto - - b.pass = true - err = ApplicationCall(ac, h, &b, ad, &ep, txnCounter) - a.Error(err) - a.Contains(err.Error(), "updateApplication app programs too long") - // check extraProgramPages is used - appr = make([]byte, 3072) + appr := make([]byte, 2*proto.MaxAppProgramLen+1) + appr[0] = 4 // version 4 + + var tests = []struct { + name string + approval []byte + clear []byte + }{ + {"approval", appr, []byte{2}}, + {"clear state", []byte{2}, appr}, + } + for _, test := range tests { + ac = transactions.ApplicationCallTxnFields{ + ApplicationID: appIdx, + OnCompletion: transactions.UpdateApplicationOC, + ApprovalProgram: test.approval, + ClearStateProgram: test.clear, + } - for i := range appr { - appr[i] = 2 + b.pass = true + err = ApplicationCall(ac, h, &b, ad, &ep, txnCounter) + a.Error(err) + a.Contains(err.Error(), fmt.Sprintf("updateApplication %s program too long", test.name)) } + + // check extraProgramPages allows length of proto.MaxAppProgramLen + 1 + appr = make([]byte, proto.MaxAppProgramLen+1) appr[0] = 4 ac = transactions.ApplicationCallTxnFields{ ApplicationID: appIdx, @@ -1045,6 +1054,17 @@ func TestAppCallApplyUpdate(t *testing.T) { err = ApplicationCall(ac, h, &b, ad, &ep, txnCounter) a.NoError(err) + // check extraProgramPages is used and long sum rejected + ac = transactions.ApplicationCallTxnFields{ + ApplicationID: appIdx, + OnCompletion: transactions.UpdateApplicationOC, + ApprovalProgram: appr, + ClearStateProgram: appr, + } + b.pass = true + err = ApplicationCall(ac, h, &b, ad, &ep, txnCounter) + a.Error(err) + a.Contains(err.Error(), "updateApplication app programs too long") } func TestAppCallApplyDelete(t *testing.T) {