Skip to content

Commit

Permalink
Merge branch 'master' of github.com:AcalaNetwork/boka
Browse files Browse the repository at this point in the history
* 'master' of github.com:AcalaNetwork/boka:
  Merkle functions (#81)
  • Loading branch information
MacOMNI committed Aug 27, 2024
2 parents c59a82f + a4eb4a4 commit 6d4fd70
Show file tree
Hide file tree
Showing 17 changed files with 297 additions and 24 deletions.
7 changes: 6 additions & 1 deletion Blockchain/Sources/Blockchain/Runtime.swift
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,14 @@ public final class Runtime {

let workReportHashes = block.extrinsic.reports.guarantees.map(\.workReport.packageSpecification.workPackageHash)

let accumulationResult = Data32() // TODO: calculate accumulation result

var mmr = history.items.last?.mmr ?? .init([])
mmr.append(accumulationResult, hasher: Keccak.self)

let newItem = try RecentHistory.HistoryItem(
headerHash: block.header.parentHash,
mmrRoots: [], // TODO: update MMR roots
mmr: mmr,
stateRoot: Data32(), // empty and will be updated upon next block
workReportHashes: ConfigLimitedSizeArray(config: config, array: workReportHashes)
)
Expand Down
6 changes: 3 additions & 3 deletions Blockchain/Sources/Blockchain/Types/RecentHistory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public struct RecentHistory: Sendable, Equatable, Codable {
public var headerHash: Data32

// b: accumulation-result mmr
public var mmrRoots: [Data32]
public var mmr: MMR

// s
public var stateRoot: Data32
Expand All @@ -17,12 +17,12 @@ public struct RecentHistory: Sendable, Equatable, Codable {

public init(
headerHash: Data32,
mmrRoots: [Data32],
mmr: MMR,
stateRoot: Data32,
workReportHashes: ConfigLimitedSizeArray<Data32, ProtocolConfig.Int0, ProtocolConfig.TotalNumberOfCores>
) {
self.headerHash = headerHash
self.mmrRoots = mmrRoots
self.mmr = mmr
self.stateRoot = stateRoot
self.workReportHashes = workReportHashes
}
Expand Down
22 changes: 11 additions & 11 deletions Utils/Sources/Utils/Either.swift
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
public enum Either<A, B> {
case left(A)
case right(B)
public enum Either<Left, Right> {
case left(Left)
case right(Right)
}

extension Either: Equatable where A: Equatable, B: Equatable {}
extension Either: Equatable where Left: Equatable, Right: Equatable {}

extension Either: Sendable where A: Sendable, B: Sendable {}
extension Either: Sendable where Left: Sendable, Right: Sendable {}

extension Either: CustomStringConvertible where A: CustomStringConvertible, B: CustomStringConvertible {
extension Either: CustomStringConvertible where Left: CustomStringConvertible, Right: CustomStringConvertible {
public var description: String {
switch self {
case let .left(a):
Expand All @@ -18,7 +18,7 @@ extension Either: CustomStringConvertible where A: CustomStringConvertible, B: C
}
}

extension Either: Codable where A: Codable, B: Codable {
extension Either: Codable where Left: Codable, Right: Codable {
enum CodingKeys: String, CodingKey {
case left
case right
Expand Down Expand Up @@ -52,10 +52,10 @@ extension Either: Codable where A: Codable, B: Codable {
let variant = try container.decode(UInt8.self)
switch variant {
case 0:
let a = try container.decode(A.self)
let a = try container.decode(Left.self)
self = .left(a)
case 1:
let b = try container.decode(B.self)
let b = try container.decode(Right.self)
self = .right(b)
default:
throw DecodingError.dataCorrupted(
Expand All @@ -68,10 +68,10 @@ extension Either: Codable where A: Codable, B: Codable {
} else {
let container = try decoder.container(keyedBy: CodingKeys.self)
if container.contains(.left) {
let a = try container.decode(A.self, forKey: .left)
let a = try container.decode(Left.self, forKey: .left)
self = .left(a)
} else if container.contains(.right) {
let b = try container.decode(B.self, forKey: .right)
let b = try container.decode(Right.self, forKey: .right)
self = .right(b)
} else {
throw DecodingError.dataCorrupted(
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
10 changes: 10 additions & 0 deletions Utils/Sources/Utils/Extensions/UnsignedInteger+Utils.swift
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)
}
}
2 changes: 1 addition & 1 deletion Utils/Sources/Utils/Hashing/Blake2b256.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Blake2
import Foundation

public struct Blake2b256: ~Copyable, Hashing {
public struct Blake2b256: /* ~Copyable, */ Hashing {
private var hasher: Blake2b

public init() {
Expand Down
7 changes: 0 additions & 7 deletions Utils/Sources/Utils/Hashing/Hasher.swift

This file was deleted.

46 changes: 46 additions & 0 deletions Utils/Sources/Utils/Hashing/Hashing.swift
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)
}
}
}
2 changes: 1 addition & 1 deletion Utils/Sources/Utils/Hashing/Keccak.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Blake2
import Foundation
import sha3_iuf

public struct Keccak: ~Copyable, Hashing {
public struct Keccak: /* ~Copyable, */ Hashing {
private var ctx: sha3_context = .init()

public init() {
Expand Down
27 changes: 27 additions & 0 deletions Utils/Sources/Utils/MaybeEither.swift
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
}
}
}
27 changes: 27 additions & 0 deletions Utils/Sources/Utils/Merklization/MMR.swift
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
}
}
}
135 changes: 135 additions & 0 deletions Utils/Sources/Utils/Merklization/Merklization.swift
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.
Loading

0 comments on commit 6d4fd70

Please sign in to comment.