# Fabric Private Data PoC - Draft 1 ## dependencies - fabric v2 with implicit private collections - hlf-proxy:1.7.0, endpoints: blocks, invoke y query ## chaincode ### model object | storage | key pattern | key e.g. | value type | value e. g. --- | --- | --- | --- | --- | --- publicdoc | public | `{countrycode}:{docsubid}` | `AR:1234` | json | `{"version":1, "status":2}` acl | public | `{doc.key}#acl` | `AR:1234#acl` | []string | `["BR", "PY"]` event | public | `{doc.key}#event:{countrycode}:{eventsubid}` | `AR:1234#event:BR:0001` | json | `{"id":"AR:1234", "k1":"v1", "k2":"V2"}` privatedoc | `_implicit_org_{MSPID}` | `{doc.key}#v{version}` | `AR:1234#v2` | json | `{"id":"AR:1234", "k1":"v1", "k2":"V2"}` ### function: AddEvent param | mandatory | type | e.g. --- | --- | --- | --- arg1:docid | x | string | `AR:1234` arg1:eventsubid | x | string | `1`, `2` arg2:eventvalue | x | json | `{"eventtype":1,"docstatus":1,"date":"2021-11-01T14:10:23.Z000"}` arg3:acl | | []string |`["BR", "PY"]` trasient1:privatedoc | | json | `{"id":"AR:1234", "k1":"v1", "k2":"V2"}` Rules: - first event of new doc requires `privatedoc` (creation) - only creator can to change `privatedoc` - any event over an existing doc requires its `acl` contains signer Flow: ``` publicdoc = shim.GetState(arg-docid) currentacl = arg-acl currentversion = 0 if publicdoc == null: // new doc shim.PutState(docid, {version: currentversion, status: arg-eventvalue.status}) else: // doc already exists if arg-acl == null: currentacl = shim.GetState(arg3-acl) aclCheck with currentacl if (arg-eventvalue.status != null and arg-eventvalue.status != publicdoc.docstatus) or trasient-privatedoc != null: if arg-eventvalue.status != null and arg-eventvalue.status != publicdoc.docstatus: publicdoc.status = arg-eventvalue.docstatus if transient-privatedoc != null: currentversion = publicdoc.version + 1 else: currentversion = publicdoc.version shim.PutState(arg1:docid, {version: currentversion, status: arg-eventvalue.status}) shim.PutState(event) if arg-acl != null: shim.PutState(arg-acl) if transient-privatedoc != null for owner and each org in currentacl shim.PutPrivateData(_implicit_org_{MSPID}, {arg-docid}#v{currentversion}, transient-privatedoc) ``` ## using hlf-proxy ### model - to copy all ledger into dababase table (_"block-consumer style"_) ``` CREATE TABLE XXXX_STATE ( APPLICATION VARCHAR2, // e. g. SINTIA BLOCK INTEGER, TX_ID VARCHAR2(100), TX_TIMESTAMP DATE, KEY VARCHAR2(100), VALUE VARCHAR2(4000) ); ``` - to copy private doc ``` CREATE TABLE XXX_PRIVATE_DOC TX_ID VARCHAR2(100), DOC_ID VARCHAR(100), DOC_VERSION INTEGER(3), DOC_CONTENT CLOB ) ``` ### Flow ``` newblock = GET hlf-proxy endpoint `blocks/{channel}/{blockNumber}?validTxsOnly` for each tx in newblock: if tx.chaincode == XXX and tx.function == 'AddEvent' and writeSet.size > 0: // Copy all ledger for item on tx.writeSet: db.saveWriteSet( newblock.block, tx.txid, ..., item.key, item.value ) // trying to recover private data docid = getDocidFromKey(tx.writeSet.item[0].key) publicdoc = filterPublicdocFromWriteSet(tx.writeSet) savedVersion = db.selectPrivatedocVersion(docid) if publicdoc != null and (savedVersion == null or savedVersion != publicdoc.version) pk = docid+'#v'+publicdoc.version privatedoc = POST hlf-proxy "query/{channel}/XXX" body {function:"GetPrivatedoc", args:[pk]} if OK // httpStatusCode 200 db.savePrivatedoc( tx.txid, docid, publicdoc.version, privatedoc ) ```