SPMessage
The fundamental building block of the Shelter Protocol, SPMessage, is composed of three sections: head, message, and sig:
{
"head": JSON.stringify({
"version": "1.0.0",
"previousHEAD": null | "<previousHEAD>",
"contractID": null | "<contractID>",
"originatingContractID": null | "<contractID>",
"op": "<opcode>",
"manifest": "<manifest-hash>",
"uuid": "<uuidv4>",
"height": <uint>
}),
"message": JSON.stringify(<op-value>),
"sig": {
"type": "<sig/key type>",
"keyId": "<keyId>",
"data": "<signature>"
}
}
Wherever hashes appear in the protocol, they are 32-byte blake2b hashes prefixed using multihash unless otherwise noted.
Section: head
versionspecifies the Shelter Protocol version being used.previousHEADspecifies the hash of the previous message in this contract chain, ornullif this is the first message.contractIDspecifies the hash of the first message in the contract chain that this message is being sent to, ornullif this is the first message.originatingContractIDif this is a message from one contract to another, this field specifies thecontractIDof the contract sending the message, andnullotherwise.opis a short string representation of one of the various opcodes (e.g."c"forOP_CONTRACT).manifestis the manifest hash of the contract code used to interpret this message.uuidis a UUIDv4 unique identifier for this message. Useful for uniquely identifying messages in case ofpreviousHEADconflicts. See Resending Messages.heightis an counter starting at0that gets incremented by1with each new message added to the chain. Useful in certain situations related to validating message signatures on rotated keys.
Section: message
This section contains the actual data payload for the <opcode> being invoked.
See ๐ Reference: Opcodes for details.
Section: sig
typespecifies a string describing the ciphers used with the key generating this signature. SeeOP_KEY_ADDfor examples.keyIdis the keyidof the key used to generate this signature. SeeOP_KEY_ADD.datais the message signature.
Generating the signature:
-
Hash the
headJSON and themessageJSON, concatenate these hashes together, and hash a third time:blake32b(`${blake32b(headJSON)}${blake32b(messageJSON)}`) -
Sign the resulting string using
<keyId>and encode the<signature>using base64.
Content Addressing
As mentioned, messages stored and referenced by their 32-byte blake2b multihash in order to ensure historical message integrity. Implementations should take care to verify that all received messages have the appropriate hash.
For example, contracts are syncโd by specifying their contractID. Implementations must make sure that when a contractID is newly synced, the first message actually does have a hash matching that contractID.
To help with syncing, the server has an API to return the latest hash of a contract chain that looks like this: /latestHash/{contractID}
During syncing of latest messages, clients should make sure to verify that hash appears among the messages received.
Resending Messages
Messages are sent to the server using the POST /event API. However, a conflict with the previousHEAD value could occur if someone else sent a message at the same time. In such situations we would want to reconstruct the message using the correct previousHEAD and resend it (repeating several times at random intervals until the message makes it in or we give up). The POST /event API conveniently gives us the latest message hash and height so that we can immediately recreate and resend the message upon conflict.
Usually we identify messages by their hash, but sometimes we want to know when a message weโve sent makes it back to us in order to take some type of action. For example, when a user sends a message to a chatroom we might want to display it in grey until we receive it back.
In this situation, itโs possible that the message will need to be recreated multiple times before it is successfully sent, resulting in a different message hash. If we set up event listeners related to the original message hash, they might never get run because a message with a different hash could be the one to actually make it in to the chain. For these types of situations the message uuid can be used as it remains consistent between recreated messages.