diff --git a/docker/build.sh b/docker/build.sh index 8d0efb891b3109a1c48d8937ebd53d0484b104ee..35a8087bdc62a197089fcf61358e1b6658084a66 100644 --- a/docker/build.sh +++ b/docker/build.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -set -e +set -ex pushd . @@ -8,7 +8,7 @@ PROJECT_ROOT=$(git rev-parse --show-toplevel) cd $PROJECT_ROOT # Find the current version from Cargo.toml -VERSION=$(grep "version" ./package.json | egrep -o "([0-9\.]+)") +VERSION=$(node -p -e "require('./package.json').version") GITUSER=7signals GITREPO=sensio-explorer PROFILE=${PROFILE:-release} @@ -21,6 +21,11 @@ echo "Image is ready" docker images | grep ${GITREPO} echo -e "\nIf you just built version ${VERSION}, you may want to update your tag:" -echo " $ docker tag ${GITUSER}/${GITREPO}:$VERSION ${GITUSER}/${GITREPO}:${VERSION}" +docker tag ${GITUSER}/${GITREPO} ${GITUSER}/${GITREPO}:${VERSION} +# docker tag ${GITUSER}/${GITREPO}:${VERSION} ${GITUSER}/${GITREPO}:latest + +echo -e "\nWhy not push the image:" +docker push ${GITUSER}/${GITREPO}:${VERSION} +# docker push ${GITUSER}/${GITREPO}:latest popd diff --git a/package.json b/package.json index 1c825320e895072a8235ced80d572f66cff4f3ac..cf0d7c34e5dbdf5f9cbb1ddc6df775da45ffd138 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sensio-explorer", - "version": "0.1.1", + "version": "0.2.0", "private": true, "author": "Daniel Maricic ", "license": "Apache-2.0", @@ -12,7 +12,9 @@ "@polkadot/ui-keyring": "^0.49.1", "@polkadot/util": "^2.6.2", "@polkadot/util-crypto": "^2.6.2", + "assert": "^2.0.0", "big.js": "^5.2.2", + "cbor": "^5.0.2", "cids": "^0.8.0", "exifreader": "^3.9.0", "history": "^4.10.1", diff --git a/public/favicon.ico b/public/favicon.ico index 81234a15ef3a404de768e5a904435127ea3166a0..7dee43d07994017b9828894c9991a607a516cf24 100644 Binary files a/public/favicon.ico and b/public/favicon.ico differ diff --git a/src/App.js b/src/App.js index 68a3a390df7c14ba6967529d7426b97e83989ee3..490bfeacceebc01db76213cbc15ca9fb974984f8 100644 --- a/src/App.js +++ b/src/App.js @@ -14,6 +14,7 @@ import AccountSelector from "./AccountSelector"; import BlockNumber from "./BlockNumber"; import Events from "./Events"; import SensioModule from "./sensio"; +import PhashInfo from "./sensio/poe/pHashes/PhashInfo"; import CreateProof from "./sensio/poe/proofs/CreateProof"; import ProofsInfo from "./sensio/poe/proofs/ProofsInfo"; import ProofsList from "./sensio/poe/proofs/ProofsList"; @@ -62,6 +63,7 @@ function Main() { + diff --git a/src/config/common.json b/src/config/common.json index acabf72559eb53a910b2c93d9a40333cee80a42b..fd441057ee6ec6f7b542ec23f4fe23bc390df77e 100644 --- a/src/config/common.json +++ b/src/config/common.json @@ -4,47 +4,87 @@ "CUSTOM_TYPES": { "Address": "AccountId", "LookupSource": "AccountId", + "PoeId": "Vec", + "CreatorId": "Vec", + "DefaultsHashing": { + "algo": "Vec", + "bits": "i32", + "skip": "bool" + }, + "DefaultsEncoding": { + "algo": "Vec", + "prefix": "bool" + }, + "DefaultsCid": { + "version": "i32", + "base": "Vec", + "codec": "Vec" + }, "DefaultValues": { - "hashAlgo": "Vec", - "hashBits": "u32", - "encodingAlgo": "Vec", - "encodingPrefix": "Vec" + "hashing": "DefaultsHashing", + "encoding": "DefaultsEncoding", + "cid": "DefaultsCid" }, "ForWhat": { "_enum": ["Generic", "Photo", "Camera", "Lens", "SmartPhone"] }, + "OperationInfo": { + "op": "Operation", + "accountId": "AccountId", + "blockNumber": "BlockNumber" + }, "Operation": { + "id": "PoeId", + "data": "OperationData" + }, + "OperationData": { "op": "Vec", - "name": "Vec", "desc": "Vec", - "hashAlgo": "Vec", - "hashBits": "u32", - "encodeAlgo": "Vec", - "prefix": "Vec", + "hashing": "DefaultsHashing", + "encoding": "DefaultsEncoding", "ops": "Vec" }, "Rule": { - "version": "u32", + "id": "PoeId", + "data": "RuleData" + }, + "RuleData": { + "version": "i32", "name": "Vec", "desc": "Vec", - "creator": "Vec", + "creator": "CreatorId", "forWhat": "ForWhat", - "parent": "Vec", - "ops": "Vec", - "buildParams": "Operation" + "parent": "PoeId", + "ops": "Vec" }, - "Proof": { - "proofId": "Vec", - "body": "Vec", - "createdAt": "u64", - "prev": "Vec", - "ruleId": "Vec", - "forWhat": "ForWhat" + "ProofParams": { + "k": "Vec", + "v": "Vec" }, "ProofInfo": { "proof": "Proof", "accountId": "AccountId", "blockNumber": "BlockNumber" + }, + "Proof": { + "id": "PoeId", + "data": "ProofData" + }, + "ProofData": { + "ruleId": "PoeId", + "prev": "PoeId", + "creator": "CreatorId", + "forWhat": "ForWhat", + "params": "Vec" + }, + "RuleInfo": { + "rule": "Rule", + "accountId": "AccountId", + "blockNumber": "BlockNumber" + }, + "PhashInfo": { + "pHash": "Vec", + "proofId": "PoeId" } } } diff --git a/src/config/production.json b/src/config/production.json index af15d9d6d547d8afd05c434fccea867ba9d979bd..70fa0c0a18c80a6cd9478dba2664a78848840257 100644 --- a/src/config/production.json +++ b/src/config/production.json @@ -1,3 +1,3 @@ { - "PROVIDER_SOCKET": "ws://127.0.0.1:9944" + "PROVIDER_SOCKET": "ws://explorer.sensio.network:9944" } diff --git a/src/sensio/poe/README.md b/src/sensio/poe/README.md new file mode 100644 index 0000000000000000000000000000000000000000..68cfd9a968a187c7590725724cda081b7896e9ab --- /dev/null +++ b/src/sensio/poe/README.md @@ -0,0 +1,18 @@ +# Proof-of-Existence Pallet + +The ultimate goal of this pallet is to create verifiable proof for any kind of data. If you can turn data into the `Buffer` you can probably create the PoE. + +Idea is simple, provide set of rules, apply them on the data, take the output and create the proof. + +This sound like any hashing function, in essence it is with the difference that in applying just the hashing function on the data we end up with the single value without any descriptors about the data, in some cases we don't even know which hash is used if not specified or implemented as multihash, but applying the rule which contains the list of operations where each operation produces `single` hash then combining that into structured way for humans, then creating content address we enable interoperability between any producer of the proof. Theoretically the proof can be generated using the any kind of language and stored anywhere, it can be verified if 3 conditions are met: + +1. Rule must be valid and verifiable and retrieved from the SensioNetwork +2. Rule must be applied exactly as it is +3. Proof ID must be broadcasted and available +4. The data can be private and never leave the users domain of control (Optional, it can be public, encrypted ....) + +The `Proof` generation depends on the `Rule` definition and its operations. + +Follow the numbers in the navigation, they will help you get the PoE. + +To learn more about Sensio why not check out our [wiki](https://wiki.sensio.dev) and join our [discord server](https://discordapp.com/invite/WHe4EuY) diff --git a/src/sensio/poe/helpers.js b/src/sensio/poe/helpers.js index 7bb9ac694f796610410395a2320b8adf6a54ad7f..dec381afb152e74b9f71a4f49789781993f5069f 100644 --- a/src/sensio/poe/helpers.js +++ b/src/sensio/poe/helpers.js @@ -1,8 +1,20 @@ -import { hexToString, stringToHex } from "@polkadot/util"; +import { hexToString } from "@polkadot/util"; import CID from "cids"; import * as imghash from "imghash"; import mh from "multihashing-async"; import { cameraRule, lensRule, photoRule } from "./rules/defaultRules"; +/** + * @return {Promise<[{ + pHash: string, + proofId: string + }]>} + * @param {ApiPromise} api + */ +export async function getPhashes(api) { + const c = await api.query.poe.pHashes.entries(); + return formatCodecResponseToObject(c); +} + /** * @return {Promise<[{ rule: Object, @@ -14,16 +26,15 @@ import { cameraRule, lensRule, photoRule } from "./rules/defaultRules"; */ export async function getRules(api) { const c = await api.query.poe.rules.entries(); - return c.map(([key, val]) => { - const [ruleId, payload, accountId, blockNumber] = val; - // must hexToString to get the decoded value - return { - rule: payload, - ruleId: ruleId.toString(), - accountId: accountId.toString(), - blockNumber: blockNumber.toNumber(), - }; - }); + return formatCodecResponseToObject(c); +} + +/** + * @param {ApiPromise} api + */ +export async function getOperations(api) { + const c = await api.query.poe.operations.entries(); + return formatCodecResponseToObject(c); } /** @@ -32,78 +43,138 @@ export async function getRules(api) { */ export async function getProofs(api) { const c = await api.query.poe.proofs.entries(); + return formatCodecResponseToObject(c); +} + +function formatCodecResponseToObject(c) { return c.map(([key, val]) => { - const r = {}; + const r = { storageKey: key.toString() }; val.forEach((s, k) => { - r[k] = s; + r[k] = k.includes("Id") ? s.toString() : s; }); return r; }); } -export function objectToString(o) { +export function pack(o) { const r = JSON.stringify(o); + // calculateSize(r); return r; } -/** - * Create Rule payload and rule ID - * - * TODO refactor this to use unified way of creating the hashes and encoding using the network defaults - * @param rule - */ -export async function createRulePayload(api, rule) { - const buf = Buffer.from(objectToString(rule)); - const hash = await calculateHash(buf); - const cid = createCID(hash); - const payload = api.createType("Rule", rule); - const ret = { ruleId: stringToHex(cid.toString()), payload }; - return ret; -} - /** * Create Rules * @param api * @param signer */ -export async function createDefaultRules(api, signer) { +export async function createDefaultRules(api, signer, ops) { // console.log(api.query.poe); // const Rule = api.createType('Rule', rule); // console.log(Rule); - return Promise.all( - [photoRule, lensRule, cameraRule].map( - async (r) => await createRulePayload(api, r) - ) - ) - .then((r) => { - return saveRuleToNetwork(api, r[0], signer) - .then(() => saveRuleToNetwork(api, r[1], signer)) - .then(() => saveRuleToNetwork(api, r[2], signer)); - // .then(() => { - // console.log("All rules are saved"); - // return true; - // }) - // .catch(console.error); - }) - .catch(console.error); + const rules = [photoRule, lensRule, cameraRule].map((r) => { + const rule = Object.assign({}, r); + rule.ops = rule.ops.map((o) => { + const found = ops.find( + ({ + op: { + data: { op }, + }, + }) => o === hexToString(op.toString()) + ); + if (!found) { + throw new Error(`Couldn't find the operation ${o}`); + } + + // return found.op; + const { op } = found; + return op; + + // const r = { + // id: hexToString(op.id.toString()), + // data: { + // op: hexToString(op.data.op.toString()), + // desc: hexToString(op.data.desc.toString()), + // hashing: { + // algo: hexToString(op.data.hashing.algo.toString()), + // bits: op.data.hashing.bits.toNumber(), + // skip: op.data.hashing.skip, + // }, + // encoding: { + // algo: hexToString(op.data.encoding.algo.toString()), + // prefix: true, + // }, + // ops: [], + // }, + // }; + + // return r; + }); + + return rule; + }); + + const txs = rules.map((r) => api.tx.poe.createRule(r)); + + return new Promise((resolve, reject) => { + api.tx.utility + .batch(txs) + .signAndSend(signer, {}, ({ events = [], status, isError }) => { + console.log(`\tTransaction status: ${status.type}`); + console.log(`\tEvents: ${events}`); + + if (status.isFinalized) { + console.log("\tFinalized block hash", status.asFinalized.toHex()); + resolve(true); + } else if (isError) { + console.error(status); + reject("Error happened, check console log"); + } + }); + }); // const createdRule = await api.tx.poe.createRule(ruleId, rule).signAndSend(signer); // console.log('CreatedRule ', createdRule.toHex()); } +/** + * Create Default Ops + * @param api + * @param signer + */ +export async function createDefaultOps(api, signer) { + const ops = api.consts.poe.defaultOperations; + const txs = ops.map((op) => api.tx.poe.createOperation(op)); + return new Promise((resolve, reject) => { + api.tx.utility + .batch(txs) + .signAndSend(signer, {}, ({ events = [], status, isError }) => { + console.log(`\tTransaction status: ${status.type}`); + console.log(`\tEvents: ${events}`); + + if (status.isFinalized) { + console.log("\tFinalized block hash", status.asFinalized.toHex()); + resolve(true); + } else if (isError) { + console.error(status); + reject("Error happened, check console log"); + } + }); + }); +} /** * Create Rule Transaction * @param api - * @param param1 + * @param payload * @param signer */ -export async function saveRuleToNetwork(api, { payload, ruleId }, signer, cb) { - // eslint-disable-next-line no-async-promise-executor +export async function saveRuleToNetwork(api, payload, signer, cb) { + // const { nonce } = await api.query.system.account(signer.address); + // console.log("Nonce: ", nonce.toString()); return new Promise((resolve, reject) => { - console.log(`\nCreating TX for the ruleId: ${ruleId}`); + console.log(`\nCreating Rule...`); return api.tx.poe - .createRule(ruleId, payload) + .createRule(payload) .signAndSend(signer, {}, ({ events = [], status, isError }) => { console.log(`\tTransaction status:${status.type}`); @@ -159,6 +230,87 @@ export async function saveRuleToNetwork(api, { payload, ruleId }, signer, cb) { }); } +/** + * Save Phash + * @param api {ApiPromise} + * @param params {PhashInfo} + * @param signer {KeyPair} + * @param cb {Function} + */ +export async function savePhashToNetwork(api, payload, signer, cb) { + const { nonce } = await api.query.system.account(signer.address); + return new Promise((resolve, reject) => { + console.log(`\nSaving Phash to the network`); + if (cb) cb([null, { message: `Saving the proof to the network` }]); + return api.tx.poe + .savePhash(payload) + .signAndSend(signer, { nonce }, ({ events = [], status, isError }) => { + console.log(`\tTransaction status: ${status.type}`); + if (cb) cb([null, { message: `Transaction status: ${status.type}` }]); + + if (status.isInBlock) { + console.log("\tIncluded at block hash", status.asInBlock.toHex()); + if (cb) + cb([ + null, + { + message: `Included at block hash: ${status.asInBlock.toHex()}`, + }, + ]); + console.log("\tEvents:", events.length); + + events.forEach(({ event, phase }) => { + const { data, method, section } = event; + const [error] = data; + + // console.log('\t', phase.toString(), `: ${section}.${method}`, data.toString()); + // console.log('\t', phase.toString(), `: ${section}.${method}`); + + if (error.isModule) { + const { + documentation, + name, + section, + } = api.registry.findMetaError(error.asModule); + console.error("\t", documentation.toString(), name, section); + console.error("\tRejecting ..."); + if (cb) cb([null, { message: documentation.toString() }]); + + // reject here would make all the other promises to fail + reject(documentation.toString()); + + // resolve(true); + } else { + console.log( + "\t", + phase.toString(), + `: ${section}.${method}`, + data.toString() + ); + if (cb) cb([null, { message: "Waiting for finalization" }]); + resolve(true); + } + }); + } else if (status.isFinalized) { + console.log("\tFinalized block hash", status.asFinalized.toHex()); + if (cb) cb([null, { message: "Finalized", success: true }]); + resolve(true); + } else if (isError) { + console.error(status); + if (cb) cb([status, null]); + reject(status); + } + + // console.log( + // `Rule created for ${ForWhat[r.forWhat]}\n hash: ${createdRuleSimple.toHex()}\n cid: ${hexToString(ruleId)}`, + // ); + }) + .catch((e) => { + console.error(e); + reject(e); + }); + }); +} /** * Create Proof Transaction * @param api {ApiPromise} @@ -176,9 +328,8 @@ export async function saveRuleToNetwork(api, { payload, ruleId }, signer, cb) { export async function saveProofToNetwork(api, proof, signer, cb) { // eslint-disable-next-line no-async-promise-executor return new Promise((resolve, reject) => { - console.log( - `\nCreating TX for the proofId: ${hexToString(proof.proofId.toString())}` - ); + console.log(`\nCreating the proof ...`); + let proofId = ""; if (cb) cb([null, { message: `Saving the proof to the network` }]); return api.tx.poe .createProof(proof) @@ -225,14 +376,27 @@ export async function saveProofToNetwork(api, proof, signer, cb) { `: ${section}.${method}`, data.toString() ); - if (cb) cb([null, { message: "Waiting for finalization" }]); - resolve(true); + if (method === "ProofCreated") { + proofId = data[1]; + } + + if (cb) { + cb([ + null, + { + message: "Waiting for finalization", + success: true, + proofId, + }, + ]); + } } }); } else if (status.isFinalized) { console.log("\tFinalized block hash", status.asFinalized.toHex()); - if (cb) cb([null, { message: "Finalized", success: true }]); - resolve(true); + if (cb) + cb([null, { message: "Finalized", finalized: true, proofId }]); + resolve({ finalized: true, proofId }); } else if (isError) { console.error(status); if (cb) cb([status, null]); @@ -283,8 +447,8 @@ export async function calculateHash(data, algo = "blake2b", length = 256) { * @param {Buffer} data * @param {*} codec */ -export function createCID(data, codec = "raw") { - return new CID(1, codec, data); +export function createCID(data, codec = "raw", baseName = "base32") { + return new CID(1, codec, data, baseName); } /** @@ -304,3 +468,29 @@ export const elapsedTime = (start) => { const elapsed = Date.now() - start; return elapsed; }; + +var toType = function (obj) { + return {}.toString + .call(obj) + .match(/\s([a-zA-Z]+)/)[1] + .toLowerCase(); +}; + +/** + * Uses JSON.stringify then creates the buffer and gets byteLength + * @param {any} a + */ +export function calculateSize(a) { + let len; + if (Buffer.isBuffer(a)) { + len = a.byteLength; + } else { + len = Buffer.from(JSON.stringify(a)).byteLength; + } + + console.log( + `Param is of type ${toType(a).toUpperCase()} with length ${parseFloat( + len / 1024 + ).toFixed(2)}kb` + ); +} diff --git a/src/sensio/poe/index.js b/src/sensio/poe/index.js index 6eae538d3a70f8f10d33cd4e415406c68421fbf6..dd3a9e540c13093993afdb1ecd6dc1fd840c3528 100644 --- a/src/sensio/poe/index.js +++ b/src/sensio/poe/index.js @@ -1,7 +1,10 @@ import React from "react"; -import { Grid, List } from "semantic-ui-react"; +import { Grid } from "semantic-ui-react"; +import CreateRuleChooser from "./rules/CreateRuleChooser"; export default function PoEModule(props) { + const { accountPair } = props; + return ( @@ -9,62 +12,33 @@ export default function PoEModule(props) { - The ultimate goal of this pallet is to create verifiable proof for any - kind of data. If you can turn data into the Buffer you - can probably create the PoE. Idea is simple, provide set of rules, - apply them on the data, take the output and create the proof. This - sound like any hashing function, in essence it is with the difference - that in applying just the hashing function on the data we end up with - the single value without any descriptors about the data, in some cases - we don't even know which hash is used if not specified or implemented - as multihash, but applying the rule which contains the list of - operations where each operation produces single hash then - combining that into structured way for humans, then creating content - address we enable interoperability between any producer of the proof. - Theoretically the proof can be generated using the any kind of - language and stored anywhere, it can be verified if 3 conditions are - met: The Proof generation depends on the{" "} - Rule definition and its operations. - - - Rule must be valid and verifiable and retrieved from the - SensioNetwork - - Rule must be applied exactly as it is - - Proof ID must be broadcasted and available - - - The data can be private and never leave the users domain of - control (Optional, it can be public, encrypted ....) - - - Follow the numbers in the navigation, they will help you get the PoE. - Also you might want to switch the Account from Alice to Bob then - create new proofs and see what will happen. -

-

- To learn more about Sensio why not check out our{" "} - - wiki. - -

- Our{" "} - - discord server is the best place to get in contact with us and ask - questions. - -

-

- Have fun and stay safe 🖖 +
+ +

Hi and welcome to Sensio Network Explorer

+

Please check following links for more info:

+ +
+
+
+
+ + +
diff --git a/src/sensio/poe/operations/genericHash/config.js b/src/sensio/poe/operations/genericHash/config.js new file mode 100644 index 0000000000000000000000000000000000000000..538642ab894ecc945b42bd1b6cf13e6b0c3385a4 --- /dev/null +++ b/src/sensio/poe/operations/genericHash/config.js @@ -0,0 +1,21 @@ + +/** + * Operation specification + */ +export default { + id: 'bafk2bzacebjktneyfak7elakh5jekmd4gapilx3wzxedbg3t7uef2en6wxsjg', + data: { + op:'generic_hash', + desc: 'Generic blake2b 256 hashing op. Any kind of data', + hashing: { + algo: 'blake2b', + bits: 256, + skip: false, + }, + encoding: { + algo: 'hex', + prefix: true, + }, + ops: [], + }, +}; diff --git a/src/sensio/poe/operations/genericHash/index.js b/src/sensio/poe/operations/genericHash/index.js new file mode 100644 index 0000000000000000000000000000000000000000..54316b3d1b2842a49554ece42902dd3cb6091667 --- /dev/null +++ b/src/sensio/poe/operations/genericHash/index.js @@ -0,0 +1,19 @@ +import { calculateHash, createCID } from "../../helpers"; +import config from "./config"; + +/** + * Execute genericHash + * @param {{file: Buffer, rawPixels: Buffer: data: Buffer}} params + * @return string + */ +export default async function genericHash({ data }) { + let ret = ""; + ret = createCID( + await calculateHash( + data, + config.data.hashing.algo, + config.data.hashing.bits + ) + ).toString(); + return ret; +} diff --git a/src/sensio/poe/operations/index.js b/src/sensio/poe/operations/index.js new file mode 100644 index 0000000000000000000000000000000000000000..8faef6940229716273d8913fc6f9381aee1683d6 --- /dev/null +++ b/src/sensio/poe/operations/index.js @@ -0,0 +1,15 @@ +export * from "./genericHash"; +export * from "./metaCopyright"; +export * from "./metaCreateDate"; +export * from "./metadataHash"; +export * from "./metaDateTimeOriginal"; +export * from "./metaDocumentId"; +export * from "./metaLensInfo"; +export * from "./metaLensModel"; +export * from "./metaLensSerialNumber"; +export * from "./metaMake"; +export * from "./metaModel"; +export * from "./metaOriginalDocumentId"; +export * from "./metaSerialNumber"; +export * from "./perceptualHash"; +export * from "./rawPixelsHash"; diff --git a/src/sensio/poe/operations/metaCopyright/config.js b/src/sensio/poe/operations/metaCopyright/config.js new file mode 100644 index 0000000000000000000000000000000000000000..2c5798feabcc18525eb90abed56f3e54dc1d086d --- /dev/null +++ b/src/sensio/poe/operations/metaCopyright/config.js @@ -0,0 +1,21 @@ + +/** + * Operation specification + */ +export default { + id: 'bafk2bzacecrub2yp6vkmmquufivmcx63nzvz4py44bih7w5gdmnahqbzvvveu', + data: { + op:'meta_copyright', + desc: 'XMP copyright field', + hashing: { + algo: 'blake2b', + bits: 256, + skip: false, + }, + encoding: { + algo: 'hex', + prefix: true, + }, + ops: [], + }, +}; diff --git a/src/sensio/poe/operations/metaCopyright/index.js b/src/sensio/poe/operations/metaCopyright/index.js new file mode 100644 index 0000000000000000000000000000000000000000..33e45e9e198e3600112452c6f06b3f97f897a5ce --- /dev/null +++ b/src/sensio/poe/operations/metaCopyright/index.js @@ -0,0 +1,28 @@ +import ExifReader from "exifreader"; +import { calculateHash, createCID } from "../../helpers"; +import config from "./config"; + +/** + * Execute metaCopyright + * @param {{file: Buffer, rawPixels: Buffer}} params + * @return string + */ +export default async function metaCopyright({ file, rawPixels }) { + let ret = ""; + let val = ""; + const tags = ExifReader.load(file); + const { Copyright } = tags; + + if (Copyright && Copyright.value) { + val = Copyright.value.toString(); + } + + ret = createCID( + await calculateHash( + Buffer.from(val), + config.data.hashing.algo, + config.data.hashing.bits + ) + ).toString(); + return ret; +} diff --git a/src/sensio/poe/operations/metaCreateDate/config.js b/src/sensio/poe/operations/metaCreateDate/config.js new file mode 100644 index 0000000000000000000000000000000000000000..8f2b1c76085035c35934593bf03b9b943ddff9f6 --- /dev/null +++ b/src/sensio/poe/operations/metaCreateDate/config.js @@ -0,0 +1,21 @@ + +/** + * Operation specification + */ +export default { + id: 'bafk2bzaceausafubvfxiikuyn3lu4lue2j23a6ap62fyfxsy6ukeq5gy6hcze', + data: { + op:'meta_create_date', + desc: 'XMP create date', + hashing: { + algo: 'blake2b', + bits: 256, + skip: false, + }, + encoding: { + algo: 'hex', + prefix: true, + }, + ops: [], + }, +}; diff --git a/src/sensio/poe/operations/metaCreateDate/index.js b/src/sensio/poe/operations/metaCreateDate/index.js new file mode 100644 index 0000000000000000000000000000000000000000..96a962d36bc78efedc02715e475c03351baef8c3 --- /dev/null +++ b/src/sensio/poe/operations/metaCreateDate/index.js @@ -0,0 +1,28 @@ +import ExifReader from "exifreader"; +import { calculateHash, createCID } from "../../helpers"; +import config from "./config"; + +/** + * Execute metaCreateDate + * @param {{file: Buffer, rawPixels: Buffer}} params + * @return string + */ +export default async function metaCreateDate({ file, rawPixels }) { + let ret = ""; + let val = ""; + const tags = ExifReader.load(file); + const { CreateDate } = tags; + + if (CreateDate && CreateDate.value) { + val = CreateDate.value.toString(); + } + + ret = createCID( + await calculateHash( + Buffer.from(val), + config.data.hashing.algo, + config.data.hashing.bits + ) + ).toString(); + return ret; +} diff --git a/src/sensio/poe/operations/metaDateTimeOriginal/config.js b/src/sensio/poe/operations/metaDateTimeOriginal/config.js new file mode 100644 index 0000000000000000000000000000000000000000..d9f2fad3e867cc7679d92c7db3b81ec06ed4f8af --- /dev/null +++ b/src/sensio/poe/operations/metaDateTimeOriginal/config.js @@ -0,0 +1,21 @@ + +/** + * Operation specification + */ +export default { + id: 'bafk2bzacebrm2qzoqhpklpqpqff5my42c4mjcqk2stvxieqrozteya2mharim', + data: { + op:'meta_date_time_original', + desc: 'XMP date time original field', + hashing: { + algo: 'blake2b', + bits: 256, + skip: false, + }, + encoding: { + algo: 'hex', + prefix: true, + }, + ops: [], + }, +}; diff --git a/src/sensio/poe/operations/metaDateTimeOriginal/index.js b/src/sensio/poe/operations/metaDateTimeOriginal/index.js new file mode 100644 index 0000000000000000000000000000000000000000..17bbc487ecb9c8955cc0de38ab1aa2f4def7c726 --- /dev/null +++ b/src/sensio/poe/operations/metaDateTimeOriginal/index.js @@ -0,0 +1,28 @@ +import ExifReader from "exifreader"; +import { calculateHash, createCID } from "../../helpers"; +import config from "./config"; + +/** + * Execute metaDateTimeOriginal + * @param {{file: Buffer, rawPixels: Buffer}} params + * @return string + */ +export default async function metaDateTimeOriginal({ file, rawPixels }) { + let ret = ""; + let val = ""; + const tags = ExifReader.load(file); + const { DateTimeOriginal } = tags; + + if (DateTimeOriginal && DateTimeOriginal.value) { + val = DateTimeOriginal.value.toString(); + } + + ret = createCID( + await calculateHash( + Buffer.from(val), + config.data.hashing.algo, + config.data.hashing.bits + ) + ).toString(); + return ret; +} diff --git a/src/sensio/poe/operations/metaDocumentId/config.js b/src/sensio/poe/operations/metaDocumentId/config.js new file mode 100644 index 0000000000000000000000000000000000000000..7d0817952ec7fd443654de138e7d678faa7ad65f --- /dev/null +++ b/src/sensio/poe/operations/metaDocumentId/config.js @@ -0,0 +1,21 @@ + +/** + * Operation specification + */ +export default { + id: 'bafk2bzacedrcm7vxh4wjacx7oftu7cp5rav4iaws7mqquncp2gb7zqiigerkw', + data: { + op:'meta_document_id', + desc: 'Document ID. The common identifier for all versions and renditions of a resource..to_vec() Found under xmp.did:GUID and parsed only the GUID part without the namespace xmp.did:', + hashing: { + algo: 'blake2b', + bits: 256, + skip: false, + }, + encoding: { + algo: 'hex', + prefix: true, + }, + ops: [], + }, +}; diff --git a/src/sensio/poe/operations/metaDocumentId/index.js b/src/sensio/poe/operations/metaDocumentId/index.js new file mode 100644 index 0000000000000000000000000000000000000000..b54825131858aab6e839fd16caa5c4fa826fe3de --- /dev/null +++ b/src/sensio/poe/operations/metaDocumentId/index.js @@ -0,0 +1,28 @@ +import ExifReader from "exifreader"; +import { calculateHash, createCID } from "../../helpers"; +import config from "./config"; + +/** + * Execute metaDocumentId + * @param {{file: Buffer, rawPixels: Buffer}} params + * @return string + */ +export default async function metaDocumentId({ file, rawPixels }) { + let ret = ""; + let val = ""; + const tags = ExifReader.load(file); + const { DocumentID } = tags; + + if (DocumentID && DocumentID.value) { + val = DocumentID.value.split(":")[1]; + } + + ret = createCID( + await calculateHash( + Buffer.from(val), + config.data.hashing.algo, + config.data.hashing.bits + ) + ).toString(); + return ret; +} diff --git a/src/sensio/poe/operations/metaLensInfo/config.js b/src/sensio/poe/operations/metaLensInfo/config.js new file mode 100644 index 0000000000000000000000000000000000000000..2f63a842bc8b8adee5a479543286edcd4f7b3d1c --- /dev/null +++ b/src/sensio/poe/operations/metaLensInfo/config.js @@ -0,0 +1,21 @@ + +/** + * Operation specification + */ +export default { + id: 'bafk2bzaceatxkctrs77acl5kdemff6q6zztmtsvawqke6mueq2wutia3bdsl6', + data: { + op:'meta_lens_info', + desc: 'Extract LensInfo from Metadata', + hashing: { + algo: 'blake2b', + bits: 256, + skip: false, + }, + encoding: { + algo: 'hex', + prefix: true, + }, + ops: [], + }, +}; diff --git a/src/sensio/poe/operations/metaLensInfo/index.js b/src/sensio/poe/operations/metaLensInfo/index.js new file mode 100644 index 0000000000000000000000000000000000000000..b47ab6069cc66f3aafa553f130feb8230ebf8e49 --- /dev/null +++ b/src/sensio/poe/operations/metaLensInfo/index.js @@ -0,0 +1,28 @@ +import ExifReader from "exifreader"; +import { calculateHash, createCID } from "../../helpers"; +import config from "./config"; + +/** + * Execute metaLensInfo + * @param {{file: Buffer, rawPixels: Buffer}} params + * @return string + */ +export default async function metaLensInfo({ file, rawPixels }) { + let ret = ""; + let val = ""; + const tags = ExifReader.load(file); + const { LensInfo } = tags; + + if (LensInfo && LensInfo.value) { + val = LensInfo.value.toString(); + } + + ret = createCID( + await calculateHash( + Buffer.from(val), + config.data.hashing.algo, + config.data.hashing.bits + ) + ).toString(); + return ret; +} diff --git a/src/sensio/poe/operations/metaLensModel/config.js b/src/sensio/poe/operations/metaLensModel/config.js new file mode 100644 index 0000000000000000000000000000000000000000..7f6edc5f0563382e5f07d50beb70cf797c46c71c --- /dev/null +++ b/src/sensio/poe/operations/metaLensModel/config.js @@ -0,0 +1,21 @@ + +/** + * Operation specification + */ +export default { + id: 'bafk2bzacecgsoo3e363rftpxjwnkahwjzslhqcxdtsnlxctf6iojnrbmbjgii', + data: { + op:'meta_lens_model', + desc: 'Extract LensModel from Metadata', + hashing: { + algo: 'blake2b', + bits: 256, + skip: false, + }, + encoding: { + algo: 'hex', + prefix: true, + }, + ops: [], + }, +}; diff --git a/src/sensio/poe/operations/metaLensModel/index.js b/src/sensio/poe/operations/metaLensModel/index.js new file mode 100644 index 0000000000000000000000000000000000000000..8c33821690746648606b3f3b9d30c2cca929f0e3 --- /dev/null +++ b/src/sensio/poe/operations/metaLensModel/index.js @@ -0,0 +1,28 @@ +import ExifReader from "exifreader"; +import { calculateHash, createCID } from "../../helpers"; +import config from "./config"; + +/** + * Execute metaLensModel + * @param {{file: Buffer, rawPixels: Buffer}} params + * @return string + */ +export default async function metaLensModel({ file, rawPixels }) { + let ret = ""; + let val = ""; + const tags = ExifReader.load(file); + const { LensModel } = tags; + + if (LensModel && LensModel.value) { + val = LensModel.value.toString(); + } + + ret = createCID( + await calculateHash( + Buffer.from(val), + config.data.hashing.algo, + config.data.hashing.bits + ) + ).toString(); + return ret; +} diff --git a/src/sensio/poe/operations/metaLensSerialNumber/config.js b/src/sensio/poe/operations/metaLensSerialNumber/config.js new file mode 100644 index 0000000000000000000000000000000000000000..c1dbfc2b49a6f6fa1fdb8181ee46f79889a8aa91 --- /dev/null +++ b/src/sensio/poe/operations/metaLensSerialNumber/config.js @@ -0,0 +1,21 @@ + +/** + * Operation specification + */ +export default { + id: 'bafk2bzacebar5hlfvmy2gmpiinyuoiupen5puwxytskhtbxhqcwzquvxe4pjc', + data: { + op:'meta_lens_serial_number', + desc: 'Extract LensSerialNumber from Metadata', + hashing: { + algo: 'blake2b', + bits: 256, + skip: false, + }, + encoding: { + algo: 'hex', + prefix: true, + }, + ops: [], + }, +}; diff --git a/src/sensio/poe/operations/metaLensSerialNumber/index.js b/src/sensio/poe/operations/metaLensSerialNumber/index.js new file mode 100644 index 0000000000000000000000000000000000000000..515a6b1bdaf91e2891fd4b21bff1f1569386c735 --- /dev/null +++ b/src/sensio/poe/operations/metaLensSerialNumber/index.js @@ -0,0 +1,28 @@ +import ExifReader from "exifreader"; +import { calculateHash, createCID } from "../../helpers"; +import config from "./config"; + +/** + * Execute metaLensSerialNumber + * @param {{file: Buffer, rawPixels: Buffer}} params + * @return string + */ +export default async function metaLensSerialNumber({ file, rawPixels }) { + let ret = ""; + let val = ""; + const tags = ExifReader.load(file); + const { LensSerialNumber } = tags; + + if (LensSerialNumber && LensSerialNumber.value) { + val = LensSerialNumber.value.toString(); + } + + ret = createCID( + await calculateHash( + Buffer.from(val), + config.data.hashing.algo, + config.data.hashing.bits + ) + ).toString(); + return ret; +} diff --git a/src/sensio/poe/operations/metaMake/config.js b/src/sensio/poe/operations/metaMake/config.js new file mode 100644 index 0000000000000000000000000000000000000000..b1cca7c43dd202ed115a6d0060c1b01579980777 --- /dev/null +++ b/src/sensio/poe/operations/metaMake/config.js @@ -0,0 +1,21 @@ + +/** + * Operation specification + */ +export default { + id: 'bafk2bzacedjcgyn7iz75u6fbiz4cmbgtc73be64qs5znmfhtaqsdnm36p4qio', + data: { + op:'meta_make', + desc: 'Extract Make from Metadata', + hashing: { + algo: 'blake2b', + bits: 256, + skip: false, + }, + encoding: { + algo: 'hex', + prefix: true, + }, + ops: [], + }, +}; diff --git a/src/sensio/poe/operations/metaMake/index.js b/src/sensio/poe/operations/metaMake/index.js new file mode 100644 index 0000000000000000000000000000000000000000..d535d1bb2bb39302d44be77bb0584578c6658fef --- /dev/null +++ b/src/sensio/poe/operations/metaMake/index.js @@ -0,0 +1,28 @@ +import ExifReader from "exifreader"; +import { calculateHash, createCID } from "../../helpers"; +import config from "./config"; + +/** + * Execute metaMake + * @param {{file: Buffer, rawPixels: Buffer}} params + * @return string + */ +export default async function metaMake({ file, rawPixels }) { + let ret = ""; + let val = ""; + const tags = ExifReader.load(file); + const { Make } = tags; + + if (Make && Make.value) { + val = Make.value.toString(); + } + + ret = createCID( + await calculateHash( + Buffer.from(val), + config.data.hashing.algo, + config.data.hashing.bits + ) + ).toString(); + return ret; +} diff --git a/src/sensio/poe/operations/metaModel/config.js b/src/sensio/poe/operations/metaModel/config.js new file mode 100644 index 0000000000000000000000000000000000000000..b30b413adde7c0eacfa830829e3c292fe2be36e7 --- /dev/null +++ b/src/sensio/poe/operations/metaModel/config.js @@ -0,0 +1,21 @@ + +/** + * Operation specification + */ +export default { + id: 'bafk2bzacechqgoh4uejxav4c22csetm3elqh7ushcp6ejmisxlfztpuhfxuem', + data: { + op:'meta_model', + desc: 'Extract Model from Metadata', + hashing: { + algo: 'blake2b', + bits: 256, + skip: false, + }, + encoding: { + algo: 'hex', + prefix: true, + }, + ops: [], + }, +}; diff --git a/src/sensio/poe/operations/metaModel/index.js b/src/sensio/poe/operations/metaModel/index.js new file mode 100644 index 0000000000000000000000000000000000000000..d3bd8264857e778dfa74695e61a3c8fe31278464 --- /dev/null +++ b/src/sensio/poe/operations/metaModel/index.js @@ -0,0 +1,28 @@ +import ExifReader from "exifreader"; +import { calculateHash, createCID } from "../../helpers"; +import config from "./config"; + +/** + * Execute metaModel + * @param {{file: Buffer, rawPixels: Buffer}} params + * @return string + */ +export default async function metaModel({ file, rawPixels }) { + let ret = ""; + let val = ""; + const tags = ExifReader.load(file); + const { Model } = tags; + + if (Model && Model.value) { + val = Model.value.toString(); + } + + ret = createCID( + await calculateHash( + Buffer.from(val), + config.data.hashing.algo, + config.data.hashing.bits + ) + ).toString(); + return ret; +} diff --git a/src/sensio/poe/operations/metaOriginalDocumentId/config.js b/src/sensio/poe/operations/metaOriginalDocumentId/config.js new file mode 100644 index 0000000000000000000000000000000000000000..5467c3a64271b26e2ef89ca53162fb9afece4f96 --- /dev/null +++ b/src/sensio/poe/operations/metaOriginalDocumentId/config.js @@ -0,0 +1,21 @@ + +/** + * Operation specification + */ +export default { + id: 'bafk2bzacecjkupobs3bpluxluzg3oleimzeplqbf7forkqqglnronnjaxbgik', + data: { + op:'meta_original_document_id', + desc: 'Original Document ID. The common identifier for the original resource from which the.to_vec() current resource is derived. For example, if you save a resource to a different format, then save that one to another format, each save operationData should generate a new xmpMM:DocumentID that uniquely identifies the resource in that format, but should retain the ID of the source file here.', + hashing: { + algo: 'blake2b', + bits: 256, + skip: false, + }, + encoding: { + algo: 'hex', + prefix: true, + }, + ops: [], + }, +}; diff --git a/src/sensio/poe/operations/metaOriginalDocumentId/index.js b/src/sensio/poe/operations/metaOriginalDocumentId/index.js new file mode 100644 index 0000000000000000000000000000000000000000..635555deff9034237f5dfc303148f56c952ee09e --- /dev/null +++ b/src/sensio/poe/operations/metaOriginalDocumentId/index.js @@ -0,0 +1,30 @@ +import ExifReader from "exifreader"; +import { calculateHash, createCID } from "../../helpers"; +import { formatToUUID } from "../../proofs/execution"; +import config from "./config"; + +/** + * Execute metaOriginalDocumentId + * @param {{file: Buffer, rawPixels: Buffer}} params + * @return string + */ +export default async function metaOriginalDocumentId({ file, rawPixels }) { + let ret = ""; + let val = ""; + const tags = ExifReader.load(file); + const { OriginalDocumentID } = tags; + + if (OriginalDocumentID && OriginalDocumentID.value) { + // in most of my photos this is uuid but without the - + val = formatToUUID(OriginalDocumentID.value); + } + + ret = createCID( + await calculateHash( + Buffer.from(val), + config.data.hashing.algo, + config.data.hashing.bits + ) + ).toString(); + return ret; +} diff --git a/src/sensio/poe/operations/metaSerialNumber/config.js b/src/sensio/poe/operations/metaSerialNumber/config.js new file mode 100644 index 0000000000000000000000000000000000000000..550da1a11355c60e7447d480f2a6370dc69153dc --- /dev/null +++ b/src/sensio/poe/operations/metaSerialNumber/config.js @@ -0,0 +1,21 @@ + +/** + * Operation specification + */ +export default { + id: 'bafk2bzacedmu4k7cl6jo7vtkbpduhtx2ilkm7qcqmc4xizy5kfzoiisn4eyrm', + data: { + op:'meta_serial_number', + desc: 'Extract SerialNumber from Metadata', + hashing: { + algo: 'blake2b', + bits: 256, + skip: false, + }, + encoding: { + algo: 'hex', + prefix: true, + }, + ops: [], + }, +}; diff --git a/src/sensio/poe/operations/metaSerialNumber/index.js b/src/sensio/poe/operations/metaSerialNumber/index.js new file mode 100644 index 0000000000000000000000000000000000000000..9204996dbff503720055f3f410d608d1063ef708 --- /dev/null +++ b/src/sensio/poe/operations/metaSerialNumber/index.js @@ -0,0 +1,28 @@ +import ExifReader from "exifreader"; +import { calculateHash, createCID } from "../../helpers"; +import config from "./config"; + +/** + * Execute metaSerialNumber + * @param {{file: Buffer, rawPixels: Buffer}} params + * @return string + */ +export default async function metaSerialNumber({ file, rawPixels }) { + let ret = ""; + let val = ""; + const tags = ExifReader.load(file); + const { SerialNumber } = tags; + + if (SerialNumber && SerialNumber.value) { + val = SerialNumber.value.toString(); + } + + ret = createCID( + await calculateHash( + Buffer.from(val), + config.data.hashing.algo, + config.data.hashing.bits + ) + ).toString(); + return ret; +} diff --git a/src/sensio/poe/operations/metadataHash/config.js b/src/sensio/poe/operations/metadataHash/config.js new file mode 100644 index 0000000000000000000000000000000000000000..d1714268ed01d75e0e741305c91644da8f5bbf36 --- /dev/null +++ b/src/sensio/poe/operations/metadataHash/config.js @@ -0,0 +1,21 @@ + +/** + * Operation specification + */ +export default { + id: 'bafk2bzacebhohew62aplnobbe6sqb655gnlntyc2nvxbyrdpvomfm3po7iu6m', + data: { + op:'metadata_hash', + desc: 'Hash of full unchanged metadata buffer (or similar). Without raw pixels', + hashing: { + algo: 'blake2b', + bits: 256, + skip: false, + }, + encoding: { + algo: 'hex', + prefix: true, + }, + ops: [], + }, +}; diff --git a/src/sensio/poe/operations/metadataHash/index.js b/src/sensio/poe/operations/metadataHash/index.js new file mode 100644 index 0000000000000000000000000000000000000000..5aee27744e6d005044d778c23a2e121399238f3c --- /dev/null +++ b/src/sensio/poe/operations/metadataHash/index.js @@ -0,0 +1,23 @@ +import { stringToU8a } from "@polkadot/util"; +import ExifReader from "exifreader"; +import { calculateHash, createCID, pack } from "../../helpers"; +import config from "./config"; + +/** + * Execute metadataHash + * @param {{file: Buffer, rawPixels: Buffer}} params + * @return string + */ +export default async function metadataHash({ file, rawPixels }) { + let ret = ""; + const tags = ExifReader.load(file); + + ret = createCID( + await calculateHash( + Buffer.from(stringToU8a(pack(tags))), + config.data.hashing.algo, + config.data.hashing.bits + ) + ).toString(); + return ret; +} diff --git a/src/sensio/poe/operations/perceptualHash/config.js b/src/sensio/poe/operations/perceptualHash/config.js new file mode 100644 index 0000000000000000000000000000000000000000..12713b5847a517b87a464a7eb19207479d83c40d --- /dev/null +++ b/src/sensio/poe/operations/perceptualHash/config.js @@ -0,0 +1,21 @@ + +/** + * Operation specification + */ +export default { + id: 'bafk2bzaced2wspmdlikt43quhqlp5jwcozrzzxd23p3d5zcnbkdv35dyrjeka', + data: { + op:'perceptual_hash', + desc: 'Perceptual hash calculation, currently implementing http://blockhash.io/', + hashing: { + algo: '', + bits: 0, + skip: true, + }, + encoding: { + algo: 'hex', + prefix: true, + }, + ops: [], + }, +}; diff --git a/src/sensio/poe/operations/perceptualHash/index.js b/src/sensio/poe/operations/perceptualHash/index.js new file mode 100644 index 0000000000000000000000000000000000000000..cddabd088b9fddfef2ae615252ab306aaeec6ee7 --- /dev/null +++ b/src/sensio/poe/operations/perceptualHash/index.js @@ -0,0 +1,19 @@ +import { calculatePhash } from "../../helpers"; +import config from "./config"; + +/** + * Execute perceptualHash + * @param {{file: Buffer, rawPixels: Buffer}} params + * @return string + */ +export default async function perceptualHash({ file }) { + let ret = ""; + const phash = await calculatePhash(file); + if (config.data.ops.length) { + throw new Error( + `ExecuteOps:: OP has more ops, we don't support that just yet.` + ); + } + ret = phash; + return ret; +} diff --git a/src/sensio/poe/operations/rawPixelsHash/config.js b/src/sensio/poe/operations/rawPixelsHash/config.js new file mode 100644 index 0000000000000000000000000000000000000000..583c20ec1094fe791144b88310f9519497255b90 --- /dev/null +++ b/src/sensio/poe/operations/rawPixelsHash/config.js @@ -0,0 +1,21 @@ + +/** + * Operation specification + */ +export default { + id: 'bafk2bzaceato2fbapmff54qgjgaba3v54qhz4g2ged54kebkio4hvowi6a3aa', + data: { + op:'raw_pixels_hash', + desc: 'Metadata must be removed and has must be created off of the RAW PIXELS', + hashing: { + algo: 'blake2b', + bits: 256, + skip: false, + }, + encoding: { + algo: 'hex', + prefix: true, + }, + ops: [], + }, +}; diff --git a/src/sensio/poe/operations/rawPixelsHash/index.js b/src/sensio/poe/operations/rawPixelsHash/index.js new file mode 100644 index 0000000000000000000000000000000000000000..680fc97536d28d848cfec3ae4ae82322b8c3745f --- /dev/null +++ b/src/sensio/poe/operations/rawPixelsHash/index.js @@ -0,0 +1,20 @@ +import { calculateHash, createCID } from "../../helpers"; +import config from "./config"; + +/** + * Execute rawPixelsHash + * @param {{file: Buffer, rawPixels: Buffer}} params + * @return string + */ +export default async function rawPixelsHash({ rawPixels }) { + let ret = ""; + + ret = createCID( + await calculateHash( + rawPixels, + config.data.hashing.algo, + config.data.hashing.bits + ) + ).toString(); + return ret; +} diff --git a/src/sensio/poe/pHashes/PhashInfo.js b/src/sensio/poe/pHashes/PhashInfo.js new file mode 100644 index 0000000000000000000000000000000000000000..eaf2a554979356c9761a814d0ba38c03c293c6a6 --- /dev/null +++ b/src/sensio/poe/pHashes/PhashInfo.js @@ -0,0 +1,28 @@ +import React, { useEffect, useState } from "react"; +import { Statistic } from "semantic-ui-react"; +import { useSubstrate } from "../../../substrate-lib"; +import { getPhashes } from "../helpers"; + +export default function PhashInfo(props) { + const { api } = useSubstrate(); + const [proofs, setProofs] = useState([]); + + useEffect(() => { + async function getInfo() { + try { + const r = await getPhashes(api); + setProofs(r); + } catch (e) { + console.error(e); + } + } + getInfo(); + + const i = setInterval(getInfo, 3000); + return () => { + clearInterval(i); + }; + }, [api]); + + return ; +} diff --git a/src/sensio/poe/proofs/CreateProof.js b/src/sensio/poe/proofs/CreateProof.js index 2c9c1b1fcbf765bd922c0d2d964bf0664ed47086..e900f4141961899cad91a86eb1746b9032939479 100644 --- a/src/sensio/poe/proofs/CreateProof.js +++ b/src/sensio/poe/proofs/CreateProof.js @@ -15,12 +15,18 @@ import { Segment, } from "semantic-ui-react"; import { useSubstrate } from "../../../substrate-lib"; -import { getRules, saveProofToNetwork } from "../helpers"; +import { + calculateSize, + getRules, + savePhashToNetwork, + saveProofToNetwork, +} from "../helpers"; import { base64ImageTo, executeRule, getImageSizes } from "./execution"; export default function CreateProof({ accountPair }) { const { api } = useSubstrate(); const history = useHistory(); + const [proofId, setProofId] = useState(); const [saving, setSaving] = useState(false); const [disableSaveToNetwork, setDisableSaveToNetwork] = useState(true); const [savingMessage, setSavingMessage] = useState(""); @@ -37,7 +43,6 @@ export default function CreateProof({ accountPair }) { const defaultPayloadHolder = { rule: null, - ruleId: null, creator: "", }; const [proofPayload, setProofPayload] = useState(defaultPayloadHolder); @@ -99,12 +104,17 @@ export default function CreateProof({ accountPair }) { function handleChangeTheRule(e, { value }) { setDisableSaveToNetwork(true); + + const foundRule = rules.find( + ({ rule: { id } }) => hexToString(id.toString()) === value + ); + setProofPayload({ ...proofPayload, - rule: rules[value].rule, - ruleId: rules[value].ruleId, + rule: foundRule.rule, }); } + function onHandleChange(e, { name, value }) { setDisableSaveToNetwork(true); @@ -175,23 +185,56 @@ export default function CreateProof({ accountPair }) { setErrorMessage(""); setSaving(true); setDisableSaveToNetwork(true); - setSavingMessage("Saving to the Network, check console for more info"); + setSavingMessage("Talking to Sensio"); try { - await saveProofToNetwork(api, proof, accountPair, ([error, res]) => { + calculateSize(proof); + saveProofToNetwork(api, proof, accountPair, ([error, res]) => { if (error) { console.error(error); setErrorMessage(error); return; } - const { success } = res; + const { success, proofId: pId, finalized } = res; addNewFeed(res); if (success) { + if (pId) { + setProofId(pId); + } + console.log("Waiting for the finalization"); + setSavingMessage("Waiting for the finalization"); + + // Independently add the Phash if that op exist to the network + } else if (finalized) { setProofSaved(true); setSaving(false); setDisableSaveToNetwork(false); } + }).then(({ finalized, proofId }) => { + if ( + proofPayload.rule.data.ops.find( + (o) => hexToString(o.data.op.toString()) === "perceptual_hash" + ) + ) { + setSaving(true); + setSavingMessage("Indexing"); + const s = proof.params.find( + (p) => hexToString(p.k.toString()) === "perceptual_hash" + ); + + savePhashToNetwork( + api, + api.createType("PhashInfo", { + pHash: s.v, // phash in the op is just a string, encode it + proofId: proofId, // this is byte array, encode it to string + }), + accountPair + ).then((r) => { + setSaving(false); + setSavingMessage(""); + }); + } }); } catch (error) { console.error(error); @@ -207,21 +250,21 @@ export default function CreateProof({ accountPair }) { try { const r = await getRules(api); if (r.length === 0) { - history.push("/poe/rules"); + history.push("/poe/create-rule"); } else { setRules(r); const _rules = r // eslint-disable-next-line array-callback-return - .map(({ rule, ruleId }, i) => { + .map(({ rule: { data: rule, id } }) => { if (rule.forWhat.toString() === "Photo") { const name = hexToString(rule.name.toString()); const v = rule.version.toNumber(); - + const key = hexToString(id.toString()); return { - key: i, + key, text: `v${v} - ${name}`, - value: i, + value: key, }; } }) @@ -355,10 +398,8 @@ export default function CreateProof({ accountPair }) { {proofSaved && ( )} @@ -383,10 +424,7 @@ export default function CreateProof({ accountPair }) {
                   {JSON.stringify(
                     {
-                      proof,
-                      bodyDecoded: JSON.parse(
-                        hexToString(proof.body.toString())
-                      ),
+                      ...proof,
                     },
                     null,
                     2
diff --git a/src/sensio/poe/proofs/ProofsList.js b/src/sensio/poe/proofs/ProofsList.js
index 45f51eab08e79c257aacbfe9b5ac2b66eaee85f0..0ffaa266804ac1d0c2c52cb43754dbfd58c55c83 100644
--- a/src/sensio/poe/proofs/ProofsList.js
+++ b/src/sensio/poe/proofs/ProofsList.js
@@ -1,21 +1,12 @@
 import { hexToString } from "@polkadot/util";
 import React, { useEffect, useState } from "react";
-import {
-  Button,
-  Container,
-  Divider,
-  Grid,
-  Icon,
-  Input,
-  List,
-} from "semantic-ui-react";
+import { Container, Divider, Grid, Icon, List } from "semantic-ui-react";
 import { useSubstrate } from "../../../substrate-lib";
 import CodeModal from "../CodeModal";
-import { getProofs } from "../helpers";
+import { calculateSize, getProofs } from "../helpers";
 
 export default function ProofsList({ accountPair }) {
   const { api } = useSubstrate();
-  const [allProofs, seAllProofs] = useState([]);
   const [proofs, setProofs] = useState([]);
   const [interval, setCurrentInterval] = useState(0);
 
@@ -23,27 +14,9 @@ export default function ProofsList({ accountPair }) {
     const getInfo = async () => {
       try {
         const p = await getProofs(api);
-        const _p = p.map(({ proof, accountId, blockNumber }) => {
-          const { proofId, body, createdAt, ruleId, forWhat } = proof;
 
-          return {
-            proof: {
-              ...proof,
-              body: {
-                ...JSON.parse(hexToString(body.toString())),
-                ruleId: hexToString(ruleId.toString()),
-              },
-              proofId: hexToString(proofId.toString()),
-              ruleId: hexToString(ruleId.toString()),
-              forWhat: forWhat.toString(),
-              createdAt: new Date(createdAt.toNumber()),
-            },
-            accountId: accountId.toString(),
-            blockNumber,
-          };
-        });
-        seAllProofs(_p);
-        setProofs(_p);
+        calculateSize(p);
+        setProofs(p);
       } catch (e) {
         console.error(e);
       }
@@ -58,15 +31,6 @@ export default function ProofsList({ accountPair }) {
     };
   }, [api, interval]);
 
-  function handleSearch(e, { name, value }) {
-    if (!value) {
-      setProofs(allProofs);
-      return;
-    }
-    const ps = proofs.filter((p) => p.proof.proofId === value);
-    setProofs(ps);
-  }
-
   return (
     
       
@@ -75,31 +39,20 @@ export default function ProofsList({ accountPair }) {
             

PoE Rules

- - - - - - - - + {proofs.length !== 0 && ( - {proofs.map(({ proof, accountId, blockNumber }, i) => { - const { proofId, body, createdAt, forWhat } = proof; + {proofs.map(({ proof, accountId }, i) => { + const { + id, + data: { params, creator, forWhat }, + } = proof; + const proofId = hexToString(id.toString()); return ( - + - {forWhat} {proofId} + {forWhat.toString()}ID: {proofId}
- Owned by: {body.owner} -
-
- Created at:{" "} - {createdAt.toISOString()} + Owned by:{" "} + {hexToString(creator.toString())}
Assigned to the account {accountId} @@ -137,7 +87,7 @@ export default function ProofsList({ accountPair }) {
-                            {JSON.stringify(body.params, null, 2)}
+                            {JSON.stringify(params, null, 2)}
                           
diff --git a/src/sensio/poe/proofs/execution.js b/src/sensio/poe/proofs/execution.js index bfbdec5d9eb52d74b6014bfa7b4855d3fae1912e..dc1695e2a2f39bc0e56593c5d201f36e9479f2cd 100644 --- a/src/sensio/poe/proofs/execution.js +++ b/src/sensio/poe/proofs/execution.js @@ -1,14 +1,6 @@ -import { hexToString, stringToU8a } from "@polkadot/util"; -import ExifReader from "exifreader"; +import { hexToString, stringCamelCase } from "@polkadot/util"; import { imageSize } from "image-size"; -import { - calculateHash, - calculatePhash, - createCID, - elapsedTime, - objectToString, -} from "../helpers"; - +import { calculateHash, createCID, elapsedTime } from "../helpers"; /** * Return the sizes of an image * @return {{height: number, width: number, type: string}} @@ -22,7 +14,7 @@ export function getImageSizes(fileReader) { /** * Transform base64 image path to array buffer - * @return {Buffer | ArrayBuffer} + * @return {Buffer | ArrayBuffer | Binary} * @param {*} base64 */ export function base64ImageTo(base64, toWhat) { @@ -56,352 +48,46 @@ export function base64ImageTo(base64, toWhat) { * metadata_hash - hex encoded * perceptual_hash - hex encoded ??? * - * @param {Rule} rule + * @param {[Operation]} operations * @param {Buffer} file - * @param {Buffer} fileWithoutMetadata + * @param {Buffer} rawPixels * @return {Promise} */ export async function executeOps(operations, file, rawPixels) { - const tags = ExifReader.load(file); return Promise.all( operations.map(async (operation) => { - const { op: OperationName, hashAlgo, hashBits, ops } = operation; - // eslint-disable-next-line prefer-const - let ret = "", - b, - h, - mh; - const op = hexToString(OperationName.toString()); - const hashAlgoDecoded = hexToString(hashAlgo.toString()); - const hashBitsDecoded = hashBits.toNumber(); - - switch (op) { - case "perceptual_hash": - const phash = await calculatePhash(file); - if (ops.length) { - throw new Error( - `ExecuteOps:: OP has more ops, we don't support that just yet.` - ); - } - ret = phash; - break; - case "meta_image_description": - ret = h; - break; - case "metadata_hash": - b = stringToU8a(objectToString(tags)); - mh = await calculateHash(b, hashAlgoDecoded, hashBitsDecoded); - h = createCID(mh); - if (ops.length) { - throw new Error( - `ExecuteOps:: OP has more ops, we don't support that just yet.` - ); - } - ret = h.toString(); - break; - case "raw_pixels_hash": - // here we should take the raw pixel hash only - mh = await calculateHash(file, hashAlgoDecoded, hashBitsDecoded); - h = createCID(mh); - - if (ops.length) { - throw new Error( - `ExecuteOps:: OP has more ops, we don't support that just yet.` - ); - } - ret = h.toString(); - break; - - case "meta_document_id": - const { DocumentID } = tags; - if (DocumentID && DocumentID.value) { - h = createCID( - await calculateHash( - Buffer.from(DocumentID.value?.split(":")[1]), - hashAlgoDecoded, - hashBitsDecoded - ) - ); - if (ops.length) { - throw new Error( - `ExecuteOps:: OP has more ops, we don't support that just yet.` - ); - } - ret = h.toString(); - } - break; + const { + data: { op: OperationName }, + } = operation; - case "meta_original_document_id": - const { OriginalDocumentID } = tags; - if (OriginalDocumentID && OriginalDocumentID.value) { - h = createCID( - await calculateHash( - Buffer.from(formatToUUID(OriginalDocumentID.value)), - hashAlgoDecoded, - hashBitsDecoded - ) - ); - if (ops.length) { - throw new Error( - `ExecuteOps:: OP has more ops, we don't support that just yet.` - ); - } - ret = h.toString(); - } - break; - case "meta_create_date": - const { CreateDate } = tags; - if (CreateDate && CreateDate.value) { - h = createCID( - await calculateHash( - Buffer.from(CreateDate.value.toString()), - hashAlgoDecoded, - hashBitsDecoded - ) - ); - if (ops.length) { - throw new Error( - `ExecuteOps:: OP has more ops, we don't support that just yet.` - ); - } - ret = h.toString(); - } - break; - case "meta_date_time_original": - const { DateTimeOriginal } = tags; - if (DateTimeOriginal && DateTimeOriginal.value) { - h = createCID( - await calculateHash( - Buffer.from(DateTimeOriginal.value.toString()), - hashAlgoDecoded, - hashBitsDecoded - ) - ); - if (ops.length) { - throw new Error( - `ExecuteOps:: OP has more ops, we don't support that just yet.` - ); - } - ret = h.toString(); - } - break; - case "meta_copyright": - const { Copyright } = tags; - if (Copyright && Copyright.value) { - h = createCID( - await calculateHash( - Buffer.from(Copyright.value.toString()), - hashAlgoDecoded, - hashBitsDecoded - ) - ); - if (ops.length) { - throw new Error( - `ExecuteOps:: OP has more ops, we don't support that just yet.` - ); - } - ret = h.toString(); - } - break; - case "meta_serial_number": - const { SerialNumber } = tags; - if (SerialNumber && SerialNumber.value) { - h = createCID( - await calculateHash( - Buffer.from(SerialNumber.value.toString()), - hashAlgoDecoded, - hashBitsDecoded - ) - ); - if (ops.length) { - throw new Error( - `ExecuteOps:: OP has more ops, we don't support that just yet.` - ); - } - ret = h.toString(); - } - break; - case "meta_make": - const { Make } = tags; - if (Make && Make.value) { - h = createCID( - await calculateHash( - Buffer.from(Make.value.toString()), - hashAlgoDecoded, - hashBitsDecoded - ) - ); - if (ops.length) { - throw new Error( - `ExecuteOps:: OP has more ops, we don't support that just yet.` - ); - } - ret = h.toString(); - } - break; - case "meta_model": - const { Model } = tags; - if (Model && Model.value) { - h = createCID( - await calculateHash( - Buffer.from(Model.value.toString()), - hashAlgoDecoded, - hashBitsDecoded - ) - ); - if (ops.length) { - throw new Error( - `ExecuteOps:: OP has more ops, we don't support that just yet.` - ); - } - ret = h.toString(); - } - break; - case "meta_lens_serial_number": - const { LensSerialNumber } = tags; - if (LensSerialNumber && LensSerialNumber.value) { - h = createCID( - await calculateHash( - Buffer.from(LensSerialNumber.value.toString()), - hashAlgoDecoded, - hashBitsDecoded - ) - ); - if (ops.length) { - throw new Error( - `ExecuteOps:: OP has more ops, we don't support that just yet.` - ); - } - ret = h.toString(); - } - break; - case "meta_lens_info": - const { LensInfo } = tags; - if (LensInfo && LensInfo.value) { - h = createCID( - await calculateHash( - Buffer.from(LensInfo.value.toString()), - hashAlgoDecoded, - hashBitsDecoded - ) - ); - if (ops.length) { - throw new Error( - `ExecuteOps:: OP has more ops, we don't support that just yet.` - ); - } - ret = h.toString(); - } - break; - case "meta_lens_model": - const { LensModel } = tags; - if (LensModel && LensModel.value) { - h = createCID( - await calculateHash( - Buffer.from(LensModel.value.toString()), - hashAlgoDecoded, - hashBitsDecoded - ) - ); - if (ops.length) { - throw new Error( - `ExecuteOps:: OP has more ops, we don't support that just yet.` - ); - } - ret = h.toString(); - } - break; - - case "poe_camera_id": - let ecOps = await executeOps(ops, file, rawPixels); - - h = createCID( - await calculateHash( - Buffer.from(objectToString(buildBody(ops, ecOps))), - hashAlgoDecoded, - hashBitsDecoded - ) - ); - ret = h.toString(); - break; - case "poe_lens_id": - let elOps = await executeOps(ops, file, rawPixels); - - h = createCID( - await calculateHash( - Buffer.from(objectToString(buildBody(ops, elOps))), - hashAlgoDecoded, - hashBitsDecoded - ) - ); - ret = h.toString(); - break; - - default: - throw new Error("No default operation accepted." + op); - } - return ret; + const op = hexToString(OperationName.toString()); + const t = await import(`../operations/${stringCamelCase(op)}`); + const output = await t.default({ rawPixels, file }); + console.debug(`Executed op ${op}:`, output); + return { k: op, v: output }; }) ); } -/** - * Map the executed ops to the ops itself. could be good thing for refactor in the future - * @param rule {Rule} - * @param executedOps {string[]} - * @return { { [ k:string ] : any } } - */ - -function buildBody(ops, executedOps) { - // eslint-disable-next-line prefer-const - let body = {}; - ops.forEach((op, i) => { - const opName = hexToString(op.op.toString()); - return (body[opName] = executedOps[i]); - }); - return body; -} - /** * * @param {Rule} rule - * @param {string} ruleId - * @param {any} body - * @param {string} creator - * @param {*} prev - * @return {Promise<{ - "proofId": string, // hex encoded string - "body": string, // hex encoded string - "createdAt": string, // hex encoded string - "prev": string, // hex encoded string - "ruleId": string, // hex encoded string - "forWhat": string, // ForWhat enum - }>} + * @param {string} ruleId Comes decoded + * @param {any} params + * @param {string} owner + * @param {string} prev + * @return {Promise} */ -async function buildProof(api, rule, ruleId, params = {}, owner, prev = "") { - const ruleIdDecoded = hexToString(ruleId); - const body = { - params, - owner: createCID(await calculateHash(owner)).toString(), - ruleId: ruleIdDecoded, - forWhat: rule.forWhat, - }; - - const bodyString = objectToString(body); - const proofId = createCID( - await calculateHash(Buffer.from(bodyString)) - ).toString(); - - const proof = { - proofId, - body: bodyString, - createdAt: Date.now(), +async function buildProof(api, rule, params = {}, owner, prev = "") { + const proofData = { + ruleId: rule.id, prev, - ruleId: ruleIdDecoded, - forWhat: rule.forWhat.toString(), + creator: createCID(await calculateHash(owner)).toString(), + forWhat: rule.data.forWhat.toString(), + params, }; - return api.createType("Proof", proof); + return api.createType("ProofData", proofData); } /** @@ -437,21 +123,16 @@ export function formatToUUID(uuid) { * @param {function} cb * @return {Promise<{payload: any, proof:string}>} */ -export async function executeRule(api, params, cb) { +export async function executeRule(api, { rule, rawPixels, file, creator }, cb) { try { const mainPerf = Date.now(); - const { rule, rawPixels, file, ruleId, creator } = params; let perf = Date.now(); - const ops = await executeOps(rule.ops, file, rawPixels); + const params = await executeOps(rule.data.ops, file, rawPixels); cb([null, { message: "Executing OPS", elapsedTime: elapsedTime(perf) }]); perf = Date.now(); - const body = buildBody(rule.ops, ops); - cb([null, { message: "Building Body", elapsedTime: elapsedTime(perf) }]); - - perf = Date.now(); - const payload = await buildProof(api, rule, ruleId, body, creator); + const payload = await buildProof(api, rule, params, creator); cb([null, { message: "Building Proof", elapsedTime: elapsedTime(perf) }]); cb([ @@ -462,7 +143,7 @@ export async function executeRule(api, params, cb) { elapsedTime: elapsedTime(mainPerf), }, ]); - + console.debug(payload); return payload; } catch (error) { console.error(error); diff --git a/src/sensio/poe/rules/CreateRule.js b/src/sensio/poe/rules/CreateRule.js index e39a839c352fe6c121d879c0074f6a077f6f0d08..92353e9e1e59e4ead4ed8876fcff7febd8f17534 100644 --- a/src/sensio/poe/rules/CreateRule.js +++ b/src/sensio/poe/rules/CreateRule.js @@ -1,6 +1,5 @@ -import { nanoid } from "nanoid"; +import { hexToString } from "@polkadot/util"; import React, { useEffect, useState } from "react"; -import { useHistory } from "react-router-dom"; import { Container, Dimmer, @@ -12,142 +11,14 @@ import { } from "semantic-ui-react"; import { CUSTOM_TYPES } from "../../../config/common.json"; import { useSubstrate } from "../../../substrate-lib"; -import { - createRulePayload, - findDuplicates, - saveRuleToNetwork, -} from "../helpers"; +import { getOperations, saveRuleToNetwork } from "../helpers"; import CreateRuleChooser from "./CreateRuleChooser"; -import { cameraRule, lensRule, photoRule } from "./defaultRules"; function formatForWhat() { return CUSTOM_TYPES.ForWhat._enum.map((m, i) => { return { key: i, text: m, value: i }; }); } -function buildCurrentOperationNames() { - let ops = [ - ...cameraRule.ops, - ...lensRule.ops, - ...photoRule.ops, - ...[cameraRule.buildParams, lensRule.buildParams, photoRule.buildParams], - ]; - - let ret = []; - ops.forEach((o, i) => { - if (ret.find((r) => r.value === o.op)) { - return; - } - - // for now don't include ops that contain other ops, they are special - if (o.ops.length !== 0) { - return; - } - ret.push({ key: i, text: o.op, value: o.op }); - }); - - return ret; -} - -const operations = buildCurrentOperationNames(); - -/** - * Functional component for creating the Operation - * @param {*} - */ -function Operation({ op, onHandleChange, allDisabled, defaultValue }) { - return ( -
- - - - - - - - - - - - -
- ); -} - -/** - * Create default structure for the Operation - */ -function createDefaultOperation() { - return { - _id: nanoid(), - name: "Change me name", - desc: "Longer description", - op: operations[0].value, - hashAlgo: "blake2b", - hashBits: 256, - encodeAlgo: "hex", - prefix: "0x", - ops: [], - }; -} export default function CreateRule(props) { const defaultVal = { @@ -157,24 +28,14 @@ export default function CreateRule(props) { creator: "urn:pgp:d1f5e247a976f1e3c14f0a437a6db9962ef3978e", forWhat: 1, ops: [], - buildParams: { - name: "Build Params", - desc: - "Build the payload in a way we need for this rule. Take all the values from each of the `ops", - op: "build_params", - hashAlgo: "", - hashBits: 0, - encodeAlgo: "hex", - prefix: "0x", - ops: [], - }, }; - const history = useHistory(); - const { api } = useSubstrate(); const { accountPair } = props; + const [ops, setOps] = useState([]); + const [ruleOps, setRuleOps] = useState([]); + const [selectableOps, setSelectableOps] = useState([]); const [rule, setRule] = useState(defaultVal); const [errorMessage, setErrorMessage] = useState(""); const [readyToSubmit, setReadyToSubmit] = useState(false); @@ -188,31 +49,20 @@ export default function CreateRule(props) { async function handleSubmit() { setErrorMessage(""); setSaving(true); - const opNames = rule.ops.map((o) => { - return o.op; - }); + const opsForRule = ops + .filter(({ op }) => ruleOps.includes(hexToString(op.id.toString()))) + .map(({ op }) => op); - const dupes = findDuplicates(opNames); - - if (dupes.length !== 0) { - setErrorMessage( - `Duplicates in ops, check the Operation field in Operations. Dupes: [${dupes.join( - "," - )}]` - ); - setSaving(false); - } - const ruleOpsClean = rule.ops.map(({ _id, ...rest }) => { - return rest; - }); - const rulePayload = await createRulePayload(api, { + const ruleData = api.createType("RuleData", { ...rule, - ops: ruleOpsClean, + ops: opsForRule, }); + console.log(ruleData); + await saveRuleToNetwork( api, - rulePayload, + ruleData, accountPair, ([error, message, success]) => { if (error) { @@ -223,70 +73,68 @@ export default function CreateRule(props) { setSavingMessage(message); if (success) { setSavingMessage(message); - setTimeout(() => { - setSaving(false); - history.push("/poe/rules"); - }, 700); + setSaving(false); } } } ); } - function handleValueChange(e, { name, value }) { + function handleOperationChange(e, { value }) { e.preventDefault(); - setRule({ - ...rule, - [name]: value, - }); + // const op = ops.find(({ op }) => { + // hexToString(op.id.toString()) === value + // ? console.log("yea", hexToString(op.id.toString())) + // : console.log("nay", hexToString(op.id.toString())); + // }); + // console.log("find op", value, ops, op); + setRuleOps(value); } - - function handleOperationValueChange(e, { name, value }, opId) { + function handleValueChange(e, { name, value }) { e.preventDefault(); - const op = rule.ops.find((o) => o._id === opId); - const opIndex = rule.ops.findIndex((o) => o._id === opId); - const ops = rule.ops; - op[name] = value; - ops[opIndex] = op; - console.log(ops); setRule({ ...rule, - ops, + [name]: value, }); } - function handleChangeBuildParams(e, { name, value }) { - e.preventDefault(); - setRule({ - ...rule, - buildParams: { [name]: value }, - }); - } + useEffect(() => { + if (ruleOps.length >= 1) { + setReadyToSubmit(true); + } + }, [ruleOps]); - function addNewOperation() { - const ops = rule.ops; - ops.unshift(createDefaultOperation()); - setRule({ - ...rule, - ops, - }); - } + useEffect(() => { + async function getOps() { + const opsList = await getOperations(api); + setOps(opsList); + const ret = []; + opsList.forEach(({ op: { id, data: o } }, i) => { + const opId = hexToString(id.toString()); + const opName = hexToString(o.op.toString()); + if (ret.find((r) => r.value === opName)) { + return; + } - function deleteOperation(id) { - const ops = rule.ops.filter((f) => f._id !== id); - setRule({ - ...rule, - ops, - }); - } + // for now don't include ops that contain other ops, they are special + if (o.ops.length !== 0) { + return; + } - useEffect(() => { - if (rule.ops.length >= 1) { - setReadyToSubmit(true); + ret.push({ + key: opId, + text: opName.replace(/_/gi, " "), + value: opId, + }); + }); + setSelectableOps(ret); } - }, [rule]); + + getOps(); + }, [api, selectableOps]); + const MessageExampleFloating = () => ( {errorMessage} @@ -357,68 +205,17 @@ export default function CreateRule(props) { name="forWhat" /> -
-
-

Operations: {rule.ops.length}

- - Add new operation - -
- -
- {rule.ops.map((op, i) => ( -
-
-

#{i + 1}

-
-
- - handleOperationValueChange(e, t, op._id) - } - /> - {rule.ops.length !== 1 && ( - deleteOperation(op._id)} - color="red" - > - Delete operation :: {op._id} - - )} -
-
- ))} -
-
- - - -
-
-
-

Build Params

-
-
- o.value === rule.buildParams.op) - .value - } - onHandleChange={handleChangeBuildParams} - allDisabled - /> -
-
-
+ { + createDefaultRules(api, accountPair, ops).then((r) => { if (r) { setSavingMessage(""); setCreating(false); } }); } + + useEffect(() => { + async function run() { + const opsList = await getOperations(api); + setOps(opsList); + } + run(); + }, [api]); return ( - + {savingMessage} ... - - Or - + -
- -
- -
- - -
- -