Skip to content

Commit

Permalink
Tree (#25)
Browse files Browse the repository at this point in the history
* tree.mjs

* test tree

* small.txt without new line

* ci on windows

* allow reading files on deno
  • Loading branch information
Trinidadec authored Oct 27, 2023
1 parent ccf8cdc commit 367b01f
Show file tree
Hide file tree
Showing 9 changed files with 136 additions and 47 deletions.
11 changes: 10 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@ jobs:
- run: npm ci
- run: npm test

node-windows:

runs-on: windows-latest

steps:
- uses: actions/checkout@v3
- run: npm ci
- run: npm test

deno:

runs-on: ubuntu-latest
Expand All @@ -39,7 +48,7 @@ jobs:
with:
deno-version: v1.x
# - run: deno run --quiet --allow-read --allow-env --allow-net --allow-hrtime ./test.mjs
- run: deno run --quiet ./test.mjs
- run: deno run --quiet --allow-read ./test.mjs

bun:

Expand Down
35 changes: 35 additions & 0 deletions base32.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/** @type {(uint5: number) => string} */
const toBase32 = uint5 => '0123456789abcdefghjkmnpqrstvwxyz'[uint5]

/** @type {(uint256: bigint) => number} */
const getParityBit = uint256 => {
let xor = uint256 ^ (uint256 >> 128n)
xor ^= xor >> 64n
xor ^= xor >> 32n
xor ^= xor >> 16n
xor ^= xor >> 8n
xor ^= xor >> 4n
xor ^= xor >> 2n
xor ^= xor >> 1n
return Number(xor & 1n)
}

/** @type {(bu224: bigint) => string} */
const toAddress = bu224 => {
let address = ''
const parity = getParityBit(bu224)
for (let j = 0; j < 45; j++) {
let uint5 = Number(bu224 & 0b11111n)
if (j == 44) {
uint5 += parity << 4
}
address += toBase32(uint5)
bu224 >>= 5n
}
return address
}

export default {
toAddress,
getParityBit
}
4 changes: 2 additions & 2 deletions digest256.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import sha224 from './sha224.mjs'
const { compress } = sha224
const { compress2 } = sha224

const maxLength = 248n

Expand Down Expand Up @@ -31,7 +31,7 @@ const merge = a => b => {
const data = getData(a) | (getData(b) << lenA);
return data | (lenAB << maxLength)
}
return compress(a | (b << 256n))
return compress2(a)(b)
}

export default {
Expand Down
1 change: 1 addition & 0 deletions examples/small.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Base32 is an encoding method based on the base-32 numeral system. It uses an alphabet of 32 digits, each of which represents a different combination of 5 bits (25). Since base32 is not very widely adopted, the question of notation—which characters to use to represent the 32 digits—is not as settled as in the case of more well-known numeral systems (such as hexadecimal), though RFCs and unofficial and de-facto standards exist. One way to represent Base32 numbers in a human-readable way is by using the digits 0–9 and the twenty-two upper-case letters A–V. However, many other variations are used in different contexts. Historically, Baudot code could be considered a modified (stateful) base32 code.
33 changes: 3 additions & 30 deletions index.mjs
Original file line number Diff line number Diff line change
@@ -1,20 +1,6 @@
import fs from 'node:fs'

/** @type {(uint5: number) => string} */
const toBase32 = uint5 => '0123456789abcdefghjkmnpqrstvwxyz'[uint5]

/** @type {(uint256: bigint) => number} */
const getParityBit = uint256 => {
let xor = uint256 ^ (uint256 >> 128n)
xor ^= xor >> 64n
xor ^= xor >> 32n
xor ^= xor >> 16n
xor ^= xor >> 8n
xor ^= xor >> 4n
xor ^= xor >> 2n
xor ^= xor >> 1n
return Number(xor & 1n)
}
import base32 from './base32.mjs'
const { toAddress } = base32

/** @type {(hash: string) => string} */
const getPath = hash => `${hash.substring(0, 2)}/${hash.substring(2, 4)}/${hash.substring(4)}`
Expand All @@ -36,16 +22,7 @@ const getBuffer = root => {
hash += BigInt(data[i + j]) << BigInt(8 * j)
}
console.log(hash)
let address = ''
const parity = getParityBit(hash)
for(let j = 0; j < 45; j++) {
let uint5 = Number(hash & 0b11111n)
if (j == 44) {
uint5 += parity << 4
}
address += toBase32(uint5)
hash >>= 5n
}
const address = toAddress(hash)
console.log(address)
buffer = Buffer.concat([buffer, getBuffer(`parts/${getPath(address)}`)])
}
Expand All @@ -63,10 +40,6 @@ const get = root => file => {
}
}

export default{
getParityBit
}

//get('mnb8j83rgrch8hgb8rbz28d64ec2wranzbzxcy4ebypd8')('out')
//get('2va87tc3cqebgg6wagd9dwe36e2vgcpdxjd26enj4c0xh')('out')
//get('d963x31mwgb8svqe0jmkxh8ar1f8p2dawebnan4aj6hvd')('out')
6 changes: 5 additions & 1 deletion sha224.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,10 @@ const compress = w => {
return x | u32Mask7
}

/** @type {(a256: bigint) => (b256: bigint) => bigint} */
const compress2 = a256 => b256 => compress(a256 | (b256 << 256n))

export default {
compress
compress,
compress2
}
7 changes: 6 additions & 1 deletion subtree.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ const height = a => b => {
return leadingZero256(v)
}

/** @type {(bu256: bigint) => State} */
const newState = bu256 => [[bu256, bu256, 0n]]

/** @type {(state: State) => (last0: bigint) => bigint} */
const end = state => last0 => {
while (true) {
Expand Down Expand Up @@ -81,5 +84,7 @@ const push = state => last0 => {
export default {
highestOne256,
height,
push
push,
newState,
end
}
40 changes: 28 additions & 12 deletions test.mjs
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import index from './index.mjs'
import base32 from './base32.mjs'
import sha224 from './sha224.mjs'
import digest256 from './digest256.mjs'
import subtree from './subtree.mjs'
/** @typedef {import('./subtree.mjs').State} State */
const { getParityBit } = index
import tree from './tree.mjs'
import fs from 'node:fs'
/** @typedef {import('./subtree.mjs').State} StateSubTree */
/** @typedef {import('./tree.mjs').State} StateTree */
const { toAddress, getParityBit } = base32
const { compress } = sha224
const { merge, byteToDigest, len } = digest256
const { highestOne256, height, push } = subtree
const { highestOne256, height, push: pushSubTree } = subtree
const { push: pushTree, end: endTree } = tree

console.log(`test start`)

Expand Down Expand Up @@ -159,34 +163,46 @@ console.log(`test start`)
let b = byteToDigest(0b10)
let c = byteToDigest(0b11)
{
/** @type {State} */
/** @type {StateSubTree} */
let state = []
push(state)(a)
pushSubTree(state)(a)
if (state.length !== 1) { throw state.length }
const state0 = state[0]
if (state0[0] !== a) { throw state0[0] }
if (state0[1] !== a) { throw state0[1] }
if (state0[2] !== 0n) { throw state0[2] }
const ab = push(state)(b)
const ab = pushSubTree(state)(b)
const mergeAB = merge(a)(b)
if (ab !== mergeAB) { throw ab }
state = state
if (state.length !== 0) { throw state.length }
}
{
/** @type {State} */
/** @type {StateSubTree} */
let state = []
let result = push(state)(c)
let result = pushSubTree(state)(c)
if (result !== null) { throw result }
result = push(state)(b)
result = pushSubTree(state)(b)
if (result !== null) { throw result }
if (state.length !== 2 ) { throw state.length }
result = push(state)(a)
result = pushSubTree(state)(a)
if (result !== null) { throw result }
if (state.length !== 2 ) { throw state.length }
result = push(state)(a)
result = pushSubTree(state)(a)
const mergeCB_AA = merge(merge(c)(b))(merge(a)(a))
if (result != mergeCB_AA) { throw result }
}
}
}

{
const data = fs.readFileSync(`examples/small.txt`)
/** @type {StateTree} */
let tree = []
for (let byte of data) {
pushTree(tree)(byte)
}
const digest = endTree(tree)
const result = toAddress(digest)
if (result !== 'vqfrc4k5j9ftnrqvzj40b67abcnd9pdjk62sq7cpbg7xe') { throw result }
}
46 changes: 46 additions & 0 deletions tree.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import digest256 from './digest256.mjs'
import subtree from './subtree.mjs'
import sha224 from './sha224.mjs'
/** @typedef {import('./subtree.mjs').State} SubTreeState */
const { byteToDigest } = digest256
const { newState: newSubTree, push: pushSubTree, end: endSubTree } = subtree
const { compress } = sha224

/**
* @typedef {SubTreeState[]} State
*/

const mask244 = ((1n << 224n) - 1n)

/** @type {(state: State) => (nu8: number) => void} */
const push = state => nu8 => {
let i = 0
let last0 = byteToDigest(nu8)
while (true) {
let subTree = state[i]
if (subTree === undefined) {
state.push(newSubTree(last0))
return
}
const last1 = pushSubTree(subTree)(last0)
if (last1 === null) {
return
}
last0 = last1
i += 1
}
}

/** @type {(state: State) => bigint} */
const end = state => {
let last0 = 0n
for (let subTree of state) {
last0 = endSubTree(subTree)(last0)
}
return compress(last0) & mask244
}

export default {
push,
end
}

0 comments on commit 367b01f

Please sign in to comment.