From 0deb6ca0b2adefbe41f4ad51387c7121e5335fda Mon Sep 17 00:00:00 2001 From: Nikolay Eskov Date: Tue, 14 Jun 2022 05:35:46 +0300 Subject: [PATCH 01/17] WIP: ignore last two complexities. --- pkg/ride/environment.go | 9 +++++++++ pkg/ride/functions_test.go | 17 +++++++++++++++++ pkg/ride/runtime.go | 1 + pkg/ride/runtime_moq_test.go | 36 ++++++++++++++++++++++++++++++++++++ pkg/ride/tree_evaluation.go | 9 +++++++-- pkg/state/script_caller.go | 3 +++ 6 files changed, 73 insertions(+), 2 deletions(-) diff --git a/pkg/ride/environment.go b/pkg/ride/environment.go index d38b8455a..09c02b80e 100644 --- a/pkg/ride/environment.go +++ b/pkg/ride/environment.go @@ -9,6 +9,8 @@ import ( "github.com/wavesplatform/gowaves/pkg/types" ) +const invokeCallComplexityV5 = 75 + var ( errDeletedEntry = errors.New("entry has been deleted") ) @@ -943,6 +945,7 @@ type EvaluationEnvironment struct { ver ast.LibraryVersion validatePaymentsAfter uint64 isBlockV5Activated bool + isRiveV5Activated bool isRiveV6Activated bool isProtobufTransaction bool mds int @@ -968,6 +971,7 @@ func NewEnvironmentWithWrappedState( payments proto.ScriptPayments, sender proto.WavesAddress, isBlockV5Activated bool, + isRideV5Activated bool, isRideV6Activated bool, isProtobufTransaction bool, rootScriptLibVersion ast.LibraryVersion, @@ -1040,6 +1044,7 @@ func NewEnvironmentWithWrappedState( validatePaymentsAfter: env.validatePaymentsAfter, mds: env.mds, isBlockV5Activated: isBlockV5Activated, + isRiveV5Activated: isRideV5Activated, isRiveV6Activated: isRideV6Activated, isProtobufTransaction: isProtobufTransaction, }, nil @@ -1049,6 +1054,10 @@ func (e *EvaluationEnvironment) rideV6Activated() bool { return e.isRiveV6Activated } +func (e *EvaluationEnvironment) rideV5Activated() bool { + return e.isRiveV5Activated +} + func (e *EvaluationEnvironment) blockV5Activated() bool { return e.isBlockV5Activated } diff --git a/pkg/ride/functions_test.go b/pkg/ride/functions_test.go index a0d1db3ec..717a08bf0 100644 --- a/pkg/ride/functions_test.go +++ b/pkg/ride/functions_test.go @@ -2,6 +2,7 @@ package ride import ( "math/rand" + "strconv" "testing" "github.com/stretchr/testify/assert" @@ -51,3 +52,19 @@ func BenchmarkCheckFunctionMap(b *testing.B) { assert.True(b, ok) } } + +func TestInvokeCallComplexityV5Constant(t *testing.T) { + const ( + invokeFunctionID = 1020 + reentrantInvokeFunctionID = 1021 + ) + catalogues := &[...]map[string]int{ + CatalogueV5, + EvaluationCatalogueV5EvaluatorV1, + EvaluationCatalogueV5EvaluatorV2, + } + for _, catalogue := range catalogues { + assert.Equal(t, catalogue[strconv.Itoa(invokeFunctionID)], invokeCallComplexityV5) + assert.Equal(t, catalogue[strconv.Itoa(reentrantInvokeFunctionID)], invokeCallComplexityV5) + } +} diff --git a/pkg/ride/runtime.go b/pkg/ride/runtime.go index 9f371071b..43cef77f4 100644 --- a/pkg/ride/runtime.go +++ b/pkg/ride/runtime.go @@ -715,6 +715,7 @@ type environment interface { libVersion() ast.LibraryVersion validateInternalPayments() bool blockV5Activated() bool + rideV5Activated() bool rideV6Activated() bool internalPaymentsValidationHeight() uint64 maxDataEntriesSize() int diff --git a/pkg/ride/runtime_moq_test.go b/pkg/ride/runtime_moq_test.go index e321354e4..bed29e804 100644 --- a/pkg/ride/runtime_moq_test.go +++ b/pkg/ride/runtime_moq_test.go @@ -47,6 +47,9 @@ var _ environment = &mockRideEnvironment{} // maxDataEntriesSizeFunc: func() int { // panic("mock out the maxDataEntriesSize method") // }, +// rideV5ActivatedFunc: func() bool { +// panic("mock out the rideV5Activated method") +// }, // rideV6ActivatedFunc: func() bool { // panic("mock out the rideV6Activated method") // }, @@ -114,6 +117,9 @@ type mockRideEnvironment struct { // maxDataEntriesSizeFunc mocks the maxDataEntriesSize method. maxDataEntriesSizeFunc func() int + // rideV5ActivatedFunc mocks the rideV5Activated method. + rideV5ActivatedFunc func() bool + // rideV6ActivatedFunc mocks the rideV6Activated method. rideV6ActivatedFunc func() bool @@ -178,6 +184,9 @@ type mockRideEnvironment struct { // maxDataEntriesSize holds details about calls to the maxDataEntriesSize method. maxDataEntriesSize []struct { } + // rideV5Activated holds details about calls to the rideV5Activated method. + rideV5Activated []struct { + } // rideV6Activated holds details about calls to the rideV6Activated method. rideV6Activated []struct { } @@ -229,6 +238,7 @@ type mockRideEnvironment struct { lockisProtobufTx sync.RWMutex locklibVersion sync.RWMutex lockmaxDataEntriesSize sync.RWMutex + lockrideV5Activated sync.RWMutex lockrideV6Activated sync.RWMutex lockscheme sync.RWMutex locksetInvocation sync.RWMutex @@ -481,6 +491,32 @@ func (mock *mockRideEnvironment) maxDataEntriesSizeCalls() []struct { return calls } +// rideV5Activated calls rideV5ActivatedFunc. +func (mock *mockRideEnvironment) rideV5Activated() bool { + if mock.rideV5ActivatedFunc == nil { + panic("mockRideEnvironment.rideV5ActivatedFunc: method is nil but environment.rideV5Activated was just called") + } + callInfo := struct { + }{} + mock.lockrideV5Activated.Lock() + mock.calls.rideV5Activated = append(mock.calls.rideV5Activated, callInfo) + mock.lockrideV5Activated.Unlock() + return mock.rideV5ActivatedFunc() +} + +// rideV5ActivatedCalls gets all the calls that were made to rideV5Activated. +// Check the length with: +// len(mockedenvironment.rideV5ActivatedCalls()) +func (mock *mockRideEnvironment) rideV5ActivatedCalls() []struct { +} { + var calls []struct { + } + mock.lockrideV5Activated.RLock() + calls = mock.calls.rideV5Activated + mock.lockrideV5Activated.RUnlock() + return calls +} + // rideV6Activated calls rideV6ActivatedFunc. func (mock *mockRideEnvironment) rideV6Activated() bool { if mock.rideV6ActivatedFunc == nil { diff --git a/pkg/ride/tree_evaluation.go b/pkg/ride/tree_evaluation.go index 34f0ef27d..dde0f9172 100644 --- a/pkg/ride/tree_evaluation.go +++ b/pkg/ride/tree_evaluation.go @@ -41,7 +41,12 @@ func CallFunction(env environment, tree *ast.Tree, name string, args proto.Argum e.complexity()+wrappedStateComplexity(env.state()), ) } - return nil, EvaluationErrorAddComplexity(err, e.complexity()+wrappedStateComplexity(env.state())) + complexity := e.complexity() + wrappedStateComplexity(env.state()) + if env.rideV5Activated() && !env.rideV6Activated() && et == InternalInvocationError { + ws := env.state().(*WrappedState) + // TODO: this should be handled only when err == ride.InternalInvocationError + } + return nil, EvaluationErrorAddComplexity(err, complexity) } dAppResult, ok := rideResult.(DAppResult) if !ok { // Unexpected result type @@ -54,7 +59,7 @@ func CallFunction(env environment, tree *ast.Tree, name string, args proto.Argum if tree.LibVersion < ast.LibV5 { // Shortcut because no wrapped state before version 5 return rideResult, nil } - maxChainInvokeComplexity, err := maxChainInvokeComplexityByVersion(ast.LibraryVersion(tree.LibVersion)) + maxChainInvokeComplexity, err := maxChainInvokeComplexityByVersion(tree.LibVersion) if err != nil { return nil, EvaluationFailure.Errorf("failed to get max chain invoke complexity: %v", err) } diff --git a/pkg/state/script_caller.go b/pkg/state/script_caller.go index 20bed3d63..a25f4f2f9 100644 --- a/pkg/state/script_caller.go +++ b/pkg/state/script_caller.go @@ -252,6 +252,7 @@ func (a *scriptCaller) invokeFunction(tree *ast.Tree, tx proto.Transaction, info payments, sender, info.blockV5Activated, + info.rideV5Activated, info.rideV6Activated, proto.IsProtobufTx(tx), tree.LibVersion, @@ -286,6 +287,7 @@ func (a *scriptCaller) invokeFunction(tree *ast.Tree, tx proto.Transaction, info payments, sender, info.blockV5Activated, + info.rideV5Activated, info.rideV6Activated, proto.IsProtobufTx(tx), tree.LibVersion, @@ -336,6 +338,7 @@ func (a *scriptCaller) invokeFunction(tree *ast.Tree, tx proto.Transaction, info payments, sender, info.blockV5Activated, + info.rideV5Activated, info.rideV6Activated, proto.IsProtobufTx(tx), tree.LibVersion, From 162b56ded2c5812c2c13b40fe4ed524707c965d0 Mon Sep 17 00:00:00 2001 From: Nikolay Eskov Date: Wed, 15 Jun 2022 00:46:11 +0300 Subject: [PATCH 02/17] Reproduce scala's node buggy behaviour in complexity calculations after RideV5 and before RideV6. --- pkg/ride/environment.go | 13 +++++++++++-- pkg/ride/functions_proto.go | 2 ++ pkg/ride/tree_evaluation.go | 37 +++++++++++++++++++++++++------------ 3 files changed, 38 insertions(+), 14 deletions(-) diff --git a/pkg/ride/environment.go b/pkg/ride/environment.go index 09c02b80e..53d9c1ef1 100644 --- a/pkg/ride/environment.go +++ b/pkg/ride/environment.go @@ -9,12 +9,20 @@ import ( "github.com/wavesplatform/gowaves/pkg/types" ) -const invokeCallComplexityV5 = 75 - var ( errDeletedEntry = errors.New("entry has been deleted") ) +type lastTwoInvokeComplexities [2]int + +func (l *lastTwoInvokeComplexities) pushComplexity(complexity int) { + l[0], l[1] = l[1], complexity +} + +func (l *lastTwoInvokeComplexities) sum() int { + return l[0] + l[1] +} + type WrappedState struct { diff diffState cle rideAddress @@ -26,6 +34,7 @@ type WrappedState struct { dataEntriesSize int rootScriptLibVersion ast.LibraryVersion rootActionsCountValidator proto.ActionsCountValidator + lastTwoInvokeComplexities lastTwoInvokeComplexities } func newWrappedState(env *EvaluationEnvironment, rootScriptLibVersion ast.LibraryVersion) *WrappedState { diff --git a/pkg/ride/functions_proto.go b/pkg/ride/functions_proto.go index 7ada9cbce..2ff79fd71 100644 --- a/pkg/ride/functions_proto.go +++ b/pkg/ride/functions_proto.go @@ -239,6 +239,8 @@ func performInvoke(invocation invocation, env environment, args ...rideType) (ri env.setInvocation(oldInvocationParam) ws.totalComplexity += res.Complexity() + // need to reproduce scala's node buggy behaviour in complexity calculations after RideV5 and before RideV6 + ws.lastTwoInvokeComplexities.pushComplexity(res.Complexity()) if res.userResult() == nil { return rideUnit{}, nil diff --git a/pkg/ride/tree_evaluation.go b/pkg/ride/tree_evaluation.go index dde0f9172..39581f5bf 100644 --- a/pkg/ride/tree_evaluation.go +++ b/pkg/ride/tree_evaluation.go @@ -6,6 +6,9 @@ import ( "github.com/wavesplatform/gowaves/pkg/types" ) +// invokeCallComplexityV5 is invoke() or reentrantInvoke() functions cost for RideV5 +const invokeCallComplexityV5 = 75 + func CallVerifier(env environment, tree *ast.Tree) (Result, error) { e, err := treeVerifierEvaluator(env, tree) if err != nil { @@ -33,18 +36,9 @@ func CallFunction(env environment, tree *ast.Tree, name string, args proto.Argum // Evaluation failed we have to return a DAppResult that contains spent execution complexity // Produced actions are not stored for failed transactions, no need to return them here et := GetEvaluationErrorType(err) + complexity := complexityInCaseOfEvaluationError(et, e, env) if et == Undefined { - return nil, EvaluationErrorAddComplexity( - et.Wrap(err, "unhandled error"), - // Error was not handled in wrapped state properly, - // so we need to add both complexity from current evaluation and from internal invokes - e.complexity()+wrappedStateComplexity(env.state()), - ) - } - complexity := e.complexity() + wrappedStateComplexity(env.state()) - if env.rideV5Activated() && !env.rideV6Activated() && et == InternalInvocationError { - ws := env.state().(*WrappedState) - // TODO: this should be handled only when err == ride.InternalInvocationError + return nil, EvaluationErrorAddComplexity(et.Wrap(err, "unhandled error"), complexity) } return nil, EvaluationErrorAddComplexity(err, complexity) } @@ -52,7 +46,7 @@ func CallFunction(env environment, tree *ast.Tree, name string, args proto.Argum if !ok { // Unexpected result type return nil, EvaluationErrorAddComplexity( EvaluationFailure.Errorf("invalid result of call function '%s'", name), - // New error, both complexities should be added + // New error, both complexities should be added (also see comment in complexityInCaseOfEvaluationError) e.complexity()+wrappedStateComplexity(env.state()), ) } @@ -93,3 +87,22 @@ func wrappedStateActions(state types.SmartState) []proto.ScriptAction { } return ws.act } + +func complexityInCaseOfEvaluationError(et EvaluationError, e *treeEvaluator, env environment) int { + // Error was not handled in wrapped state properly, + // so we need to add both complexity from current evaluation and from internal invokes + complexity := e.complexity() + wrappedStateComplexity(env.state()) + // reproduce scala's node buggy behaviour + if ws, ok := env.state().(*WrappedState); ok && env.rideV5Activated() && !env.rideV6Activated() && et == InternalInvocationError { + // if invoke script tx nesting level is 2 or less ==> complexity should be set to 0 + // invCount() is calls count of invoke() or reentrantInvoke() functions ==> txNestingLevel = 1 + invCount() + if txNestingLevel := 1 + ws.invCount(); txNestingLevel <= 2 { + complexity = 0 + } else { + // if nesting level is 3 or greater, then we should sub last two invoke complexities plus + // cost of the last invoke() or reentrantInvoke() function call + complexity -= ws.lastTwoInvokeComplexities.sum() + invokeCallComplexityV5 + } + } + return complexity +} From 22cd254fb3fa0d5335f4a5c48db66be30a5abcbd Mon Sep 17 00:00:00 2001 From: Nikolay Eskov Date: Wed, 15 Jun 2022 00:48:25 +0300 Subject: [PATCH 03/17] Return an error in case when total block complexity exceeds limit. --- pkg/state/appender.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/state/appender.go b/pkg/state/appender.go index 9930686a1..1014ce511 100644 --- a/pkg/state/appender.go +++ b/pkg/state/appender.go @@ -275,8 +275,9 @@ func (a *txAppender) checkScriptsLimits(scriptsRuns uint64, blockID proto.BlockI } maxBlockComplexity := NewMaxScriptsComplexityInBlock().GetMaxScriptsComplexityInBlock(rideV5Activated) if a.sc.getTotalComplexity() > uint64(maxBlockComplexity) { - // TODO this is definitely an error, should return it - zap.S().Warnf("Complexity of scripts (%d) in block '%s' exceeds limit of %d", a.sc.getTotalComplexity(), blockID.String(), maxBlockComplexity) + return errors.Errorf("complexity of scripts (%d) in block '%s' exceeds limit of %d", + a.sc.getTotalComplexity(), blockID.String(), maxBlockComplexity, + ) } return nil } else if smartAccountsActivated { From ac129f2964e34c80612aa2e771995aafd2f12970 Mon Sep 17 00:00:00 2001 From: Nikolay Eskov Date: Wed, 15 Jun 2022 01:53:17 +0300 Subject: [PATCH 04/17] Fixed 'TestInvokeFailedBalanceValidationV6' test. --- pkg/ride/tree_evaluation_test.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pkg/ride/tree_evaluation_test.go b/pkg/ride/tree_evaluation_test.go index f666fa127..4146ecd40 100644 --- a/pkg/ride/tree_evaluation_test.go +++ b/pkg/ride/tree_evaluation_test.go @@ -1118,6 +1118,9 @@ var envDappFromDapp = &mockRideEnvironment{ blockV5ActivatedFunc: func() bool { return true }, + rideV5ActivatedFunc: func() bool { + return true + }, rideV6ActivatedFunc: noRideV6, validateInternalPaymentsFunc: func() bool { return true @@ -1140,6 +1143,9 @@ func tearDownDappFromDapp() { addressCallable = proto.WavesAddress{} addrPK = crypto.PublicKey{} addressCallablePK = crypto.PublicKey{} + envDappFromDapp.rideV5ActivatedFunc = func() bool { + return true + } envDappFromDapp.rideV6ActivatedFunc = noRideV6 thisAddress = proto.WavesAddress{} From 22c52a5984a41ed28c52bf6cb6453932a4adb084 Mon Sep 17 00:00:00 2001 From: Nikolay Eskov Date: Fri, 17 Jun 2022 03:35:14 +0300 Subject: [PATCH 05/17] Edit comments in 'complexityInCaseOfEvaluationError' function. --- pkg/ride/tree_evaluation.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/ride/tree_evaluation.go b/pkg/ride/tree_evaluation.go index 39581f5bf..12b0b38d1 100644 --- a/pkg/ride/tree_evaluation.go +++ b/pkg/ride/tree_evaluation.go @@ -94,12 +94,12 @@ func complexityInCaseOfEvaluationError(et EvaluationError, e *treeEvaluator, env complexity := e.complexity() + wrappedStateComplexity(env.state()) // reproduce scala's node buggy behaviour if ws, ok := env.state().(*WrappedState); ok && env.rideV5Activated() && !env.rideV6Activated() && et == InternalInvocationError { - // if invoke script tx nesting level is 2 or less ==> complexity should be set to 0 - // invCount() is calls count of invoke() or reentrantInvoke() functions ==> txNestingLevel = 1 + invCount() - if txNestingLevel := 1 + ws.invCount(); txNestingLevel <= 2 { + // if invoke script tx depth level is 2 or less ==> complexity should be set to 0 + // invCount() is calls count of invoke() or reentrantInvoke() functions ==> txDepthLevel = 1 + invCount() + if txDepthLevel := 1 + ws.invCount(); txDepthLevel <= 2 { complexity = 0 } else { - // if nesting level is 3 or greater, then we should sub last two invoke complexities plus + // if depth level is 3 or greater, then we should sub last two invoke complexities plus // cost of the last invoke() or reentrantInvoke() function call complexity -= ws.lastTwoInvokeComplexities.sum() + invokeCallComplexityV5 } From 26d409213b10e28b78a31149ebd5504c9a699a11 Mon Sep 17 00:00:00 2001 From: Nikolay Eskov Date: Fri, 17 Jun 2022 03:38:44 +0300 Subject: [PATCH 06/17] Fixed 'TestFailedInvokeApplicationComplexity' test. --- pkg/state/invoke_applier_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/state/invoke_applier_test.go b/pkg/state/invoke_applier_test.go index 6c6500d8c..7f1c8f0a8 100644 --- a/pkg/state/invoke_applier_test.go +++ b/pkg/state/invoke_applier_test.go @@ -809,7 +809,7 @@ func TestFailedInvokeApplicationComplexity(t *testing.T) { // This transaction reaches data entries size limit (16 KB) after reaching 1000 complexity limit fcSizeLimitAfterComplexityLimit := proto.FunctionCall{Name: "keyvalue", Arguments: []proto.Argument{&proto.IntegerArgument{Value: 99}, &proto.StringArgument{Value: strings.Repeat("0", 150)}}} // This transaction reaches data entries size limit (16 KB) before reaching 1000 complexity limit - fcSizeLimitBeforeComplexityLimit := proto.FunctionCall{Name: "keyvalue", Arguments: []proto.Argument{&proto.IntegerArgument{Value: 11}, &proto.StringArgument{Value: strings.Repeat("0", 2000)}}} + fcSizeLimitBeforeComplexityLimit := proto.FunctionCall{Name: "keyvalue", Arguments: []proto.Argument{&proto.IntegerArgument{Value: 14}, &proto.StringArgument{Value: strings.Repeat("0", 2000)}}} tests := []invokeApplierTestData{ { payments: []proto.ScriptPayment{}, From 3027513aa34e59a6ec948bbc90240d5cdc84728a Mon Sep 17 00:00:00 2001 From: Nikolay Eskov Date: Fri, 17 Jun 2022 04:25:54 +0300 Subject: [PATCH 07/17] Fixed tests in 'ride' package. Refactored a bit. --- pkg/ride/environment.go | 13 +- pkg/ride/functions_proto_test.go | 19 +- pkg/ride/tree_evaluation_test.go | 301 ++++++++++++------------------- 3 files changed, 132 insertions(+), 201 deletions(-) diff --git a/pkg/ride/environment.go b/pkg/ride/environment.go index 53d9c1ef1..21b4ad281 100644 --- a/pkg/ride/environment.go +++ b/pkg/ride/environment.go @@ -13,6 +13,13 @@ var ( errDeletedEntry = errors.New("entry has been deleted") ) +var ( + libV2CheckMessageLength = func(int) bool { return true } + libV3CheckMessageLength = func(l int) bool { + return l <= maxMessageLength + } +) + type lastTwoInvokeComplexities [2]int func (l *lastTwoInvokeComplexities) pushComplexity(complexity int) { @@ -969,7 +976,7 @@ func NewEnvironment(scheme proto.Scheme, state types.SmartState, internalPayment sch: scheme, st: state, h: rideInt(height), - check: func(int) bool { return true }, // By default, for versions below 2 there was no check, always ok. + check: libV2CheckMessageLength, // By default, for versions below 2 there was no check, always ok. takeStr: func(s string, n int) rideString { panic("function 'takeStr' was not initialized") }, validatePaymentsAfter: internalPaymentsValidationHeight, }, nil @@ -1081,9 +1088,7 @@ func (e *EvaluationEnvironment) ChooseTakeString(isRideV5 bool) { func (e *EvaluationEnvironment) ChooseSizeCheck(v ast.LibraryVersion) { e.ver = v if v > ast.LibV2 { - e.check = func(l int) bool { - return l <= maxMessageLength - } + e.check = libV3CheckMessageLength } } diff --git a/pkg/ride/functions_proto_test.go b/pkg/ride/functions_proto_test.go index b58ea1591..c91c94f30 100644 --- a/pkg/ride/functions_proto_test.go +++ b/pkg/ride/functions_proto_test.go @@ -19,16 +19,17 @@ import ( ) var ( - v2check = func(int) bool { - return true - } - v3check = func(size int) bool { - return size <= maxMessageLength - } + trueFn = func() bool { return true } + falseFn = func() bool { return false } +) + +var ( + v2check = libV2CheckMessageLength + v3check = libV3CheckMessageLength v5takeString = takeRideString - noRideV6 = func() bool { - return false - } + yesRideV5 = trueFn + yesRideV6 = trueFn + noRideV6 = falseFn ) func TestAddressFromString(t *testing.T) { diff --git a/pkg/ride/tree_evaluation_test.go b/pkg/ride/tree_evaluation_test.go index 4146ecd40..8324bf586 100644 --- a/pkg/ride/tree_evaluation_test.go +++ b/pkg/ride/tree_evaluation_test.go @@ -28,9 +28,7 @@ var ( }, rideV6ActivatedFunc: noRideV6, } - isProtobufTx = func() bool { - return true - } + isProtobufTx = trueFn ) func TestSimpleScriptEvaluation(t *testing.T) { @@ -176,10 +174,8 @@ func TestFunctionsEvaluation(t *testing.T) { } return obj }, - validateInternalPaymentsFunc: func() bool { - return false - }, - rideV6ActivatedFunc: noRideV6, + validateInternalPaymentsFunc: falseFn, + rideV6ActivatedFunc: noRideV6, } envWithDataTX := &mockRideEnvironment{ transactionFunc: func() rideObject { @@ -1115,16 +1111,10 @@ var envDappFromDapp = &mockRideEnvironment{ timestampFunc: func() uint64 { return 1564703444249 }, - blockV5ActivatedFunc: func() bool { - return true - }, - rideV5ActivatedFunc: func() bool { - return true - }, - rideV6ActivatedFunc: noRideV6, - validateInternalPaymentsFunc: func() bool { - return true - }, + blockV5ActivatedFunc: trueFn, + rideV5ActivatedFunc: yesRideV5, + rideV6ActivatedFunc: noRideV6, + validateInternalPaymentsFunc: trueFn, internalPaymentsValidationHeightFunc: func() uint64 { return 0 }, @@ -1143,9 +1133,7 @@ func tearDownDappFromDapp() { addressCallable = proto.WavesAddress{} addrPK = crypto.PublicKey{} addressCallablePK = crypto.PublicKey{} - envDappFromDapp.rideV5ActivatedFunc = func() bool { - return true - } + envDappFromDapp.rideV5ActivatedFunc = trueFn envDappFromDapp.rideV6ActivatedFunc = noRideV6 thisAddress = proto.WavesAddress{} @@ -1513,9 +1501,7 @@ func TestInvokeBalanceValidationV6(t *testing.T) { thisAddress = addr env := envDappFromDapp - env.rideV6ActivatedFunc = func() bool { - return true - } + env.rideV6ActivatedFunc = trueFn _, tree := parseBase64Script(t, firstScript) @@ -1671,9 +1657,7 @@ func TestInvokeFailedBalanceValidationV6(t *testing.T) { thisAddress = addr env := envDappFromDapp - env.rideV6ActivatedFunc = func() bool { - return true - } + env.rideV6ActivatedFunc = trueFn _, tree := parseBase64Script(t, firstScript) @@ -5596,10 +5580,8 @@ func TestMathDApp(t *testing.T) { require.NoError(t, err) return obj }, - validateInternalPaymentsFunc: func() bool { - return false - }, - rideV6ActivatedFunc: noRideV6, + validateInternalPaymentsFunc: falseFn, + rideV6ActivatedFunc: noRideV6, } code := "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" @@ -7067,13 +7049,9 @@ func TestOriginCaller(t *testing.T) { maxDataEntriesSizeFunc: func() int { return proto.MaxDataEntriesScriptActionsSizeInBytesV2 }, - validateInternalPaymentsFunc: func() bool { - return true - }, - blockV5ActivatedFunc: func() bool { - return true - }, - isProtobufTxFunc: isProtobufTx, + validateInternalPaymentsFunc: trueFn, + blockV5ActivatedFunc: trueFn, + isProtobufTxFunc: isProtobufTx, } mockState := &MockSmartState{ @@ -7238,17 +7216,14 @@ func TestInternalPaymentsValidationFailure(t *testing.T) { setInvocationFunc: func(inv rideObject) { testInv = inv }, - blockV5ActivatedFunc: func() bool { - return true - }, - rideV6ActivatedFunc: noRideV6, + blockV5ActivatedFunc: trueFn, + rideV5ActivatedFunc: yesRideV5, + rideV6ActivatedFunc: noRideV6, setNewDAppAddressFunc: func(address proto.WavesAddress) { testDAppAddress = address testState.cle = rideAddress(address) }, - validateInternalPaymentsFunc: func() bool { - return true - }, + validateInternalPaymentsFunc: trueFn, maxDataEntriesSizeFunc: func() int { return proto.MaxDataEntriesScriptActionsSizeInBytesV2 }, @@ -7327,9 +7302,7 @@ func TestInternalPaymentsValidationFailure(t *testing.T) { require.Error(t, err) // Turning off internal payments validation - env.validateInternalPaymentsFunc = func() bool { - return false - } + env.validateInternalPaymentsFunc = falseFn testInv, err = invocationToObject(5, proto.MainNetScheme, tx) require.NoError(t, err) testDAppAddress = dApp1 @@ -7420,17 +7393,13 @@ func TestAliasesInInvokes(t *testing.T) { invocationFunc: func() rideObject { return testInv }, - blockV5ActivatedFunc: func() bool { - return true - }, + blockV5ActivatedFunc: trueFn, rideV6ActivatedFunc: noRideV6, checkMessageLengthFunc: v3check, setInvocationFunc: func(inv rideObject) { testInv = inv }, - validateInternalPaymentsFunc: func() bool { - return true - }, + validateInternalPaymentsFunc: trueFn, maxDataEntriesSizeFunc: func() int { return proto.MaxDataEntriesScriptActionsSizeInBytesV2 }, @@ -7661,13 +7630,9 @@ func TestIssueAndTransferInInvoke(t *testing.T) { setInvocationFunc: func(inv rideObject) { testInv = inv }, - blockV5ActivatedFunc: func() bool { - return true - }, - rideV6ActivatedFunc: noRideV6, - validateInternalPaymentsFunc: func() bool { - return true - }, + blockV5ActivatedFunc: trueFn, + rideV6ActivatedFunc: noRideV6, + validateInternalPaymentsFunc: trueFn, txIDFunc: func() rideType { return rideBytes(tx.ID.Bytes()) }, @@ -7856,22 +7821,17 @@ func TestTransferUnavailableFundsInInvoke(t *testing.T) { setInvocationFunc: func(inv rideObject) { testInv = inv }, - validateInternalPaymentsFunc: func() bool { - return true - }, + validateInternalPaymentsFunc: trueFn, txIDFunc: func() rideType { return rideBytes(tx.ID.Bytes()) }, maxDataEntriesSizeFunc: func() int { return proto.MaxDataEntriesScriptActionsSizeInBytesV2 }, - blockV5ActivatedFunc: func() bool { - return true - }, - rideV6ActivatedFunc: func() bool { - return true - }, - isProtobufTxFunc: isProtobufTx, + blockV5ActivatedFunc: trueFn, + rideV5ActivatedFunc: yesRideV5, + rideV6ActivatedFunc: yesRideV6, + isProtobufTxFunc: isProtobufTx, } mockState := &MockSmartState{ @@ -8018,10 +7978,9 @@ func TestBurnAndFailOnTransferInInvokeAfterRideV6(t *testing.T) { thisFunc: func() rideType { return rideAddress(testDAppAddress) }, - blockV5ActivatedFunc: func() bool { - return true - }, - rideV6ActivatedFunc: noRideV6, + blockV5ActivatedFunc: trueFn, + rideV5ActivatedFunc: yesRideV5, + rideV6ActivatedFunc: noRideV6, transactionFunc: func() rideObject { obj, err := transactionToObject(proto.TestNetScheme, tx) require.NoError(t, err) @@ -8034,9 +7993,7 @@ func TestBurnAndFailOnTransferInInvokeAfterRideV6(t *testing.T) { setInvocationFunc: func(inv rideObject) { testInv = inv }, - validateInternalPaymentsFunc: func() bool { - return true - }, + validateInternalPaymentsFunc: trueFn, maxDataEntriesSizeFunc: func() int { return proto.MaxDataEntriesScriptActionsSizeInBytesV2 }, @@ -8228,17 +8185,13 @@ func TestReissueInInvoke(t *testing.T) { setInvocationFunc: func(inv rideObject) { testInv = inv }, - validateInternalPaymentsFunc: func() bool { - return true - }, + validateInternalPaymentsFunc: trueFn, maxDataEntriesSizeFunc: func() int { return proto.MaxDataEntriesScriptActionsSizeInBytesV2 }, - blockV5ActivatedFunc: func() bool { - return true - }, - rideV6ActivatedFunc: noRideV6, - isProtobufTxFunc: isProtobufTx, + blockV5ActivatedFunc: trueFn, + rideV6ActivatedFunc: noRideV6, + isProtobufTxFunc: isProtobufTx, } mockState := &MockSmartState{ @@ -8438,11 +8391,10 @@ func TestNegativePayments(t *testing.T) { maxDataEntriesSizeFunc: func() int { return proto.MaxDataEntriesScriptActionsSizeInBytesV2 }, - blockV5ActivatedFunc: func() bool { - return true - }, - rideV6ActivatedFunc: noRideV6, - isProtobufTxFunc: isProtobufTx, + blockV5ActivatedFunc: trueFn, + rideV5ActivatedFunc: yesRideV5, + rideV6ActivatedFunc: noRideV6, + isProtobufTxFunc: isProtobufTx, } mockState := &MockSmartState{ @@ -8642,17 +8594,13 @@ func TestComplexityOverflow(t *testing.T) { setInvocationFunc: func(inv rideObject) { testInv = inv }, - validateInternalPaymentsFunc: func() bool { - return true - }, + validateInternalPaymentsFunc: trueFn, maxDataEntriesSizeFunc: func() int { return proto.MaxDataEntriesScriptActionsSizeInBytesV2 }, - blockV5ActivatedFunc: func() bool { - return true - }, - rideV6ActivatedFunc: noRideV6, - isProtobufTxFunc: isProtobufTx, + blockV5ActivatedFunc: trueFn, + rideV6ActivatedFunc: noRideV6, + isProtobufTxFunc: isProtobufTx, libVersionFunc: func() ast.LibraryVersion { return ast.LibV5 }, @@ -8786,17 +8734,13 @@ func TestDateEntryPutAfterRemoval(t *testing.T) { setInvocationFunc: func(inv rideObject) { testInv = inv }, - validateInternalPaymentsFunc: func() bool { - return true - }, + validateInternalPaymentsFunc: trueFn, maxDataEntriesSizeFunc: func() int { return proto.MaxDataEntriesScriptActionsSizeInBytesV2 }, - blockV5ActivatedFunc: func() bool { - return true - }, - rideV6ActivatedFunc: noRideV6, - isProtobufTxFunc: isProtobufTx, + blockV5ActivatedFunc: trueFn, + rideV6ActivatedFunc: noRideV6, + isProtobufTxFunc: isProtobufTx, libVersionFunc: func() ast.LibraryVersion { return ast.LibV5 }, @@ -8928,17 +8872,14 @@ func TestFailRejectMultiLevelInvokesBeforeRideV6(t *testing.T) { setInvocationFunc: func(inv rideObject) { testInv = inv }, - validateInternalPaymentsFunc: func() bool { - return true - }, + validateInternalPaymentsFunc: trueFn, maxDataEntriesSizeFunc: func() int { return proto.MaxDataEntriesScriptActionsSizeInBytesV2 }, - blockV5ActivatedFunc: func() bool { - return true - }, - rideV6ActivatedFunc: noRideV6, - isProtobufTxFunc: isProtobufTx, + blockV5ActivatedFunc: trueFn, + rideV5ActivatedFunc: yesRideV5, + rideV6ActivatedFunc: noRideV6, + isProtobufTxFunc: isProtobufTx, libVersionFunc: func() ast.LibraryVersion { return ast.LibV5 }, @@ -9103,22 +9044,17 @@ func TestInvokeFailForRideV4(t *testing.T) { setInvocationFunc: func(inv rideObject) { testInv = inv }, - validateInternalPaymentsFunc: func() bool { - return true - }, + validateInternalPaymentsFunc: trueFn, txIDFunc: func() rideType { return rideBytes(tx.ID.Bytes()) }, maxDataEntriesSizeFunc: func() int { return proto.MaxDataEntriesScriptActionsSizeInBytesV2 }, - blockV5ActivatedFunc: func() bool { - return true - }, - rideV6ActivatedFunc: func() bool { - return true - }, - isProtobufTxFunc: isProtobufTx, + blockV5ActivatedFunc: trueFn, + rideV5ActivatedFunc: yesRideV5, + rideV6ActivatedFunc: yesRideV6, + isProtobufTxFunc: isProtobufTx, } mockState := &MockSmartState{ @@ -9287,17 +9223,14 @@ func TestInvokeActionsCountRestrictionsV6ToV5Positive(t *testing.T) { invocationFunc: func() rideObject { return testInv }, - blockV5ActivatedFunc: func() bool { - return true - }, - rideV6ActivatedFunc: noRideV6, + blockV5ActivatedFunc: trueFn, + rideV5ActivatedFunc: yesRideV5, + rideV6ActivatedFunc: yesRideV6, checkMessageLengthFunc: v3check, setInvocationFunc: func(inv rideObject) { testInv = inv }, - validateInternalPaymentsFunc: func() bool { - return true - }, + validateInternalPaymentsFunc: trueFn, maxDataEntriesSizeFunc: func() int { return proto.MaxDataEntriesScriptActionsSizeInBytesV2 }, @@ -9347,19 +9280,21 @@ func TestInvokeActionsCountRestrictionsV6ToV5Positive(t *testing.T) { return proto.WavesAddress{}, errors.Errorf("unexpected alias '%s'", alias.String()) } }, - NewestWavesBalanceFunc: func(account proto.Recipient) (uint64, error) { + NewestFullWavesBalanceFunc: func(account proto.Recipient) (*proto.FullWavesBalance, error) { + var available uint64 switch account.String() { case dApp1.String(): - return 0, nil + available = 0 case caller.String(): - return 0, nil + available = 0 case dApp2.String(): - return 100_000_000_000, nil + available = 100_000_000_000 case callee.String(): - return 100_000_000_000, nil + available = 100_000_000_000 default: - return 0, errors.Errorf("unexpected account '%s'", account.String()) + return nil, errors.Errorf("unexpected account '%s'", account.String()) } + return &proto.FullWavesBalance{Available: available}, nil }, WavesBalanceProfileFunc: func(id proto.AddressID) (*types.WavesBalanceProfile, error) { switch id { @@ -9543,17 +9478,14 @@ func TestInvokeActionsCountRestrictionsV6ToV5NestedPositive(t *testing.T) { invocationFunc: func() rideObject { return testInv }, - blockV5ActivatedFunc: func() bool { - return true - }, - rideV6ActivatedFunc: noRideV6, + blockV5ActivatedFunc: trueFn, + rideV5ActivatedFunc: yesRideV5, + rideV6ActivatedFunc: yesRideV6, checkMessageLengthFunc: v3check, setInvocationFunc: func(inv rideObject) { testInv = inv }, - validateInternalPaymentsFunc: func() bool { - return true - }, + validateInternalPaymentsFunc: trueFn, maxDataEntriesSizeFunc: func() int { return proto.MaxDataEntriesScriptActionsSizeInBytesV2 }, @@ -9603,19 +9535,21 @@ func TestInvokeActionsCountRestrictionsV6ToV5NestedPositive(t *testing.T) { return proto.WavesAddress{}, errors.Errorf("unexpected alias '%s'", alias.String()) } }, - NewestWavesBalanceFunc: func(account proto.Recipient) (uint64, error) { + NewestFullWavesBalanceFunc: func(account proto.Recipient) (*proto.FullWavesBalance, error) { + var available uint64 switch account.String() { case dApp1.String(): - return 100_000_000_000, nil + available = 100_000_000_000 case caller.String(): - return 100_000_000_000, nil + available = 100_000_000_000 case dApp2.String(): - return 100_000_000_000, nil + available = 100_000_000_000 case callee.String(): - return 100_000_000_000, nil + available = 100_000_000_000 default: - return 0, errors.Errorf("unexpected account '%s'", account.String()) + return nil, errors.Errorf("unexpected account '%s'", account.String()) } + return &proto.FullWavesBalance{Available: available}, nil }, WavesBalanceProfileFunc: func(id proto.AddressID) (*types.WavesBalanceProfile, error) { switch id { @@ -9760,17 +9694,14 @@ func TestInvokeActionsCountRestrictionsV6ToV5OverflowNegative(t *testing.T) { invocationFunc: func() rideObject { return testInv }, - blockV5ActivatedFunc: func() bool { - return true - }, - rideV6ActivatedFunc: noRideV6, + blockV5ActivatedFunc: trueFn, + rideV5ActivatedFunc: yesRideV5, + rideV6ActivatedFunc: yesRideV6, checkMessageLengthFunc: v3check, setInvocationFunc: func(inv rideObject) { testInv = inv }, - validateInternalPaymentsFunc: func() bool { - return true - }, + validateInternalPaymentsFunc: trueFn, maxDataEntriesSizeFunc: func() int { return proto.MaxDataEntriesScriptActionsSizeInBytesV2 }, @@ -9820,19 +9751,21 @@ func TestInvokeActionsCountRestrictionsV6ToV5OverflowNegative(t *testing.T) { return proto.WavesAddress{}, errors.Errorf("unexpected alias '%s'", alias.String()) } }, - NewestWavesBalanceFunc: func(account proto.Recipient) (uint64, error) { + NewestFullWavesBalanceFunc: func(account proto.Recipient) (*proto.FullWavesBalance, error) { + var available uint64 switch account.String() { case dApp1.String(): - return 0, nil + available = 0 case caller.String(): - return 0, nil + available = 0 case dApp2.String(): - return 100_000_000_000, nil + available = 100_000_000_000 case callee.String(): - return 100_000_000_000, nil + available = 100_000_000_000 default: - return 0, errors.Errorf("unexpected account '%s'", account.String()) + return nil, errors.Errorf("unexpected account '%s'", account.String()) } + return &proto.FullWavesBalance{Available: available}, nil }, WavesBalanceProfileFunc: func(id proto.AddressID) (*types.WavesBalanceProfile, error) { switch id { @@ -9985,17 +9918,14 @@ func TestInvokeActionsCountRestrictionsV6ToV5PNegative(t *testing.T) { invocationFunc: func() rideObject { return testInv }, - blockV5ActivatedFunc: func() bool { - return true - }, - rideV6ActivatedFunc: noRideV6, + blockV5ActivatedFunc: trueFn, + rideV5ActivatedFunc: yesRideV5, + rideV6ActivatedFunc: yesRideV6, checkMessageLengthFunc: v3check, setInvocationFunc: func(inv rideObject) { testInv = inv }, - validateInternalPaymentsFunc: func() bool { - return true - }, + validateInternalPaymentsFunc: trueFn, maxDataEntriesSizeFunc: func() int { return proto.MaxDataEntriesScriptActionsSizeInBytesV2 }, @@ -10045,19 +9975,21 @@ func TestInvokeActionsCountRestrictionsV6ToV5PNegative(t *testing.T) { return proto.WavesAddress{}, errors.Errorf("unexpected alias '%s'", alias.String()) } }, - NewestWavesBalanceFunc: func(account proto.Recipient) (uint64, error) { + NewestFullWavesBalanceFunc: func(account proto.Recipient) (*proto.FullWavesBalance, error) { + var available uint64 switch account.String() { case dApp1.String(): - return 0, nil + available = 0 case caller.String(): - return 0, nil + available = 0 case dApp2.String(): - return 100_000_000_000, nil + available = 100_000_000_000 case callee.String(): - return 100_000_000_000, nil + available = 100_000_000_000 default: - return 0, errors.Errorf("unexpected account '%s'", account.String()) + return nil, errors.Errorf("unexpected account '%s'", account.String()) } + return &proto.FullWavesBalance{Available: available}, nil }, WavesBalanceProfileFunc: func(id proto.AddressID) (*types.WavesBalanceProfile, error) { switch id { @@ -10178,18 +10110,15 @@ func TestInvokeDappAttachedPaymentsLimitAfterV6(t *testing.T) { setInvocationFunc: func(inv rideObject) { testInv = inv }, - validateInternalPaymentsFunc: func() bool { - return true - }, + validateInternalPaymentsFunc: trueFn, txIDFunc: func() rideType { return rideBytes(tx.ID.Bytes()) }, maxDataEntriesSizeFunc: func() int { return proto.MaxDataEntriesScriptActionsSizeInBytesV2 }, - blockV5ActivatedFunc: func() bool { - return true - }, + blockV5ActivatedFunc: trueFn, + rideV5ActivatedFunc: yesRideV5, rideV6ActivatedFunc: func() bool { return rideV6Activated }, @@ -10343,15 +10272,11 @@ func TestInvokeDappFromDappWithZeroPayments(t *testing.T) { setInvocationFunc: func(inv rideObject) { testInv = inv }, - validateInternalPaymentsFunc: func() bool { - return true - }, + validateInternalPaymentsFunc: trueFn, maxDataEntriesSizeFunc: func() int { return proto.MaxDataEntriesScriptActionsSizeInBytesV2 }, - blockV5ActivatedFunc: func() bool { - return true - }, + blockV5ActivatedFunc: trueFn, rideV6ActivatedFunc: func() bool { return rideV6Activated }, From 55c37a1abbe8d98d47f1a6a0acec1adc37c6a342 Mon Sep 17 00:00:00 2001 From: Nikolay Eskov Date: Fri, 17 Jun 2022 04:48:44 +0300 Subject: [PATCH 08/17] Refactored a bit tests in 'state' package. --- pkg/state/blockreadwriter_test.go | 40 ++++++++++--------------------- 1 file changed, 13 insertions(+), 27 deletions(-) diff --git a/pkg/state/blockreadwriter_test.go b/pkg/state/blockreadwriter_test.go index 33499a540..773d084ac 100644 --- a/pkg/state/blockreadwriter_test.go +++ b/pkg/state/blockreadwriter_test.go @@ -13,6 +13,7 @@ import ( "github.com/wavesplatform/gowaves/pkg/crypto" "github.com/wavesplatform/gowaves/pkg/proto" "github.com/wavesplatform/gowaves/pkg/util/common" + "go.uber.org/atomic" ) const ( @@ -283,19 +284,16 @@ func TestSimultaneousReadWrite(t *testing.T) { if err != nil { t.Fatalf("Can not read blocks from blockchain file: %v", err) } - var mtx sync.Mutex var wg sync.WaitGroup ctx, cancel := context.WithCancel(context.Background()) - errCounter := 0 + var errCounter atomic.Int64 readTasks := make(chan *readTask, tasksChanBufferSize) wg.Add(1) go func() { defer wg.Done() err1 := writeBlocks(ctx, to.rw, blocks, readTasks, true, false) if err1 != nil { - mtx.Lock() - errCounter++ - mtx.Unlock() + errCounter.Inc() fmt.Printf("Writer error: %v\n", err1) cancel() } @@ -306,16 +304,14 @@ func TestSimultaneousReadWrite(t *testing.T) { defer wg.Done() err1 := testReader(to.rw, readTasks) if err1 != nil { - mtx.Lock() - errCounter++ - mtx.Unlock() + errCounter.Inc() fmt.Printf("Reader error: %v\n", err1) cancel() } }() } wg.Wait() - if errCounter != 0 { + if errCounter.Load() != 0 { t.Fatalf("Reader/writer error.") } } @@ -338,19 +334,16 @@ func TestReadNewest(t *testing.T) { if err != nil { t.Fatalf("Can not read blocks from blockchain file: %v", err) } - var mtx sync.Mutex var wg sync.WaitGroup ctx, cancel := context.WithCancel(context.Background()) - errCounter := 0 + var errCounter atomic.Int64 readTasks := make(chan *readTask, tasksChanBufferSize) wg.Add(1) go func() { defer wg.Done() err1 := writeBlocks(ctx, to.rw, blocks, readTasks, false, false) if err1 != nil { - mtx.Lock() - errCounter++ - mtx.Unlock() + errCounter.Inc() fmt.Printf("Writer error: %v\n", err1) cancel() } @@ -361,16 +354,14 @@ func TestReadNewest(t *testing.T) { defer wg.Done() err1 := testNewestReader(to.rw, readTasks) if err1 != nil { - mtx.Lock() - errCounter++ - mtx.Unlock() + errCounter.Inc() fmt.Printf("Reader error: %v\n", err1) cancel() } }() } wg.Wait() - if errCounter != 0 { + if errCounter.Load() != 0 { t.Fatalf("Reader/writer error.") } } @@ -480,19 +471,16 @@ func TestProtobufReadWrite(t *testing.T) { prevId = protobufBlocks[i].BlockID() } - var mtx sync.Mutex var wg sync.WaitGroup ctx, cancel := context.WithCancel(context.Background()) - errCounter := 0 + var errCounter atomic.Int64 readTasks := make(chan *readTask, tasksChanBufferSize) wg.Add(1) go func() { defer wg.Done() err1 := writeBlocks(ctx, to.rw, protobufBlocks, readTasks, true, true) if err1 != nil { - mtx.Lock() - errCounter++ - mtx.Unlock() + errCounter.Inc() fmt.Printf("Writer error: %v\n", err1) cancel() } @@ -503,16 +491,14 @@ func TestProtobufReadWrite(t *testing.T) { defer wg.Done() err1 := testReader(to.rw, readTasks) if err1 != nil { - mtx.Lock() - errCounter++ - mtx.Unlock() + errCounter.Inc() fmt.Printf("Reader error: %v\n", err1) cancel() } }() } wg.Wait() - if errCounter != 0 { + if errCounter.Load() != 0 { t.Fatalf("Reader/writer error.") } } From 8b4e48f1ad2f8c45d701c259296466efefe0b81a Mon Sep 17 00:00:00 2001 From: Nikolay Eskov Date: Fri, 17 Jun 2022 13:07:24 +0300 Subject: [PATCH 09/17] Nullify failed invoke complexity in case of 'InternalInvocationError' and tx depth level is 2 or less. --- pkg/ride/tree_evaluation.go | 50 ++++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/pkg/ride/tree_evaluation.go b/pkg/ride/tree_evaluation.go index 12b0b38d1..8b4c1db20 100644 --- a/pkg/ride/tree_evaluation.go +++ b/pkg/ride/tree_evaluation.go @@ -35,12 +35,7 @@ func CallFunction(env environment, tree *ast.Tree, name string, args proto.Argum if err != nil { // Evaluation failed we have to return a DAppResult that contains spent execution complexity // Produced actions are not stored for failed transactions, no need to return them here - et := GetEvaluationErrorType(err) - complexity := complexityInCaseOfEvaluationError(et, e, env) - if et == Undefined { - return nil, EvaluationErrorAddComplexity(et.Wrap(err, "unhandled error"), complexity) - } - return nil, EvaluationErrorAddComplexity(err, complexity) + return nil, handleComplexityInCaseOfEvaluationError(err, e, env) } dAppResult, ok := rideResult.(DAppResult) if !ok { // Unexpected result type @@ -88,21 +83,36 @@ func wrappedStateActions(state types.SmartState) []proto.ScriptAction { return ws.act } -func complexityInCaseOfEvaluationError(et EvaluationError, e *treeEvaluator, env environment) int { +func evaluationErrorSetComplexity(err error, complexity int) error { + if ee, ok := err.(evaluationError); ok { + ee.spentComplexity = complexity + return ee + } + return err +} + +func handleComplexityInCaseOfEvaluationError(err error, e *treeEvaluator, env environment) error { // Error was not handled in wrapped state properly, - // so we need to add both complexity from current evaluation and from internal invokes - complexity := e.complexity() + wrappedStateComplexity(env.state()) - // reproduce scala's node buggy behaviour - if ws, ok := env.state().(*WrappedState); ok && env.rideV5Activated() && !env.rideV6Activated() && et == InternalInvocationError { - // if invoke script tx depth level is 2 or less ==> complexity should be set to 0 - // invCount() is calls count of invoke() or reentrantInvoke() functions ==> txDepthLevel = 1 + invCount() - if txDepthLevel := 1 + ws.invCount(); txDepthLevel <= 2 { - complexity = 0 - } else { - // if depth level is 3 or greater, then we should sub last two invoke complexities plus - // cost of the last invoke() or reentrantInvoke() function call - complexity -= ws.lastTwoInvokeComplexities.sum() + invokeCallComplexityV5 + // so we need to add complexities from current evaluation, from internal invokes and from internal failed invoke + totalComplexity := e.complexity() + wrappedStateComplexity(env.state()) + EvaluationErrorSpentComplexity(err) + switch et := GetEvaluationErrorType(err); et { + case Undefined: + return evaluationErrorSetComplexity(et.Wrap(err, "unhandled error"), totalComplexity) + case InternalInvocationError: + // reproduce scala's node buggy behaviour + if ws, ok := env.state().(*WrappedState); ok && env.rideV5Activated() && !env.rideV6Activated() { + // if invoke script tx depth level is 2 or less ==> complexity should be set to 0 + // invCount() is calls count of invoke() or reentrantInvoke() functions ==> txDepthLevel = 1 + invCount() + if txDepthLevel := 1 + ws.invCount(); txDepthLevel <= 2 { + totalComplexity = 0 + } else { + // if depth level is 3 or greater, then we should sub last two invoke complexities plus + // cost of the last invoke() or reentrantInvoke() function call + totalComplexity -= ws.lastTwoInvokeComplexities.sum() + invokeCallComplexityV5 + } } + return evaluationErrorSetComplexity(err, totalComplexity) + default: + return evaluationErrorSetComplexity(err, totalComplexity) } - return complexity } From c2445cf375a951151249881d14a2725c3fbf2e65 Mon Sep 17 00:00:00 2001 From: Alexey Kiselev Date: Wed, 22 Jun 2022 18:16:50 +0400 Subject: [PATCH 10/17] Tests from scala implementation added --- pkg/ride/evaluation_complexity_test.go | 503 +++++++++++++++++++++++++ 1 file changed, 503 insertions(+) create mode 100644 pkg/ride/evaluation_complexity_test.go diff --git a/pkg/ride/evaluation_complexity_test.go b/pkg/ride/evaluation_complexity_test.go new file mode 100644 index 000000000..56ebe8859 --- /dev/null +++ b/pkg/ride/evaluation_complexity_test.go @@ -0,0 +1,503 @@ +package ride + +import ( + "encoding/base64" + "strconv" + "strings" + "testing" + + "github.com/mr-tron/base58" + "github.com/pkg/errors" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/wavesplatform/gowaves/pkg/crypto" + "github.com/wavesplatform/gowaves/pkg/proto" + "github.com/wavesplatform/gowaves/pkg/ride/ast" + "github.com/wavesplatform/gowaves/pkg/types" +) + +type testStage struct { + env *mockRideEnvironment + state *WrappedState + inv rideObject + this proto.WavesAddress + libVersion ast.LibraryVersion + rideV5Activated bool + rideV6Activated bool + trees map[proto.WavesAddress]*ast.Tree + publicKeys map[proto.WavesAddress]crypto.PublicKey +} + +func makeTestStage(inv, tx rideObject, this proto.WavesAddress, rideV5, rideV6 bool, libVersion ast.LibraryVersion, + trees map[proto.WavesAddress]*ast.Tree, publicKeys map[proto.WavesAddress]crypto.PublicKey) *testStage { + r := &testStage{ + inv: inv, + this: this, + libVersion: libVersion, + rideV5Activated: rideV5, + rideV6Activated: rideV6, + trees: trees, + publicKeys: publicKeys, + } + r.env = &mockRideEnvironment{ + schemeFunc: func() byte { + return proto.TestNetScheme + }, + thisFunc: func() rideType { + return rideAddress(r.this) + }, + transactionFunc: func() rideObject { + return tx + }, + invocationFunc: func() rideObject { + return r.inv + }, + checkMessageLengthFunc: v3check, + setInvocationFunc: func(inv rideObject) { + r.inv = inv + }, + validateInternalPaymentsFunc: trueFn, + maxDataEntriesSizeFunc: func() int { + return proto.MaxDataEntriesScriptActionsSizeInBytesV2 + }, + blockV5ActivatedFunc: func() bool { + return true + }, + rideV5ActivatedFunc: func() bool { + return r.rideV5Activated + }, + rideV6ActivatedFunc: func() bool { + return r.rideV6Activated + }, + isProtobufTxFunc: isProtobufTx, + libVersionFunc: func() ast.LibraryVersion { + return r.libVersion + }, + } + state := &MockSmartState{ + NewestScriptByAccountFunc: func(recipient proto.Recipient) (*ast.Tree, error) { + if tree, ok := r.trees[*recipient.Address]; ok { + return tree, nil + } + return nil, errors.Errorf("unexpected recipient '%s'", recipient.String()) + }, + NewestScriptPKByAddrFunc: func(addr proto.WavesAddress) (crypto.PublicKey, error) { + if pk, ok := r.publicKeys[addr]; ok { + return pk, nil + } + return crypto.PublicKey{}, errors.Errorf("unexpected address %s", addr.String()) + }, + NewestRecipientToAddressFunc: func(recipient proto.Recipient) (*proto.WavesAddress, error) { + if _, ok := r.trees[*recipient.Address]; ok { + return recipient.Address, nil + } + return nil, errors.Errorf("unexpected recipient '%s'", recipient.String()) + }, + NewestWavesBalanceFunc: func(account proto.Recipient) (uint64, error) { + if _, ok := r.trees[*account.Address]; ok { + return 10_00000000, nil + } + return 0, errors.Errorf("unxepected account '%s'", account.String()) + }, + NewestFullWavesBalanceFunc: func(account proto.Recipient) (*proto.FullWavesBalance, error) { + if _, ok := r.trees[*account.Address]; ok { + return &proto.FullWavesBalance{ + Regular: 10_00000000, + Generating: 10_00000000, + Available: 10_00000000, + Effective: 10_00000000, + LeaseIn: 0, + LeaseOut: 0, + }, nil + } + return &proto.FullWavesBalance{}, nil + }, + } + r.state = &WrappedState{ + diff: newDiffState(state), + cle: r.env.this().(rideAddress), + scheme: r.env.scheme(), + rootScriptLibVersion: r.libVersion, + rootActionsCountValidator: proto.NewScriptActionsCountValidator(), + } + r.env.stateFunc = func() types.SmartState { + return r.state + } + r.env.setNewDAppAddressFunc = func(address proto.WavesAddress) { + r.this = address + r.state.cle = rideAddress(address) // We have to update wrapped state's `cle` + } + return r +} + +// parseArguments converts string of comma separated string with prefix encoded values to the slice of proto.Argument. +// To encode proto.StringArgument use string "s'xxx'", "xxx" is the argument value. +// To encode proto.IntegerArgument use string "i'12345'", 12345 is the value of the argument. +// To encode proto.BooleanArgument use string "b'true'" or "b'false'". +// To encode proto.BinaryArgument use string "b58'1111'" or "b64'dGVzdA=='. +// Eg, "s'TEST', i'123456', b'true', "b64'dGVzdA=='" +func parseArguments(t *testing.T, arguments string) proto.Arguments { + if arguments == "" { + return proto.Arguments{} + } + args := strings.Split(arguments, ",") + r := make(proto.Arguments, len(args)) + for i, a := range args { + sp := strings.Split(strings.TrimSpace(a), "'") + switch sp[0] { + case "s": + r[i] = proto.StringArgument{Value: sp[1]} + case "i": + v, err := strconv.ParseInt(sp[1], 10, 64) + require.NoError(t, err) + r[i] = proto.IntegerArgument{Value: v} + case "b": + v, err := strconv.ParseBool(sp[1]) + require.NoError(t, err) + r[i] = proto.BooleanArgument{Value: v} + case "b64": + v, err := base64.StdEncoding.DecodeString(sp[1]) + require.NoError(t, err) + r[i] = proto.BinaryArgument{Value: v} + case "b58": + v, err := base58.Decode(sp[1]) + require.NoError(t, err) + r[i] = proto.BinaryArgument{Value: v} + default: + t.Fatalf("unsupported argument prefix '%s'", sp[0]) + } + } + return r +} + +func makeInvokeTransactionTestObjects(t *testing.T, senderPK crypto.PublicKey, dAppAddress proto.WavesAddress, functionName, arguments string) (rideObject, rideObject) { + call := proto.FunctionCall{ + Default: false, + Name: functionName, + Arguments: parseArguments(t, arguments), + } + tx := &proto.InvokeScriptWithProofs{ + Type: proto.InvokeScriptTransaction, + Version: 1, + ID: makeRandomTxID(t), + Proofs: proto.NewProofs(), + ChainID: proto.TestNetScheme, + SenderPK: senderPK, + ScriptRecipient: proto.NewRecipientFromAddress(dAppAddress), + FunctionCall: call, + Payments: proto.ScriptPayments{}, + FeeAsset: proto.OptionalAsset{}, + Fee: 500000, + Timestamp: 1624967106278, + } + invObj, err := invocationToObject(5, proto.TestNetScheme, tx) + require.NoError(t, err) + txObj, err := transactionToObject(proto.TestNetScheme, tx) + return invObj, txObj +} + +func TestComplexitiesV5V6(t *testing.T) { + _, dApp1PK, dApp1 := makeAddressAndPK(t, "DAPP1") // 3MzDtgL5yw73C2xVLnLJCrT5gCL4357a4sz + _, dApp2PK, dApp2 := makeAddressAndPK(t, "DAPP2") // 3N7Te7NXtGVoQqFqktwrFhQWAkc6J8vfPQ1 + _, dApp3PK, dApp3 := makeAddressAndPK(t, "DAPP3") // 3N186hYM5PFwGdkVUsLJaBvpPEECrSj5CJh + _, dApp4PK, dApp4 := makeAddressAndPK(t, "DAPP4") // 3Mtfbvy5nEGNR2ZNAWJUHauEfFsBysAr1S6 + _, senderPK, sender := makeAddressAndPK(t, "SENDER") // 3N8CkZAyS4XcDoJTJoKNuNk2xmNKmQj7myW + + /* On dApp1 + {-# STDLIB_VERSION 5 #-} + {-# CONTENT_TYPE DAPP #-} + {-# SCRIPT_TYPE ACCOUNT #-} + + let dapp2 = Address(base58'3N7Te7NXtGVoQqFqktwrFhQWAkc6J8vfPQ1') + + let message = base58'emsY' + let pub = base58'HnU9jfhpMcQNaG5yQ46eR43RnkWKGxerw2zVrbpnbGof' + let sig = base58'4uXfw7162zaopAkTNa7eo6YK2mJsTiHGJL3dCtRRH63z1nrdoHBHyhbvrfZovkxf2jKsi2vPsaP2XykfZmUiwPeg' + + let complex = sigVerify(message, sig, pub) && sigVerify(message, sig, pub) && sigVerify(message, sig, pub) && sigVerify_64Kb(message, sig, pub) + let complexCase4 = sigVerify(message, sig, pub) && sigVerify(message, sig, pub) && sigVerify(message, sig, pub) && sigVerify_64Kb(message, sig, pub) + let complexCase56 = sigVerify(message, sig, pub) && sigVerify(message, sig, pub) && sigVerify(message, sig, pub) && sigVerify_32Kb(message, sig, pub) + + @Callable(i) + func case1() = { + strict inv = invoke(dapp2, "case1", [complex], []) + [] + } + + @Callable(i) + func case2() = { + strict inv = invoke(dapp2, "case2", [complex], []) + [] + } + + @Callable(i) + func case3() = { + strict inv = invoke(dapp2, "case3", [complex], []) + [] + } + + @Callable(i) + func case4() = { + strict inv = invoke(dapp2, "case4", [complex && complexCase4], []) + [] + } + + @Callable(i) + func case5() = { + strict inv = invoke(dapp2, "case5", [complexCase56], []) + [] + } + + @Callable(i) + func case6() = { + strict inv = invoke(dapp2, "case6", [complexCase56], []) + [] + } + */ + code1 := "AAIFAAAAAAAAAA4IAhIAEgASABIAEgASAAAAAAcAAAAABWRhcHAyCQEAAAAHQWRkcmVzcwAAAAEBAAAAGgFUwHIGfTfL6MC+bgzmzz/fWbF5GHfdVq+uAAAAAAdtZXNzYWdlAQAAAANwdWsAAAAAA3B1YgEAAAAg+WDTl1J9TPwoZK08IrX5nepU6IE6+Sop9sM4jqz/Ti4AAAAAA3NpZwEAAABAw1mP1rioR7NALQUl9VfqzYKgyTigqanddUm2A1+z5zJdAr+g4iKaVKvD4agOP4Nsv84I8Ht1zgDkT9N9jRU0gQAAAAAHY29tcGxleAMDAwkAAfQAAAADBQAAAAdtZXNzYWdlBQAAAANzaWcFAAAAA3B1YgkAAfQAAAADBQAAAAdtZXNzYWdlBQAAAANzaWcFAAAAA3B1YgcJAAH0AAAAAwUAAAAHbWVzc2FnZQUAAAADc2lnBQAAAANwdWIHCQAJxwAAAAMFAAAAB21lc3NhZ2UFAAAAA3NpZwUAAAADcHViBwAAAAAMY29tcGxleENhc2U0AwMDCQAB9AAAAAMFAAAAB21lc3NhZ2UFAAAAA3NpZwUAAAADcHViCQAB9AAAAAMFAAAAB21lc3NhZ2UFAAAAA3NpZwUAAAADcHViBwkAAfQAAAADBQAAAAdtZXNzYWdlBQAAAANzaWcFAAAAA3B1YgcJAAnHAAAAAwUAAAAHbWVzc2FnZQUAAAADc2lnBQAAAANwdWIHAAAAAA1jb21wbGV4Q2FzZTU2AwMDCQAB9AAAAAMFAAAAB21lc3NhZ2UFAAAAA3NpZwUAAAADcHViCQAB9AAAAAMFAAAAB21lc3NhZ2UFAAAAA3NpZwUAAAADcHViBwkAAfQAAAADBQAAAAdtZXNzYWdlBQAAAANzaWcFAAAAA3B1YgcJAAnGAAAAAwUAAAAHbWVzc2FnZQUAAAADc2lnBQAAAANwdWIHAAAABgAAAAFpAQAAAAVjYXNlMQAAAAAEAAAAA2ludgkAA/wAAAAEBQAAAAVkYXBwMgIAAAAFY2FzZTEJAARMAAAAAgUAAAAHY29tcGxleAUAAAADbmlsBQAAAANuaWwDCQAAAAAAAAIFAAAAA2ludgUAAAADaW52BQAAAANuaWwJAAACAAAAAQIAAAAkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuAAAAAWkBAAAABWNhc2UyAAAAAAQAAAADaW52CQAD/AAAAAQFAAAABWRhcHAyAgAAAAVjYXNlMgkABEwAAAACBQAAAAdjb21wbGV4BQAAAANuaWwFAAAAA25pbAMJAAAAAAAAAgUAAAADaW52BQAAAANpbnYFAAAAA25pbAkAAAIAAAABAgAAACRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4AAAABaQEAAAAFY2FzZTMAAAAABAAAAANpbnYJAAP8AAAABAUAAAAFZGFwcDICAAAABWNhc2UzCQAETAAAAAIFAAAAB2NvbXBsZXgFAAAAA25pbAUAAAADbmlsAwkAAAAAAAACBQAAAANpbnYFAAAAA2ludgUAAAADbmlsCQAAAgAAAAECAAAAJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgAAAAFpAQAAAAVjYXNlNAAAAAAEAAAAA2ludgkAA/wAAAAEBQAAAAVkYXBwMgIAAAAFY2FzZTQJAARMAAAAAgMFAAAAB2NvbXBsZXgFAAAADGNvbXBsZXhDYXNlNAcFAAAAA25pbAUAAAADbmlsAwkAAAAAAAACBQAAAANpbnYFAAAAA2ludgUAAAADbmlsCQAAAgAAAAECAAAAJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgAAAAFpAQAAAAVjYXNlNQAAAAAEAAAAA2ludgkAA/wAAAAEBQAAAAVkYXBwMgIAAAAFY2FzZTUJAARMAAAAAgUAAAANY29tcGxleENhc2U1NgUAAAADbmlsBQAAAANuaWwDCQAAAAAAAAIFAAAAA2ludgUAAAADaW52BQAAAANuaWwJAAACAAAAAQIAAAAkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuAAAAAWkBAAAABWNhc2U2AAAAAAQAAAADaW52CQAD/AAAAAQFAAAABWRhcHAyAgAAAAVjYXNlNgkABEwAAAACBQAAAA1jb21wbGV4Q2FzZTU2BQAAAANuaWwFAAAAA25pbAMJAAAAAAAAAgUAAAADaW52BQAAAANpbnYFAAAAA25pbAkAAAIAAAABAgAAACRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4AAAAA5tVlrQ==" + _, tree1 := parseBase64Script(t, code1) + + assert.NotNil(t, tree1) + /* On dApp2 + {-# STDLIB_VERSION 5 #-} + {-# CONTENT_TYPE DAPP #-} + {-# SCRIPT_TYPE ACCOUNT #-} + + let dapp3 = Address(base58'3N186hYM5PFwGdkVUsLJaBvpPEECrSj5CJh') + + let message = base58'emsY' + let pub = base58'HnU9jfhpMcQNaG5yQ46eR43RnkWKGxerw2zVrbpnbGof' + let sig = base58'4uXfw7162zaopAkTNa7eo6YK2mJsTiHGJL3dCtRRH63z1nrdoHBHyhbvrfZovkxf2jKsi2vPsaP2XykfZmUiwPeg' + + let complexCase1 = sigVerify_8Kb(message, sig, pub) + let complexCase2 = sigVerify_32Kb(message, sig, pub) && sigVerify_16Kb(message, sig, pub) + let complexCase34 = sigVerify(message, sig, pub) && sigVerify(message, sig, pub) + let complexCase5 = sigVerify_8Kb(message, sig, pub) && sigVerify_8Kb(message, sig, pub) + let complexCase6 = sigVerify_64Kb(message, sig, pub) && sigVerify_32Kb(message, sig, pub) + + @Callable(i) + func case1(bool:Boolean) = { + strict inv = invoke(dapp3, "case1", [complexCase1], []) + [] + } + + @Callable(i) + func case2(bool:Boolean) = { + strict inv = invoke(dapp3, "case2", [complexCase2], []) + [] + } + + @Callable(i) + func case3(bool:Boolean) = { + strict inv = invoke(dapp3, "case3", [complexCase34], []) + [] + } + + @Callable(i) + func case4(bool:Boolean) = { + if (complexCase34) then { + [ScriptTransfer(dapp3, 99900000000, unit)] + } else [] + } + + @Callable(i) + func case5(bool:Boolean) = { + strict inv = invoke(dapp3, "case5", [complexCase5], []) + [] + } + + @Callable(i) + func case6(bool:Boolean) = { + strict inv = invoke(dapp3, "case6", [complexCase6], []) + [] + } + */ + code2 := "AAIFAAAAAAAAACAIAhIDCgEEEgMKAQQSAwoBBBIDCgEEEgMKAQQSAwoBBAAAAAkAAAAABWRhcHAzCQEAAAAHQWRkcmVzcwAAAAEBAAAAGgFUeu8lmsRjc2kucGmTq6Am5fkIjxQl3OMuAAAAAAdtZXNzYWdlAQAAAANwdWsAAAAAA3B1YgEAAAAg+WDTl1J9TPwoZK08IrX5nepU6IE6+Sop9sM4jqz/Ti4AAAAAA3NpZwEAAABAw1mP1rioR7NALQUl9VfqzYKgyTigqanddUm2A1+z5zJdAr+g4iKaVKvD4agOP4Nsv84I8Ht1zgDkT9N9jRU0gQAAAAAMY29tcGxleENhc2UxCQAJxAAAAAMFAAAAB21lc3NhZ2UFAAAAA3NpZwUAAAADcHViAAAAAAxjb21wbGV4Q2FzZTIDCQAJxgAAAAMFAAAAB21lc3NhZ2UFAAAAA3NpZwUAAAADcHViCQAJxQAAAAMFAAAAB21lc3NhZ2UFAAAAA3NpZwUAAAADcHViBwAAAAANY29tcGxleENhc2UzNAMJAAH0AAAAAwUAAAAHbWVzc2FnZQUAAAADc2lnBQAAAANwdWIJAAH0AAAAAwUAAAAHbWVzc2FnZQUAAAADc2lnBQAAAANwdWIHAAAAAAxjb21wbGV4Q2FzZTUDCQAJxAAAAAMFAAAAB21lc3NhZ2UFAAAAA3NpZwUAAAADcHViCQAJxAAAAAMFAAAAB21lc3NhZ2UFAAAAA3NpZwUAAAADcHViBwAAAAAMY29tcGxleENhc2U2AwkACccAAAADBQAAAAdtZXNzYWdlBQAAAANzaWcFAAAAA3B1YgkACcYAAAADBQAAAAdtZXNzYWdlBQAAAANzaWcFAAAAA3B1YgcAAAAGAAAAAWkBAAAABWNhc2UxAAAAAQAAAARib29sBAAAAANpbnYJAAP8AAAABAUAAAAFZGFwcDMCAAAABWNhc2UxCQAETAAAAAIFAAAADGNvbXBsZXhDYXNlMQUAAAADbmlsBQAAAANuaWwDCQAAAAAAAAIFAAAAA2ludgUAAAADaW52BQAAAANuaWwJAAACAAAAAQIAAAAkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuAAAAAWkBAAAABWNhc2UyAAAAAQAAAARib29sBAAAAANpbnYJAAP8AAAABAUAAAAFZGFwcDMCAAAABWNhc2UyCQAETAAAAAIFAAAADGNvbXBsZXhDYXNlMgUAAAADbmlsBQAAAANuaWwDCQAAAAAAAAIFAAAAA2ludgUAAAADaW52BQAAAANuaWwJAAACAAAAAQIAAAAkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuAAAAAWkBAAAABWNhc2UzAAAAAQAAAARib29sBAAAAANpbnYJAAP8AAAABAUAAAAFZGFwcDMCAAAABWNhc2UzCQAETAAAAAIFAAAADWNvbXBsZXhDYXNlMzQFAAAAA25pbAUAAAADbmlsAwkAAAAAAAACBQAAAANpbnYFAAAAA2ludgUAAAADbmlsCQAAAgAAAAECAAAAJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgAAAAFpAQAAAAVjYXNlNAAAAAEAAAAEYm9vbAMFAAAADWNvbXBsZXhDYXNlMzQJAARMAAAAAgkBAAAADlNjcmlwdFRyYW5zZmVyAAAAAwUAAAAFZGFwcDMAAAAAF0KBBwAFAAAABHVuaXQFAAAAA25pbAUAAAADbmlsAAAAAWkBAAAABWNhc2U1AAAAAQAAAARib29sBAAAAANpbnYJAAP8AAAABAUAAAAFZGFwcDMCAAAABWNhc2U1CQAETAAAAAIFAAAADGNvbXBsZXhDYXNlNQUAAAADbmlsBQAAAANuaWwDCQAAAAAAAAIFAAAAA2ludgUAAAADaW52BQAAAANuaWwJAAACAAAAAQIAAAAkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuAAAAAWkBAAAABWNhc2U2AAAAAQAAAARib29sBAAAAANpbnYJAAP8AAAABAUAAAAFZGFwcDMCAAAABWNhc2U2CQAETAAAAAIFAAAADGNvbXBsZXhDYXNlNgUAAAADbmlsBQAAAANuaWwDCQAAAAAAAAIFAAAAA2ludgUAAAADaW52BQAAAANuaWwJAAACAAAAAQIAAAAkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuAAAAAKUeqZ4=" + _, tree2 := parseBase64Script(t, code2) + + /* On dApp3 + {-# STDLIB_VERSION 5 #-} + {-# CONTENT_TYPE DAPP #-} + {-# SCRIPT_TYPE ACCOUNT #-} + + let message = base58'emsY' + let pub = base58'HnU9jfhpMcQNaG5yQ46eR43RnkWKGxerw2zVrbpnbGof' + let sig = base58'4uXfw7162zaopAkTNa7eo6YK2mJsTiHGJL3dCtRRH63z1nrdoHBHyhbvrfZovkxf2jKsi2vPsaP2XykfZmUiwPeg' + + let dapp4 = Address(base58'3Mtfbvy5nEGNR2ZNAWJUHauEfFsBysAr1S6') + + let complexCase56 = sigVerify_16Kb(message, sig, pub) && sigVerify_8Kb(message, sig, pub) + + @Callable(i) + func case1(bool:Boolean) = { + if (sigVerify(message, sig, pub)) then + [ScriptTransfer(i.caller, 99900000000, unit)] + else [] + } + + @Callable(i) + func case2(bool:Boolean) = { + [ScriptTransfer(i.caller, 99900000000, unit)] + } + + @Callable(i) + func case3(bool:Boolean) = { + [ScriptTransfer(i.caller, 99900000000, unit)] + } + + @Callable(i) + func case5(bool:Boolean) = { + strict inv = invoke(dapp4, "case5", [complexCase56], []) + [] + } + + @Callable(i) + func case6(bool:Boolean) = { + strict inv = invoke(dapp4, "case6", [complexCase56], []) + [] + } + */ + code3 := "AAIFAAAAAAAAABsIAhIDCgEEEgMKAQQSAwoBBBIDCgEEEgMKAQQAAAAFAAAAAAdtZXNzYWdlAQAAAANwdWsAAAAAA3B1YgEAAAAg+WDTl1J9TPwoZK08IrX5nepU6IE6+Sop9sM4jqz/Ti4AAAAAA3NpZwEAAABAw1mP1rioR7NALQUl9VfqzYKgyTigqanddUm2A1+z5zJdAr+g4iKaVKvD4agOP4Nsv84I8Ht1zgDkT9N9jRU0gQAAAAAFZGFwcDQJAQAAAAdBZGRyZXNzAAAAAQEAAAAaAVQ0G4/8L+t0U77mKK9peiJfObsVZZe0WycAAAAADWNvbXBsZXhDYXNlNTYDCQAJxQAAAAMFAAAAB21lc3NhZ2UFAAAAA3NpZwUAAAADcHViCQAJxAAAAAMFAAAAB21lc3NhZ2UFAAAAA3NpZwUAAAADcHViBwAAAAUAAAABaQEAAAAFY2FzZTEAAAABAAAABGJvb2wDCQAB9AAAAAMFAAAAB21lc3NhZ2UFAAAAA3NpZwUAAAADcHViCQAETAAAAAIJAQAAAA5TY3JpcHRUcmFuc2ZlcgAAAAMIBQAAAAFpAAAABmNhbGxlcgAAAAAXQoEHAAUAAAAEdW5pdAUAAAADbmlsBQAAAANuaWwAAAABaQEAAAAFY2FzZTIAAAABAAAABGJvb2wJAARMAAAAAgkBAAAADlNjcmlwdFRyYW5zZmVyAAAAAwgFAAAAAWkAAAAGY2FsbGVyAAAAABdCgQcABQAAAAR1bml0BQAAAANuaWwAAAABaQEAAAAFY2FzZTMAAAABAAAABGJvb2wJAARMAAAAAgkBAAAADlNjcmlwdFRyYW5zZmVyAAAAAwgFAAAAAWkAAAAGY2FsbGVyAAAAABdCgQcABQAAAAR1bml0BQAAAANuaWwAAAABaQEAAAAFY2FzZTUAAAABAAAABGJvb2wEAAAAA2ludgkAA/wAAAAEBQAAAAVkYXBwNAIAAAAFY2FzZTUJAARMAAAAAgUAAAANY29tcGxleENhc2U1NgUAAAADbmlsBQAAAANuaWwDCQAAAAAAAAIFAAAAA2ludgUAAAADaW52BQAAAANuaWwJAAACAAAAAQIAAAAkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuAAAAAWkBAAAABWNhc2U2AAAAAQAAAARib29sBAAAAANpbnYJAAP8AAAABAUAAAAFZGFwcDQCAAAABWNhc2U2CQAETAAAAAIFAAAADWNvbXBsZXhDYXNlNTYFAAAAA25pbAUAAAADbmlsAwkAAAAAAAACBQAAAANpbnYFAAAAA2ludgUAAAADbmlsCQAAAgAAAAECAAAAJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgAAAAAmhwaJ" + _, tree3 := parseBase64Script(t, code3) + + /* On dApp4 + {-# STDLIB_VERSION 5 #-} + {-# CONTENT_TYPE DAPP #-} + {-# SCRIPT_TYPE ACCOUNT #-} + + @Callable(i) + func case5(bool:Boolean) = { + [ScriptTransfer(i.caller, 99900000000, unit)] + } + + @Callable(i) + func case6(bool:Boolean) = { + [ScriptTransfer(i.caller, 99900000000, unit)] + } + */ + code4 := "AAIFAAAAAAAAAAwIAhIDCgEEEgMKAQQAAAAAAAAAAgAAAAFpAQAAAAVjYXNlNQAAAAEAAAAEYm9vbAkABEwAAAACCQEAAAAOU2NyaXB0VHJhbnNmZXIAAAADCAUAAAABaQAAAAZjYWxsZXIAAAAAF0KBBwAFAAAABHVuaXQFAAAAA25pbAAAAAFpAQAAAAVjYXNlNgAAAAEAAAAEYm9vbAkABEwAAAACCQEAAAAOU2NyaXB0VHJhbnNmZXIAAAADCAUAAAABaQAAAAZjYWxsZXIAAAAAF0KBBwAFAAAABHVuaXQFAAAAA25pbAAAAABv44L5" + _, tree4 := parseBase64Script(t, code4) + + invObj, txObj := makeInvokeTransactionTestObjects(t, senderPK, dApp1, "case1", "") + tst := makeTestStage(invObj, txObj, dApp1, true, false, ast.LibV5, + map[proto.WavesAddress]*ast.Tree{dApp1: tree1, dApp2: tree2, dApp3: tree3, dApp4: tree4}, + map[proto.WavesAddress]crypto.PublicKey{dApp1: dApp1PK, dApp2: dApp2PK, dApp3: dApp3PK, dApp4: dApp4PK, sender: senderPK}) + _, err := CallFunction(tst.env, tree1, "case1", parseArguments(t, "")) + require.Error(t, err) + assert.Equal(t, 797+130, EvaluationErrorSpentComplexity(err)) + + tst.rideV6Activated = true + _, err = CallFunction(tst.env, tree1, "case1", parseArguments(t, "")) + require.Error(t, err) + assert.Equal(t, 1105, EvaluationErrorSpentComplexity(err)) + + invObj, txObj = makeInvokeTransactionTestObjects(t, senderPK, dApp1, "case2", "") + tst = makeTestStage(invObj, txObj, dApp1, true, false, ast.LibV5, + map[proto.WavesAddress]*ast.Tree{dApp1: tree1, dApp2: tree2, dApp3: tree3, dApp4: tree4}, + map[proto.WavesAddress]crypto.PublicKey{dApp1: dApp1PK, dApp2: dApp2PK, dApp3: dApp3PK, dApp4: dApp4PK, sender: senderPK}) + _, err = CallFunction(tst.env, tree1, "case2", parseArguments(t, "")) + require.Error(t, err) + assert.Equal(t, 797+214, EvaluationErrorSpentComplexity(err)) + + tst.rideV6Activated = true + _, err = CallFunction(tst.env, tree1, "case2", parseArguments(t, "")) + require.Error(t, err) + assert.Equal(t, 985, EvaluationErrorSpentComplexity(err)) + + invObj, txObj = makeInvokeTransactionTestObjects(t, senderPK, dApp1, "case3", "") + tst = makeTestStage(invObj, txObj, dApp1, true, false, ast.LibV5, + map[proto.WavesAddress]*ast.Tree{dApp1: tree1, dApp2: tree2, dApp3: tree3, dApp4: tree4}, + map[proto.WavesAddress]crypto.PublicKey{dApp1: dApp1PK, dApp2: dApp2PK, dApp3: dApp3PK, dApp4: dApp4PK, sender: senderPK}) + _, err = CallFunction(tst.env, tree1, "case3", parseArguments(t, "")) + require.Error(t, err) + assert.Equal(t, 797+487, EvaluationErrorSpentComplexity(err)) + + tst.rideV6Activated = true + _, err = CallFunction(tst.env, tree1, "case3", parseArguments(t, "")) + require.Error(t, err) + assert.Equal(t, 1258, EvaluationErrorSpentComplexity(err)) + + invObj, txObj = makeInvokeTransactionTestObjects(t, senderPK, dApp1, "case4", "") + tst = makeTestStage(invObj, txObj, dApp1, true, false, ast.LibV5, + map[proto.WavesAddress]*ast.Tree{dApp1: tree1, dApp2: tree2, dApp3: tree3, dApp4: tree4}, + map[proto.WavesAddress]crypto.PublicKey{dApp1: dApp1PK, dApp2: dApp2PK, dApp3: dApp3PK, dApp4: dApp4PK, sender: senderPK}) + _, err = CallFunction(tst.env, tree1, "case4", parseArguments(t, "")) + require.Error(t, err) + assert.Equal(t, 1516, EvaluationErrorSpentComplexity(err)) + + tst.rideV6Activated = true + _, err = CallFunction(tst.env, tree1, "case4", parseArguments(t, "")) + require.Error(t, err) + assert.Equal(t, 1884, EvaluationErrorSpentComplexity(err)) + + invObj, txObj = makeInvokeTransactionTestObjects(t, senderPK, dApp1, "case5", "") + tst = makeTestStage(invObj, txObj, dApp1, true, false, ast.LibV5, + map[proto.WavesAddress]*ast.Tree{dApp1: tree1, dApp2: tree2, dApp3: tree3, dApp4: tree4}, + map[proto.WavesAddress]crypto.PublicKey{dApp1: dApp1PK, dApp2: dApp2PK, dApp3: dApp3PK, dApp4: dApp4PK, sender: senderPK}) + _, err = CallFunction(tst.env, tree1, "case5", parseArguments(t, "")) + require.Error(t, err) + assert.Equal(t, 765+181+191, EvaluationErrorSpentComplexity(err)) + + tst.rideV6Activated = true + _, err = CallFunction(tst.env, tree1, "case5", parseArguments(t, "")) + require.Error(t, err) + assert.Equal(t, 1101, EvaluationErrorSpentComplexity(err)) + + invObj, txObj = makeInvokeTransactionTestObjects(t, senderPK, dApp1, "case6", "") + tst = makeTestStage(invObj, txObj, dApp1, true, false, ast.LibV5, + map[proto.WavesAddress]*ast.Tree{dApp1: tree1, dApp2: tree2, dApp3: tree3, dApp4: tree4}, + map[proto.WavesAddress]crypto.PublicKey{dApp1: dApp1PK, dApp2: dApp2PK, dApp3: dApp3PK, dApp4: dApp4PK, sender: senderPK}) + _, err = CallFunction(tst.env, tree1, "case6", parseArguments(t, "")) + require.Error(t, err) + assert.Equal(t, 765+259+191, EvaluationErrorSpentComplexity(err)) + + tst.rideV6Activated = true + _, err = CallFunction(tst.env, tree1, "case6", parseArguments(t, "")) + require.Error(t, err) + assert.Equal(t, 1179, EvaluationErrorSpentComplexity(err)) +} + +func TestSelfInvokeComplexities(t *testing.T) { + _, dApp1PK, dApp1 := makeAddressAndPK(t, "DAPP1") // 3MzDtgL5yw73C2xVLnLJCrT5gCL4357a4sz + _, senderPK, sender := makeAddressAndPK(t, "SENDER") // 3N8CkZAyS4XcDoJTJoKNuNk2xmNKmQj7myW + + /* On dApp1 + {-# STDLIB_VERSION 5 #-} + {-# CONTENT_TYPE DAPP #-} + {-# SCRIPT_TYPE ACCOUNT #-} + + @Callable(i) + func call( r: Int ) = { + if( r == 0 ) then [ ScriptTransfer(Address(base58'3N8CkZAyS4XcDoJTJoKNuNk2xmNKmQj7myW'), 1000000000, unit ) ] else + let f = fraction( fraction( r, 1, 1 ), 1, 1 ) + strict g = invoke( this, "call", [ f - 1 ], [] ) + [] + } + */ + code := "AAIFAAAAAAAAAAcIAhIDCgEBAAAAAAAAAAEAAAABaQEAAAAEY2FsbAAAAAEAAAABcgMJAAAAAAAAAgUAAAABcgAAAAAAAAAAAAkABEwAAAACCQEAAAAOU2NyaXB0VHJhbnNmZXIAAAADCQEAAAAHQWRkcmVzcwAAAAEBAAAAGgFUyJlKpWZVh64i0Ak7F/0vFuHDEV0ZUdLtAAAAAAA7msoABQAAAAR1bml0BQAAAANuaWwEAAAAAWYJAABrAAAAAwkAAGsAAAADBQAAAAFyAAAAAAAAAAABAAAAAAAAAAABAAAAAAAAAAABAAAAAAAAAAABBAAAAAFnCQAD/AAAAAQFAAAABHRoaXMCAAAABGNhbGwJAARMAAAAAgkAAGUAAAACBQAAAAFmAAAAAAAAAAABBQAAAANuaWwFAAAAA25pbAMJAAAAAAAAAgUAAAABZwUAAAABZwUAAAADbmlsCQAAAgAAAAECAAAAJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgAAAAAyP+y9" + _, tree := parseBase64Script(t, code) + + invObj, txObj := makeInvokeTransactionTestObjects(t, senderPK, dApp1, "call", "i'9'") + tst := makeTestStage(invObj, txObj, dApp1, true, false, ast.LibV5, + map[proto.WavesAddress]*ast.Tree{dApp1: tree}, + map[proto.WavesAddress]crypto.PublicKey{dApp1: dApp1PK, sender: senderPK}) + _, err := CallFunction(tst.env, tree, "call", parseArguments(t, "i'9'")) + require.Error(t, err) + assert.Equal(t, 904, EvaluationErrorSpentComplexity(err)) + + tst.rideV6Activated = true + _, err = CallFunction(tst.env, tree, "call", parseArguments(t, "i'9'")) + require.Error(t, err) + assert.Equal(t, 958, EvaluationErrorSpentComplexity(err)) + + tst.rideV6Activated = false + _, err = CallFunction(tst.env, tree, "call", parseArguments(t, "i'10'")) + require.Error(t, err) + assert.Equal(t, 1017, EvaluationErrorSpentComplexity(err)) + + tst.rideV6Activated = true + _, err = CallFunction(tst.env, tree, "call", parseArguments(t, "i'10'")) + require.Error(t, err) + assert.Equal(t, 1064, EvaluationErrorSpentComplexity(err)) + +} From ab781e3c65c359852989c6601afa0ddd939aebbc Mon Sep 17 00:00:00 2001 From: Nikolay Eskov Date: Tue, 28 Jun 2022 01:21:17 +0300 Subject: [PATCH 11/17] Fix linter issue in 'makeInvokeTransactionTestObjects' func. --- pkg/ride/evaluation_complexity_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/ride/evaluation_complexity_test.go b/pkg/ride/evaluation_complexity_test.go index 56ebe8859..215d821fa 100644 --- a/pkg/ride/evaluation_complexity_test.go +++ b/pkg/ride/evaluation_complexity_test.go @@ -193,6 +193,7 @@ func makeInvokeTransactionTestObjects(t *testing.T, senderPK crypto.PublicKey, d invObj, err := invocationToObject(5, proto.TestNetScheme, tx) require.NoError(t, err) txObj, err := transactionToObject(proto.TestNetScheme, tx) + require.NoError(t, err) return invObj, txObj } From afcb9c7606c00dcf76a0a3b57ddf9cbc877e0633 Mon Sep 17 00:00:00 2001 From: Nikolay Eskov Date: Tue, 28 Jun 2022 01:44:33 +0300 Subject: [PATCH 12/17] Change method receivers for types which implement 'proto.Argument' interface. --- pkg/proto/transactions_test.go | 12 ++++---- pkg/proto/types.go | 40 +++++++++++++------------- pkg/ride/evaluation_complexity_test.go | 10 +++---- 3 files changed, 31 insertions(+), 31 deletions(-) diff --git a/pkg/proto/transactions_test.go b/pkg/proto/transactions_test.go index f600f0305..1126b8971 100644 --- a/pkg/proto/transactions_test.go +++ b/pkg/proto/transactions_test.go @@ -5983,7 +5983,7 @@ func TestInvokeScriptWithProofsValidations(t *testing.T) { repeat := func(arg StringArgument, n int) Arguments { r := make([]Argument, n) for i := 0; i < n; i++ { - r = append(r, arg) + r = append(r, &arg) } return r } @@ -6000,13 +6000,13 @@ func TestInvokeScriptWithProofsValidations(t *testing.T) { err string version byte }{ - {ScriptPayments{}, "foo", Arguments{IntegerArgument{Value: 1234567890}}, 0, "fee should be positive", 1}, - {ScriptPayments{{12345, *a1}}, "foo", Arguments{StringArgument{Value: "some value should be ok"}}, math.MaxInt64 + 1, "fee is too big", 1}, + {ScriptPayments{}, "foo", Arguments{&IntegerArgument{Value: 1234567890}}, 0, "fee should be positive", 1}, + {ScriptPayments{{12345, *a1}}, "foo", Arguments{&StringArgument{Value: "some value should be ok"}}, math.MaxInt64 + 1, "fee is too big", 1}, {ScriptPayments{{12345, *a1}}, strings.Repeat("foo", 100), Arguments{}, 13245, "function name is too big", 1}, {ScriptPayments{{12345, *a1}}, "foo", repeat(StringArgument{Value: "some value should be ok"}, 100), 13245, "too many arguments", 1}, - {ScriptPayments{{0, *a1}}, "foo", Arguments{StringArgument{Value: "some value should be ok"}}, 1234, "at least one payment has a non-positive amount", 1}, - {ScriptPayments{{math.MaxInt64 + 123, *a1}}, "foo", Arguments{StringArgument{Value: "some value should be ok"}}, 12345, "at least one payment has a too big amount", 1}, - {ScriptPayments{}, "foo", Arguments{IntegerArgument{Value: 1234567890}}, 1, "unexpected version 128 for InvokeScriptWithProofs", 128}, + {ScriptPayments{{0, *a1}}, "foo", Arguments{&StringArgument{Value: "some value should be ok"}}, 1234, "at least one payment has a non-positive amount", 1}, + {ScriptPayments{{math.MaxInt64 + 123, *a1}}, "foo", Arguments{&StringArgument{Value: "some value should be ok"}}, 12345, "at least one payment has a too big amount", 1}, + {ScriptPayments{}, "foo", Arguments{&IntegerArgument{Value: 1234567890}}, 1, "unexpected version 128 for InvokeScriptWithProofs", 128}, //TODO: add test on arguments evaluation } for _, tc := range tests { diff --git a/pkg/proto/types.go b/pkg/proto/types.go index 89208956b..a18806443 100644 --- a/pkg/proto/types.go +++ b/pkg/proto/types.go @@ -3189,16 +3189,16 @@ func NewIntegerArgument(i int64) *IntegerArgument { } //GetValueType returns the value type of the entry. -func (a IntegerArgument) GetValueType() ArgumentValueType { +func (a *IntegerArgument) GetValueType() ArgumentValueType { return ArgumentInteger } -func (a IntegerArgument) BinarySize() int { +func (a *IntegerArgument) BinarySize() int { return integerArgumentLen } //MarshalBinary marshals the integer argument in its bytes representation. -func (a IntegerArgument) MarshalBinary() ([]byte, error) { +func (a *IntegerArgument) MarshalBinary() ([]byte, error) { buf := make([]byte, a.BinarySize()) pos := 0 buf[pos] = byte(ArgumentInteger) @@ -3208,7 +3208,7 @@ func (a IntegerArgument) MarshalBinary() ([]byte, error) { } //Serialize the integer argument in its bytes representation. -func (a IntegerArgument) Serialize(s *serializer.Serializer) error { +func (a *IntegerArgument) Serialize(s *serializer.Serializer) error { err := s.Byte(byte(ArgumentInteger)) if err != nil { return err @@ -3255,16 +3255,16 @@ type BooleanArgument struct { } //GetValueType returns the data type (Boolean) of the argument. -func (a BooleanArgument) GetValueType() ArgumentValueType { +func (a *BooleanArgument) GetValueType() ArgumentValueType { return ArgumentBoolean } -func (a BooleanArgument) BinarySize() int { +func (a *BooleanArgument) BinarySize() int { return booleanArgumentLen } //MarshalBinary writes a byte representation of the boolean data entry. -func (a BooleanArgument) MarshalBinary() ([]byte, error) { +func (a *BooleanArgument) MarshalBinary() ([]byte, error) { buf := make([]byte, a.BinarySize()) if a.Value { buf[0] = byte(ArgumentValueTrue) @@ -3275,7 +3275,7 @@ func (a BooleanArgument) MarshalBinary() ([]byte, error) { } //Serialize argument to its byte representation. -func (a BooleanArgument) Serialize(s *serializer.Serializer) error { +func (a *BooleanArgument) Serialize(s *serializer.Serializer) error { buf := byte(0) if a.Value { buf = byte(ArgumentValueTrue) @@ -3328,16 +3328,16 @@ type BinaryArgument struct { } //GetValueType returns the type of value (Binary) stored in an argument. -func (a BinaryArgument) GetValueType() ArgumentValueType { +func (a *BinaryArgument) GetValueType() ArgumentValueType { return ArgumentBinary } -func (a BinaryArgument) BinarySize() int { +func (a *BinaryArgument) BinarySize() int { return binaryArgumentMinLen + len(a.Value) } //MarshalBinary writes an argument to its byte representation. -func (a BinaryArgument) MarshalBinary() ([]byte, error) { +func (a *BinaryArgument) MarshalBinary() ([]byte, error) { buf := make([]byte, a.BinarySize()) pos := 0 buf[pos] = byte(ArgumentBinary) @@ -3347,7 +3347,7 @@ func (a BinaryArgument) MarshalBinary() ([]byte, error) { } //Serialize argument to its byte representation. -func (a BinaryArgument) Serialize(s *serializer.Serializer) error { +func (a *BinaryArgument) Serialize(s *serializer.Serializer) error { err := s.Byte(byte(ArgumentBinary)) if err != nil { return err @@ -3402,16 +3402,16 @@ func NewStringArgument(s string) *StringArgument { } //GetValueType returns the type of value of the argument. -func (a StringArgument) GetValueType() ArgumentValueType { +func (a *StringArgument) GetValueType() ArgumentValueType { return ArgumentString } -func (a StringArgument) BinarySize() int { +func (a *StringArgument) BinarySize() int { return stringArgumentMinLen + len(a.Value) } //MarshalBinary converts the argument to its byte representation. -func (a StringArgument) MarshalBinary() ([]byte, error) { +func (a *StringArgument) MarshalBinary() ([]byte, error) { buf := make([]byte, a.BinarySize()) pos := 0 buf[pos] = byte(ArgumentString) @@ -3421,7 +3421,7 @@ func (a StringArgument) MarshalBinary() ([]byte, error) { } //Serialize argument to its byte representation. -func (a StringArgument) Serialize(s *serializer.Serializer) error { +func (a *StringArgument) Serialize(s *serializer.Serializer) error { err := s.Byte(byte(ArgumentString)) if err != nil { return err @@ -3471,16 +3471,16 @@ type ListArgument struct { } //GetValueType returns the type of value of the argument. -func (a ListArgument) GetValueType() ArgumentValueType { +func (a *ListArgument) GetValueType() ArgumentValueType { return ArgumentList } -func (a ListArgument) BinarySize() int { +func (a *ListArgument) BinarySize() int { return 1 + a.Items.BinarySize() } //MarshalBinary converts the argument to its byte representation. -func (a ListArgument) MarshalBinary() ([]byte, error) { +func (a *ListArgument) MarshalBinary() ([]byte, error) { buf := make([]byte, a.BinarySize()) pos := 0 buf[pos] = byte(ArgumentList) @@ -3494,7 +3494,7 @@ func (a ListArgument) MarshalBinary() ([]byte, error) { } //Serialize argument to its byte representation. -func (a ListArgument) Serialize(s *serializer.Serializer) error { +func (a *ListArgument) Serialize(s *serializer.Serializer) error { err := s.Byte(byte(ArgumentList)) if err != nil { return err diff --git a/pkg/ride/evaluation_complexity_test.go b/pkg/ride/evaluation_complexity_test.go index 215d821fa..c9162eaac 100644 --- a/pkg/ride/evaluation_complexity_test.go +++ b/pkg/ride/evaluation_complexity_test.go @@ -146,23 +146,23 @@ func parseArguments(t *testing.T, arguments string) proto.Arguments { sp := strings.Split(strings.TrimSpace(a), "'") switch sp[0] { case "s": - r[i] = proto.StringArgument{Value: sp[1]} + r[i] = &proto.StringArgument{Value: sp[1]} case "i": v, err := strconv.ParseInt(sp[1], 10, 64) require.NoError(t, err) - r[i] = proto.IntegerArgument{Value: v} + r[i] = &proto.IntegerArgument{Value: v} case "b": v, err := strconv.ParseBool(sp[1]) require.NoError(t, err) - r[i] = proto.BooleanArgument{Value: v} + r[i] = &proto.BooleanArgument{Value: v} case "b64": v, err := base64.StdEncoding.DecodeString(sp[1]) require.NoError(t, err) - r[i] = proto.BinaryArgument{Value: v} + r[i] = &proto.BinaryArgument{Value: v} case "b58": v, err := base58.Decode(sp[1]) require.NoError(t, err) - r[i] = proto.BinaryArgument{Value: v} + r[i] = &proto.BinaryArgument{Value: v} default: t.Fatalf("unsupported argument prefix '%s'", sp[0]) } From 16e78ab16c756ea0252071644f54738324e278ea Mon Sep 17 00:00:00 2001 From: Nikolay Eskov Date: Thu, 30 Jun 2022 20:44:26 +0300 Subject: [PATCH 13/17] Changed dapp balances in 'evaluation_complexity_test.go' to 9.99 WAVES. Set 10 WAVES balance for regular adresses. --- pkg/ride/evaluation_complexity_test.go | 62 +++++++++++++++++--------- 1 file changed, 41 insertions(+), 21 deletions(-) diff --git a/pkg/ride/evaluation_complexity_test.go b/pkg/ride/evaluation_complexity_test.go index c9162eaac..eb8b29bc8 100644 --- a/pkg/ride/evaluation_complexity_test.go +++ b/pkg/ride/evaluation_complexity_test.go @@ -30,6 +30,10 @@ type testStage struct { func makeTestStage(inv, tx rideObject, this proto.WavesAddress, rideV5, rideV6 bool, libVersion ast.LibraryVersion, trees map[proto.WavesAddress]*ast.Tree, publicKeys map[proto.WavesAddress]crypto.PublicKey) *testStage { + const ( + dAppBalance = 10_00000000 - 1000000 // 9.99 WAVES + accountBalance = 10_00000000 // 10 WAVES + ) r := &testStage{ inv: inv, this: this, @@ -91,21 +95,37 @@ func makeTestStage(inv, tx rideObject, this proto.WavesAddress, rideV5, rideV6 b if _, ok := r.trees[*recipient.Address]; ok { return recipient.Address, nil } + if _, ok := r.publicKeys[*recipient.Address]; ok { + return recipient.Address, nil + } return nil, errors.Errorf("unexpected recipient '%s'", recipient.String()) }, NewestWavesBalanceFunc: func(account proto.Recipient) (uint64, error) { if _, ok := r.trees[*account.Address]; ok { - return 10_00000000, nil + return dAppBalance, nil + } + if _, ok := r.publicKeys[*account.Address]; ok { + return accountBalance, nil } return 0, errors.Errorf("unxepected account '%s'", account.String()) }, NewestFullWavesBalanceFunc: func(account proto.Recipient) (*proto.FullWavesBalance, error) { if _, ok := r.trees[*account.Address]; ok { return &proto.FullWavesBalance{ - Regular: 10_00000000, - Generating: 10_00000000, - Available: 10_00000000, - Effective: 10_00000000, + Regular: dAppBalance, + Generating: dAppBalance, + Available: dAppBalance, + Effective: dAppBalance, + LeaseIn: 0, + LeaseOut: 0, + }, nil + } + if _, ok := r.publicKeys[*account.Address]; ok { + return &proto.FullWavesBalance{ + Regular: accountBalance, + Generating: accountBalance, + Available: accountBalance, + Effective: accountBalance, LeaseIn: 0, LeaseOut: 0, }, nil @@ -385,12 +405,12 @@ func TestComplexitiesV5V6(t *testing.T) { map[proto.WavesAddress]crypto.PublicKey{dApp1: dApp1PK, dApp2: dApp2PK, dApp3: dApp3PK, dApp4: dApp4PK, sender: senderPK}) _, err := CallFunction(tst.env, tree1, "case1", parseArguments(t, "")) require.Error(t, err) - assert.Equal(t, 797+130, EvaluationErrorSpentComplexity(err)) + assert.Equal(t, 797+130, EvaluationErrorSpentComplexity(err), err.Error()) tst.rideV6Activated = true _, err = CallFunction(tst.env, tree1, "case1", parseArguments(t, "")) require.Error(t, err) - assert.Equal(t, 1105, EvaluationErrorSpentComplexity(err)) + assert.Equal(t, 1105, EvaluationErrorSpentComplexity(err), err.Error()) invObj, txObj = makeInvokeTransactionTestObjects(t, senderPK, dApp1, "case2", "") tst = makeTestStage(invObj, txObj, dApp1, true, false, ast.LibV5, @@ -398,12 +418,12 @@ func TestComplexitiesV5V6(t *testing.T) { map[proto.WavesAddress]crypto.PublicKey{dApp1: dApp1PK, dApp2: dApp2PK, dApp3: dApp3PK, dApp4: dApp4PK, sender: senderPK}) _, err = CallFunction(tst.env, tree1, "case2", parseArguments(t, "")) require.Error(t, err) - assert.Equal(t, 797+214, EvaluationErrorSpentComplexity(err)) + assert.Equal(t, 797+214, EvaluationErrorSpentComplexity(err), err.Error()) tst.rideV6Activated = true _, err = CallFunction(tst.env, tree1, "case2", parseArguments(t, "")) require.Error(t, err) - assert.Equal(t, 985, EvaluationErrorSpentComplexity(err)) + assert.Equal(t, 985, EvaluationErrorSpentComplexity(err), err.Error()) invObj, txObj = makeInvokeTransactionTestObjects(t, senderPK, dApp1, "case3", "") tst = makeTestStage(invObj, txObj, dApp1, true, false, ast.LibV5, @@ -411,12 +431,12 @@ func TestComplexitiesV5V6(t *testing.T) { map[proto.WavesAddress]crypto.PublicKey{dApp1: dApp1PK, dApp2: dApp2PK, dApp3: dApp3PK, dApp4: dApp4PK, sender: senderPK}) _, err = CallFunction(tst.env, tree1, "case3", parseArguments(t, "")) require.Error(t, err) - assert.Equal(t, 797+487, EvaluationErrorSpentComplexity(err)) + assert.Equal(t, 797+487, EvaluationErrorSpentComplexity(err), err.Error()) tst.rideV6Activated = true _, err = CallFunction(tst.env, tree1, "case3", parseArguments(t, "")) require.Error(t, err) - assert.Equal(t, 1258, EvaluationErrorSpentComplexity(err)) + assert.Equal(t, 1258, EvaluationErrorSpentComplexity(err), err.Error()) invObj, txObj = makeInvokeTransactionTestObjects(t, senderPK, dApp1, "case4", "") tst = makeTestStage(invObj, txObj, dApp1, true, false, ast.LibV5, @@ -424,12 +444,12 @@ func TestComplexitiesV5V6(t *testing.T) { map[proto.WavesAddress]crypto.PublicKey{dApp1: dApp1PK, dApp2: dApp2PK, dApp3: dApp3PK, dApp4: dApp4PK, sender: senderPK}) _, err = CallFunction(tst.env, tree1, "case4", parseArguments(t, "")) require.Error(t, err) - assert.Equal(t, 1516, EvaluationErrorSpentComplexity(err)) + assert.Equal(t, 1516, EvaluationErrorSpentComplexity(err), err.Error()) tst.rideV6Activated = true _, err = CallFunction(tst.env, tree1, "case4", parseArguments(t, "")) require.Error(t, err) - assert.Equal(t, 1884, EvaluationErrorSpentComplexity(err)) + assert.Equal(t, 1884, EvaluationErrorSpentComplexity(err), err.Error()) invObj, txObj = makeInvokeTransactionTestObjects(t, senderPK, dApp1, "case5", "") tst = makeTestStage(invObj, txObj, dApp1, true, false, ast.LibV5, @@ -437,12 +457,12 @@ func TestComplexitiesV5V6(t *testing.T) { map[proto.WavesAddress]crypto.PublicKey{dApp1: dApp1PK, dApp2: dApp2PK, dApp3: dApp3PK, dApp4: dApp4PK, sender: senderPK}) _, err = CallFunction(tst.env, tree1, "case5", parseArguments(t, "")) require.Error(t, err) - assert.Equal(t, 765+181+191, EvaluationErrorSpentComplexity(err)) + assert.Equal(t, 765+181+191, EvaluationErrorSpentComplexity(err), err.Error()) tst.rideV6Activated = true _, err = CallFunction(tst.env, tree1, "case5", parseArguments(t, "")) require.Error(t, err) - assert.Equal(t, 1101, EvaluationErrorSpentComplexity(err)) + assert.Equal(t, 1101, EvaluationErrorSpentComplexity(err), err.Error()) invObj, txObj = makeInvokeTransactionTestObjects(t, senderPK, dApp1, "case6", "") tst = makeTestStage(invObj, txObj, dApp1, true, false, ast.LibV5, @@ -450,12 +470,12 @@ func TestComplexitiesV5V6(t *testing.T) { map[proto.WavesAddress]crypto.PublicKey{dApp1: dApp1PK, dApp2: dApp2PK, dApp3: dApp3PK, dApp4: dApp4PK, sender: senderPK}) _, err = CallFunction(tst.env, tree1, "case6", parseArguments(t, "")) require.Error(t, err) - assert.Equal(t, 765+259+191, EvaluationErrorSpentComplexity(err)) + assert.Equal(t, 765+259+191, EvaluationErrorSpentComplexity(err), err.Error()) tst.rideV6Activated = true _, err = CallFunction(tst.env, tree1, "case6", parseArguments(t, "")) require.Error(t, err) - assert.Equal(t, 1179, EvaluationErrorSpentComplexity(err)) + assert.Equal(t, 1179, EvaluationErrorSpentComplexity(err), err.Error()) } func TestSelfInvokeComplexities(t *testing.T) { @@ -484,21 +504,21 @@ func TestSelfInvokeComplexities(t *testing.T) { map[proto.WavesAddress]crypto.PublicKey{dApp1: dApp1PK, sender: senderPK}) _, err := CallFunction(tst.env, tree, "call", parseArguments(t, "i'9'")) require.Error(t, err) - assert.Equal(t, 904, EvaluationErrorSpentComplexity(err)) + assert.Equal(t, 904, EvaluationErrorSpentComplexity(err), err.Error()) tst.rideV6Activated = true _, err = CallFunction(tst.env, tree, "call", parseArguments(t, "i'9'")) require.Error(t, err) - assert.Equal(t, 958, EvaluationErrorSpentComplexity(err)) + assert.Equal(t, 958, EvaluationErrorSpentComplexity(err), err.Error()) tst.rideV6Activated = false _, err = CallFunction(tst.env, tree, "call", parseArguments(t, "i'10'")) require.Error(t, err) - assert.Equal(t, 1017, EvaluationErrorSpentComplexity(err)) + assert.Equal(t, 1017, EvaluationErrorSpentComplexity(err), err.Error()) tst.rideV6Activated = true _, err = CallFunction(tst.env, tree, "call", parseArguments(t, "i'10'")) require.Error(t, err) - assert.Equal(t, 1064, EvaluationErrorSpentComplexity(err)) + assert.Equal(t, 1064, EvaluationErrorSpentComplexity(err), err.Error()) } From 6de4dcdbd23d8fd26bff4ffe17224c04fc6f8624 Mon Sep 17 00:00:00 2001 From: Nikolay Eskov Date: Fri, 1 Jul 2022 03:32:36 +0300 Subject: [PATCH 14/17] Create reverse complexities list in 'evaluationError' type. --- pkg/ride/environment.go | 11 ----------- pkg/ride/errors.go | 30 ++++++++++++++++++++++++------ pkg/ride/functions_invocations.go | 2 +- pkg/ride/functions_proto.go | 15 +++++++++------ pkg/ride/tree_evaluation.go | 11 +++++++++-- 5 files changed, 43 insertions(+), 26 deletions(-) diff --git a/pkg/ride/environment.go b/pkg/ride/environment.go index 21b4ad281..a47f661f5 100644 --- a/pkg/ride/environment.go +++ b/pkg/ride/environment.go @@ -20,16 +20,6 @@ var ( } ) -type lastTwoInvokeComplexities [2]int - -func (l *lastTwoInvokeComplexities) pushComplexity(complexity int) { - l[0], l[1] = l[1], complexity -} - -func (l *lastTwoInvokeComplexities) sum() int { - return l[0] + l[1] -} - type WrappedState struct { diff diffState cle rideAddress @@ -41,7 +31,6 @@ type WrappedState struct { dataEntriesSize int rootScriptLibVersion ast.LibraryVersion rootActionsCountValidator proto.ActionsCountValidator - lastTwoInvokeComplexities lastTwoInvokeComplexities } func newWrappedState(env *EvaluationEnvironment, rootScriptLibVersion ast.LibraryVersion) *WrappedState { diff --git a/pkg/ride/errors.go b/pkg/ride/errors.go index fe37e879e..a8cd31fbf 100644 --- a/pkg/ride/errors.go +++ b/pkg/ride/errors.go @@ -21,17 +21,13 @@ type evaluationError struct { originalError error callStack []string spentComplexity int + complexities []int } func (e evaluationError) Error() string { return e.originalError.Error() } -func (e evaluationError) AddComplexity(complexity int) error { - e.spentComplexity += complexity - return e -} - func (e EvaluationError) New(msg string) error { return evaluationError{errorType: e, originalError: errors.New(msg)} } @@ -62,6 +58,13 @@ func EvaluationErrorCallStack(err error) []string { return nil } +func EvaluationErrorReverseComplexitiesList(err error) []int { + if ee, ok := err.(evaluationError); ok { + return ee.complexities + } + return nil +} + func EvaluationErrorSpentComplexity(err error) int { if ee, ok := err.(evaluationError); ok { return ee.spentComplexity @@ -71,12 +74,27 @@ func EvaluationErrorSpentComplexity(err error) int { func EvaluationErrorPush(err error, format string, args ...interface{}) error { if ee, ok := err.(evaluationError); ok { - ee.callStack = append([]string{fmt.Sprintf(format, args...)}, ee.callStack...) + elem := fmt.Sprintf(format, args...) + if cap(ee.callStack) > len(ee.callStack) { // reusing the same memory area + ee.callStack = append(ee.callStack[:1], ee.callStack...) + ee.callStack[0] = elem + } else { // allocating memory + ee.callStack = append([]string{elem}, ee.callStack...) + } return ee } return errors.Wrapf(err, format, args...) } +func EvaluationErrorPushComplexity(err error, complexity int) error { + if ee, ok := err.(evaluationError); ok { + ee.complexities = append(ee.complexities, complexity) + ee.spentComplexity += complexity + return ee + } + return err +} + func EvaluationErrorAddComplexity(err error, complexity int) error { if ee, ok := err.(evaluationError); ok { ee.spentComplexity += complexity diff --git a/pkg/ride/functions_invocations.go b/pkg/ride/functions_invocations.go index 3cdac834f..3101f3735 100644 --- a/pkg/ride/functions_invocations.go +++ b/pkg/ride/functions_invocations.go @@ -24,7 +24,7 @@ func invokeFunctionFromDApp(env environment, recipient proto.Recipient, fnName r res, err := e.evaluate() if err != nil { // Evaluation failed we have to add spent execution complexity to an error - return nil, EvaluationErrorAddComplexity(err, e.complexity()) + return nil, EvaluationErrorPushComplexity(err, e.complexity()) } return res, nil } diff --git a/pkg/ride/functions_proto.go b/pkg/ride/functions_proto.go index 2ff79fd71..ea6f3219d 100644 --- a/pkg/ride/functions_proto.go +++ b/pkg/ride/functions_proto.go @@ -111,7 +111,7 @@ func (i *reentrantInvocation) blocklist() bool { return false } -func performInvoke(invocation invocation, env environment, args ...rideType) (rideType, error) { +func performInvoke(invocation invocation, env environment, args ...rideType) (out rideType, err error) { ws, ok := env.state().(*WrappedState) if !ok { return nil, EvaluationFailure.Errorf("%s: wrong state", invocation.name()) @@ -214,6 +214,12 @@ func performInvoke(invocation invocation, env environment, args ...rideType) (ri if err != nil { return nil, EvaluationErrorPush(err, "%s at '%s' function '%s' with arguments %v", invocation.name(), recipient.Address.String(), fn, arguments) } + defer func() { + if err != nil { + // Evaluation checks failed, but we have to add spent execution complexity to an error + err = EvaluationErrorPushComplexity(err, res.Complexity()) + } + }() err = ws.smartAppendActions(res.ScriptActions(), env, &localActionsCountValidator) if err != nil { @@ -223,9 +229,7 @@ func performInvoke(invocation invocation, env environment, args ...rideType) (ri return nil, err } - if env.validateInternalPayments() && !env.rideV6Activated() { - err = ws.validateBalances(env.rideV6Activated()) - } else if env.rideV6Activated() { + if env.validateInternalPayments() && !env.rideV6Activated() || env.rideV6Activated() { err = ws.validateBalances(env.rideV6Activated()) } if err != nil { @@ -238,9 +242,8 @@ func performInvoke(invocation invocation, env environment, args ...rideType) (ri env.setNewDAppAddress(proto.WavesAddress(callerAddress)) env.setInvocation(oldInvocationParam) + // after this line no error should be returned because it can lead to complexity doubling (see defer above) ws.totalComplexity += res.Complexity() - // need to reproduce scala's node buggy behaviour in complexity calculations after RideV5 and before RideV6 - ws.lastTwoInvokeComplexities.pushComplexity(res.Complexity()) if res.userResult() == nil { return rideUnit{}, nil diff --git a/pkg/ride/tree_evaluation.go b/pkg/ride/tree_evaluation.go index 8b4c1db20..9f1624530 100644 --- a/pkg/ride/tree_evaluation.go +++ b/pkg/ride/tree_evaluation.go @@ -98,7 +98,7 @@ func handleComplexityInCaseOfEvaluationError(err error, e *treeEvaluator, env en switch et := GetEvaluationErrorType(err); et { case Undefined: return evaluationErrorSetComplexity(et.Wrap(err, "unhandled error"), totalComplexity) - case InternalInvocationError: + case InternalInvocationError: //, RuntimeError: // TODO: ask about RuntimeError // reproduce scala's node buggy behaviour if ws, ok := env.state().(*WrappedState); ok && env.rideV5Activated() && !env.rideV6Activated() { // if invoke script tx depth level is 2 or less ==> complexity should be set to 0 @@ -108,7 +108,14 @@ func handleComplexityInCaseOfEvaluationError(err error, e *treeEvaluator, env en } else { // if depth level is 3 or greater, then we should sub last two invoke complexities plus // cost of the last invoke() or reentrantInvoke() function call - totalComplexity -= ws.lastTwoInvokeComplexities.sum() + invokeCallComplexityV5 + reverseComplexitiesList := EvaluationErrorReverseComplexitiesList(err) + if l := len(reverseComplexitiesList); l < 2 { + return evaluationErrorSetComplexity(Undefined.Wrapf(err, + "reverseComplexitiesStack size=%d must be greater than 2", l, + ), totalComplexity) + } + lastTwoInvokesCost := reverseComplexitiesList[0] + reverseComplexitiesList[1] + invokeCallComplexityV5 + totalComplexity -= lastTwoInvokesCost } } return evaluationErrorSetComplexity(err, totalComplexity) From 043ac074de3a64ca8bba40d0737d412f82ea9008 Mon Sep 17 00:00:00 2001 From: Nikolay Eskov Date: Fri, 1 Jul 2022 17:48:29 +0300 Subject: [PATCH 15/17] Create 'invokeApplier.handleInvokeScriptInvocationError' method. --- pkg/state/invoke_applier.go | 107 +++++++++++++++++++----------------- 1 file changed, 58 insertions(+), 49 deletions(-) diff --git a/pkg/state/invoke_applier.go b/pkg/state/invoke_applier.go index 9705975e0..4312ab550 100644 --- a/pkg/state/invoke_applier.go +++ b/pkg/state/invoke_applier.go @@ -839,55 +839,7 @@ func (ia *invokeApplier) applyInvokeScript(tx proto.Transaction, info *fallibleV // Call script function. r, err := ia.sc.invokeFunction(tree, tx, info, *scriptAddr) if err != nil { - // Script returned error, it's OK, but we have to decide is it failed or rejected transaction. - // After activation of RideV6 feature transactions are failed if they are not cheap regardless the error kind. - isCheap := int(ia.sc.recentTxComplexity) <= FailFreeInvokeComplexity - if info.rideV6Activated { - if !info.acceptFailed || isCheap { - return nil, errors.Wrapf( - err, "transaction rejected with spent complexity %d and following call stack:\n%s", - ride.EvaluationErrorSpentComplexity(err), - strings.Join(ride.EvaluationErrorCallStack(err), "\n"), - ) - } - res := &invocationResult{failed: true, code: proto.DAppError, text: err.Error(), changes: failedChanges} - return ia.handleInvocationResult(txID, info, res) - } - // Before RideV6 activation in the following cases the transaction is rejected: - // 1) Failing of transactions is not activated yet, reject everything - // 2) The error is ride.InternalInvocationError and correct fail/reject behaviour is activated - // 3) The spent complexity is less than limit - switch ride.GetEvaluationErrorType(err) { - case ride.UserError, ride.RuntimeError: - // Usual script error produced by user code or system functions. - // We reject transaction if spent complexity is less than limit. - if !info.acceptFailed || isCheap { // Reject transaction if no failed transactions or the transaction is cheap - return nil, errors.Wrapf( - err, "transaction rejected with spent complexity %d and following call stack:\n%s", - ride.EvaluationErrorSpentComplexity(err), - strings.Join(ride.EvaluationErrorCallStack(err), "\n"), - ) - } - res := &invocationResult{failed: true, code: proto.DAppError, text: err.Error(), changes: failedChanges} - return ia.handleInvocationResult(txID, info, res) - case ride.InternalInvocationError: - // Special script error produced by internal script invocation or application of results. - // Reject transaction after certain height - rejectOnInvocationError := info.checkerInfo.height >= ia.settings.InternalInvokeCorrectFailRejectBehaviourAfterHeight - if !info.acceptFailed || rejectOnInvocationError || isCheap { - return nil, errors.Wrapf( - err, "transaction rejected with spent complexity %d and following call stack:\n%s", - ride.EvaluationErrorSpentComplexity(err), - strings.Join(ride.EvaluationErrorCallStack(err), "\n"), - ) - } - res := &invocationResult{failed: true, code: proto.DAppError, text: err.Error(), changes: failedChanges} - return ia.handleInvocationResult(txID, info, res) - case ride.Undefined, ride.EvaluationFailure: // Unhandled or evaluator error - return nil, errors.Wrapf(err, "invocation of transaction '%s' failed", txID.String()) - default: - return nil, errors.Wrapf(err, "invocation of transaction '%s' failed", txID.String()) - } + return ia.handleInvokeScriptInvocationError(err, txID, failedChanges, info) } var scriptRuns uint64 = 0 // After activation of RideV5 (16) feature we don't take extra fee for execution of smart asset scripts. @@ -949,6 +901,63 @@ func (ia *invokeApplier) applyInvokeScript(tx proto.Transaction, info *fallibleV return ia.handleInvocationResult(txID, info, res) } +func (ia *invokeApplier) handleInvokeScriptInvocationError( + err error, + txID crypto.Digest, + failedChanges txBalanceChanges, + info *fallibleValidationParams, +) (*applicationResult, error) { + // Script returned error, it's OK, but we have to decide is it failed or rejected transaction. + // After activation of RideV6 feature transactions are failed if they are not cheap regardless the error kind. + isCheap := int(ia.sc.recentTxComplexity) <= FailFreeInvokeComplexity + if info.rideV6Activated { + if !info.acceptFailed || isCheap { + return nil, errors.Wrapf( + err, "transaction rejected with spent complexity %d and following call stack:\n%s", + ride.EvaluationErrorSpentComplexity(err), + strings.Join(ride.EvaluationErrorCallStack(err), "\n"), + ) + } + res := &invocationResult{failed: true, code: proto.DAppError, text: err.Error(), changes: failedChanges} + return ia.handleInvocationResult(txID, info, res) + } + // Before RideV6 activation in the following cases the transaction is rejected: + // 1) Failing of transactions is not activated yet, reject everything + // 2) The error is ride.InternalInvocationError and correct fail/reject behaviour is activated + // 3) The spent complexity is less than limit + switch ride.GetEvaluationErrorType(err) { + case ride.UserError, ride.RuntimeError: + // Usual script error produced by user code or system functions. + // We reject transaction if spent complexity is less than limit. + if !info.acceptFailed || isCheap { // Reject transaction if no failed transactions or the transaction is cheap + return nil, errors.Wrapf( + err, "transaction rejected with spent complexity %d and following call stack:\n%s", + ride.EvaluationErrorSpentComplexity(err), + strings.Join(ride.EvaluationErrorCallStack(err), "\n"), + ) + } + res := &invocationResult{failed: true, code: proto.DAppError, text: err.Error(), changes: failedChanges} + return ia.handleInvocationResult(txID, info, res) + case ride.InternalInvocationError: + // Special script error produced by internal script invocation or application of results. + // Reject transaction after certain height + rejectOnInvocationError := info.checkerInfo.height >= ia.settings.InternalInvokeCorrectFailRejectBehaviourAfterHeight + if !info.acceptFailed || rejectOnInvocationError || isCheap { + return nil, errors.Wrapf( + err, "transaction rejected with spent complexity %d and following call stack:\n%s", + ride.EvaluationErrorSpentComplexity(err), + strings.Join(ride.EvaluationErrorCallStack(err), "\n"), + ) + } + res := &invocationResult{failed: true, code: proto.DAppError, text: err.Error(), changes: failedChanges} + return ia.handleInvocationResult(txID, info, res) + case ride.Undefined, ride.EvaluationFailure: // Unhandled or evaluator error + return nil, errors.Wrapf(err, "invocation of transaction '%s' failed", txID.String()) + default: + return nil, errors.Wrapf(err, "invocation of transaction '%s' failed", txID.String()) + } +} + type invocationResult struct { failed bool code proto.TxFailureReason From 1bf1b3c421c3940144bcc3c7bb25c7c1810169b0 Mon Sep 17 00:00:00 2001 From: Nikolay Eskov Date: Fri, 1 Jul 2022 18:53:09 +0300 Subject: [PATCH 16/17] Refactor 'invokeApplier.handleInvokeScriptInvocationError' method. --- pkg/state/invoke_applier.go | 53 +++++++++++++++---------------------- 1 file changed, 21 insertions(+), 32 deletions(-) diff --git a/pkg/state/invoke_applier.go b/pkg/state/invoke_applier.go index 4312ab550..de243c060 100644 --- a/pkg/state/invoke_applier.go +++ b/pkg/state/invoke_applier.go @@ -902,60 +902,49 @@ func (ia *invokeApplier) applyInvokeScript(tx proto.Transaction, info *fallibleV } func (ia *invokeApplier) handleInvokeScriptInvocationError( - err error, + invErr error, txID crypto.Digest, failedChanges txBalanceChanges, info *fallibleValidationParams, ) (*applicationResult, error) { - // Script returned error, it's OK, but we have to decide is it failed or rejected transaction. - // After activation of RideV6 feature transactions are failed if they are not cheap regardless the error kind. - isCheap := int(ia.sc.recentTxComplexity) <= FailFreeInvokeComplexity - if info.rideV6Activated { - if !info.acceptFailed || isCheap { + handleError := func(rejectTx bool) (*applicationResult, error) { + if rejectTx { return nil, errors.Wrapf( - err, "transaction rejected with spent complexity %d and following call stack:\n%s", - ride.EvaluationErrorSpentComplexity(err), - strings.Join(ride.EvaluationErrorCallStack(err), "\n"), + invErr, "transaction rejected with spent complexity %d and following call stack:\n%s", + ride.EvaluationErrorSpentComplexity(invErr), + strings.Join(ride.EvaluationErrorCallStack(invErr), "\n"), ) } - res := &invocationResult{failed: true, code: proto.DAppError, text: err.Error(), changes: failedChanges} + res := &invocationResult{failed: true, code: proto.DAppError, text: invErr.Error(), changes: failedChanges} return ia.handleInvocationResult(txID, info, res) } + // Script returned error, it's OK, but we have to decide is it failed or rejected transaction. + // After activation of RideV6 feature transactions are failed if they are not cheap regardless the error kind. + var ( + isCheap = int(ia.sc.recentTxComplexity) <= FailFreeInvokeComplexity + rejectTx = !info.acceptFailed || isCheap // Reject transaction if no failed transactions or the transaction is cheap + ) + if info.rideV6Activated { + return handleError(rejectTx) + } // Before RideV6 activation in the following cases the transaction is rejected: // 1) Failing of transactions is not activated yet, reject everything // 2) The error is ride.InternalInvocationError and correct fail/reject behaviour is activated // 3) The spent complexity is less than limit - switch ride.GetEvaluationErrorType(err) { + switch et := ride.GetEvaluationErrorType(invErr); et { case ride.UserError, ride.RuntimeError: // Usual script error produced by user code or system functions. // We reject transaction if spent complexity is less than limit. - if !info.acceptFailed || isCheap { // Reject transaction if no failed transactions or the transaction is cheap - return nil, errors.Wrapf( - err, "transaction rejected with spent complexity %d and following call stack:\n%s", - ride.EvaluationErrorSpentComplexity(err), - strings.Join(ride.EvaluationErrorCallStack(err), "\n"), - ) - } - res := &invocationResult{failed: true, code: proto.DAppError, text: err.Error(), changes: failedChanges} - return ia.handleInvocationResult(txID, info, res) case ride.InternalInvocationError: // Special script error produced by internal script invocation or application of results. // Reject transaction after certain height - rejectOnInvocationError := info.checkerInfo.height >= ia.settings.InternalInvokeCorrectFailRejectBehaviourAfterHeight - if !info.acceptFailed || rejectOnInvocationError || isCheap { - return nil, errors.Wrapf( - err, "transaction rejected with spent complexity %d and following call stack:\n%s", - ride.EvaluationErrorSpentComplexity(err), - strings.Join(ride.EvaluationErrorCallStack(err), "\n"), - ) - } - res := &invocationResult{failed: true, code: proto.DAppError, text: err.Error(), changes: failedChanges} - return ia.handleInvocationResult(txID, info, res) + rejectTx = rejectTx || info.checkerInfo.height >= ia.settings.InternalInvokeCorrectFailRejectBehaviourAfterHeight case ride.Undefined, ride.EvaluationFailure: // Unhandled or evaluator error - return nil, errors.Wrapf(err, "invocation of transaction '%s' failed", txID.String()) + return nil, errors.Wrapf(invErr, "invocation of transaction '%s' failed with error type (%T)", txID.String(), et) default: - return nil, errors.Wrapf(err, "invocation of transaction '%s' failed", txID.String()) + return nil, errors.Wrapf(invErr, "invocation of transaction '%s' failed with unknown error type (%T)", txID.String(), et) } + return handleError(rejectTx) } type invocationResult struct { From 17eaca079377595259fc6542e74a18d77cfac3e7 Mon Sep 17 00:00:00 2001 From: Nikolay Eskov Date: Mon, 4 Jul 2022 11:20:48 +0300 Subject: [PATCH 17/17] Fix error message in 'handleInvokeScriptInvocationError'. --- pkg/state/invoke_applier.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/state/invoke_applier.go b/pkg/state/invoke_applier.go index de243c060..bf18e55fc 100644 --- a/pkg/state/invoke_applier.go +++ b/pkg/state/invoke_applier.go @@ -940,9 +940,9 @@ func (ia *invokeApplier) handleInvokeScriptInvocationError( // Reject transaction after certain height rejectTx = rejectTx || info.checkerInfo.height >= ia.settings.InternalInvokeCorrectFailRejectBehaviourAfterHeight case ride.Undefined, ride.EvaluationFailure: // Unhandled or evaluator error - return nil, errors.Wrapf(invErr, "invocation of transaction '%s' failed with error type (%T)", txID.String(), et) + return nil, errors.Wrapf(invErr, "invocation of transaction '%s' failed with error type value (%v)", txID.String(), et) default: - return nil, errors.Wrapf(invErr, "invocation of transaction '%s' failed with unknown error type (%T)", txID.String(), et) + return nil, errors.Wrapf(invErr, "invocation of transaction '%s' failed with unknown error type value (%v)", txID.String(), et) } return handleError(rejectTx) }