diff --git a/CHANGELOG.md b/CHANGELOG.md index 54194dc90b..750a8ce120 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -120,6 +120,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [#1467](https://github.com/regen-network/regen-ledger/pull/1467) Add `ClassFee` state validation checks - [#1467](https://github.com/regen-network/regen-ledger/pull/1467) Add `BasketFee` state validation checks - [#1484](https://github.com/regen-network/regen-ledger/pull/1484) Add `Msg/UpdateCurator` +- [#1625](https://github.com/regen-network/regen-ledger/pull/1625) Add `reason`/`retirement_reason` #### Changed diff --git a/proto/regen/ecocredit/marketplace/v1/tx.proto b/proto/regen/ecocredit/marketplace/v1/tx.proto index aaa8077357..7aa880426e 100644 --- a/proto/regen/ecocredit/marketplace/v1/tx.proto +++ b/proto/regen/ecocredit/marketplace/v1/tx.proto @@ -172,7 +172,7 @@ message MsgBuyDirect { // retiring credits. The reason will be included in EventRetire and is not // stored in state. // - // Since Revision 2 + // Since Revision 1 string retirement_reason = 7; } } diff --git a/x/ecocredit/base/keeper/features/msg_retire.feature b/x/ecocredit/base/keeper/features/msg_retire.feature index c3b32161f3..48d945d996 100644 --- a/x/ecocredit/base/keeper/features/msg_retire.feature +++ b/x/ecocredit/base/keeper/features/msg_retire.feature @@ -131,4 +131,4 @@ Feature: Msg/Retire "jurisdiction": "US-WA", "reason": "offsetting electricity consumption" } - """ \ No newline at end of file + """ diff --git a/x/ecocredit/base/types/v1/features/msg_retire.feature b/x/ecocredit/base/types/v1/features/msg_retire.feature index 32a9f4dba1..cfa5db9154 100644 --- a/x/ecocredit/base/types/v1/features/msg_retire.feature +++ b/x/ecocredit/base/types/v1/features/msg_retire.feature @@ -148,6 +148,24 @@ Feature: MsgRetire When the message is validated Then expect the error "jurisdiction: expected format [-[ ]]: parse error: invalid request" + Scenario: an error is returned if reason exceeds 512 characters + Given the message + """ + { + "owner": "regen1depk54cuajgkzea6zpgkq36tnjwdzv4ak663u6", + "credits": [ + { + "batch_denom": "C01-001-20200101-20210101-001", + "amount": "100" + } + ], + "jurisdiction": "US-WA" + } + """ + And reason with length "513" + When the message is validated + Then expect the error "reason: max length 512: limit exceeded" + Scenario: a valid amino message Given the message """ diff --git a/x/ecocredit/base/types/v1/features/msg_send.feature b/x/ecocredit/base/types/v1/features/msg_send.feature index c24b48ff36..4a2aa6df7d 100644 --- a/x/ecocredit/base/types/v1/features/msg_send.feature +++ b/x/ecocredit/base/types/v1/features/msg_send.feature @@ -156,7 +156,7 @@ Feature: MsgSend } """ When the message is validated - Then expect the error "tradable amount or retired amount required: invalid request" + Then expect the error "credits[0]: tradable amount or retired amount required: invalid request" Scenario: an error is returned if credits tradable amount is a negative decimal Given the message @@ -173,7 +173,7 @@ Feature: MsgSend } """ When the message is validated - Then expect the error "expected a non-negative decimal, got -100: invalid decimal string" + Then expect the error "credits[0]: expected a non-negative decimal, got -100: invalid decimal string" Scenario: an error is returned if credits retired amount is a negative decimal Given the message @@ -190,7 +190,7 @@ Feature: MsgSend } """ When the message is validated - Then expect the error "expected a non-negative decimal, got -100: invalid decimal string" + Then expect the error "credits[0]: expected a non-negative decimal, got -100: invalid decimal string" Scenario: an error is returned if credits retired amount is positive and retirement jurisdiction is empty Given the message @@ -207,7 +207,7 @@ Feature: MsgSend } """ When the message is validated - Then expect the error "retirement jurisdiction: empty string is not allowed: parse error: invalid request" + Then expect the error "credits[0]: retirement jurisdiction: empty string is not allowed: parse error: invalid request" Scenario: an error is returned if credits retired amount is positive and retirement jurisdiction is not formatted Given the message @@ -225,7 +225,26 @@ Feature: MsgSend } """ When the message is validated - Then expect the error "retirement jurisdiction: expected format [-[ ]]: parse error: invalid request" + Then expect the error "credits[0]: retirement jurisdiction: expected format [-[ ]]: parse error: invalid request" + + Scenario: an error is returned if credits retired amount is positive and retirement reason exceeds 512 characters + Given the message + """ + { + "sender": "regen1depk54cuajgkzea6zpgkq36tnjwdzv4ak663u6", + "recipient": "regen1tnh2q55v8wyygtt9srz5safamzdengsnlm0yy4", + "credits": [ + { + "batch_denom": "C01-001-20200101-20210101-001", + "retired_amount": "100", + "retirement_jurisdiction": "US-WA" + } + ] + } + """ + And retirement reason with length "513" + When the message is validated + Then expect the error "credits[0]: retirement reason: max length 512: limit exceeded" Scenario: a valid amino message Given the message diff --git a/x/ecocredit/base/types/v1/features/types_batch_issuance.feature b/x/ecocredit/base/types/v1/features/types_batch_issuance.feature index 55866553f9..6f9bb7d8d2 100644 --- a/x/ecocredit/base/types/v1/features/types_batch_issuance.feature +++ b/x/ecocredit/base/types/v1/features/types_batch_issuance.feature @@ -86,7 +86,7 @@ Feature: BatchIssuance When the batch issuance is validated Then expect the error "retired amount: expected a non-negative decimal, got -100: invalid decimal string" - Scenario: an error is returned if issuance retired amount is positive and jurisdiction is empty + Scenario: an error is returned if issuance retired amount is positive and retirement jurisdiction is empty Given the batch issuance """ { @@ -97,7 +97,7 @@ Feature: BatchIssuance When the batch issuance is validated Then expect the error "retirement jurisdiction: empty string is not allowed: parse error: invalid request" - Scenario: an error is returned if issuance retired amount is positive and jurisdiction is not formatted + Scenario: an error is returned if issuance retired amount is positive and retirement jurisdiction is not formatted Given the batch issuance """ { @@ -108,3 +108,16 @@ Feature: BatchIssuance """ When the batch issuance is validated Then expect the error "retirement jurisdiction: expected format [-[ ]]: parse error: invalid request" + + Scenario: an error is returned if issuance retired amount is positive and retirement reason exceeds 512 characters + Given the batch issuance + """ + { + "recipient": "regen1depk54cuajgkzea6zpgkq36tnjwdzv4ak663u6", + "retired_amount": "100", + "retirement_jurisdiction": "US-WA" + } + """ + And retirement reason with length "513" + When the batch issuance is validated + Then expect the error "retirement reason: max length 512: limit exceeded" diff --git a/x/ecocredit/base/types/v1/msg_retire.go b/x/ecocredit/base/types/v1/msg_retire.go index f9b04c4345..37db1ffbbe 100644 --- a/x/ecocredit/base/types/v1/msg_retire.go +++ b/x/ecocredit/base/types/v1/msg_retire.go @@ -3,6 +3,8 @@ package v1 import ( "cosmossdk.io/errors" + "github.com/regen-network/regen-ledger/x/ecocredit" + sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx" @@ -43,6 +45,10 @@ func (m *MsgRetire) ValidateBasic() error { return sdkerrors.ErrInvalidRequest.Wrapf("jurisdiction: %s", err) } + if len(m.Reason) > base.MaxNoteLength { + return ecocredit.ErrMaxLimit.Wrapf("reason: max length %d", base.MaxNoteLength) + } + return nil } diff --git a/x/ecocredit/base/types/v1/msg_retire_test.go b/x/ecocredit/base/types/v1/msg_retire_test.go index 91d1589098..1ab5d2b1da 100644 --- a/x/ecocredit/base/types/v1/msg_retire_test.go +++ b/x/ecocredit/base/types/v1/msg_retire_test.go @@ -3,6 +3,8 @@ package v1 import ( "bytes" "encoding/json" + "strconv" + "strings" "testing" "github.com/gogo/protobuf/jsonpb" @@ -31,6 +33,13 @@ func (s *msgRetire) TheMessage(a gocuke.DocString) { require.NoError(s.t, err) } +func (s *msgRetire) ReasonWithLength(a string) { + length, err := strconv.ParseInt(a, 10, 64) + require.NoError(s.t, err) + + s.msg.Reason = strings.Repeat("x", int(length)) +} + func (s *msgRetire) TheMessageIsValidated() { s.err = s.msg.ValidateBasic() } diff --git a/x/ecocredit/base/types/v1/msg_send.go b/x/ecocredit/base/types/v1/msg_send.go index 1202b48ee8..61e5e5f250 100644 --- a/x/ecocredit/base/types/v1/msg_send.go +++ b/x/ecocredit/base/types/v1/msg_send.go @@ -3,11 +3,13 @@ package v1 import ( "fmt" + "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx" - "github.com/regen-network/regen-ledger/types/math" + "github.com/regen-network/regen-ledger/x/ecocredit" "github.com/regen-network/regen-ledger/x/ecocredit/base" ) @@ -46,21 +48,25 @@ func (m *MsgSend) ValidateBasic() error { } if credits.TradableAmount == "" && credits.RetiredAmount == "" { - return sdkerrors.ErrInvalidRequest.Wrap("tradable amount or retired amount required") + return sdkerrors.ErrInvalidRequest.Wrapf("%s: tradable amount or retired amount required", creditIndex) } if _, err := math.NewNonNegativeDecFromString(credits.TradableAmount); err != nil { - return err + return errors.Wrapf(err, "%s", creditIndex) } retiredAmount, err := math.NewNonNegativeDecFromString(credits.RetiredAmount) if err != nil { - return err + return errors.Wrapf(err, "%s", creditIndex) } if !retiredAmount.IsZero() { if err = base.ValidateJurisdiction(credits.RetirementJurisdiction); err != nil { - return sdkerrors.ErrInvalidRequest.Wrapf("retirement jurisdiction: %s", err) + return sdkerrors.ErrInvalidRequest.Wrapf("%s: retirement jurisdiction: %s", creditIndex, err) + } + + if len(credits.RetirementReason) > base.MaxNoteLength { + return ecocredit.ErrMaxLimit.Wrapf("%s: retirement reason: max length %d", creditIndex, base.MaxNoteLength) } } } diff --git a/x/ecocredit/base/types/v1/msg_send_test.go b/x/ecocredit/base/types/v1/msg_send_test.go index c1221a8ede..186a02abf1 100644 --- a/x/ecocredit/base/types/v1/msg_send_test.go +++ b/x/ecocredit/base/types/v1/msg_send_test.go @@ -3,6 +3,8 @@ package v1 import ( "bytes" "encoding/json" + "strconv" + "strings" "testing" "github.com/gogo/protobuf/jsonpb" @@ -31,6 +33,13 @@ func (s *msgSend) TheMessage(a gocuke.DocString) { require.NoError(s.t, err) } +func (s *msgSend) RetirementReasonWithLength(a string) { + length, err := strconv.ParseInt(a, 10, 64) + require.NoError(s.t, err) + + s.msg.Credits[0].RetirementReason = strings.Repeat("x", int(length)) +} + func (s *msgSend) TheMessageIsValidated() { s.err = s.msg.ValidateBasic() } diff --git a/x/ecocredit/base/types/v1/types_batch_issuance.go b/x/ecocredit/base/types/v1/types_batch_issuance.go index ddedbd7ad6..3c861e9ef5 100644 --- a/x/ecocredit/base/types/v1/types_batch_issuance.go +++ b/x/ecocredit/base/types/v1/types_batch_issuance.go @@ -3,6 +3,8 @@ package v1 import ( "cosmossdk.io/errors" + "github.com/regen-network/regen-ledger/x/ecocredit" + sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" @@ -36,6 +38,10 @@ func (i *BatchIssuance) Validate() error { if err = base.ValidateJurisdiction(i.RetirementJurisdiction); err != nil { return sdkerrors.ErrInvalidRequest.Wrapf("retirement jurisdiction: %s", err) } + + if len(i.RetirementReason) > base.MaxNoteLength { + return ecocredit.ErrMaxLimit.Wrapf("retirement reason: max length %d", base.MaxNoteLength) + } } } diff --git a/x/ecocredit/base/types/v1/types_batch_issuance_test.go b/x/ecocredit/base/types/v1/types_batch_issuance_test.go index 75f433cc11..24bd0a645b 100644 --- a/x/ecocredit/base/types/v1/types_batch_issuance_test.go +++ b/x/ecocredit/base/types/v1/types_batch_issuance_test.go @@ -1,6 +1,8 @@ package v1 import ( + "strconv" + "strings" "testing" "github.com/gogo/protobuf/jsonpb" @@ -28,6 +30,13 @@ func (s *batchIssuance) TheBatchIssuance(a gocuke.DocString) { require.NoError(s.t, err) } +func (s *batchIssuance) RetirementReasonWithLength(a string) { + length, err := strconv.ParseInt(a, 10, 64) + require.NoError(s.t, err) + + s.issuance.RetirementReason = strings.Repeat("x", int(length)) +} + func (s *batchIssuance) TheBatchIssuanceIsValidated() { s.err = s.issuance.Validate() } diff --git a/x/ecocredit/basket/types/v1/features/msg_take.feature b/x/ecocredit/basket/types/v1/features/msg_take.feature index 4a51a9dab5..a1b5ed1dc3 100644 --- a/x/ecocredit/basket/types/v1/features/msg_take.feature +++ b/x/ecocredit/basket/types/v1/features/msg_take.feature @@ -156,6 +156,21 @@ Feature: MsgTake When the message is validated Then expect no error + Scenario: an error is returned if retirement reason exceeds 512 characters + Given the message + """ + { + "owner": "regen1elq7ys34gpkj3jyvqee0h6yk4h9wsfxmgqelsw", + "basket_denom": "eco.uC.NCT", + "amount": "100", + "retirement_jurisdiction": "US-WA", + "retire_on_take": true + } + """ + And retirement reason with length "513" + When the message is validated + Then expect the error "retirement reason: max length 512: limit exceeded" + Scenario: a valid amino message Given the message """ diff --git a/x/ecocredit/basket/types/v1/msg_take.go b/x/ecocredit/basket/types/v1/msg_take.go index ec30492b3b..0657c75013 100644 --- a/x/ecocredit/basket/types/v1/msg_take.go +++ b/x/ecocredit/basket/types/v1/msg_take.go @@ -4,6 +4,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx" + "github.com/regen-network/regen-ledger/x/ecocredit" "github.com/regen-network/regen-ledger/x/ecocredit/basket" @@ -64,6 +65,10 @@ func (m MsgTake) ValidateBasic() error { return sdkerrors.ErrInvalidRequest.Wrapf("retirement jurisdiction: %s", err) } } + + if len(m.RetirementReason) > base.MaxNoteLength { + return ecocredit.ErrMaxLimit.Wrapf("retirement reason: max length %d", base.MaxNoteLength) + } } return nil diff --git a/x/ecocredit/basket/types/v1/msg_take_test.go b/x/ecocredit/basket/types/v1/msg_take_test.go index 70dcf2595d..1e616ca15d 100644 --- a/x/ecocredit/basket/types/v1/msg_take_test.go +++ b/x/ecocredit/basket/types/v1/msg_take_test.go @@ -3,6 +3,8 @@ package v1 import ( "bytes" "encoding/json" + "strconv" + "strings" "testing" "github.com/gogo/protobuf/jsonpb" @@ -31,6 +33,13 @@ func (s *msgTakeSuite) TheMessage(a gocuke.DocString) { require.NoError(s.t, err) } +func (s *msgTakeSuite) RetirementReasonWithLength(a string) { + length, err := strconv.ParseInt(a, 10, 64) + require.NoError(s.t, err) + + s.msg.RetirementReason = strings.Repeat("x", int(length)) +} + func (s *msgTakeSuite) TheMessageIsValidated() { s.err = s.msg.ValidateBasic() } diff --git a/x/ecocredit/marketplace/types/v1/features/msg_buy_direct.feature b/x/ecocredit/marketplace/types/v1/features/msg_buy_direct.feature index 2c531f128f..efb6fc4474 100644 --- a/x/ecocredit/marketplace/types/v1/features/msg_buy_direct.feature +++ b/x/ecocredit/marketplace/types/v1/features/msg_buy_direct.feature @@ -300,6 +300,28 @@ Feature: MsgBuyDirect When the message is validated Then expect the error "orders[0]: retirement jurisdiction: expected format [-[ ]]: parse error: invalid request" + Scenario: an error is returned if disable auto-retire is true and retirement reason exceeds 512 characters + Given the message + """ + { + "buyer": "regen1elq7ys34gpkj3jyvqee0h6yk4h9wsfxmgqelsw", + "orders": [ + { + "sell_order_id": 1, + "quantity": "100", + "bid_price": { + "denom": "regen", + "amount": "100" + }, + "retirement_jurisdiction": "US-WA" + } + ] + } + """ + And retirement reason with length "513" + When the message is validated + Then expect the error "orders[0]: retirement reason: max length 512: limit exceeded" + Scenario: a valid amino message Given the message """ diff --git a/x/ecocredit/marketplace/types/v1/msg_buy_direct.go b/x/ecocredit/marketplace/types/v1/msg_buy_direct.go index 2f07359260..ee143c2ad6 100644 --- a/x/ecocredit/marketplace/types/v1/msg_buy_direct.go +++ b/x/ecocredit/marketplace/types/v1/msg_buy_direct.go @@ -6,6 +6,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx" + "github.com/regen-network/regen-ledger/x/ecocredit" "github.com/regen-network/regen-ledger/types/math" "github.com/regen-network/regen-ledger/x/ecocredit/base" @@ -71,6 +72,10 @@ func (m MsgBuyDirect) ValidateBasic() error { if err := base.ValidateJurisdiction(order.RetirementJurisdiction); err != nil { return sdkerrors.ErrInvalidRequest.Wrapf("%s: retirement jurisdiction: %s", orderIndex, err) } + + if len(order.RetirementReason) > base.MaxNoteLength { + return ecocredit.ErrMaxLimit.Wrapf("%s: retirement reason: max length %d", orderIndex, base.MaxNoteLength) + } } } diff --git a/x/ecocredit/marketplace/types/v1/msg_buy_direct_test.go b/x/ecocredit/marketplace/types/v1/msg_buy_direct_test.go index 5d4e9747d5..b5bf2603cb 100644 --- a/x/ecocredit/marketplace/types/v1/msg_buy_direct_test.go +++ b/x/ecocredit/marketplace/types/v1/msg_buy_direct_test.go @@ -3,6 +3,8 @@ package v1 import ( "bytes" "encoding/json" + "strconv" + "strings" "testing" "github.com/gogo/protobuf/jsonpb" @@ -31,6 +33,13 @@ func (s *msgBuyDirectSuite) TheMessage(a gocuke.DocString) { require.NoError(s.t, err) } +func (s *msgBuyDirectSuite) RetirementReasonWithLength(a string) { + length, err := strconv.ParseInt(a, 10, 64) + require.NoError(s.t, err) + + s.msg.Orders[0].RetirementReason = strings.Repeat("x", int(length)) +} + func (s *msgBuyDirectSuite) TheMessageIsValidated() { s.err = s.msg.ValidateBasic() }