Compare commits

..

No commits in common. "master" and "draft" have entirely different histories.

4 changed files with 285 additions and 165 deletions

View File

@ -1,12 +0,0 @@
name: Push changes to upstream subtree
on: [push]
jobs:
trigger-index-workflow:
runs-on: ubuntu-latest
steps:
- run: |
curl -X POST -H "Authorization: token ${{ secrets.GH_WORKFLOW_PAT }}" -H "Content-Type: application/json" \
https://api.github.com/repos/infogulch/index/dispatches \
-d '{"event_type": "notebooks_push"}'

View File

@ -2,22 +2,11 @@
"cells": [
{
"cell_type": "markdown",
"id": "83dd7287-bca5-49f9-b927-31bbc519d5b9",
"id": "2bdec887-ee29-4bef-8978-88a81940f7bc",
"metadata": {},
"source": [
"# Merklist\n",
"> An associative definition for the hash of an ordered sequence of elements\n",
"\n",
"- toc: true\n",
"- categories: [merklist]"
]
},
{
"cell_type": "markdown",
"id": "bf97974c-5582-4bf5-8ed8-6c43daf5036c",
"metadata": {},
"source": [
"Matrix multiplication's associativity and non-commutativity properties provide a natural definition for a [cryptographic hash](https://en.wikipedia.org/wiki/Cryptographic_hash_function) / digest / summary of an ordered list of elements while preserving concatenation operations. Due to the non-commutativity property, lists that differ in element order result in a different summary. Due to the associativity property, arbitrarily divided adjacent sub-lists can be summarized independently and combined to find the summary of their concatenation in one operation. This definition provides exactly the properties needed to define a list, and does not impose any unnecessary structure that could cause two equivalent lists to produce different summaries. The name *Merklist* is intended to be reminicent of other hash-based data structures like [Merkle Tree](https://en.wikipedia.org/wiki/Merkle_tree) and [Merklix Tree](https://www.deadalnix.me/2016/09/24/introducing-merklix-tree-as-an-unordered-merkle-tree-on-steroid/)."
"Using matrix multiplication's associativity and non-commutativity properties provides a natural definition of a cryptographic hash / digest / summary of an ordered list of elements. Due to the non-commutativity property, lists that only differ in element order result in a different summary. Due to the associativity property, arbitrarily divided adjacent sub-lists can be summarized independently and combined to quickly find the summary of their concatenation. This definition provides exactly the properties needed to define a list, and does not impose any unnecessary structure that could cause two equivalent lists to produce different summaries. The name *Merklist* is intended to be reminicent of other hash-based data structures like [Merkle Tree](https://en.wikipedia.org/wiki/Merkle_tree) and [Merklix Tree](https://www.deadalnix.me/2016/09/24/introducing-merklix-tree-as-an-unordered-merkle-tree-on-steroid/)."
]
},
{
@ -38,10 +27,10 @@
"This construction has a couple notable concequences:\n",
"\n",
"* The hash of a list with only one item is just the hash of the item itself.\n",
"* You can calculate the hash of any list concatenated with a copy of itself by matrix multiplication of the the hash with itself. This works for single elements as well as arbitrarily long lists.\n",
"* A list can have multiple copies of the same list item, and swapping them does not affect the list hash. Consider how swapping the first two elements in `[1, 1, 2]` has no discernible effect.\n",
"* The hash of the concatenation of two lists is the matrix multiplication of their hashes.\n",
"* Concatenating a list with a list of 0 elements yields the same hash.\n",
"* You can calculate the hash of any list concatenated with itself by matrix multiplication of the the hash with itself. This works for single elements as well as arbitrarily long lists.\n",
"* A list can have multiple copies of the same list item, and swapping them does not affect the list hash. Consider how swapping the first two elements in `[1, 1, 2]` doesn't change it.\n",
"* Concatenating two lists is accomplished by matrix multiplication of their hashes, in the correct order.\n",
"* Appending or prepending lists of 0 elements yields the same hash, as expected.\n",
"\n",
"Lets explore this definition in more detail with a simple implementation in python+numpy."
]
@ -51,15 +40,12 @@
"execution_count": 1,
"id": "99b521d8-1c66-49d7-98e9-6fa1d8d7c18f",
"metadata": {
"jupyter": {
"source_hidden": true
},
"tags": []
},
"outputs": [],
"source": [
"#collapse-hide\n",
"# Setup and imports\n",
"# setup\n",
"\n",
"import hashlib\n",
"import numpy as np\n",
"from functools import reduce\n",
@ -77,13 +63,13 @@
"metadata": {},
"source": [
"### The hash of a list element - `hash_m/1`\n",
"The function `hash_m/1` takes a buffer of bytes as its first argument, and returns the sha512 hash of the bytes formatted as an 8×8 2-d array of 8-bit unsigned integers with wrapping overflow. **We define this hash to be the hash of the list element.** Based on a shallow wikipedia dive, someone familiar with linear algebra might say it's a [matrix ring](https://en.wikipedia.org/wiki/Matrix_ring), $R_{256}^{8×8}$. Not coincidentally, sha512 outputs 512 bits = 64 bytes = 8 * 8 array of bytes, how convenient. (In fact, that might even be the primary reason why I chose sha512!)"
"The function `hash_m/1` takes a buffer of bytes as its first argument, and returns the sha512 hash of the bytes formatted as an 8×8 2-d array of 8-bit unsigned integers with wrapping overflow. **This is the hash of a list element consisting of those bytes.** Based on a shallow wikipedia dive, someone familiar with linear algebra might say it's a [matrix ring](https://en.wikipedia.org/wiki/Matrix_ring), $R_{256}^{8×8}$. Not coincidentally, sha512 outputs 512 bits = 64 bytes = 8 * 8 array of bytes, how convenient. (In fact, that might even be the primary reason why I chose sha512!)"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "7f76f998-94a9-4002-adb3-73ccf88223e2",
"id": "3ccc7fdc-fa6a-48e3-accb-3c1070b4559c",
"metadata": {
"tags": []
},
@ -96,12 +82,15 @@
},
{
"cell_type": "markdown",
"id": "11c69415-fac2-46a5-97a3-5a7f58376664",
"id": "04132091-21b1-4fbb-99df-711ae5e0c819",
"metadata": {
"slideshow": {
"slide_type": "skip"
},
"tags": []
},
"source": [
"Lets see it in use:"
"8×8 seems big compared to 3×3 or 4×4 matrixes. The values are as random as you might expect a cryptographic hash to be, and range from 0-255:"
]
},
{
@ -145,19 +134,6 @@
"print(hash_m(b\"Hello B\"))"
]
},
{
"cell_type": "markdown",
"id": "04132091-21b1-4fbb-99df-711ae5e0c819",
"metadata": {
"slideshow": {
"slide_type": "skip"
},
"tags": []
},
"source": [
"8×8 seems big compared to 3×3 or 4×4 matrixes, but otherwise everything looks normal. The values are as random as you might expect a cryptographic hash to be, and range from 0-255."
]
},
{
"cell_type": "markdown",
"id": "c0c37110-b38d-4420-adf9-11ff5c5cd590",
@ -189,21 +165,18 @@
{
"cell_type": "code",
"execution_count": 5,
"id": "eb84e6e1-b1c1-48f4-aa50-3ae0edfc78af",
"id": "6ae6ea62-8fb9-4015-8a62-4b5c3dbd98d3",
"metadata": {},
"outputs": [],
"source": [
"#\n",
"# `elements` is a list of 3 elements\n",
"# list1 contains 3 elements\n",
"elements = [b\"A\", b\"Hello\", b\"World\"]\n",
"# first, make a new list with the hash of each element\n",
"# first hash each element\n",
"element_hashes = [hash_m(e) for e in elements]\n",
"# get the hash of the list by reducing the hashes by matrix multiplication\n",
"list_hash1 = mul_m(mul_m(element_hashes[0], element_hashes[1]), element_hashes[2])\n",
"# an alternative way to write the reduction\n",
"list_hash2 = reduce(mul_m, element_hashes)\n",
"# check that these are equivalent\n",
"assert_equal(list_hash1, list_hash2)"
"list_hash2 = reduce(mul_m, element_hashes)"
]
},
{
@ -219,10 +192,10 @@
"output_type": "stream",
"text": [
"List of elements:\n",
" [b'A', b'Hello', b'World'] \n",
"[b'A', b'Hello', b'World']\n",
"\n",
"List of element hashes:\n",
" [array([[ 33, 180, 244, 189, 158, 100, 237, 53],\n",
"Hash of each element:\n",
"[array([[ 33, 180, 244, 189, 158, 100, 237, 53],\n",
" [ 92, 62, 182, 118, 162, 142, 190, 218],\n",
" [246, 216, 241, 123, 220, 54, 89, 149],\n",
" [179, 25, 9, 113, 83, 4, 64, 128],\n",
@ -243,26 +216,28 @@
" [187, 104, 46, 253, 214, 197, 221, 19],\n",
" [193, 23, 224, 139, 212, 170, 239, 113],\n",
" [ 41, 29, 138, 172, 226, 248, 144, 39],\n",
" [ 48, 129, 208, 103, 124, 22, 223, 15]], dtype=uint8)] \n",
" [ 48, 129, 208, 103, 124, 22, 223, 15]], dtype=uint8)]\n",
"\n",
"Merklist hash of the list [b'A', b'Hello', b'World'] :\n",
" [[178 188 57 157 60 136 190 127]\n",
"Hash of full list:\n",
"[[178 188 57 157 60 136 190 127]\n",
" [ 40 234 254 224 38 46 250 52]\n",
" [156 72 193 136 219 98 28 4]\n",
" [197 2 43 132 132 232 254 198]\n",
" [ 93 64 113 215 2 246 130 192]\n",
" [ 91 107 85 13 149 60 19 173]\n",
" [ 84 77 244 98 0 239 123 17]\n",
" [ 58 112 98 250 163 20 27 6]] \n",
"\n"
" [ 58 112 98 250 163 20 27 6]]\n"
]
}
],
"source": [
"#collapse-output\n",
"print(\"List of elements:\\n\", elements, \"\\n\")\n",
"print(\"List of element hashes:\\n\", element_hashes, \"\\n\")\n",
"print(\"Merklist hash of the list\", elements, \":\\n\", list_hash1, \"\\n\")"
"print(\"List of elements:\")\n",
"print(elements)\n",
"print(\"\\nHash of each element:\")\n",
"print(element_hashes)\n",
"print(\"\\nHash of full list:\")\n",
"print(list_hash1)\n",
"assert_equal(list_hash1, list_hash2)"
]
},
{
@ -275,20 +250,28 @@
"* [Associativity](#Associativity) - Associativity enables you to reduce a computation using any partitioning because all partitionings yield the same result. Addition is associative $(1+2)+3 = 1+(2+3)$, subtraction is not $(5-3)-2\\neq5-(3-2)$. ([Associative property](https://en.wikipedia.org/wiki/Associative_property))\n",
"* [Non-Commutativity](#Non-Commutativity) - Commutativity allows you to swap elements without affecting the result. Addition is commutative $1+2 = 2+1$, but division is not $1\\div2 \\neq2\\div1$. And neither is matrix multiplication. ([Commutative property](https://en.wikipedia.org/wiki/Commutative_property))\n",
"\n",
"This is an unusual combination of properties for an operation. It's at least not a combination encountered in introductory algebra:\n",
"This is an unusual combination of properties for an operation, at least not a combination encountered under normal algebra operations:\n",
"\n",
"| | associative | commutative |\n",
"| --- | --- | --- |\n",
"| $a+b$ | ✅ | ✅ |\n",
"| $a*b$ | ✅ | ✅ |\n",
"| $a-b$ | ❌ | ❌ |\n",
"| $a/b$ | ❌ | ❌ |\n",
"| $a^b$ | ❌ | ❌ |\n",
"| $M×M$ | ✅ | ❌ |\n",
"| + | ✅ | ✅ |\n",
"| * | ✅ | ✅ |\n",
"| - | ❌ | ❌ |\n",
"| / | ❌ | ❌ |\n",
"| exp | ❌ | ❌ |\n",
"| M×M | ✅ | ❌ |\n",
"\n",
"Upon consideration, these are the exact properties that one would want in order to define the hash of a list of items. Non-commutativity enables the order of elements in the list to be well defined, since swapping different elements produces a different hash. Associativity enables calculating the hash of the list by performing the reduction operations in any order, and you still get the same hash.\n",
"Upon consideration, these are the exact properties that one would want in order to define the hash of a list of items. Non-commutativity enables the order of elements in the list to be well-defined, since swapping different elements produces a different hash. Associativity enables caching the summary of an arbitrary sublist; I expect that doing this heirarchally on a huge list enables an algorithm to calculate the hash of any sublist at the cost of `O(log(N))` time and space.\n",
"\n",
"Lets sanity-check that these properties can hold for the construction described above. For setup, lets consider the hashes of three elements, $he_1$, $he_2$, and $he_3$:"
"Lets sanity-check that these properties can hold for the construction described above."
]
},
{
"cell_type": "markdown",
"id": "c6c8ef5e-99d2-4a7e-887f-54b93a7baf4a",
"metadata": {},
"source": [
"### Associativity"
]
},
{
@ -300,34 +283,27 @@
},
"outputs": [],
"source": [
"he1 = hash_m(b\"A\")\n",
"he2 = hash_m(b\"B\")\n",
"he3 = hash_m(b\"C\")"
]
},
{
"cell_type": "markdown",
"id": "c6c8ef5e-99d2-4a7e-887f-54b93a7baf4a",
"metadata": {},
"source": [
"### Associativity\n",
"\n",
"If it's associative, we should get the same hash if we rearrange the parenthesis to indicate reduction in a different operation order. $((he_1 × he_2) × he_3) = (he_1 × (he_2 × he_3))$"
"f1 = hash_m(b\"Hello A\")\n",
"f2 = hash_m(b\"Hello B\")\n",
"f3 = hash_m(b\"Hello C\")"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "0452955b-2d7e-41e4-924f-8f00ef0c46cf",
"id": "b631007c-3784-4c32-9c3e-447267c45a24",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"x = np.matmul(np.matmul(he1, he2), he3)\n",
"y = np.matmul(he1, np.matmul(he2, he3))\n",
"# x is calculated by association ((f1 × f2) × f3)\n",
"x = np.matmul(np.matmul(f1, f2), f3)\n",
"\n",
"# observe that they produce the same summary\n",
"# y is calculated by association (f1 × (f2 × f3))\n",
"y = np.matmul(f1, np.matmul(f2, f3))\n",
"\n",
"# observe that they produce the same result\n",
"assert_equal(x, y)"
]
},
@ -343,29 +319,30 @@
"name": "stdout",
"output_type": "stream",
"text": [
"[[236 186 154 122 127 118 202 95]\n",
" [132 196 196 78 80 160 68 166]\n",
" [208 62 184 219 85 118 143 111]\n",
" [ 7 176 219 162 243 25 29 54]\n",
" [233 49 39 237 233 209 201 221]\n",
" [187 196 20 155 17 241 207 101]\n",
" [132 186 67 153 231 160 68 62]\n",
" [ 8 184 160 149 100 160 189 56]] \n",
"[[ 58 12 144 134 100 158 159 51]\n",
" [ 73 206 202 190 87 79 223 2]\n",
" [210 122 142 117 37 148 106 45]\n",
" [175 146 187 223 235 171 64 226]\n",
" [149 85 203 87 92 251 243 206]\n",
" [ 18 252 160 103 125 251 181 133]\n",
" [191 132 220 104 213 154 34 154]\n",
" [127 197 95 87 166 3 22 3]]\n",
"\n",
" [[236 186 154 122 127 118 202 95]\n",
" [132 196 196 78 80 160 68 166]\n",
" [208 62 184 219 85 118 143 111]\n",
" [ 7 176 219 162 243 25 29 54]\n",
" [233 49 39 237 233 209 201 221]\n",
" [187 196 20 155 17 241 207 101]\n",
" [132 186 67 153 231 160 68 62]\n",
" [ 8 184 160 149 100 160 189 56]]\n"
"[[ 58 12 144 134 100 158 159 51]\n",
" [ 73 206 202 190 87 79 223 2]\n",
" [210 122 142 117 37 148 106 45]\n",
" [175 146 187 223 235 171 64 226]\n",
" [149 85 203 87 92 251 243 206]\n",
" [ 18 252 160 103 125 251 181 133]\n",
" [191 132 220 104 213 154 34 154]\n",
" [127 197 95 87 166 3 22 3]]\n"
]
}
],
"source": [
"#collapse-output\n",
"print(x, \"\\n\\n\", y)"
"print(x)\n",
"print()\n",
"print(y)"
]
},
{
@ -373,24 +350,25 @@
"id": "c0fb04da-2cbd-4fa1-8b85-d48441cc8962",
"metadata": {},
"source": [
"### Non-Commutativity\n",
"\n",
"If it's not commutative, then swapping different elements should produce a different hash. That is, $he_1 × he_2 \\ne he_2 × he_1$:"
"### Non-Commutativity"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "2f3d139a-6c48-4ddb-9a34-7b2aa00853d6",
"id": "182652ac-a08f-4009-9354-7aaa8632d921",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"x = np.matmul(he1, he2)\n",
"y = np.matmul(he2, he1)\n",
"# x is f1 × f2\n",
"x = np.matmul(f1, f2)\n",
"\n",
"# observe that they produce different summaries\n",
"# y is f2 × f1\n",
"y = np.matmul(f2, f1)\n",
"\n",
"# observe that they produce different results\n",
"assert_not_equal(x, y)"
]
},
@ -406,29 +384,30 @@
"name": "stdout",
"output_type": "stream",
"text": [
"[[ 3 123 188 218 224 245 207 138]\n",
" [ 6 234 158 180 156 14 148 118]\n",
" [ 1 224 198 114 201 102 144 157]\n",
" [ 11 141 205 36 186 218 16 40]\n",
" [ 76 243 130 82 188 146 230 84]\n",
" [211 24 6 46 87 234 89 206]\n",
" [ 16 239 112 27 11 202 139 137]\n",
" [103 30 53 187 203 140 26 146]] \n",
"[[ 87 79 149 131 148 247 195 90]\n",
" [249 84 195 58 142 133 211 15]\n",
" [177 93 69 254 240 234 97 37]\n",
" [ 46 84 76 253 55 200 43 236]\n",
" [ 21 84 99 157 55 148 170 2]\n",
" [168 123 6 250 64 144 54 242]\n",
" [230 78 164 76 30 29 214 68]\n",
" [ 47 183 156 239 157 177 192 184]]\n",
"\n",
" [[253 223 41 178 183 140 102 18]\n",
" [111 185 54 143 153 96 117 15]\n",
" [109 111 168 203 253 0 102 84]\n",
" [106 74 245 252 178 202 226 201]\n",
" [ 12 105 29 102 167 152 161 226]\n",
" [253 244 27 149 227 247 235 21]\n",
" [121 15 36 5 9 32 10 92]\n",
" [ 42 189 91 117 135 176 52 152]]\n"
"[[149 18 239 238 84 188 191 109]\n",
" [239 150 214 235 59 161 9 133]\n",
" [ 89 174 59 14 70 113 124 243]\n",
" [ 66 113 176 124 227 247 17 25]\n",
" [247 138 152 181 177 143 184 97]\n",
" [113 249 199 153 154 75 45 105]\n",
" [121 201 225 42 249 213 180 244]\n",
" [ 85 31 72 28 181 182 140 176]]\n"
]
}
],
"source": [
"#collapse-output\n",
"print(x, \"\\n\\n\", y)"
"print(x)\n",
"print()\n",
"print(y)"
]
},
{
@ -465,7 +444,7 @@
"def identity_m():\n",
" return np.identity(8, dtype=np.uint8)\n",
"\n",
"# generalize double_m to any length, not just doublings, performed in ln(N) matmuls\n",
"# generalize to any length, not just doublings, performed in ln(N) matmuls\n",
"def repeat_m(m, n):\n",
" res = identity_m()\n",
" while n > 0:\n",
@ -511,21 +490,34 @@
" [ 19 135 80 115 75 242 242 5]\n",
" [244 165 250 28 76 43 188 254]\n",
" [233 46 187 39 151 241 175 130]\n",
" [132 138 6 215 20 132 89 33]] \n",
" [132 138 6 215 20 132 89 33]]\n",
"\n",
" [[ 68 252 159 3 14 52 199 199]\n",
"[[ 68 252 159 3 14 52 199 199]\n",
" [136 124 6 34 58 174 206 54]\n",
" [ 3 234 2 13 120 240 7 163]\n",
" [102 47 66 61 87 234 246 72]\n",
" [ 19 135 80 115 75 242 242 5]\n",
" [244 165 250 28 76 43 188 254]\n",
" [233 46 187 39 151 241 175 130]\n",
" [132 138 6 215 20 132 89 33]]\n"
" [132 138 6 215 20 132 89 33]]\n",
"\n",
"[[1 0 0 0 0 0 0 0]\n",
" [0 1 0 0 0 0 0 0]\n",
" [0 0 1 0 0 0 0 0]\n",
" [0 0 0 1 0 0 0 0]\n",
" [0 0 0 0 1 0 0 0]\n",
" [0 0 0 0 0 1 0 0]\n",
" [0 0 0 0 0 0 1 0]\n",
" [0 0 0 0 0 0 0 1]]\n"
]
}
],
"source": [
"print(hash1, \"\\n\\n\", hash2)"
"print(hash1)\n",
"print()\n",
"print(hash2)\n",
"print()\n",
"print(np.identity(8,\"B\"))"
]
},
{
@ -568,9 +560,7 @@
"id": "6c62dcbc-63a7-4a20-8f15-295e7675f7a8",
"metadata": {},
"source": [
"Flex that associativity - this statement is true and equivalent to the assertion below:\n",
"\n",
"$(a × (a499 × b × a500) × (a500 × b × a499) × a) = (a500 × b × (a500 × a500) × b × a500)$"
"Flex that associativity `(a × (a499 × b × a500) × (a500 × b × a499) × a)` = `(a500 × b × (a500 × a500) × b × a500)`"
]
},
{
@ -585,32 +575,23 @@
},
{
"cell_type": "markdown",
"id": "624d5cb8-01cb-4c6d-b80a-4b2079e4ad54",
"id": "6cc30cb3-8079-4f8a-9b7c-a3b4f7e384a3",
"metadata": {},
"source": [
"## Security\n",
"# Unknowns\n",
"\n",
"The security of the design of merklist leans on existing cryptographic hash function by the requiring that values that go into the final list digest came from some real hashed preimage, and any implementation can require that the primage of every element must be provided. This means that it's not possible to choose an *arbitrary* element hash as an input to the reduction operation, limiting the blast radius that any specific element can have on the final list digest to the bit fixing that is possible by asic bitcoin miners today. So then the question is if bit fixing is enough to degrade the preimage resistance of the merklist design, and if that is impossible to overcome. (Challenges anyone?)"
]
},
{
"cell_type": "markdown",
"id": "9515603f-f8b5-482a-ae1b-ed504358b860",
"metadata": {},
"source": [
"## Unknowns\n",
"\n",
"This appears to me to be a reasonable way to define the hash of a list. The mathematical description of a list aligns very nicely with the properties offered by matrix multiplication. But is it appropriate to use for the same things that a Merkle Tree would be? The big questions are related to the valuable properties of hash functions:\n",
"This appears to me to be a reasonable way to define the hash of a list. The mathematical definition of a list aligns very nicely with the properties offered by matrix multiplication. But is it appropriate to use for the same things that a Merkle Tree would be? The big questions are related to the valuable properties of hash functions:\n",
"\n",
"* Given a merklist summary or sublist summaries of it, can you derive the hashes of elements or their order? (Elements themselves are protected by the preimage resistance of the underlying hash function.)\n",
" * If yes, when is that a problem?\n",
"* Given a merklist summary but not the elements, is it possible to produce a different list of elements that hash to the same summary? (~preimage resistance)\n",
"* Given a merklist summary or sublist summaries of it, can you derive the hashes of elements or their order?\n",
"* Is it possible to predictably alter the merklist summary by concatenating it with some other sublist of real elements?\n",
"* Are there other desirable security properties that would be valuable for a list hash?\n",
"* Is there a better choice of hash function as a primitive than sha512?\n",
"* Is there a better choice of reduction function that still retains associativity+non-commutativity than simple matmul?\n",
"* Is there a more appropriate size than an 8x8 matrix / 64 bytes to represent merklist summaries?\n",
"\n",
"Matrixes are well-studied objects, perhaps some of these questions already have an answer. If *you* know something about deriving a preimage of a [matrix ring](https://en.wikipedia.org/wiki/Matrix_ring) $R_{256}^{8×8}$ using just multiplication, I would be very interested to know. Given the simplicity of the definition, I wouldn't be surprised if there was already a big wrench in the cogs here."
"Matrixes are well-studied objects, perhaps such information is already known. If *you* know something about deriving the preimage of the multiplication of a [matrix ring](https://en.wikipedia.org/wiki/Matrix_ring), $R_{256}^{8×8}$, I would be very interested to know."
]
},
{
@ -618,15 +599,15 @@
"id": "4c4d4a83-8e2e-46d7-b2e3-2d59ba9c9e8c",
"metadata": {},
"source": [
"## What's next?\n",
"# What's next?\n",
"\n",
"*If this construction has appropriate security properties*, it seems to be a better merkle tree in all respects. Any use of a merkle tree could be replaced with this, and it could enable use-cases where merkle trees aren't useful. Some examples of what I think might be possible:\n",
"***If** this construction has the appropriate security properties*, it seems to be a better merkle tree in all respects. Any use of a merkle tree could be replaced with this, and it could enable use-cases where merkle trees aren't useful. Some examples of what I think might be possible:\n",
"\n",
"* Using a Merklist with a sublist summary tree structure enables creating a $O(1)$-sized 'Merklist Proof' that can verify the addition and subtraction of any number of elements at any single point in the list using only $O(log(N))$ time and $O(log(N))$ static space. As a bonus the proof generator and verifier can have totally different tree structures and can still communicate the proof successfully.\n",
"* Using a Merklist summary tree you can create a consistent hash of any ordered key-value store (like a btree) that can be maintained incrementally inline with regular node updates, e.g. as part of a [LSM-tree](https://en.wikipedia.org/wiki/Log-structured_merge-tree). This could facilitate verification and sync between database replicas.\n",
"* The sublist summary tree structure can be as dense or sparse as desired. You could summarize down to pairs of elements akin to a merkle tree, but you could also summarize a compressed sublist of hundreds or even millions of elements with a single hash. Of course, calculating or verifying a proof of changes to the middle of that sublist would require rehashing the whole sublist, but this turns it from a fixed structure into a tuneable parameter.\n",
"* If all possible elements had an easily calculatable inverse, that would enable \"subtracting\" an element by inserting its inverse in front of it. That would basically extend the group from a ring into a field, and might have interesting implications.\n",
" * For example you could define a cryptographically-secure rolling hash where advancing either end can be calculated in `O(1)` time and space.\n",
" * For example you could define a cryptographically-secure rolling hash where advancing either end can be calculated in `O(1)` time.\n",
"\n",
"To be continued..."
]

View File

@ -0,0 +1,115 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"id": "acb06966-8cfa-4b28-9fa9-30286c33d20c",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"id": "203b80b6-7559-4861-ab9c-538885b8d223",
"metadata": {
"tags": []
},
"source": [
"## Inverse?"
]
},
{
"cell_type": "code",
"execution_count": 16,
"id": "66031921-f6b4-4c32-bc8b-97a92ea935df",
"metadata": {
"jupyter": {
"source_hidden": true
},
"tags": []
},
"outputs": [],
"source": [
"# Q: If you compute the inverse of one of these matrixes and multiply it to the right of the position\n",
"# where it was originally multiplied it should \"delete\" that element as if the element was never added.\n",
"# I'm uncertain of the possibility and frequency of such inverse matrices over finite fields.\n",
"# If this scenario is possible to construct, I think we should \"define\" this operation as deletion.\n",
"\n",
"# See: https://en.wikipedia.org/wiki/Determinant#Properties_of_the_determinant\n",
"\n",
"def det2(mat, m=256):\n",
" ((a, b),\n",
" (c, c)) = mat\n",
" return (a*c - b*c) % m\n",
"\n",
"def det3(mat, m=256):\n",
" ((a, b, c),\n",
" (d, e, f),\n",
" (g, h, i)) = mat\n",
" return (a*e*i + b*f*g + c*d*h - c*e*g - b*d*i - a*f*h) % m\n",
"\n",
"def det(mat, m=256):\n",
" # I dislike having to round here, but it returns a float\n",
" # I wish it returned a native \"B\" type\n",
" return int(round(np.linalg.det(mat),0)) % m\n",
"\n",
"a = ((-2,2,-3),(-1,1,3),(2,0,-1))\n",
"assert_equal(det(a), 18)\n",
"assert_equal(det(a), det3(a))\n",
"\n",
"# ?\n",
"# assert that they didn't change since the last time I ran it\n",
"assert_equal(det(f1), 128)\n",
"assert_equal(det(f2), 160)\n",
"assert_equal(det(f3), 128)"
]
},
{
"cell_type": "markdown",
"id": "568ad4ed-7894-465b-9bf3-f19f2269c0f3",
"metadata": {
"tags": []
},
"source": [
"[This](https://math.stackexchange.com/questions/273054/how-to-find-matrix-inverse-over-finite-field) suggests to find the inverse matrix by first finding the adjugate then applying\n",
"\n",
"> (1/det(A))adj(A)=inv(A)\n",
"\n",
"Under mod p\n",
"\n",
"---\n",
"\n",
"The [adjugate](https://en.wikipedia.org/wiki/Adjugate_matrix) is the inverse of the cofactor matrix.\n",
"\n",
"---\n",
"\n",
"The cofactor matrix is made by multiplying the matrix minor of each (i,j) of the original matrix times a sign factor.\n",
"\n",
"---\n",
"\n",
"The (i,j)-minor of matrix A is the determinant of the matrix A with row i & column j removed."
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"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.2"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

View File

@ -0,0 +1,36 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "d6b5f16e-76a4-473f-a8cd-efd532f8673f",
"metadata": {},
"source": [
"# Merklist Tree\n",
"\n",
"Part 2 of the Merklist idea. Constructing a tree structure of summarized sublists, so that mutations to the list can be computed, verified, and stored using $O(log(N))$ time and space.\n",
"\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"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.2"
}
},
"nbformat": 4,
"nbformat_minor": 5
}