Skip to content

Commit

Permalink
Payments fix (#1468)
Browse files Browse the repository at this point in the history
* Add 'disable-bloom' bool flag for importer utility.

* WIP: workaround.

Moved payments check before light node activation after the nested
invoke actions application.

* WIP: workaround

Add new behavior for payments check in nested invokes.

* Add 'disable-bloom' bool flag for statehash utility.

* Use 'newBehavior' only at known problem height.

* WIP: apply 'payments-fix-test.patch' from @alexeykiselev

* Add 'payments_fix_after_height' setting parameter.

* Pass 'payments_fix_after_height' functionality settings param to an Ride evaluation environment.

* First clean version of fix.

* Fix lint.

* Set 'payments_fix_after_height' to zero for testnet.

* Add default mock function for 'testEnv' test builder.

* Implemented correct behaviour for payments fix.

* Removed 'FIXME' in 'TestEvaluatorComplexityFailedPaymentsCheck'.

* Fixed 'TestRegularAvailableBalanceSwitchOnV5ToV6'.

* Fixed 'TestInvokePaymentsCheckBeforeAndAfterInvoke'.

---------

Co-authored-by: Alexey Kiselev <[email protected]>
  • Loading branch information
nickeskov and alexeykiselev authored Aug 19, 2024
1 parent 9fd82f7 commit 6c25ea2
Show file tree
Hide file tree
Showing 13 changed files with 400 additions and 31 deletions.
17 changes: 17 additions & 0 deletions pkg/ride/complexity.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type complexityCalculator interface {
testPropertyComplexity() error
addPropertyComplexity()
setLimit(limit uint32)
clone() complexityCalculator
}

type complexityCalculatorError interface {
Expand Down Expand Up @@ -138,6 +139,14 @@ func (cc *complexityCalculatorV1) limit() int {
return cc.l
}

func (cc *complexityCalculatorV1) clone() complexityCalculator {
return &complexityCalculatorV1{
err: cc.err,
c: cc.c,
l: cc.l,
}
}

func (cc *complexityCalculatorV1) testNativeFunctionComplexity(name string, fc int) error {
nc, err := common.AddInt(cc.c, fc)
if err != nil {
Expand Down Expand Up @@ -236,6 +245,14 @@ func (cc *complexityCalculatorV2) limit() int {
return cc.l
}

func (cc *complexityCalculatorV2) clone() complexityCalculator {
return &complexityCalculatorV2{
err: cc.err,
c: cc.c,
l: cc.l,
}
}

func (cc *complexityCalculatorV2) testNativeFunctionComplexity(name string, fc int) error {
if fc == 0 { // sanity check: zero complexity for functions is not allowed since Ride V6
return newZeroComplexityError(name)
Expand Down
29 changes: 25 additions & 4 deletions pkg/ride/environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,7 @@ func (ws *WrappedState) validateAsset(action proto.ScriptAction, asset proto.Opt
env.scheme(),
env.state(),
env.internalPaymentsValidationHeight(),
env.paymentsFixAfterHeight(),
env.blockV5Activated(),
env.rideV6Activated(),
env.consensusImprovementsActivated(),
Expand Down Expand Up @@ -504,6 +505,8 @@ func (ws *WrappedState) validatePaymentAction(res *proto.AttachedPaymentScriptAc
return nil
}

var errNegativeBalanceAfterPaymentsApplication = errors.New("negative balance after payments application")

func (ws *WrappedState) validateBalancesAfterPaymentsApplication(env environment, addr proto.WavesAddress, payments proto.ScriptPayments) error {
for _, payment := range payments {
var balance int64
Expand All @@ -528,7 +531,8 @@ func (ws *WrappedState) validateBalancesAfterPaymentsApplication(env environment
}
}
if (env.validateInternalPayments() || env.rideV6Activated()) && balance < 0 {
return errors.Errorf("not enough money in the DApp, balance of asset %s on address %s after payments application is %d",
return errors.Wrapf(errNegativeBalanceAfterPaymentsApplication,
"not enough money in the DApp, balance of asset %s on address %s after payments application is %d",
payment.Asset.String(), addr.String(), balance)
}
}
Expand Down Expand Up @@ -1031,7 +1035,8 @@ type EvaluationEnvironment struct {
takeStr func(s string, n int) rideString
inv rideType
ver ast.LibraryVersion
validatePaymentsAfter uint64
validatePaymentsAfter proto.Height
paymentsFixAfter proto.Height
isBlockV5Activated bool
isRideV6Activated bool
isConsensusImprovementsActivated bool // isConsensusImprovementsActivated => nodeVersion >= 1.4.12
Expand All @@ -1050,7 +1055,10 @@ func bytesSizeCheckV3V6(l int) bool {
return l <= proto.MaxDataWithProofsBytes
}

func NewEnvironment(scheme proto.Scheme, state types.SmartState, internalPaymentsValidationHeight uint64,
func NewEnvironment(
scheme proto.Scheme,
state types.SmartState,
internalPaymentsValidationHeight, paymentsFixAfterHeight proto.Height,
blockV5, rideV6, consensusImprovements, blockRewardDistribution, lightNode bool,
) (*EvaluationEnvironment, error) {
height, err := state.AddingBlockHeight()
Expand All @@ -1064,6 +1072,7 @@ func NewEnvironment(scheme proto.Scheme, state types.SmartState, internalPayment
check: bytesSizeCheckV1V2, // By default almost unlimited
takeStr: func(s string, n int) rideString { panic("function 'takeStr' was not initialized") },
validatePaymentsAfter: internalPaymentsValidationHeight,
paymentsFixAfter: paymentsFixAfterHeight,
isBlockV5Activated: blockV5,
isRideV6Activated: rideV6,
isBlockRewardDistributionActivated: blockRewardDistribution,
Expand Down Expand Up @@ -1341,10 +1350,18 @@ func (e *EvaluationEnvironment) validateInternalPayments() bool {
return int(e.h) > int(e.validatePaymentsAfter)
}

func (e *EvaluationEnvironment) internalPaymentsValidationHeight() uint64 {
func (e *EvaluationEnvironment) internalPaymentsValidationHeight() proto.Height {
return e.validatePaymentsAfter
}

func (e *EvaluationEnvironment) paymentsFixActivated() bool {
return int(e.h) > int(e.paymentsFixAfter)
}

func (e *EvaluationEnvironment) paymentsFixAfterHeight() proto.Height {
return e.paymentsFixAfter
}

func (e *EvaluationEnvironment) maxDataEntriesSize() int {
return e.mds
}
Expand All @@ -1356,3 +1373,7 @@ func (e *EvaluationEnvironment) isProtobufTx() bool {
func (e *EvaluationEnvironment) complexityCalculator() complexityCalculator {
return e.cc
}

func (e *EvaluationEnvironment) setComplexityCalculator(cc complexityCalculator) {
e.cc = cc
}
45 changes: 35 additions & 10 deletions pkg/ride/functions_proto.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,19 +262,37 @@ func performInvoke(invocation invocation, env environment, args ...rideType) (ri
}
return nil, err
}
checkPaymentsAfterApplication := func(errT EvaluationError) error {
err = ws.validateBalancesAfterPaymentsApplication(env, proto.WavesAddress(callerAddress), attachedPayments)
if err != nil && GetEvaluationErrorType(err) == Undefined {
err = errT.Wrapf(err, "%s: failed to apply attached payments", invocation.name())

var (
checkPaymentsApplication = func(errT EvaluationError) error {
err = ws.validateBalancesAfterPaymentsApplication(env, proto.WavesAddress(callerAddress), attachedPayments)
if err != nil && GetEvaluationErrorType(err) == Undefined {
err = errT.Wrapf(err, "%s: failed to apply attached payments", invocation.name())
}
return err
}
return err
}
lightNodeActivated := env.lightNodeActivated()
if lightNodeActivated { // Check payments result balances here AFTER Light Node activation
if pErr := checkPaymentsAfterApplication(NegativeBalanceAfterPayment); pErr != nil {
lightNodeActivated = env.lightNodeActivated()
paymentsFixActivated = env.paymentsFixActivated() // payments fix cant be activated without light node activation
)
if lightNodeActivated && paymentsFixActivated { // Check payments result balances here AFTER Payments Fix activation
if pErr := checkPaymentsApplication(EvaluationFailure); pErr != nil {
return nil, pErr
}
}
var (
restoreComplexityCalculator = func() {} // no-op by default
)
if lightNodeActivated && !paymentsFixActivated {
if pErr := checkPaymentsApplication(NegativeBalanceAfterPayment); pErr != nil {
if !errors.Is(pErr, errNegativeBalanceAfterPaymentsApplication) { // unexpected error
return nil, errors.Wrap(pErr, "failed to check payments after application")
}
complexityCalcClone := env.complexityCalculator().clone() // clone calculator before the invoke call
restoreComplexityCalculator = func() {
env.setComplexityCalculator(complexityCalcClone) // restore complexity calculator
}
}
}

address, err := env.state().NewestRecipientToAddress(recipient)
if err != nil {
Expand Down Expand Up @@ -307,7 +325,7 @@ func performInvoke(invocation invocation, env environment, args ...rideType) (ri
}

if !lightNodeActivated { // Check payments result balances here BEFORE Light Node activation
if pErr := checkPaymentsAfterApplication(InternalInvocationError); pErr != nil {
if pErr := checkPaymentsApplication(InternalInvocationError); pErr != nil {
return nil, pErr
}
}
Expand All @@ -319,6 +337,13 @@ func performInvoke(invocation invocation, env environment, args ...rideType) (ri
}
return nil, err
}
// Check payments result balances here AFTER Light Node activation and BEFORE Payments Fix activation
if lightNodeActivated && !paymentsFixActivated {
if pErr := checkPaymentsApplication(NegativeBalanceAfterPayment); pErr != nil {
restoreComplexityCalculator() // restore complexity calculator
return nil, pErr
}
}

if env.validateInternalPayments() || env.rideV6Activated() {
err = ws.validateBalances(env.rideV6Activated())
Expand Down
5 changes: 4 additions & 1 deletion pkg/ride/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -434,8 +434,11 @@ type environment interface {
consensusImprovementsActivated() bool
blockRewardDistributionActivated() bool
lightNodeActivated() bool
internalPaymentsValidationHeight() uint64
internalPaymentsValidationHeight() proto.Height
paymentsFixAfterHeight() proto.Height
paymentsFixActivated() bool
maxDataEntriesSize() int
isProtobufTx() bool
complexityCalculator() complexityCalculator
setComplexityCalculator(cc complexityCalculator)
}
118 changes: 118 additions & 0 deletions pkg/ride/runtime_moq_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 6c25ea2

Please sign in to comment.