From c4826355a4306cfd71c72f8d9cd616b710fe65a4 Mon Sep 17 00:00:00 2001 From: Tom Trevethan Date: Wed, 27 Sep 2023 13:32:53 +0100 Subject: [PATCH 1/2] Create mainstay.md --- doc/mainstay.md | 127 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 doc/mainstay.md diff --git a/doc/mainstay.md b/doc/mainstay.md new file mode 100644 index 0000000..f901469 --- /dev/null +++ b/doc/mainstay.md @@ -0,0 +1,127 @@ +# Mainstay integration + +Options for attesting state to a mainstay proof-of-publication service. + +Assumptions: + +Mainstay service is available over http interface (or via SOCKS5 Tor proxy). +Mainstay service is available and funded with a valid `token_id` for verifiation. +Funding (via LN payment) is performed in advance and out of band for subscrption. (i.e. `token_id` is already performed.) +Mainstay proofs are stored and made available, but that verification against `bitcoind` and staychain occur separately. + +## Config + +Node is configured with the mainstay server URL, the slot index and the authentication token: + +``` +pub struct MainstayConfig { + url: String, + position: u64, + token: String, +} +``` + +This can be added to `/src/config.rs` + +## Commitment function + +Impliementation of a commitment function that performs a POST request to the `/commitment/send` mainstay service route, with payload: + +``` +payload = { + commitment: commitment, + position: 0, + token: '4c8c006d-4cee-4fef-8e06-bb8112db6314', +}; +``` + +`commitment` is a 32 byte value encoded as a 64 character hex string + +This can be performed using the `Reqwest` http client library (as in mercury server), e.g. + +``` +use reqwest; + +pub struct Request(reqwest::blocking::RequestBuilder); + +impl Request { + //Construct a request from the give payload and config + pub fn from( + payload: Option<&Payload>, + command: &String, + config: &MainstayConfig, + signature: Option, + ) -> Result { + //Build request + let client = reqwest::blocking::Client::new(); + let url = reqwest::Url::parse(&format!("{}/{}", config.url(), command))?; + + //If there is a payload this is a 'POST' request, otherwise a 'GET' request + let req = match payload { + Some(p) => { + let payload_str = String::from(serde_json::to_string(&p)?); + let payload_enc = encode(payload_str); + let mut data = HashMap::new(); + data.insert("X-MAINSTAY-PAYLOAD", &payload_enc); + let sig_str = match signature { + Some(s) => s, + None => String::from(""), + }; + data.insert("X-MAINSTAY-SIGNATURE", &sig_str); + client + .post(url) + .header(reqwest::header::CONTENT_TYPE, "application/json") + .json(&data) + } + None => client + .get(url) + .header(reqwest::header::CONTENT_TYPE, "application/json"), + }; + + Ok(Self(req)) + } + + pub fn send(self) -> std::result::Result { + self.0.send() + } +} +``` + +## Commitment construction + +The node will construct commitments from specified *events* () in `src/events.rs`. + +The commitment can be simply constructed from the sha256 hash of each event (encoded as a string) similar to: + +``` +pub fn make_commitment(data: &String) -> (String) { + let mut data_vec = data.as_bytes().iter().cloned().collect::>(); + + let commitment = sha256d::Hash::hash(&data_vec); + return (commitment.to_string()); +} +``` + +Will determine which events need to be attested. + +## Commitment compression + +Initially assume every event will be committed to the mainstay service endpoint. + +It may be more efficient to compress several events into a single commitment and then only commit every `commitment_interval`. + +## Proof retreival + +After each commitment, retreive the slot proof from the mainstay server API (call to GET `/commitment/commitment` route with the `commitment` hex string). This will return attestion info (`TxID`) and the slot proof (Merkle proof). + +``` + pub struct Proof { + merkle_root: Commitment, + commitment: Commitment, + ops: Vec, + append: Vec, + position: u64, + } +``` + +This will need to be stored in a new DB table corresponding to events. From 5421dd9aacf3529e034d26fec068221cc4d1694c Mon Sep 17 00:00:00 2001 From: Tom Trevethan Date: Mon, 9 Oct 2023 17:27:23 +0100 Subject: [PATCH 2/2] Update mainstay.md --- doc/mainstay.md | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/doc/mainstay.md b/doc/mainstay.md index f901469..352d881 100644 --- a/doc/mainstay.md +++ b/doc/mainstay.md @@ -89,29 +89,30 @@ impl Request { ## Commitment construction -The node will construct commitments from specified *events* () in `src/events.rs`. +The node will construct commitments from specified *events* () in `src/events.rs`. The `event_id` already hashes the full payload of the event object and can be used for commitment directly. -The commitment can be simply constructed from the sha256 hash of each event (encoded as a string) similar to: +Initially assume all events are committed. Can add config to set commitment for specific events. -``` -pub fn make_commitment(data: &String) -> (String) { - let mut data_vec = data.as_bytes().iter().cloned().collect::>(); +## Commitment compression - let commitment = sha256d::Hash::hash(&data_vec); - return (commitment.to_string()); -} -``` +By committing a a *cumulative* hash to the mainstay slot, and saving the cumulative hash alongside the `event_id`, then if individual commitment operations fail, or the mainstay service is temporarily unavailable, the unbroken sequence of events is verifiable as unquine up until the the latest commitment operation. -Will determine which events need to be attested. +In this approach, for each *event* that occurs (in time sequence), the `event_id` is concatenated with the previous cumulative hash and committed to mainstay. -## Commitment compression +So, for the first event: `event_id` is used as the commitment and sent to the mainstay commitment endpoint. This is labeled `event_id[0]`. + +For the next event (`event_id[1]`), the commitment hash is computed: `comm_hash[1] = SHA256(event_id[0] || event_id[1])` and committted to the mainstay service API. -Initially assume every event will be committed to the mainstay service endpoint. +For the next event (`event_id[2]`), the commitment hash is computed: `comm_hash[2] = SHA256(comm_hash[1] || event_id[2])` and committted to the mainstay service API. -It may be more efficient to compress several events into a single commitment and then only commit every `commitment_interval`. +For the next event (`event_id[3]`), the commitment hash is computed: `comm_hash[3] = SHA256(comm_hash[2] || event_id[3])` and committted to the mainstay service API. + +And so on, committing the chain. `comm_hash[n]` does not strictly need to be saved as the chain can be reconstructed directly from the `event_id[n]` saved in the DB. ## Proof retreival +TODO + After each commitment, retreive the slot proof from the mainstay server API (call to GET `/commitment/commitment` route with the `commitment` hex string). This will return attestion info (`TxID`) and the slot proof (Merkle proof). ```