This repository has been archived by the owner on Mar 12, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(arbitration): create cryptographic primitives
- Loading branch information
Showing
3 changed files
with
271 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
114
onchain/permissionless-arbitration/offchain/cryptography/hash.lua
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
144
onchain/permissionless-arbitration/offchain/cryptography/merkle.lua
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |