Update GRIDS

2026-05-13 16:30:10 +09:00
parent 70643184c3
commit fbd888aa6e
+254 -271
@@ -1,271 +1,254 @@
# GRIDS: Gajumaru Remote Instruction Dispatch System v1
# GRIDS: Gajumaru Remote Instruction Dispatch System v1
- **Document Created**: 2025-01-17
- **Document Created**: 2025-01-17 - **Document Last Modified**: 2025-12-08
- **Document Last Modified**: 2025-12-08 - **Document Author**: Craig Everett `<craigeverett@qpq.swiss>`
- **Document Author**: Craig Everett `<craigeverett@qpq.swiss>` - **Document Version**: 3 (For Implementation)
- **Document Version**: 3 (For Implementation) - **Wiki Page Created**: 2026-05-02
- **Wiki Page Created**: 2026-05-02 - **Wiki Page Last Modified**: 2026-05-02
- **Wiki Page Last Modified**: 2026-05-02 - **Wiki Page Author**: Peter Harpending `<peterharpending@qpq.swiss>`
- **Wiki Page Author**: Peter Harpending `<peterharpending@qpq.swiss>` - **Notes**: Copied from random PDF I (PRH) found on my computer written by
- **Notes**: Copied from random PDF I (PRH) found on my computer written by Craig, dated 2025-01-17.
Craig, dated 2025-01-17.
The GRIDS protocol defines methods for encoding three basic actions within the
The GRIDS protocol defines methods for encoding three basic actions within the Gajumaru ecosystem:
Gajumaru ecosystem:
1. A method for encoding "spend" transactions in a URL where the host section
1. A method for encoding "spend" transactions in a URL where the host section indicates a specific endpoint node to use, the required network ID being
indicates a specific endpoint node to use, the required network ID being then queried from the endpoint.
then queried from the endpoint. 2. A method for encoding "transfer" transactions in a URL where the host
2. A method for encoding "transfer" transactions in a URL where the host section indicates a network to use but leaves resolving an endpoint node up
section indicates a network to use but leaves resolving an endpoint node up to the client.
to the client. 3. A method for performing digital "dead drop" signatures, queuing signature
3. A method for performing digital "dead drop" signatures, queuing signature requests on a server, retrieving them with a client, and returning the
requests on a server, retrieving them with a client, and returning the resulting signed output to the server.
resulting signed output to the server.
## The Problem
## The Problem
In computing there is a strong tradeoff between usability and security.
In computing there is a strong tradeoff between usability and security. In the In the cryptocurrency world this is compounded by the inherent complexity of the systems involved.
cryptocurrency world this is compounded by the inherent complexity of the This has resulted in a common pattern of developers coding wallet features directly into the local execution environments of the applications they wish to integrate with a cryptocurrency system (typically a blockchain).
systems involved. This has resulted in a common pattern of developers coding
wallet features directly into the local execution environments of the For example, it is common for games based around on-chain tokens to integrate wallet features directly into the game client itself.
applications they wish to integrate with a cryptocurrency system (typically a This prevents the user from having to leave the game, open another application (their wallet), sign a transaction, and then return to the game.
blockchain).
As another example, it is overwhelmingly common for developers to write wallets as browser plugins that interact directly with in-page applications running in the same browser.
For example, it is common for games based around on-chain tokens to integrate
wallet features directly into the game client itself. This prevents the user In both cases the incentives for bad actors to compromise the execution environment itself in order to capture private key data or seed phrases is overwhelming.
from having to leave the game, open another application (their wallet), sign a Separation of signature and app execution context is needed.
transaction, and then return to the game. Separating the execution context requires a way for apps requesting signatures and apps capable of providing signatures to communicate not only across execution environments on a single system, but also across completely separate systems.
As another example, it is overwhelmingly common for developers to write GRIDS defines a way to to combine communication of limited, known transaction types (such as simple spends) with a way to convey arbitrarily complex transaction data that may require out-of-band communication with a signature client.
wallets as browser plugins that interact directly with in-page applications
running in the same browser. ## The Schema
In both cases the incentives for bad actors to compromise the execution The GRIDS schema is very similar to HTTP/HTTPS, and even translates directly to an HTTP request URL in the case of a dead-drop instruction.
environment itself in order to capture private key data or seed phrases is
overwhelming. Separation of signature and app execution context is needed. The basic schema is:
Separating the execution context requires a way for apps requesting signatures
and apps capable of providing signatures to communicate not only across ```
execution environments on a single system, but also across completely separate [grid(s)]://[host]/[version]/[instruction]/[path](?a=[amount]&p=[payload])
systems. ```
GRIDS defines a way to to combine communication of limited, known transaction Where:
types (such as simple spends) with a way to convey arbitrarily complex
transaction data that may require out-of-band communication with a signature - The schema identifier is either `grids` or `grid`. If the instruction is a
client. dead-drop signature then the schema may be `grid` to indicate that the
initial pick up request must be made over HTTP rather than HTTPS. If the
## The Schema instruction is not a dead-drop, then there is no difference between the two.
- The `host` element is interpreted differently based on instruction type:
The GRIDS schema is very similar to HTTP/HTTPS, and even translates directly - For `s` (short for "spend") it is the network ID where the spend is to
to an HTTP request URL in the case of a dead-drop instruction. take place.
- For `t` (short for "transfer") it is a `host:port` location of a node
The basic schema is: where the client can retrieve the network ID.
- For `d` (short for "dead-drop") it is a `host:port` location of a service
``` where a message to be signed is awaiting pickup.
[grid(s)]://[host]/[version]/[instruction]/[path](?a=[amount]&p=[payload]) - The `version` element is the protocol version.
``` - As mentioned above, the `instruction` element is one of `s` (for "spend"),
`t` (for "transfer"—same as spend but with different host semantics) or `d`
Where: (for "dead-drop").
- In the case of dead-drops, the `path` element is the HTTP path to the pickup
- The schema identifier is either `grids` or `grid`. If the instruction is a location. In the case of spends/transfers it is the address of the party to
dead-drop signature then the schema may be `grid` to indicate that the be paid, with (optional) amount and payload details packed into query args
initial pick up request must be made over HTTP rather than HTTPS. If the of the forms
instruction is not a dead-drop, then there is no difference between the two.
- The `host` element is interpreted differently based on instruction type: - `a=[amount]` denominated in pucks, represented in the numerals `[0-9]`
- For `s` (short for "spend") it is the network ID where the spend is to - `p=[payload]` represented as URL-encoded binary data
take place.
- For `t` (short for "transfer") it is a `host:port` location of a node ## The simple case: SpendTX
where the client can retrieve the network ID.
- For `d` (short for "dead-drop") it is a `host:port` location of a service Spends are simple on-chain transactions that transfer coins (Gajus/Pucks) from
where a message to be signed is awaiting pickup. one account to another.
- The `version` element is the protocol version.
- As mentioned above, the `instruction` element is one of `s` (for "spend"), Defined as a record, the inner data in a SpendTX looks like this:
`t` (for "transfer"—same as spend but with different host semantics) or `d`
(for "dead-drop"). ```erlang
- In the case of dead-drops, the `path` element is the HTTP path to the pickup -record(spend_tx,
location. In the case of spends/transfers it is the address of the party to {sender_id :: string(),
be paid, with (optional) amount and payload details packed into query args recipient_id :: string(),
of the forms amount :: non_neg_integer(),
gas_price :: pos_integer(),
- `a=[amount]` denominated in pucks, represented in the numerals `[0-9]` gas :: pos_integer(),
- `p=[payload]` represented as URL-encoded binary data ttl :: pos_integer(),
nonce :: pos_integer(),
## The simple case: SpendTX payload :: binary()}).
```
Spends are simple on-chain transactions that transfer coins (Gajus/Pucks) from
one account to another. All elements are required with the exception of the `payload`, which can be
empty. The payload is used to tag spend transactions with meaningful data such
Defined as a record, the inner data in a SpendTX looks like this: as text indicating what a transaction was for or an invoice number useful for
tracking within an accounting system. Elements such as the `gas_price`, `gas`,
```erlang `ttl` and `nonce` are all elements that a client wallet is expected to
-record(spend_tx, populate on its own.
{sender_id :: string(),
recipient_id :: string(), The external data required for a SpendTX is the network ID of the specific
amount :: non_neg_integer(), chain that the spend transaction is intended for. The network ID is part of
gas_price :: pos_integer(), the external encoding of the TX, the encoded representation of the transaction
gas :: pos_integer(), (including the network ID) is what the client ultimately signs. The
ttl :: pos_integer(), combination of the nonce and network ID prevents TXs from being replayed on
nonce :: pos_integer(), the same network or replayed across different networks.
payload :: binary()}).
``` The information required from a party requesting an on-chain payment is
therefore reduced to:
All elements are required with the exception of the `payload`, which can be
empty. The payload is used to tag spend transactions with meaningful data such 1. The account to be paid
as text indicating what a transaction was for or an invoice number useful for 2. The amount
tracking within an accounting system. Elements such as the `gas_price`, `gas`, 3. Any payload desired (an invoice number, for example)
`ttl` and `nonce` are all elements that a client wallet is expected to 4. The network ID or a host address of a node from which the network ID can be
populate on its own. obtained.
The external data required for a SpendTX is the network ID of the specific This data is encoded in a GRIDS URL as noted above.
chain that the spend transaction is intended for. The network ID is part of
the external encoding of the TX, the encoded representation of the transaction GRIDS URLs should be provided to users as both plain text and a QR code to
(including the network ID) is what the client ultimately signs. The make it easy to verify their content as well as make it possible to easily use
combination of the nonce and network ID prevents TXs from being replayed on both desktop (copy-paste) and mobile (camera input) wallet apps.
the same network or replayed across different networks.
### Example Spends
The information required from a party requesting an on-chain payment is
therefore reduced to: Let's say a point-of-sale system at shop needs to charge a customer 25 Gajus.
The POS system knows its own account address for the register, knows the
1. The account to be paid invoice number, knows the amount to charge, and knows that it is operating on
2. The amount the `groot.mainnet` network, but does not know the
3. Any payload desired (an invoice number, for example) customer's account number.
4. The network ID or a host address of a node from which the network ID can be
obtained. Assume the following payment information:
This data is encoded in a GRIDS URL as noted above. - Store account: `ak_srFAGY9Dq6p8LVoPSQ8o86s6EFAqWsXHKLDvpzPFcZ8txtR6U`
- Amount: 25 Gajus
GRIDS URLs should be provided to users as both plain text and a QR code to - Invoice number: `20250117-0001`
make it easy to verify their content as well as make it possible to easily use
both desktop (copy-paste) and mobile (camera input) wallet apps. The POS system can generate a GRIDS request encoded as:
### Example Spends ```
grids://groot.mainnet/1/s/ak_srFAGY9Dq6p8LVoPSQ8o86s6EFAqWsXHKLDvpzPFcZ8txtR6U?a=25000000000000000000&p=20250117-0001
Let's say a point-of-sale system at shop needs to charge a customer 25 Gajus. ```
The POS system knows its own account address for the register, knows the
invoice number, knows the amount to charge, and knows that it is operating on Alternately, if the POS system is operating on an associate chain that isn't as
the `groot.mainnet` network, but does not know the common, or is running its own backend node for efficiency purposes, it could
customer's account number. request a "transfer" instruction, specifying its
node's public address and the wallet can retrieve the network ID from there:
Assume the following payment information:
```
- Store account: `ak_srFAGY9Dq6p8LVoPSQ8o86s6EFAqWsXHKLDvpzPFcZ8txtR6U` grids://some.shop:4321/1/t/ak_srFAGY9Dq6p8LVoPSQ8o86s6EFAqWsXHKLDvpzPFcZ8txtR6U?a=25000000000000000000&p=20250117-0001
- Amount: 25 Gajus ```
- Invoice number: `20250117-0001`
If the node running at `some.shop:4321` is a part of `groot.mainnet`, then
The POS system can generate a GRIDS request encoded as: these two request URLs are effectively identical.
``` ## Dead-Drops
grids://groot.mainnet/1/s/ak_srFAGY9Dq6p8LVoPSQ8o86s6EFAqWsXHKLDvpzPFcZ8txtR6U?a=25000000000000000000&p=20250117-0001
``` The most flexible case for GRIDS is the dead-drop. Using this technique
anything can be communicated to a client, signed, and returned. Dead-drops can
Alternately, if the POS system is operating on an associate chain that isn't as be used for signatures of contract calls, login messages, or any other kind of
common, or is running its own backend node for efficiency purposes, it could message signature one might require.
request a "transfer" instruction, specifying its
node's public address and the wallet can retrieve the network ID from there: As mentioned above, dead-drop GRIDS URLs are converted to HTTP URLs by
stripping off the GRIDS version and instruction tags and reassembling the
``` request as an HTTP or HTTPS request.
grids://some.shop:4321/1/t/ak_srFAGY9Dq6p8LVoPSQ8o86s6EFAqWsXHKLDvpzPFcZ8txtR6U?a=25000000000000000000&p=20250117-0001
``` For example:
If the node running at `some.shop:4321` is a part of `groot.mainnet`, then ```
these two request URLs are effectively identical. grids://foo.com/1/d/some_random_path -> https://foo.com/some_random_path
grid://foo.com/1/d/some_random_path -> http://foo.com/some_random_path
## Dead-Drops ```
The most flexible case for GRIDS is the dead-drop. Using this technique After that conversion takes place, the client makes a GET request to the HTTP
anything can be communicated to a client, signed, and returned. Dead-drops can URL indicated. The response from the server is a JSON object containing the
be used for signatures of contract calls, login messages, or any other kind of unsigned data and some metadata that tells the client what kind of message is
message signature one might require. being signed so that the client can select the correct signature method.
As mentioned above, dead-drop GRIDS URLs are converted to HTTP URLs by After signing, the JSON object is updated with the signed data and the public
stripping off the GRIDS version and instruction tags and reassembling the half of the signing key. The client then submits in a POST request the updated
request as an HTTP or HTTPS request. JSON object back to the original endpoint from which the unsigned data was
retrieved.
For example:
### Signature Request Messages (initial GET)
```
grids://foo.com/1/d/some_random_path -> https://foo.com/some_random_path The response to the initial GET request takes the form of a JSON object with this shape:
grid://foo.com/1/d/some_random_path -> http://foo.com/some_random_path
``` ```json
{"grids" : 1,
After that conversion takes place, the client makes a GET request to the HTTP "chain" : "gajumaru",
URL indicated. The response from the server is a JSON object containing the "network_id" : "groot.mainnet",
unsigned data and some metadata that tells the client what kind of message is "type" : "tx",
being signed so that the client can select the correct signature method. "public_id" : "ak_2SLsxkPavd1oDwUgCyZkMoQJJvMkUWymg1BEK7egx5rVU8xanV",
"payload" : "Any data here"}
After signing, the JSON object is updated with the signed data and the public ```
half of the signing key. The client then submits in a POST request the updated
JSON object back to the original endpoint from which the unsigned data was - The `"grids"` version will always match the version encoded in the request URL.
retrieved. - The `"chain"` element exists in case other projects want to adopt the GRIDS
protocol, in which case the protocol rules of the specific chain family will
### Signature Request Messages (initial GET) dictate things like serialization details.
- The `"network_id"` is necessary for the client (and end-user) to verify that
The response to the initial GET request takes the form of a JSON object with this shape: activity is occurring where expected and binds the resulting signature to
that specific network.
```json - The `"type"` may be `"message"`, `"binary"`, `"tx"` or `"ack"`.
{"grids" : 1, - A `"message"` signature is a UTF-8 string used either for verifiable messages or login schemes.
"chain" : "gajumaru", - A `"binary"` signature is the same as a `"message"` signature, but is performed over binary data encoded as base64.
"network_id" : "groot.mainnet", - A `"tx"` is a request for signature of a transaction (usually call data or contract deployment).
"type" : "tx", - An `"ack"` is an optional response to a `"tx"` or `"message"` response POST.
"public_id" : "ak_2SLsxkPavd1oDwUgCyZkMoQJJvMkUWymg1BEK7egx5rVU8xanV", - The `"public_id"` can either be a public key (on-chain account) or the **boolean** `false` (note absence of quotes).
"payload" : "Any data here"} - If the `public_id` is `false`, then it is up to the client to pick a key to
``` use (and then populate this field).
- If an ID is specified, then the client should select that account or fail if it is not available.
- The `"grids"` version will always match the version encoded in the request URL. - The `"payload"` is the data to be signed. In the case of a
- The `"chain"` element exists in case other projects want to adopt the GRIDS `"tx"` request (a contract call, for example), this data
protocol, in which case the protocol rules of the specific chain family will will be transaction data encoded according to the Gajumaru
dictate things like serialization details. protocol's rules. Any Gajumaru signature routine will
- The `"network_id"` is necessary for the client (and end-user) to verify that understand how to interpret and handle this data. In the
activity is occurring where expected and binds the resulting signature to case of a `"message"` request, this will typically be a
that specific network. simple string in plain text that the client interprets as a
- The `"type"` may be `"message"`, `"tx"` or `"ack"`. A `"message"` signature binary string, prepends the binary string (Erlang binary notation)
is used either for verifiable messages or login schemes. An `"ack"` is an `<<"Gajumaru Signed Message:\n">>`, and then signs.
optional response to a `"tx"` or `"message"` response POST. Prefixing message request payloads with this string prevents
- The `"public_id"` can either be a public key (on-chain account) or the attackers from disguising transaction signature requests as
**boolean** `false` (note absence of quotes). text message signatures.
- If the `public_id` is `false`, then it is up to the client to pick a key to ### Signed Data Messages (subsequent POST)
use (and then populate this field).
- If an ID is specified, then the client should select that account or fail if it is not available. After the client signs the payload, it will populate the
`"public_id"` element (if it was `false` previously) and add
- The `"payload"` is the data to be signed. In the case of a `"signature"` element with the requested signature. The
`"tx"` request (a contract call, for example), this data resulting object that must be POSTed back to the origin is
will be transaction data encoded according to the Gajumaru therefore:
protocol's rules. Any Gajumaru signature routine will
understand how to interpret and handle this data. In the ```json
case of a `"message"` request, this will typically be a {"grids" : 1,
simple string in plain text that the client interprets as a "chain" : "gajumaru",
binary string, appends the prefix (Erlang binary notation) "network_id" : "groot.mainnet",
`<<"Gajumaru Signed Message:\n">>`, and then signs. "type" : "tx",
Prefixing message request payloads with this string prevents "public_id" : "ak_2SLsxkPavd1oDwUgCyZkMoQJJvMkUWymg1BEK7egx5rVU8xanV",
attackers from disguising transaction signature requests as "payload" : "Any data here",
text message signatures. "signature" : "XiuCkEOHINkQN83QUxmIAfmiiRN2XflktnNMtdZDzgW0TQK/YQaurQjfL3R5eoXJvCuUZRgPpKfCvtsXbphDCQ=="}
```
### Signed Data Messages (subsequent POST)
The server (or any other observer of this message) now has
After the client signs the payload, it will populate the enough information to establish for certain whether whoever
`"public_id"` element (if it was `false` previously) and add signed this message has control of the private key that
`"signature"` element with the requested signature. The corresponds to the public key indicated in the message.
resulting object that must be POSTed back to the origin is
therefore: Signing of randomized and timestamped messages can be used to
implement First-Party Single Sign-On (1pSSO) schemes without
```json any requirement that the server side maintain private data.
{"grids" : 1,
"chain" : "gajumaru",
"network_id" : "groot.mainnet",
"type" : "tx",
"public_id" : "ak_2SLsxkPavd1oDwUgCyZkMoQJJvMkUWymg1BEK7egx5rVU8xanV",
"payload" : "Any data here",
"signature" : "XiuCkEOHINkQN83QUxmIAfmiiRN2XflktnNMtdZDzgW0TQK/YQaurQjfL3R5eoXJvCuUZRgPpKfCvtsXbphDCQ=="}
```
The server (or any other observer of this message) now has
enough information to establish for certain whether whoever
signed this message has control of the private key that
corresponds to the public key indicated in the message.
Signing of randomized and timestamped messages can be used to
implement First-Party Single Sign-On (1pSSO) schemes without
any requirement that the server side maintain private data.