Skip to content
This repository has been archived by the owner on Mar 12, 2024. It is now read-only.

Commit

Permalink
feat(arbitration): create cryptographic primitives
Browse files Browse the repository at this point in the history
  • Loading branch information
GCdePaula committed Jun 19, 2023
1 parent f99523d commit 097699c
Show file tree
Hide file tree
Showing 3 changed files with 271 additions and 0 deletions.
13 changes: 13 additions & 0 deletions onchain/permissionless-arbitration/offchain/.luarc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"$schema": "https://raw.githubusercontent.com/sumneko/vscode-lua/master/setting/schema.json",

"runtime.version": "Lua 5.4",

"diagnostics": {
"enable": true
},

"workspace.library": {
"runtime/lua": true
}
}
114 changes: 114 additions & 0 deletions onchain/permissionless-arbitration/offchain/cryptography/hash.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
-- local keccak_bin = require "cartesi".keccak

local function hex_from_bin(bin)
assert(bin:len() == 32)
return "0x" .. string.gsub(bin, ".", function(c)
return string.format("%02x", string.byte(c))
end)
end

-- local function keccak(...)
-- return hex_from_bin(keccak_bin(...))
-- end

local function keccak(...)
local args = {...}
assert(#args > 0)
for _,v in ipairs(args) do
assert(v:len() == 66, "string not a hash: " .. v)
end
local args_str = table.concat(args, " ")


local cmd = string.format(
[[cast keccak $(cast --concat-hex %s)]],
args_str
)

local handle = io.popen(cmd)
assert(handle)

local digest = handle:read()
handle:close()
return digest
end

local internalized_hahes = {}
local iterateds = {}

local Hash = {}
Hash.__index = Hash

function Hash:from_digest(digest_hex)
assert(type(digest_hex) == "string", digest_hex:len() == 66)

local x = internalized_hahes[digest_hex]
if x then return x end

local h = {digest_hex = digest_hex}
iterateds[h] = {h}
setmetatable(h, self)
internalized_hahes[digest_hex] = h
return h
end

function Hash:from_digest_bin(digest_bin)
local digest_hex = hex_from_bin(digest_bin)
return self:from_digest(digest_hex)
end

function Hash:from_data(data)
local digest_hex = keccak(data)
return self:from_digest(digest_hex)
end

function Hash:join(other_hash)
assert(getmetatable(other_hash) == Hash)
local digest_hex = keccak(self.digest_hex, other_hash.digest_hex)
local ret = Hash:from_digest(digest_hex)
ret.left = self.digest_hex
ret.right = other_hash.digest_hex
return ret
end

function Hash:children()
local left, right= self.left, self.right
if left and right then
return true, left, right
else
return false
end
end

function Hash:iterated_merkle(level)
level = level + 1
local iterated = iterateds[self]

local ret = iterated[level]
if ret then return ret end

local i = #iterated -- at least 1
local highest_level = iterated[i]
while i < level do
highest_level = highest_level:join(highest_level)
i = i + 1
iterated[i] = highest_level
end

return highest_level
end

Hash.__tostring = function (x)
return x.digest_hex
end

local zero_bytes32 = "0x0000000000000000000000000000000000000000000000000000000000000000"
local zero_hash = Hash:from_digest(zero_bytes32)

Hash.zero = zero_hash

function Hash:is_zero()
return self == zero_hash
end

return Hash
144 changes: 144 additions & 0 deletions onchain/permissionless-arbitration/offchain/cryptography/merkle.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
local function ulte(a, b)
return a == b or math.ult(a, b)
end

local function is_pow2(x)
return x & (x-1) == 0
end

-- Returns number of leading zeroes of x. Shamelessly stolen from the book
-- Hacker's Delight.
local function clz(x)
if x == 0 then return 64 end
local n = 0
if (x & 0xFFFFFFFF00000000) == 0 then n = n + 32; x = x << 32 end
if (x & 0xFFFF000000000000) == 0 then n = n + 16; x = x << 16 end
if (x & 0xFF00000000000000) == 0 then n = n + 8; x = x << 8 end
if (x & 0xF000000000000000) == 0 then n = n + 4; x = x << 4 end
if (x & 0xC000000000000000) == 0 then n = n + 2; x = x << 2 end
if (x & 0x8000000000000000) == 0 then n = n + 1 end
return n
end

-- Returns number of trailing zeroes of x. Shamelessly stolen from the book
-- Hacker's Delight.
local function ctz(x)
x = x & (~x + 1)
return 63 - clz(x)
end


local Slice = {}
Slice.__index = Slice

function Slice:new(arr, start_idx_inc, end_idx_ex)
start_idx_inc = start_idx_inc or 1
end_idx_ex = end_idx_ex or #arr + 1
assert(start_idx_inc > 0)
assert(ulte(start_idx_inc, end_idx_ex))
assert(end_idx_ex <= #arr + 1)
local s = {
arr = arr,
start_idx_inc = start_idx_inc,
end_idx_ex = end_idx_ex,
}
setmetatable(s, self)
return s
end

function Slice:slice(si, ei)
assert(si > 0)
assert(ulte(si, ei))
local start_idx_inc = self.start_idx_inc + si - 1
local end_idx_ex = self.start_idx_inc + ei - 1
assert(ulte(end_idx_ex, self.end_idx_ex))
return Slice:new(self.arr, start_idx_inc, end_idx_ex)
end

function Slice:len()
return self.end_idx_ex - self.start_idx_inc
end

function Slice:get(idx)
idx = assert(math.tointeger(idx))
assert(idx > 0)
local i = self.start_idx_inc + idx - 1
assert(i < self.end_idx_ex)
return self.arr[i]
end

local function semi_sum(a, b)
assert(ulte(a, b))
return a + (b - a) // 2
end

function Slice:find_cell_containing(elem)
local l, r = 1, self:len()
while l < r do
local m = semi_sum(l, r)
if self:get(m).accumulated_count < elem then
l = m + 1
else
r = m
end
end
if l == self:len() then
assert(elem <= self:get(l).accumulated_count + 1)
end
return l
end


local MerkleBuilder = {}
MerkleBuilder.__index = MerkleBuilder

function MerkleBuilder:new()
local m = {
leafs = {}
}
setmetatable(m, self)
return m
end

function MerkleBuilder:add(hash, rep)
rep = rep or 1

local last_accumulated_count
if self.leafs[#self.leafs] then
last_accumulated_count = self.leafs[#self.leafs].accumulated_count
else
last_accumulated_count = 0
end

local accumulated_count = rep + last_accumulated_count

table.insert(self.leafs, {hash = hash, accumulated_count = accumulated_count})
end

local function merkle(leafs, log2size, stride)
local first_time = stride * (1 << log2size) + 1
local last_time = (stride + 1) * (1 << log2size)

local first_cell = leafs:find_cell_containing(first_time)
local last_cell = leafs:find_cell_containing(last_time)

if first_cell == last_cell then
return leafs:get(first_cell).hash:iterated_merkle(log2size)
end

local slice = leafs:slice(first_cell, last_cell + 1)
local hash_left = merkle(slice, log2size - 1, 2 * stride)
local hash_right = merkle(slice, log2size - 1, 2 * stride + 1)

return hash_left:join(hash_right)
end

function MerkleBuilder:build()
local last = assert(self.leafs[#self.leafs])
local count = last.accumulated_count
assert(is_pow2(count))
local log2size = ctz(count)
return merkle(Slice:new(self.leafs), log2size, 0)
end

return MerkleBuilder

0 comments on commit 097699c

Please sign in to comment.