{ "cells": [ { "cell_type": "markdown", "id": "3e4eb758", "metadata": {}, "source": [ "## Hello World" ] }, { "cell_type": "markdown", "id": "2460ecfc", "metadata": {}, "source": [ "In this demo notebook we will showcase a self-sovereign data scenario:\n", "\n", "- we will create a table in a shared data collection, that can be read by anyone in the network\n", "- mark some fields as personal information that is not to be shared\n", "- write a record\n", "- read it locally (and be able to see all fields)\n", "- read all records from a remote server\n", " \n", "The default Weavechain node installation is preconfigured to support this scenario (by connecting to a public weave, having a *shared* data collection defined and mapped to a in-process SQLite instance and read rights for that collection already given)." ] }, { "cell_type": "markdown", "id": "8e0dbccf", "metadata": {}, "source": [ "### 1. Create an API session" ] }, { "cell_type": "code", "execution_count": 1, "id": "d96cfcaf", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{\"res\":\"ok\",\"data\":\"pong 1674668207181\"}\n" ] } ], "source": [ "import pandas as pd\n", "\n", "from weaveapi.records import *\n", "from weaveapi.options import *\n", "from weaveapi.filter import *\n", "from weaveapi.weaveh import *\n", "\n", "WEAVE_CONFIG = \"config/demo_client_local.config\"\n", "nodeApi, session = connect_weave_api(WEAVE_CONFIG)\n", "\n", "scope = \"shared\"\n", "table = \"directory\"" ] }, { "cell_type": "markdown", "id": "ac907012", "metadata": {}, "source": [ "### 2. Create a local table" ] }, { "cell_type": "markdown", "id": "7b4a5e29", "metadata": {}, "source": [ "- we drop the existing table if already existing and re-create it from scratch\n", "- a weavechain node can also connect to existing tables, reading their structure, but in this case we create it via the API" ] }, { "cell_type": "code", "execution_count": 2, "id": "17b212cf", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'res': 'ok', 'target': {'operationType': 'CREATE_TABLE', 'organization': 'weavedemo', 'account': 'weaveyh5R1ytoUCZnr3JjqMDfhUrXwqWC2EWnZX3q7krKLPcg', 'scope': 'shared', 'table': 'directory'}}\n" ] } ], "source": [ "layout = { \n", " \"columns\": { \n", " \"id\": { \"type\": \"LONG\", \"isIndexed\": True, \"isUnique\": True, \"isNullable\": False },\n", " \"name_nickname\": { \"type\": \"STRING\" },\n", " \"name_last\": { \"type\": \"STRING\" },\n", " \"name_first\": { \"type\": \"STRING\" },\n", " \"birthday\": { \"type\": \"STRING\" },\n", " \"email_personal\": { \"type\": \"STRING\" },\n", " \"phone_number\": { \"type\": \"STRING\" },\n", " \"address_country\": { \"type\": \"STRING\" },\n", " \"address_summary\": { \"type\": \"STRING\" },\n", " \"address_timezone\": { \"type\": \"STRING\" },\n", " \"linkedin_url\": { \"type\": \"STRING\" },\n", " \"discord_username\": { \"type\": \"STRING\" },\n", " \"telegram_username\": { \"type\": \"STRING\" },\n", " \"ethereum_wallet_address\": { \"type\": \"STRING\" }\n", " }, \n", " \"idColumnIndex\": 0, \n", " \"isLocal\": False,\n", " \"applyReadTransformations\": True\n", "}\n", "\n", "nodeApi.dropTable(session, scope, table).get()\n", "reply = nodeApi.createTable(session, scope, table, CreateOptions(False, False, layout)).get()\n", "print(reply)" ] }, { "cell_type": "markdown", "id": "03d82eca", "metadata": {}, "source": [ "### 3. Mark some fields for erasure" ] }, { "cell_type": "markdown", "id": "817c68c3", "metadata": {}, "source": [ "- the purpose is to protect certain fields when shared" ] }, { "cell_type": "code", "execution_count": 3, "id": "18be11b4", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'res': 'ok', 'target': {'operationType': 'UPDATE_LAYOUT', 'organization': 'weavedemo', 'account': 'weaveyh5R1ytoUCZnr3JjqMDfhUrXwqWC2EWnZX3q7krKLPcg', 'scope': 'shared', 'table': 'directory'}}\n" ] } ], "source": [ "reply = nodeApi.getTableDefinition(session, scope, table).get()\n", "#print(reply)\n", "layout = json.loads(reply[\"data\"])[\"layout\"]\n", "layout[\"columns\"]\n", "\n", "newLayout = layout.copy()\n", "del newLayout[\"layout\"]\n", "del newLayout[\"indexes\"]\n", "del newLayout[\"columnNames\"]\n", "newLayout[\"columns\"] = { i[\"columnName\"]: i for i in layout[\"columns\"]}\n", "\n", "newLayout[\"columns\"][\"phone_number\"][\"readTransform\"] = \"ERASURE\"\n", "newLayout[\"columns\"][\"address_summary\"][\"readTransform\"] = \"ERASURE\"\n", "newLayout[\"columns\"][\"ethereum_wallet_address\"][\"readTransform\"] = \"ERASURE\"\n", "newLayout[\"columns\"][\"birthday\"][\"readTransform\"] = \"ERASURE\"\n", "\n", "reply = nodeApi.updateLayout(session, scope, table, newLayout).get()\n", "print(reply)" ] }, { "cell_type": "markdown", "id": "256f11bb", "metadata": {}, "source": [ "### 4. Write a record in the local storage" ] }, { "cell_type": "code", "execution_count": 4, "id": "ddea986d", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'res': 'ok', 'target': {'operationType': 'WRITE', 'organization': 'weavedemo', 'account': 'weaveyh5R1ytoUCZnr3JjqMDfhUrXwqWC2EWnZX3q7krKLPcg', 'scope': 'shared', 'table': 'directory'}, 'data': 'weaveyh5R1ytoUCZnr3JjqMDfhUrXwqWC2EWnZX3q7krKLPcg,USr/yA9isOGMQ3/gNenSpZLi2VhwIP9x6UacGTkuBVc=,4YjeYJpUrcrpCf4oTEvuAspeJYiQYEy1AvTgbLsTo8UNzYbHKbNiV6jZihb7si5yc8MbXcr16kmGrieJgNW8s75e'}\n" ] } ], "source": [ "records = Records(table, [ \n", " [ 1, 'Nickname', 'Last Name', 'First name', '1980-01-01', 'email@gmail.com', '+40712345678', 'US', 'Secret', 'EST', 'https://www.linkedin.com/in/linkedin/', 'discord#1234', '@telegram', '0xwallet' ]\n", "])\n", "reply = nodeApi.write(session, scope, records, WRITE_DEFAULT).get()\n", "print(reply)" ] }, { "cell_type": "markdown", "id": "b1c4be2c", "metadata": {}, "source": [ "### 5. Read the local record, from the local storage" ] }, { "cell_type": "markdown", "id": "996711b0", "metadata": {}, "source": [ "- since we read with the owner key and from the local node, we expect the records to have all fields visible" ] }, { "cell_type": "code", "execution_count": 5, "id": "cb2dd661", "metadata": { "scrolled": false }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
idname_nicknamename_lastname_firstbirthdayemail_personalphone_numberaddress_countryaddress_summaryaddress_timezonelinkedin_urldiscord_usernametelegram_usernameethereum_wallet_address
01NicknameLast NameFirst name1980-01-01email@gmail.com+40712345678USSecretESThttps://www.linkedin.com/in/linkedin/discord#1234@telegram0xwallet
\n", "
" ], "text/plain": [ " id name_nickname name_last name_first birthday email_personal \\\n", "0 1 Nickname Last Name First name 1980-01-01 email@gmail.com \n", "\n", " phone_number address_country address_summary address_timezone \\\n", "0 +40712345678 US Secret EST \n", "\n", " linkedin_url discord_username telegram_username \\\n", "0 https://www.linkedin.com/in/linkedin/ discord#1234 @telegram \n", "\n", " ethereum_wallet_address \n", "0 0xwallet " ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "scope = \"shared\"\n", "table = \"directory\"\n", "\n", "reply = nodeApi.read(session, scope, table, None, READ_DEFAULT_NO_CHAIN).get()\n", "#print(reply)\n", "df = pd.DataFrame(reply[\"data\"])\n", "\n", "df.head()" ] }, { "cell_type": "markdown", "id": "49d86941", "metadata": {}, "source": [ "### 5. Connect to proxy server" ] }, { "cell_type": "code", "execution_count": 6, "id": "6f52c82f", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{\"res\":\"ok\",\"data\":\"pong 1674668209764\"}\n" ] } ], "source": [ "WEAVE_CONFIG_REMOTE = \"config/demo_client_remote.config\"\n", "nodeApi2, session2 = connect_weave_api(WEAVE_CONFIG_REMOTE)" ] }, { "cell_type": "markdown", "id": "9eb7b533", "metadata": {}, "source": [ "### 6. Read all shared records" ] }, { "cell_type": "markdown", "id": "dd6c037d", "metadata": {}, "source": [ "- we expect the records that we don't own to have certain fields erased for privacy" ] }, { "cell_type": "code", "execution_count": 7, "id": "5015c87f", "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
idname_nicknamename_lastname_firstbirthdayemail_personalphone_numberaddress_countryaddress_summaryaddress_timezonelinkedin_urldiscord_usernametelegram_usernameethereum_wallet_address_nodeKey
01.0NicknameLast NameFirst nameemail@gmail.comUSESThttps://www.linkedin.com/in/linkedin/discord#1234@telegramweaveyh5R1ytoUCZnr3JjqMDfhUrXwqWC2EWnZX3q7krKLPcg
\n", "
" ], "text/plain": [ " id name_nickname name_last name_first birthday email_personal \\\n", "0 1.0 Nickname Last Name First name email@gmail.com \n", "\n", " phone_number address_country address_summary address_timezone \\\n", "0 US EST \n", "\n", " linkedin_url discord_username telegram_username \\\n", "0 https://www.linkedin.com/in/linkedin/ discord#1234 @telegram \n", "\n", " ethereum_wallet_address _nodeKey \n", "0 weaveyh5R1ytoUCZnr3JjqMDfhUrXwqWC2EWnZX3q7krKLPcg " ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "scope = \"shared\"\n", "table = \"directory\"\n", "\n", "reply = nodeApi2.read(session2, scope, table, None, READ_DEFAULT_MUX_NO_CHAIN).get()\n", "#print(reply)\n", "df = pd.DataFrame(reply[\"data\"].get(\"result\"))\n", "\n", "df.head()" ] }, { "cell_type": "markdown", "id": "8200fb82", "metadata": {}, "source": [ "### 7. Check the local node for all operations on the shared data" ] }, { "cell_type": "code", "execution_count": 9, "id": "9532baa7", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
historyrecordrecordId
1[{'account': 'weaveyh5R1ytoUCZnr3JjqMDfhUrXwqW...[1, Nickname, Last Name, First name, 1980-01-0...1
\n", "
" ], "text/plain": [ " history \\\n", "1 [{'account': 'weaveyh5R1ytoUCZnr3JjqMDfhUrXwqW... \n", "\n", " record recordId \n", "1 [1, Nickname, Last Name, First name, 1980-01-0... 1 " ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
accountoperationtimestampipapiKey
0weaveyh5R1ytoUCZnr3JjqMDfhUrXwqWC2EWnZX3q7krKLPcgwrite1674668207300999936127.0.0.1d2f86331322b497fb644e09862b681fdc2a9aa5180d0011c
1weaveyh5R1ytoUCZnr3JjqMDfhUrXwqWC2EWnZX3q7krKLPcgread1674668207315000064127.0.0.1d2f86331322b497fb644e09862b681fdc2a9aa5180d0011c
2weavexUTKAe7J5faqmiq94DXXWntyRBA8bPwmrUbCtebxWd3fread167466821135399987234.28.85.1361e2f9e5e7c9f406081256bf5709dd1eae5ccf2e324469e1c
\n", "
" ], "text/plain": [ " account operation \\\n", "0 weaveyh5R1ytoUCZnr3JjqMDfhUrXwqWC2EWnZX3q7krKLPcg write \n", "1 weaveyh5R1ytoUCZnr3JjqMDfhUrXwqWC2EWnZX3q7krKLPcg read \n", "2 weavexUTKAe7J5faqmiq94DXXWntyRBA8bPwmrUbCtebxWd3f read \n", "\n", " timestamp ip \\\n", "0 1674668207300999936 127.0.0.1 \n", "1 1674668207315000064 127.0.0.1 \n", "2 1674668211353999872 34.28.85.136 \n", "\n", " apiKey \n", "0 d2f86331322b497fb644e09862b681fdc2a9aa5180d0011c \n", "1 d2f86331322b497fb644e09862b681fdc2a9aa5180d0011c \n", "2 1e2f9e5e7c9f406081256bf5709dd1eae5ccf2e324469e1c " ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "reply = nodeApi.history(session, scope, table, None, HISTORY_DEFAULT).get()\n", "#print(reply)\n", "df = pd.DataFrame(reply[\"data\"]).transpose()\n", "display(df)\n", "\n", "pd.DataFrame(df.iloc[0][\"history\"])" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.16" } }, "nbformat": 4, "nbformat_minor": 5 }