Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Value Renderer for repeated #13604

Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 11 additions & 8 deletions docs/architecture/adr-050-sign-mode-textual-annex1.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,20 +62,23 @@ Value Renderers describe how values of different Protobuf types should be encode
- A repeated type has the following template:

```
<message_name> has <int> <field_name>
<field_name> (<int>/<int>): <value rendered 1st line>
<field_name>: <int> <field_kind>
<field_name> (<index>/<int>): <value rendered 1st line>
<optional value rendered in the next lines>
<field_name> (<int>/<int>): <value rendered 1st line>
<field_name> (<index>/<int>): <value rendered 1st line>
<optional value rendered in the next lines>
End of <field_name>.
```

where:

- `message_name` is the name of the Protobuf message which holds the `repeated` field,
- `int` is the length of the array,
- `field_name` is the Protobuf field name of the repeated field,
- add an optional `s` at the end if `<int> > 1` and the `field_name` doesn't already end with `s`.
- `field_name` is the Protobuf field name of the repeated field
- `field_kind`:
- if the type of the repeated field is a message, `field_kind` is the message name
- if the type of the repeated field is an enum, `field_kind` is the enum name
- in any other case, `field_kind` is the protobuf primitive type (e.g. "string" or "bytes")
- `int` is the length of the array
- `index` is one based index of the repeated field

#### Examples

Expand Down Expand Up @@ -150,7 +153,7 @@ we get the following encoding for the `Vote` message:
```
Vote object
> Proposal id: 4
> Vote: cosmos1abc...def
> Voter: cosmos1abc...def
> Options: 2 WeightedVoteOptions
> Options (1/2): WeightedVoteOption object
>> Option: VOTE_OPTION_YES
Expand Down
161 changes: 161 additions & 0 deletions tx/textual/internal/testdata/repeated.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
[
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add these test cases to message.json instead of using a separate file - allows us to reuse the same test driver code.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I agree with this one. I still think the reason we put repeated logic in messageValueRenderer is an implementation detail. Conceptually, I prefer them to be 2 different value renderers. I'd love to know how JS implements this.

{
"proto": {},
"parses": true,
"screens": [
{"text": "Qux object", "indent": 0}
]
},
{
"proto": {
"messages": [
{
"full_name": "thing one",
"nickname": ":thing two"
}
]
},
"parses": true,
"screens": [
{"text": "Qux object", "indent": 0},
{"text": "Messages: 1 Foo", "indent": 1},
{"text": "Messages (1/1): Foo object", "indent": 2},
{"text": "Full name: thing one", "indent": 3},
{"text": "Nickname: :thing two", "indent": 3},
{"text": "End of Messages", "indent": 1}
]
},
{
"proto": {
"messages": [
{
"full_name": "thing one",
"nickname": "thing two"
},
{
"full_name": "thing three",
"nickname": "thing four"
}
]
},
"parses": true,
"screens": [
{"text": "Qux object", "indent": 0},
{"text": "Messages: 2 Foo", "indent": 1},
joeabbey marked this conversation as resolved.
Show resolved Hide resolved
{"text": "Messages (1/2): Foo object", "indent": 2},
{"text": "Full name: thing one", "indent": 3},
{"text": "Nickname: thing two", "indent": 3},
{"text": "Messages (2/2): Foo object", "indent": 2},
{"text": "Full name: thing three", "indent": 3},
{"text": "Nickname: thing four", "indent": 3},
{"text": "End of Messages", "indent": 1}

]
},
{
"proto": {
"string_messages": [
"/OmniFlix.onft.v1beta1.MsgTransferONFT"
]
},
"parses": true,
"screens": [
{"text": "Qux object", "indent": 0},
{"text": "String messages: 1 String", "indent": 1},
{"text": "String messages (1/1): /OmniFlix.onft.v1beta1.MsgTransferONFT", "indent": 2},
{"text": "End of String messages", "indent": 1}
]
},
{
"proto": {
"string_messages": [
"/OmniFlix.onft.v1beta1.MsgTransferONFT",
"/OmniFlix.onft.v1beta1.MsgBurnONFT",
"/OmniFlix.marketplace.v1beta1.MsgListNFT"
]
},
"parses": true,
"screens": [
{"text": "Qux object", "indent": 0},
{"text": "String messages: 3 String", "indent": 1},
{"text": "String messages (1/3): /OmniFlix.onft.v1beta1.MsgTransferONFT", "indent": 2},
{"text": "String messages (2/3): /OmniFlix.onft.v1beta1.MsgBurnONFT", "indent": 2},
{"text": "String messages (3/3): /OmniFlix.marketplace.v1beta1.MsgListNFT", "indent": 2},
{"text": "End of String messages", "indent": 1}
]
},
{
"proto": {
"vote" : {
"proposal_id": 4,
"voter": "cosmos1abc...def",
"options": [
{
"option": "Yes",
"weight": "0.7"
},
{
"option": "No",
"weight": "0.3"
}
]
}
},
"parses": false,
"screens": [
{"text": "Qux object", "indent": 0},
{"text": "Vote: Ballot object", "indent": 1},
{"text": "Proposal id: 4", "indent": 2},
{"text": "Voter: cosmos1abc...def", "indent": 2},
{"text": "Options: 2 WeightedBallotOption", "indent": 2},
{"text": "Options (1/2): WeightedBallotOption object", "indent": 3},
{"text": "Option: Yes", "indent": 4},
{"text": "Weight: 0.7", "indent": 4},
{"text": "Options (2/2): WeightedBallotOption object", "indent": 3},
{"text": "Option: No", "indent": 4},
{"text": "Weight: 0.3", "indent": 4},
{"text": "End of Options", "indent": 2}
]
},
{
"proto": {
"price": [
{ "amount": "1", "denom": "ucosm" },
{ "amount": "3", "denom": "ustake" }
]
},
"parses": false,
"screens": [
{"text": "Qux object", "indent": 0},
{"text": "Price: 1 ucosm, 3 ustake", "indent": 1}
]
},
{
"proto": {
"expirations": [
{"seconds": 0, "nanos": 1},
{"seconds": 0, "nanos": 10},
{"seconds": 0, "nanos": 100},
{"seconds": 0, "nanos": 1000},
{"seconds": 0, "nanos": 10000},
{"seconds": 0, "nanos": 100000},
{"seconds": 0, "nanos": 1000000},
{"seconds": 0, "nanos": 10000000}
]
},
"parses": true,
"screens": [
{"text": "Qux object", "indent": 0},
{"text": "Expirations: 8 Timestamp", "indent": 1},
{"text": "Expirations (1/8): 1970-01-01T00:00:00.000000001Z", "indent": 2},
{"text": "Expirations (2/8): 1970-01-01T00:00:00.00000001Z", "indent": 2},
{"text": "Expirations (3/8): 1970-01-01T00:00:00.0000001Z", "indent": 2},
{"text": "Expirations (4/8): 1970-01-01T00:00:00.000001Z", "indent": 2},
{"text": "Expirations (5/8): 1970-01-01T00:00:00.00001Z", "indent": 2},
{"text": "Expirations (6/8): 1970-01-01T00:00:00.0001Z", "indent": 2},
{"text": "Expirations (7/8): 1970-01-01T00:00:00.001Z", "indent": 2},
{"text": "Expirations (8/8): 1970-01-01T00:00:00.01Z", "indent": 2},
{"text": "End of Expirations", "indent": 1}
]
}
]
102 changes: 63 additions & 39 deletions tx/textual/internal/testpb/1.proto
Original file line number Diff line number Diff line change
@@ -1,57 +1,57 @@
syntax = "proto3";

option go_package = "cosmossdk.io/tx/textual/internal/testpb";

import "cosmos/base/v1beta1/coin.proto";
import "cosmos_proto/cosmos.proto";
import "google/protobuf/any.proto";
import "google/protobuf/descriptor.proto";
import "google/protobuf/duration.proto";
import "google/protobuf/timestamp.proto";
import "cosmos_proto/cosmos.proto";
import "cosmos/base/v1beta1/coin.proto";

option go_package = "cosmossdk.io/tx/textual/internal/testpb";

// A is used for testing value renderers.
message A {
// Fields that are parseable by SIGN_MODE_TEXTUAL.
uint32 UINT32 = 1;
uint64 UINT64 = 2;
int32 INT32 = 3;
int64 INT64 = 4;
string SDKINT = 5 [(cosmos_proto.scalar) = "cosmos.Int"];
string SDKDEC = 6 [(cosmos_proto.scalar) = "cosmos.Dec"];
cosmos.base.v1beta1.Coin COIN = 7;
repeated cosmos.base.v1beta1.Coin COINS = 8;
bytes BYTES = 9;
google.protobuf.Timestamp TIMESTAMP = 10;
google.protobuf.Duration DURATION = 11;
ExternalEnum ENUM = 12;
google.protobuf.Any ANY = 13;
uint32 UINT32 = 1;
uint64 UINT64 = 2;
int32 INT32 = 3;
int64 INT64 = 4;
string SDKINT = 5 [(cosmos_proto.scalar) = "cosmos.Int"];
string SDKDEC = 6 [(cosmos_proto.scalar) = "cosmos.Dec"];
cosmos.base.v1beta1.Coin COIN = 7;
repeated cosmos.base.v1beta1.Coin COINS = 8;
bytes BYTES = 9;
google.protobuf.Timestamp TIMESTAMP = 10;
google.protobuf.Duration DURATION = 11;
ExternalEnum ENUM = 12;
google.protobuf.Any ANY = 13;

// Fields that are not handled by SIGN_MODE_TEXTUAL.
sint32 SINT32 = 101;
sint64 SINT64 = 102;
sfixed32 SFIXED32 = 105;
fixed32 FIXED32 = 106;
float FLOAT = 107;
sfixed64 SFIXED64 = 108;
fixed64 FIXED64 = 109;
double DOUBLE = 110;
map<string, A> MAP = 111;
sint32 SINT32 = 101;
sint64 SINT64 = 102;
sfixed32 SFIXED32 = 105;
fixed32 FIXED32 = 106;
float FLOAT = 107;
sfixed64 SFIXED64 = 108;
fixed64 FIXED64 = 109;
double DOUBLE = 110;
map<string, A> MAP = 111;
}

// Foo is a sample message type used for testing message rendering.
message Foo {
string full_name = 1;
string nickname = 2;
google.protobuf.Timestamp mtime = 3;
Foo left = 4;
Foo right = 5;
Bar bar = 8; // skip some field numbers
string full_name = 1;
string nickname = 2;
google.protobuf.Timestamp mtime = 3;
Foo left = 4;
Foo right = 5;
Bar bar = 8; // skip some field numbers
}

// Bar is a sample message type used for testing message rendering.
message Bar {
string bar_id = 1;
bytes data = 2;
string bar_id = 1;
bytes data = 2;
google.protobuf.Any payload = 3;
}

Expand All @@ -71,13 +71,37 @@ message Baz {
ExternalEnum ee = 1;
Internal_Enum ie = 2;
BallotOption option = 3;

}

enum BallotOption {
BALLOT_OPTION_UNSPECIFIED = 0;
BALLOT_OPTION_YES = 1;
BALLOT_OPTION_ABSTAIN = 2;
BALLOT_OPTION_NO = 3;
BALLOT_OPTION_UNSPECIFIED = 0;
BALLOT_OPTION_YES = 1;
BALLOT_OPTION_ABSTAIN = 2;
BALLOT_OPTION_NO = 3;
BALLOT_OPTION_NO_WITH_VETO = 4;
}

// Qux is a sample message type used for testing repeated rendering.
message Qux {
repeated Foo messages = 1;
repeated string string_messages = 2;
Ballot vote = 3;
repeated cosmos.base.v1beta1.Coin price = 4;
repeated google.protobuf.Timestamp expirations = 5;
}

message WeightedBallotOption {
// TODO: Enumeration rendering
// BallotOption option = 1;
string option = 1;
string weight = 2 [(cosmos_proto.scalar) = "cosmos.Dec"];
}

message Ballot {
uint64 proposal_id = 1;
// TODO: cosmos.AddressString rendering
// string voter = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"];
string voter = 2;
reserved 3;
repeated WeightedBallotOption options = 4;
}
Loading