-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' of github.com:AcalaNetwork/boka
* 'master' of github.com:AcalaNetwork/boka: Merkle functions (#81)
- Loading branch information
Showing
17 changed files
with
297 additions
and
24 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
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
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
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
10 changes: 10 additions & 0 deletions
10
Utils/Sources/Utils/Extensions/UnsignedInteger+Utils.swift
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,10 @@ | ||
extension FixedWidthInteger { | ||
/// Return the next power of two that is equal or greater than self. | ||
/// Returns nil if self is 0 or the next power of two is greater than `Self.max`. | ||
public var nextPowerOfTwo: Self? { | ||
guard self > 0 else { return nil } | ||
let leadingZeroBitCount = (self - 1).leadingZeroBitCount | ||
guard leadingZeroBitCount > 0 else { return nil } | ||
return 1 << (bitWidth - leadingZeroBitCount) | ||
} | ||
} |
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
This file was deleted.
Oops, something went wrong.
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,46 @@ | ||
import Blake2 | ||
import Foundation | ||
|
||
public typealias DataPtrRepresentable = Blake2.DataPtrRepresentable | ||
|
||
// Waiting for NoncopyableGenerics to be available | ||
public protocol Hashing /*: ~Copyable */ { | ||
init() | ||
mutating func update(_ data: some DataPtrRepresentable) | ||
consuming func finalize() -> Data32 | ||
} | ||
|
||
extension Hashing { | ||
public static func hash(data: some DataPtrRepresentable) -> Data32 { | ||
var hasher = Self() | ||
hasher.update(data) | ||
return hasher.finalize() | ||
} | ||
} | ||
|
||
extension FixedSizeData: DataPtrRepresentable { | ||
public typealias Ptr = UnsafeRawBufferPointer | ||
|
||
public func withPtr<R>( | ||
cb: (UnsafeRawBufferPointer) throws -> R | ||
) rethrows -> R { | ||
try data.withUnsafeBytes(cb) | ||
} | ||
} | ||
|
||
extension Either: DataPtrRepresentable, PtrRepresentable where Left: DataPtrRepresentable, Right: DataPtrRepresentable, | ||
Left.Ptr == Right.Ptr | ||
{ | ||
public typealias Ptr = Left.Ptr | ||
|
||
public func withPtr<R>( | ||
cb: (Left.Ptr) throws -> R | ||
) rethrows -> R { | ||
switch self { | ||
case let .left(left): | ||
try left.withPtr(cb: cb) | ||
case let .right(right): | ||
try right.withPtr(cb: cb) | ||
} | ||
} | ||
} |
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
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,27 @@ | ||
public struct MaybeEither<Left, Right> { | ||
public var value: Either<Left, Right> | ||
|
||
public init(_ value: Either<Left, Right>) { | ||
self.value = value | ||
} | ||
|
||
public init(left value: Left) { | ||
self.value = .left(value) | ||
} | ||
|
||
public init(right value: Right) { | ||
self.value = .right(value) | ||
} | ||
} | ||
|
||
extension MaybeEither where Left == Right { | ||
typealias Unwrapped = Left | ||
public var unwrapped: Left { | ||
switch value { | ||
case let .left(left): | ||
left | ||
case let .right(right): | ||
right | ||
} | ||
} | ||
} |
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,27 @@ | ||
// TODO: add tests | ||
// Merkle Mountain Range | ||
public struct MMR: Sendable, Equatable, Codable { | ||
public var peaks: [Data32?] | ||
|
||
public init(_ peaks: [Data32?]) { | ||
self.peaks = peaks | ||
} | ||
|
||
public mutating func append(_ data: Data32, hasher: Hashing.Type = Blake2b256.self) { | ||
append(data, at: 0, hasher: hasher) | ||
} | ||
|
||
private mutating func append(_ data: Data32, at index: Int, hasher: Hashing.Type = Blake2b256.self) { | ||
if index >= peaks.count { | ||
peaks.append(data) | ||
} else if let current = peaks[index] { | ||
var hash = hasher.init() | ||
hash.update(current) | ||
hash.update(data) | ||
peaks[index] = nil | ||
append(hash.finalize(), at: index + 1, hasher: hasher) | ||
} else { | ||
peaks[index] = data | ||
} | ||
} | ||
} |
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,135 @@ | ||
import Foundation | ||
|
||
// TODO: add tests | ||
public enum Merklization { | ||
// roundup of half | ||
private static func half(_ i: Int) -> Int { | ||
(i + 1) / 2 | ||
} | ||
|
||
private static func binaryMerklizeHelper<T, U>( | ||
_ nodes: T, | ||
hasher: Hashing.Type = Blake2b256.self | ||
) -> MaybeEither<U, Data32> | ||
where T: RandomAccessCollection<U>, T.Index == Int, U: DataPtrRepresentable | ||
{ | ||
switch nodes.count { | ||
case 0: | ||
return .init(right: Data32()) | ||
case 1: | ||
return .init(left: nodes.first!) | ||
default: | ||
let midIndex = nodes.startIndex + half(nodes.count) | ||
let l = nodes[nodes.startIndex ..< midIndex] | ||
let r = nodes[midIndex ..< nodes.endIndex] | ||
var hash = hasher.init() | ||
hash.update("node") | ||
hash.update(binaryMerklizeHelper(l).value) | ||
hash.update(binaryMerklizeHelper(r).value) | ||
return .init(right: hash.finalize()) | ||
} | ||
} | ||
|
||
// well-balanced binary Merkle function defined in GP E.1.1 | ||
public static func binaryMerklize<T: RandomAccessCollection<Data>>(_ nodes: T, hasher: Hashing.Type = Blake2b256.self) -> Data32 | ||
where T.Index == Int | ||
{ | ||
switch binaryMerklizeHelper(nodes, hasher: hasher).value { | ||
case let .left(data): | ||
hasher.hash(data: data) | ||
case let .right(data): | ||
data | ||
} | ||
} | ||
|
||
private static func traceImpl<T, U>( | ||
_ nodes: T, | ||
index: T.Index, | ||
hasher: Hashing.Type, | ||
output: (MaybeEither<U, Data32>) -> Void | ||
) | ||
where T: RandomAccessCollection<U>, T.Index == Int, U: DataPtrRepresentable | ||
{ | ||
if nodes.count == 0 { | ||
return | ||
} | ||
|
||
func selectPart(left: Bool, nodes: T, index: T.Index) -> T.SubSequence { | ||
let h = half(nodes.count) | ||
if (index < h) == left { | ||
return nodes[nodes.startIndex ..< nodes.startIndex + h] | ||
} else { | ||
return nodes[nodes.startIndex + h ..< nodes.endIndex] | ||
} | ||
} | ||
|
||
func selectIndex(nodes: T, index: T.Index) -> T.Index { | ||
let h = half(nodes.count) | ||
if index < h { | ||
return 0 | ||
} | ||
return h | ||
} | ||
|
||
let l = binaryMerklizeHelper(selectPart(left: true, nodes: nodes, index: index), hasher: hasher) | ||
output(l) | ||
traceImpl( | ||
selectPart(left: false, nodes: nodes, index: index), | ||
index: index - selectIndex(nodes: nodes, index: index), | ||
hasher: hasher, | ||
output: output | ||
) | ||
} | ||
|
||
public static func trace<T, U>( | ||
_ nodes: T, | ||
index: T.Index, | ||
hasher: Hashing.Type = Blake2b256.self | ||
) -> [Either<U, Data32>] | ||
where T: RandomAccessCollection<U>, T.Index == Int, U: DataPtrRepresentable | ||
{ | ||
var res: [Either<U, Data32>] = [] | ||
traceImpl(nodes, index: index, hasher: hasher) { res.append($0.value) } | ||
return res | ||
} | ||
|
||
private static func constancyPreprocessor( | ||
_ nodes: some RandomAccessCollection<Data>, | ||
hasher: Hashing.Type = Blake2b256.self | ||
) -> [Data32] { | ||
let length = UInt32(nodes.count) | ||
let newLength = Int(length.nextPowerOfTwo ?? 0) | ||
var res: [Data32] = [] | ||
res.reserveCapacity(newLength) | ||
for node in nodes { | ||
var hash = hasher.init() | ||
hash.update("leaf") | ||
hash.update(node) | ||
res.append(hash.finalize()) | ||
} | ||
// fill the rest with zeros | ||
for _ in nodes.count ..< newLength { | ||
res.append(Data32()) | ||
} | ||
return res | ||
} | ||
|
||
// constant-depth binary merkle function defined in GP E.1.2 | ||
public static func constantDepthMerklize<T: RandomAccessCollection<Data>>(_ nodes: T, hasher: Hashing.Type = Blake2b256.self) -> Data32 | ||
where T.Index == Int | ||
{ | ||
binaryMerklizeHelper(constancyPreprocessor(nodes, hasher: hasher)).unwrapped | ||
} | ||
|
||
public static func generateJustification<T>( | ||
_ nodes: T, | ||
index: T.Index, | ||
hasher: Hashing.Type = Blake2b256.self | ||
) -> [Data32] | ||
where T: RandomAccessCollection<Data>, T.Index == Int | ||
{ | ||
var res: [Data32] = [] | ||
traceImpl(constancyPreprocessor(nodes, hasher: hasher), index: index, hasher: hasher) { res.append($0.unwrapped) } | ||
return res | ||
} | ||
} |
File renamed without changes.
Oops, something went wrong.