diff --git a/README.md b/README.md index 702c468..c74f3e6 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ The implemented peer exposes select interfaces to be used with Aqua and operates The PoC implementation does not provide integration with external Snapshot distributed persistence but allows for easy extension to incorporate exogenous storage solutions. The validation process, including not implemented checks, can be found in [eip_validation](./src/eip_processor.ts) and the local persistence in [local sqlite](./src/local_db.ts). -In order to access the services with Aqua, please see [implementation](./aqua/snapshot.aqua), which can be fired from a Typescript client, another peer or the `fldist` command line tool. +In order to access the services with Aqua, please see the [implementation](./aqua/snapshot.aqua), which can be fired from a Typescript client, another peer or the [`fldist`](https://github.com/fluencelabs/fldist) command line tool or it's successor cli [Aqua](https://github.com/fluencelabs/aqua). In addition, Aqua can be used to query a Peer's local database for already processed validations. This allows new peers, for example, to build up a local history of previously validated events, if so desired. Please note that a consensus algorithm should be implemented and used to manage the sync process. The query process is outlined in Figure 2 below and the Aqua queries are located in [snapshot aqua](./aqua/snapshot.aqua). @@ -73,23 +73,88 @@ TODO: ## Running A Peer -Install the Node dependencies and start the peer in the `` directory: +In your terminal in the `peer-node` directory, install the dependencies, compile the Aqua script and start the peer: ```bash npm i +npm run compile-aqua npm start ``` -Or run with +And the ensuing terminal output should look like this: ```bash -nohup node start & +> snapshot-node-poc@0.1.0 start +> node -r ts-node/register src/index.ts + +Snapshot service node running with ... +wallet from sk: 0x14791697260E4c9A71f18484C9f997B308e59325 +wallet pk: 0x046655feed4d214c261e0a6b554395596f1f1476a77d999560e5a8df9b8a1a3515217e88dd05e938efdd71b2cce322bf01da96cd42087b236e8f5043157a9c068e +PeerId: 12D3KooWFCY8xqebtZqNeiA5took71bUNAedzCCDuCuM1QTdTbWT +Relay id: 12D3KooWSD5PToNiLQwKDXsu8JSysCwUt8BVUJEqCHcDe7P5h45e +crtl-c to exit ``` -for a long-running daemon. +## Running A Client -With the node up and running, we can access the validation and query capabilities with Aqua initiated from any other (client) peer including a browser. We can currently call the validation process from [Aqua](./aqua/snapshot.aqua) with two methods: `validate` and `validate_from_url` with the former accepting a json string and the later the url to a json body. +With the node up and running, open a new terminal window and in the `client-peer` directory install the dependencies and start the client: -Using the command line utility [`fldist`]("https://doc.fluence.dev/docs/knowledge_tools"), for example, we can run: +```bash +npm i +npm run compile-aqua +npm start +``` +The client executes a validation and a few node-local database calls specified in the `aqua\demo_validation.aqua` file. Note that the client could be a browser, see the [Quickstart](https://doc.fluence.dev/docs/quick-start) documentation for examples. The expected output for the demo should looks like this: -TBD +```bash +> snapshot-demo-client@0.1.0 start +> node -r ts-node/register src/index.ts + +Welcome to Snapshot PoC demo. +Created Fluence client with +peer id: 12D3KooWNRrP7cZ5VcYrCeYBc9RuWz5ijcayD6iGkJKdayzhtQaG +relay id 12D3KooWKnEqMfYo9zvfHmqTLpLdiHXPe4SVqUWcWHDJdFGrSmcA + + +Roundtrip Validation demo. + +Let's check the node db and clear all records if need be: +deleting 1 records +Lets validate proposal https://ipfs.fleek.co/ipfs/QmWGzSQFm57ohEq2ATw4UNHWmYU2HkMjtedcNLodYywpmS, which is old and should fail. +signed eip validation result: { + stderr: '', + stdout: { + signature: '0x2571d1f9d003bd5b24f26abd21e0ebafc57aa61f0c6e85f85a9e298ff577e03445cbf182991cf263e7a3ef505276eaa9d160b780355379bed55c912dfa23623f1b', + validation: { + peer_id: '0x14791697260E4c9A71f18484C9f997B308e59325', + timestamp: 1635119977, + eip_validation: true, + ts_validation: false + } + } +} +We should have one record in the node db and have 1 record(s). +We know from the EIP document that the snapshot is 9278489, which i sued as a unique key in the sqlite db. + and we can call individual recirds by snapshot: +result for call with 9278489: { + stderr: '', + stdout: [ + { + snapshot_id: 9278489, + event_address: '0xeF8305E140ac520225DAf050e2f71d5fBcC543e7', + event_signature: '0xc0a90a0bf43c0b774570608bf0279143b366b7880798112b678b416a7500576b41e19f7b4eb457d58de29be3a201f700fafab1f02179da0faae653b7e8ecf82b1c', + eip712_doc: '{"domain":{"name":"snapshot","version":"0.1.4"},"types":{"Proposal":[{"name":"from","type":"address"},{"name":"space","type":"string"},{"name":"timestamp","type":"uint64"},{"name":"type","type":"string"},{"name":"title","type":"string"},{"name":"body","type":"string"},{"name":"choices","type":"string[]"},{"name":"start","type":"uint64"},{"name":"end","type":"uint64"},{"name":"snapshot","type":"uint64"},{"name":"network","type":"string"},{"name":"strategies","type":"string"},{"name":"plugins","type":"string"},{"name":"metadata","type":"string"}]},"message":{"space":"fabien.eth","type":"single-choice","title":"This is a long title this is a long title this is a long title this is a long title this is a long title this is a long","body":"This is a long title this is a long title this is a long title title this is a long title this is a long title title this is a long title this is a long title title this is a long title this is a long title title this is a long title this is a long title title this is a long title this is a long title title this is a long title this is a long title title this is a long title this is a long title title this is a long title this is a long title title this is a long title this is a long title title this is a long title this is a long title title this is a long title this is a long title title this is a long title this is a long title title this is a long title this is a long title title this is a long title this is a long title.","choices":["Approve","Reject"],"start":1630472400,"end":1640926800,"snapshot":9278489,"network":"4","strategies":"[{\\"name\\":\\"ticket\\",\\"params\\":{\\"value\\":100,\\"symbol\\":\\"$\\"}}]","plugins":"{}","metadata":"{}","from":"0xeF8305E140ac520225DAf050e2f71d5fBcC543e7","timestamp":1631432106}}', + peer_id: '0x14791697260E4c9A71f18484C9f997B308e59325', + timestamp: 1635119977, + eip_validation: 1, + ts_validation: 0, + signed_response: '0x2571d1f9d003bd5b24f26abd21e0ebafc57aa61f0c6e85f85a9e298ff577e03445cbf182991cf263e7a3ef505276eaa9d160b780355379bed55c912dfa23623f1b' + } + ] +} +result for call with bad 92784890: { stderr: '', stdout: [ null ] } +``` + +## Integration With Additional Store Solutions + +Adding (distributed) store solutions for query and persistance on both node and client is straight forward. For Aqua-based IPFS and Ceramic integration examples see [aqua-ipfs](https://github.com/fluencelabs/examples/tree/main/aqua-examples/aqua-ipfs-integration) and [aqua-ipfs lib](https://doc.fluence.dev/aqua-book/libraries/aqua-ipfs) and [ceramic-ipfs](https://github.com/fluencelabs/examples/tree/main/aqua-examples/aqua-ceramic-integration), respectively. diff --git a/client-peer/aqua/demo_validation.aqua b/client-peer/aqua/demo_validation.aqua index f478f53..0c9b342 100644 --- a/client-peer/aqua/demo_validation.aqua +++ b/client-peer/aqua/demo_validation.aqua @@ -1,3 +1,18 @@ +data Response: + peer_id: string + timestamp: u64 + eip_validation: bool + ts_validation: bool + +data EipResponse: + signature: string + validation: Response + +data ValidationResult: + stderr: string + stdout: EipResponse + + data DBRecord: snapshot_id: u64 event_address: string @@ -14,8 +29,8 @@ data DBResult: stdout: []DBRecord service EIPValidator("EIPValidator"): - eip712_validation_string(eip_str: string, peer_id: string) -> string - eip712_validation_url(eip_str: string, peer_id: string) -> string + eip712_validation_string(eip_str: string, peer_id: string) -> ValidationResult + eip712_validation_url(eip_str: string, peer_id: string) -> ValidationResult service DataProvider("DataProvider"): get_records() -> DBResult @@ -24,7 +39,7 @@ service DataProvider("DataProvider"): delete_records(password: string) -> DBResult -func validate(eip712_url: string, node: string, relay:string) -> string: +func validate(eip712_url: string, node: string, relay:string) -> ValidationResult: on node via relay: res <- EIPValidator.eip712_validation_url(eip712_url, node) <- res diff --git a/client-peer/src/_aqua/demo_validation.ts b/client-peer/src/_aqua/demo_validation.ts index b22ae5d..9b81224 100644 --- a/client-peer/src/_aqua/demo_validation.ts +++ b/client-peer/src/_aqua/demo_validation.ts @@ -17,8 +17,8 @@ import { // Services export interface EIPValidatorDef { - eip712_validation_string: (eip_str: string, peer_id: string, callParams: CallParams<'eip_str' | 'peer_id'>) => string | Promise; -eip712_validation_url: (eip_str: string, peer_id: string, callParams: CallParams<'eip_str' | 'peer_id'>) => string | Promise; + eip712_validation_string: (eip_str: string, peer_id: string, callParams: CallParams<'eip_str' | 'peer_id'>) => { stderr: string; stdout: { signature: string; validation: { eip_validation: boolean; peer_id: string; timestamp: number; ts_validation: boolean; }; }; } | Promise<{ stderr: string; stdout: { signature: string; validation: { eip_validation: boolean; peer_id: string; timestamp: number; ts_validation: boolean; }; }; }>; +eip712_validation_url: (eip_str: string, peer_id: string, callParams: CallParams<'eip_str' | 'peer_id'>) => { stderr: string; stdout: { signature: string; validation: { eip_validation: boolean; peer_id: string; timestamp: number; ts_validation: boolean; }; }; } | Promise<{ stderr: string; stdout: { signature: string; validation: { eip_validation: boolean; peer_id: string; timestamp: number; ts_validation: boolean; }; }; }>; } export function registerEIPValidator(service: EIPValidatorDef): void; export function registerEIPValidator(serviceId: string, service: EIPValidatorDef): void; @@ -338,9 +338,9 @@ export function delete_records(...args: any) { } - -export function validate(eip712_url: string, node: string, relay: string, config?: {ttl?: number}): Promise; -export function validate(peer: FluencePeer, eip712_url: string, node: string, relay: string, config?: {ttl?: number}): Promise; +export type ValidateResult = { stderr: string; stdout: { signature: string; validation: { eip_validation: boolean; peer_id: string; timestamp: number; ts_validation: boolean; }; }; } +export function validate(eip712_url: string, node: string, relay: string, config?: {ttl?: number}): Promise; +export function validate(peer: FluencePeer, eip712_url: string, node: string, relay: string, config?: {ttl?: number}): Promise; export function validate(...args: any) { let script = ` diff --git a/client-peer/src/index.ts b/client-peer/src/index.ts index 2843344..296aa63 100644 --- a/client-peer/src/index.ts +++ b/client-peer/src/index.ts @@ -61,26 +61,22 @@ async function main() { console.log("Lets validate proposal %s, which is old and should fail.", EIP712_URL); let doc_val = await validate(EIP712_URL, poc_topologies[0].node_id, poc_topologies[0].relay_id); // if (doc_val.stderr.length > 0) {} - console.log("doc val: ", doc_val); - - rec_count = await get_record_count(poc_topologies[0].node_id, poc_topologies[0].relay_id); - console.log("record count: ", rec_count); + console.log("signed eip validation result: ", doc_val); let records = await get_records(poc_topologies[0].node_id, poc_topologies[0].relay_id); if (records.stderr.length > 0) { - console.log("Records fetch error: ", records.stderr); + console.log("We should have one record in the node db but do not: ", records.stderr); } else { - console.log("record length: ", records.stdout.length); + console.log("We should have one record in the node db and have %s record(s).", records.stdout.length); } - - // verify test - // const address = ethers.utils.verifyMessage(resp_str, signed_response); - // console.log("verify signature. peer_id: ", peer_id, " verified addr: ", address, " equal: ", peer_id === address); - - // console.log(resp_str); - + console.log("We know from the EIP document that the snapshot is 9278489, which i sued as a unique key in the sqlite db.") + console.log(" and we can call individual recirds by snapshot:") + let good_record = await get_record(9278489, poc_topologies[0].node_id, poc_topologies[0].relay_id); + let bad_record = await get_record(92784890, poc_topologies[0].node_id, poc_topologies[0].relay_id); + console.log("result for call with 9278489: ", good_record); + console.log("result for call with bad 92784890: ", bad_record); return; } diff --git a/client-peer/src/index.ts.bk b/client-peer/src/index.ts.bk deleted file mode 100644 index b063b59..0000000 --- a/client-peer/src/index.ts.bk +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2021 Fluence Labs Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Fluence, KeyPair } from "@fluencelabs/fluence"; -import { krasnodar } from "@fluencelabs/fluence-network-environment"; -import { validate } from "./_aqua/demo_validation"; - - -const NODE_ID: string = "12D3KooWFCY8xqebtZqNeiA5took71bUNAedzCCDuCuM1QTdTbWT"; -const RELAY_ID: string = krasnodar[0].peerId; -const EIP_URL: string = "https://ipfs.fleek.co/ipfs/QmWGzSQFm57ohEq2ATw4UNHWmYU2HkMjtedcNLodYywpmS"; - -async function main() { - await Fluence.start({ - connectTo: krasnodar[0], - }); - - console.log("application started"); - console.log("peer id is: ", Fluence.getStatus().peerId); - console.log("relay is: ", Fluence.getStatus().relayPeerId, "\n\n"); - - - const result = await validate(EIP_URL, NODE_ID, RELAY_ID); - console.log("validation result: ", result); - - await Fluence.stop(); -} - -main() - .then(() => process.exit(0)) - .catch((error) => { - console.error('something went wrong: ', error); - process.exit(1); - }); diff --git a/peer-node/data/snapshot.db b/peer-node/data/snapshot.db index 1828d3d..9c3a90a 100644 Binary files a/peer-node/data/snapshot.db and b/peer-node/data/snapshot.db differ diff --git a/peer-node/src/index.ts b/peer-node/src/index.ts index 0389049..1f728f0 100644 --- a/peer-node/src/index.ts +++ b/peer-node/src/index.ts @@ -116,25 +116,21 @@ async function startFluencePeer(skBytes: Uint8Array): Promise { async function main() { + console.log("Snapshot service node running with ...") let wallet = new ethers.Wallet(SecretKey); console.log("wallet from sk: ", wallet.address); console.log("wallet pk: ", wallet.publicKey); const skBytes: Uint8Array = ethers.utils.arrayify(SecretKey); - console.log("arrayify: ", skBytes); - await startFluencePeer(skBytes); - console.log("PeerId: ", Fluence.getStatus().peerId); console.log("Relay id: ", Fluence.getStatus().relayPeerId); - // let peer = Fluence.getPeer(); - // console.log(peer); - // console.log(Fluence.KeyPair); - + // register the Aqua-programmable services registerEIPValidator("EIPValidator", new EIPValidator()); registerDataProvider("DataProvider", new DataProvider); + console.log("crtl-c to exit"); // await Fluence.stop(); }