Skip to content
Discussions/App Development/Constructing a gRPC exercise command by HandForum ↗

Constructing a gRPC exercise command by Hand

App Development10 posts505 views5 likesLast activity Oct 2024
DA
DarkoOP
Jan 2023

Hi all,

I’m trying to construct an exercise command by hand and send it to the LedgerAPI using Postman.
Unfortunately, I’m getting an error, and I was hoping someone could point me to what’s going wrong.

The choice I want to exercise is:

choice CreateProposals : (ContractId TransactionManifest, [ContractId TransferProposal])
  with
    messageIdToLegs: [(Text, Leg)]
    assembler: Party

data Leg = Leg with
    legPayload: Text
    approversToSettlementSteps: [(Party, SettlementStep)]

data SettlementStep = SettlementStep with
    sender: Optional Text -- sender account iban
    receiver: Optional Text -- receiver account iban
    delivery: Instrument

data Instrument = Instrument with
    amount: Decimal
    label: Text

This is what I came up with:

{
    "commands": {
        "act_as": [
            "scheduler::12203a80aea09ceaca412f7ae3d5a7a0ebba76cf78b352a2b450d566c445cabb40f5"
        ],
        "application_id": "app",
        "command_id": "myId",
        "ledger_id": "sandbox",
        "party": "scheduler::12203a80aea09ceaca412f7ae3d5a7a0ebba76cf78b352a2b450d566c445cabb40f5",
        "submission_id": "subId",
        "workflow_id": "workflowId",
        "commands": [
            {
                "exercise": {
                    "choice": "CreateProposals",
                    "template_id": {
                        "package_id": "1ff2164777e6e7bc31a87b53c8301743a8594fd03c4c92abbbc7b57bb63c309b",
                        "entity_name": "InitiateTransfer",
                        "module_name": "Workflow.InitiateTransfer"
                    },
                    "choice_argument": {
                        "assembler": "assembler::12203a80aea09ceaca412f7ae3d5a7a0ebba76cf78b352a2b450d566c445cabb40f5",
                        "messageIdToLegs": {
                            "elements": [
                                "MessageId",
                                {
                                    "legPayload": "PAYLOAD",
                                    "approversToSettlementSteps": {
                                        "elements": [
                                            "bankA::12203a80aea09ceaca412f7ae3d5a7a0ebba76cf78b352a2b450d566c445cabb40f5",
                                            {
                                                "sender": "SenderIBAN",
                                                "receiver": "ReceiverIBAN",
                                                "delivery": {
                                                    "amount": "100.0",
                                                    "label": "USD"
                                                }
                                            }
                                        ]

                                    }
                                }
                            ]
                        }
                    },
                    "contract_id": "00daa8c866c648e13a28797b964f07208077bcff9e9af2099134700ce8f63bcd29ca01122027171d4055492024a620041a6a70f0b52bb97a1db5db191e6b193fecf4362845"
                }
            }
        ]
    }
}

The error message I received was

MISSING_FIELD(8,subId): The submitted command is missing a mandatory field: value

Unfortunately I don’t know where value is expected.

I constructed this message by using postman’s “generate example message” and looking at Ledger API Reference — Daml SDK 2.5.0 documentation

Thank you for your help.

Best,
darko

DA
Darko
Jan 2023

Also, is there a simple config setting for the sandbox / ledger API, such that it prints all commands it receives into the logs? Then I could simply grab it from there after executing the exercise through Navigator.

WA
WallaceKelly
Jan 2023

FYI… I found gRPC UI to be very helpful for assembling gRPC payloads.

CO
cocreature
Jan 2023

The protobuf encoding is a lot more explicit than in the JSON API. You have to explicitly specify types everywhere. Here is an example of a record with two fields where the first is a single contract id and the second is a list of contract ids:

choice_argument {
  record {
    fields {
      label: "a"
      value {
        contract_id: "0081e0cdddb59f9d2c6e8bdd2eb896d792486e6e96c24fcdeac629d3b5d0ad4b77ca011220a280cc011b9eccf9153575c92710bb4e429af1f109d5e957f68d5ac0a598d096"
      }
    }
    fields {
      label: "b"
      value {
        list {
          elements {
            contract_id: "004327202b9208d246ce8c01f336eafafdfc73c5882f48c3dec148d6246dbb62daca0112205c559083e0a5eb7e5d8c2921cc7f9b8dca38b7a2d63ab40556d9222e4549c1fa"
          }
          elements {
            contract_id: "00f757f7309da7637fa27af2786f513ca80cd7085fb39a6fbdab7ef1f825b7f312ca01122022d1fefd3109b720c376af67b6897489387a5d0bce785fb3a797dc68ec1f28e9"
          }
          elements {
            contract_id: "00ecdb3f29c63443aecd79086064a459848ec3935f1a1260db9199b2b3d7dc950eca011220373d0ed93aea51ac457bf884e132ef759b55bf5586c37263a88c3ef991b10aa3"
          }
        }
      }
    }
  }
}

Should be relatively easy to see hopefully how that matches up with the corresponding protobuf in daml/value.proto at 8f5b25fc1f80dd7baee671ed981b3b076a9e573b · digital-asset/daml · GitHub

As for your question of seeing request payloads, Canton has options for detailed logs that will show you exactly that. That’s actually where I copied the above from.

ST
stefanobaghino-da
Jan 2023
Darko:

Unfortunately I don’t know where value is expected.

Looking at the code (here and here) it looks like you might be missing something in a record or variant. It’s weird that this points to subId apparently though.

Darko:

Also, is there a simple config setting for the sandbox / ledger API, such that it prints all commands it receives into the logs? Then I could simply grab it from there after executing the exercise through Navigator.

The commands are added to the logging context as soon as they arrive on the Ledger API server and will be printed alongside every log entry related to that command submission. I’m not sure whether the default logger config for the sandbox is configured to display the logging context though – if it’s not, you can touch up the Logback configuration file to make sure that the %marker (which carries the logging context) is printed alongside the rest. Here is the default Logback file for the HTTP JSON API Service for reference (which prints the context):

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <if condition='isDefined("LOG_FORMAT_JSON")'>
            <then>
                <encoder class="net.logstash.logback.encoder.LogstashEncoder"/>
            </then>
            <else>
                <!-- encoders are assigned the type
                    ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
                <encoder>
                    <pattern>%date{"dd-MM-yyyy HH:mm:ss.SSS", UTC} [%thread] %-5level %logger{36} - %msg%replace(, context: %marker){', context: $', ''} %n</pattern>
                </encoder>
            </else>
        </if>
    </appender>

    <logger name="io.netty" level="WARN" />
    <logger name="io.grpc.netty" level="WARN" />
    <logger name="ch.qos.logback" level="WARN" />

    <root level="INFO">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>

If you don’t have Commands are also printed straight out to the output if you set the logging level to TRACE. That might be easier to set up but you are probably going to get a lot more logging output to search through (source).

DA
Darko
Jan 2023

Thanks a lot for everyone’s input. I’ve made some progress, but I’m again hitting the same error.

Can someone please verify if the encoding of the Optional Text type is correct? My understanding was, that it should look like this:

"optional": {
  "value": {
    "text": "mySomeValue"
  }
}

What wasn’t clear to me from looking at the protos was how I would encode a None.

CO
cocreature
Jan 2023

That looks right. To encode None, simply omit the value field. All fields of message types are optional in protobuf.

"optional": {
}
DA
Darko
Jan 2023

Thank you!

Phew, that was a tough one.
If someone in the future is looking for a slightly bigger example, here is a successful encoding of the choice CreateProposals as defined as follows:

choice CreateProposals : (ContractId TransactionManifest, [ContractId TransferProposal])
  with
    messageIdToLegs: [(Text, Leg)]
    assembler: Party

data Leg = Leg with
    legPayload: Text
    approversToSettlementSteps: [(Party, SettlementStep)]

data SettlementStep = SettlementStep with
    sender: Optional Text -- sender account iban
    receiver: Optional Text -- receiver account iban
    delivery: Instrument

data Instrument = Instrument with
    amount: Decimal
    label: Text

The json encoded body of the gRPC message looks like this (example running on my sandbox with the partyIds allocated as part of a startup script).

{
  "commands": {
    "act_as": [
      "scheduler::1220682c7ac95e8397a6ef0461c25d7b3a6205c66a07331f451e5da7ffa88988cbbe"
    ],
    "application_id": "app",
    "command_id": "myCommandId",
    "ledger_id": "sandbox",
    "submission_id": "submissionId",
    "workflow_id": "workflowId",
    "commands": [
      {
        "exercise": {
          "choice": "CreateProposals",
          "template_id": {
            "package_id": "1ff2164777e6e7bc31a87b53c8301743a8594fd03c4c92abbbc7b57bb63c309b",
            "entity_name": "InitiateTransfer",
            "module_name": "Workflow.InitiateTransfer"
          },
          "contract_id": "00d2b098ecd5dd550d59422ab03cacd0d2e4a458fda7180ff33feed246ce494eadca011220b087ca031bda84190981872bca256949491bea2d8faaec288a2ae6fda4e96e34",
          "choice_argument": {
            "record": {
              "fields": [
                {
                  "label": "assembler",
                  "value": {
                    "party": "assembler::1220682c7ac95e8397a6ef0461c25d7b3a6205c66a07331f451e5da7ffa88988cbbe"
                  }
                },
                {
                  "label": "messageIdToLegs",
                  "value": {
                    "list": {
                      "elements": [
                        {
                          "record": {
                            "fields": [
                              {
                                "label": "_1",
                                "value": {
                                  "text": "myMessageIdToLegs"
                                }
                              },
                              {
                                "label": "_2",
                                "value": {
                                  "record": {
                                    "fields": [
                                      {
                                        "label": "legPayload",
                                        "value": {
                                          "text": "myLegPayload"
                                        }
                                      },
                                      {
                                        "label": "approversToSettlementSteps",
                                        "value": {
                                          "list": {
                                            "elements": [
                                              {
                                                "record": {
                                                  "fields": [
                                                    {
                                                      "label": "_1",
                                                      "value": {
                                                        "party": "bankB::1220682c7ac95e8397a6ef0461c25d7b3a6205c66a07331f451e5da7ffa88988cbbe"
                                                      }
                                                    },
                                                    {
                                                      "label": "_2",
                                                      "value": {
                                                        "record": {
                                                          "fields": [
                                                            {
                                                              "label": "sender",
                                                              "value": {
                                                                "optional": {
                                                                  "value": {
                                                                    "text": "mySenderAcc"
                                                                  }
                                                                }
                                                              }
                                                            },
                                                            {
                                                              "label": "receiver",
                                                              "value": {
                                                                "optional": {
                                                                  "value": {
                                                                    "text": "myReceiverAcc"
                                                                  }
                                                                }
                                                              }
                                                            },
                                                            {
                                                              "label": "delivery",
                                                              "value": {
                                                                "record": {
                                                                  "fields": [
                                                                    {
                                                                      "label": "amount",
                                                                      "value": {
                                                                        "numeric": "123.0"
                                                                      }
                                                                    },
                                                                    {
                                                                      "label": "label",
                                                                      "value": {
                                                                        "text": "myUSD"
                                                                      }
                                                                    }
                                                                  ]
                                                                }
                                                              }
                                                            }
                                                          ]
                                                        }
                                                      }
                                                    }
                                                  ]
                                                }
                                              }
                                            ]
                                          }
                                        }
                                      }
                                    ]
                                  }
                                }
                              }
                            ]
                          }
                        }
                      ]
                    }
                  }
                }
              ]
            }
          }
        }
      }
    ]
  }
}

ST
stefanobaghino-da
Jan 2023
Darko:
                                                                      }
                                                                    }
                                                                  ]
                                                                }
                                                              }
                                                            }
                                                          ]
                                                        }
                                                      }
                                                    }
                                                  ]
                                                }
                                              }
                                            ]
                                          }
                                        }
                                      }
                                    ]
                                  }
                                }
                              }
                            ]
                          }
                        }
                      ]
                    }
                  }
                }
              ]
            }
          }
        }
      }
    ]
  }
}

And this is why we offer bindings with codegen. :wink:

WA
WallaceKelly
Oct 2024

Dear Future Self,

If you get this error message…

MISSING_FIELD(8,subId): The submitted command is missing a mandatory field: value

… for something like this…

"fields": [
  {
    "label": "operator",
    "value:": {
      "party": "operator::12200...1de9f"
    }
  },

…check that you’re not doing this…

"value:" vs. "value":

:man_facepalming:

← Back to Discussions