Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 75 additions & 63 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -1,64 +1,76 @@
{
"configurations": [
{
"type": "lldb",
"request": "launch",
"sourceLanguages": [
"swift"
],
"args": [],
"cwd": "${workspaceFolder:boka}/Boka",
"name": "Debug Boka (Boka)",
"program": "${workspaceFolder:boka}/Boka/.build/debug/Boka",
"preLaunchTask": "swift: Build Debug Boka (Boka)"
},
{
"type": "lldb",
"request": "launch",
"sourceLanguages": [
"swift"
],
"args": [],
"cwd": "${workspaceFolder:boka}/Boka",
"name": "Release Boka (Boka)",
"program": "${workspaceFolder:boka}/Boka/.build/release/Boka",
"preLaunchTask": "swift: Build Release Boka (Boka)"
},
{
"type": "lldb",
"request": "launch",
"args": [],
"cwd": "${workspaceFolder:boka}/Cli",
"name": "Debug Cli (Cli)",
"program": "${workspaceFolder:boka}/Cli/.build/debug/Cli",
"preLaunchTask": "swift: Build Debug Cli (Cli)"
},
{
"type": "lldb",
"request": "launch",
"args": [],
"cwd": "${workspaceFolder:boka}/Cli",
"name": "Release Cli (Cli)",
"program": "${workspaceFolder:boka}/Cli/.build/release/Cli",
"preLaunchTask": "swift: Build Release Cli (Cli)"
},
{
"type": "lldb",
"request": "launch",
"args": [],
"cwd": "${workspaceFolder:boka}/Tools",
"name": "Debug Tools (Tools)",
"program": "${workspaceFolder:boka}/Tools/.build/debug/Tools",
"preLaunchTask": "swift: Build Debug Tools (Tools)"
},
{
"type": "lldb",
"request": "launch",
"args": [],
"cwd": "${workspaceFolder:boka}/Tools",
"name": "Release Tools (Tools)",
"program": "${workspaceFolder:boka}/Tools/.build/release/Tools",
"preLaunchTask": "swift: Build Release Tools (Tools)"
}
]
}
"configurations": [
{
"type": "swift",
"request": "launch",
"args": [],
"cwd": "${workspaceFolder:boka}/Boka",
"name": "Debug Boka (Boka)",
"program": "${workspaceFolder:boka}/Boka/.build/debug/Boka",
"preLaunchTask": "swift: Build Debug Boka (Boka)"
},
{
"type": "swift",
"request": "launch",
"args": [],
"cwd": "${workspaceFolder:boka}/Boka",
"name": "Release Boka (Boka)",
"program": "${workspaceFolder:boka}/Boka/.build/release/Boka",
"preLaunchTask": "swift: Build Release Boka (Boka)"
},
{
"type": "lldb",
"request": "launch",
"args": [],
"cwd": "${workspaceFolder:boka}/Cli",
"name": "Debug Cli (Cli)",
"program": "${workspaceFolder:boka}/Cli/.build/debug/Cli",
"preLaunchTask": "swift: Build Debug Cli (Cli)"
},
{
"type": "lldb",
"request": "launch",
"args": [],
"cwd": "${workspaceFolder:boka}/Cli",
"name": "Release Cli (Cli)",
"program": "${workspaceFolder:boka}/Cli/.build/release/Cli",
"preLaunchTask": "swift: Build Release Cli (Cli)"
},
{
"type": "lldb",
"request": "launch",
"args": [],
"cwd": "${workspaceFolder:boka}/Tools",
"name": "Debug Tools (Tools)",
"program": "${workspaceFolder:boka}/Tools/.build/debug/Tools",
"preLaunchTask": "swift: Build Debug Tools (Tools)"
},
{
"type": "lldb",
"request": "launch",
"args": [],
"cwd": "${workspaceFolder:boka}/Tools",
"name": "Release Tools (Tools)",
"program": "${workspaceFolder:boka}/Tools/.build/release/Tools",
"preLaunchTask": "swift: Build Release Tools (Tools)"
},
{
"type": "swift",
"request": "launch",
"args": [],
"cwd": "${workspaceFolder:boka}/Fuzzing",
"name": "Debug BokaFuzzer (Fuzzing)",
"program": "${workspaceFolder:boka}/Fuzzing/.build/debug/BokaFuzzer",
"preLaunchTask": "swift: Build Debug BokaFuzzer (Fuzzing)"
},
{
"type": "swift",
"request": "launch",
"args": [],
"cwd": "${workspaceFolder:boka}/Fuzzing",
"name": "Release BokaFuzzer (Fuzzing)",
"program": "${workspaceFolder:boka}/Fuzzing/.build/release/BokaFuzzer",
"preLaunchTask": "swift: Build Release BokaFuzzer (Fuzzing)"
}
]
}
31 changes: 29 additions & 2 deletions Blockchain/Sources/Blockchain/State/StateBackend.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,35 @@
throw StateBackendError.missingState(key: key)
}

public func getKeys(_ prefix: Data31, _ startKey: Data31?, _ limit: UInt32?) async throws -> [(key: Data, value: Data)] {
try await impl.readAll(prefix: prefix.data, startKey: startKey?.data, limit: limit)
public func getKeys(_ prefix: Data31?, _ startKey: Data31?, _ limit: UInt32?) async throws -> [(key: Data, value: Data)] {
let prefixData = prefix?.data ?? Data()
let allTrieNodes = try await impl.readAll(prefix: prefixData, startKey: startKey?.data, limit: nil)

var stateKeyValues: [(key: Data, value: Data)] = []

for (_, trieNodeData) in allTrieNodes {
if let limit, stateKeyValues.count >= Int(limit) {
break
}

// check if it's a leaf node
guard trieNodeData.count == 64 else { continue }
let firstByte = trieNodeData[0]
let isLeaf = (firstByte & 0b1100_0000) == 0b1000_0000 || (firstByte & 0b1100_0000) == 0b1100_0000

if isLeaf {
// extract the state key (skip first byte flags)
let stateKey = Data(trieNodeData[1 ..< 32])
// check if this state key matches input prefix
if prefixData.isEmpty || stateKey.starts(with: prefixData) {
if let value = try await trie.read(key: Data31(stateKey)!) {
stateKeyValues.append((key: stateKey, value: value))
}
}
}

Check warning on line 66 in Blockchain/Sources/Blockchain/State/StateBackend.swift

View check run for this annotation

Codecov / codecov/patch

Blockchain/Sources/Blockchain/State/StateBackend.swift#L48-L66

Added lines #L48 - L66 were not covered by tests
}

return stateKeyValues
}

public func batchRead(_ keys: [any StateKey]) async throws -> [(key: any StateKey, value: (Codable & Sendable)?)] {
Expand Down
2 changes: 2 additions & 0 deletions Boka/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ let package = Package(
dependencies: [
.package(path: "../Node"),
.package(path: "../TracingUtils"),
.package(path: "../Fuzzing"),
.package(url: "https://github.com/slashmo/swift-otel.git", from: "0.9.0"),
.package(url: "https://github.com/swift-server/swift-service-lifecycle.git", from: "2.6.0"),
.package(url: "https://github.com/vapor/console-kit.git", from: "4.15.0"),
Expand All @@ -25,6 +26,7 @@ let package = Package(
dependencies: [
"Node",
"TracingUtils",
"Fuzzing",
.product(name: "ServiceLifecycle", package: "swift-service-lifecycle"),
.product(name: "OTel", package: "swift-otel"),
.product(name: "OTLPGRPC", package: "swift-otel"),
Expand Down
2 changes: 1 addition & 1 deletion Boka/Sources/Boka.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ struct Boka: AsyncParsableCommand {
static let configuration = CommandConfiguration(
abstract: "JAM built with Swift",
version: "0.0.1",
subcommands: [Generate.self]
subcommands: [Generate.self, Fuzz.self]
)

@Option(name: .shortAndLong, help: "Base path to database files.")
Expand Down
80 changes: 80 additions & 0 deletions Boka/Sources/Fuzz.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import ArgumentParser
import Foundation
import Fuzzing
import Logging

extension Boka {
struct Fuzz: AsyncParsableCommand {
static let configuration = CommandConfiguration(
commandName: "fuzz",
abstract: "JAM Conformance Protocol",
subcommands: [Target.self, Fuzzer.self]
)
}
}

extension Boka.Fuzz {
struct Target: AsyncParsableCommand {
static let configuration = CommandConfiguration(
abstract: "Run fuzzing target - waits for fuzzer connections"
)

@Option(help: "Unix socket path for fuzzing protocol")
var socketPath: String = "/tmp/jam_conformance.sock"

Check warning on line 23 in Boka/Sources/Fuzz.swift

View check run for this annotation

Codecov / codecov/patch

Boka/Sources/Fuzz.swift#L22-L23

Added lines #L22 - L23 were not covered by tests

@Option(help: "JAM Protocol configuration preset, tiny or full")
var config: String = "tiny"

Check warning on line 26 in Boka/Sources/Fuzz.swift

View check run for this annotation

Codecov / codecov/patch

Boka/Sources/Fuzz.swift#L25-L26

Added lines #L25 - L26 were not covered by tests

func run() async throws {
let env = ProcessInfo.processInfo.environment
LoggingSystem.bootstrap { label in
var handler = StreamLogHandler.standardOutput(label: label)
handler.logLevel = parseLevel(env["LOG_LEVEL"] ?? "") ?? .info
return handler
}

let fuzzTarget = try FuzzingTarget(
socketPath: socketPath,
config: config
)

try await fuzzTarget.run()
}

Check warning on line 42 in Boka/Sources/Fuzz.swift

View check run for this annotation

Codecov / codecov/patch

Boka/Sources/Fuzz.swift#L28-L42

Added lines #L28 - L42 were not covered by tests
}

struct Fuzzer: AsyncParsableCommand {
static let configuration = CommandConfiguration(
abstract: "Run fuzzing fuzzer - connects to targets"
)

@Option(help: "Unix socket path for fuzzing protocol.")
var socketPath: String = "/tmp/jam_conformance.sock"

Check warning on line 51 in Boka/Sources/Fuzz.swift

View check run for this annotation

Codecov / codecov/patch

Boka/Sources/Fuzz.swift#L50-L51

Added lines #L50 - L51 were not covered by tests

@Option(help: "JAM Protocol configuration preset.")
var config: String = "tiny"

Check warning on line 54 in Boka/Sources/Fuzz.swift

View check run for this annotation

Codecov / codecov/patch

Boka/Sources/Fuzz.swift#L53-L54

Added lines #L53 - L54 were not covered by tests

@Option(name: .long, help: "Random seed for deterministic testing. Default is random")
var seed: UInt64 = .random(in: 0 ... UInt64.max)

Check warning on line 57 in Boka/Sources/Fuzz.swift

View check run for this annotation

Codecov / codecov/patch

Boka/Sources/Fuzz.swift#L56-L57

Added lines #L56 - L57 were not covered by tests

@Option(name: .long, help: "Number of blocks to process.")
var blocks: Int = 100

Check warning on line 60 in Boka/Sources/Fuzz.swift

View check run for this annotation

Codecov / codecov/patch

Boka/Sources/Fuzz.swift#L59-L60

Added lines #L59 - L60 were not covered by tests

func run() async throws {
let env = ProcessInfo.processInfo.environment
LoggingSystem.bootstrap { label in
var handler = StreamLogHandler.standardOutput(label: label)
handler.logLevel = parseLevel(env["LOG_LEVEL"] ?? "") ?? .info
return handler
}

let fuzzer = try FuzzingClient(
socketPath: socketPath,
config: config,
seed: seed,
blockCount: blocks
)

try await fuzzer.run()
}

Check warning on line 78 in Boka/Sources/Fuzz.swift

View check run for this annotation

Codecov / codecov/patch

Boka/Sources/Fuzz.swift#L62-L78

Added lines #L62 - L78 were not covered by tests
}
}
2 changes: 1 addition & 1 deletion Boka/Sources/Tracing.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
)
}

private func parseLevel(_ level: String) -> Logger.Level? {
public func parseLevel(_ level: String) -> Logger.Level? {

Check warning on line 43 in Boka/Sources/Tracing.swift

View check run for this annotation

Codecov / codecov/patch

Boka/Sources/Tracing.swift#L43

Added line #L43 was not covered by tests
switch level.lowercased().trimmingCharacters(in: .whitespaces) {
case "trace": .trace
case "debug": .debug
Expand Down
48 changes: 48 additions & 0 deletions Fuzzing/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// swift-tools-version: 6.0

import PackageDescription

let package = Package(
name: "Fuzzing",
platforms: [
.macOS(.v15),
],
products: [
.library(
name: "Fuzzing",
targets: ["Fuzzing"]
),
],
dependencies: [
.package(path: "../Blockchain"),
.package(path: "../Codec"),
.package(path: "../TracingUtils"),
.package(path: "../Utils"),
.package(url: "https://github.com/apple/swift-testing.git", branch: "6.0.0"),
],
targets: [
.target(
name: "Fuzzing",
dependencies: [
"Blockchain",
"Codec",
"TracingUtils",
"Utils",
],
swiftSettings: [
.interoperabilityMode(.Cxx),
]
),
.testTarget(
name: "FuzzingTests",
dependencies: [
"Fuzzing",
.product(name: "Testing", package: "swift-testing"),
],
swiftSettings: [
.interoperabilityMode(.Cxx),
]
),
],
swiftLanguageModes: [.version("6")]
)
29 changes: 29 additions & 0 deletions Fuzzing/Sources/Fuzzing/FuzzGenerator/FuzzGenerator.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import Blockchain
import Codec
import Foundation
import Utils

/// Protocol for generating fuzzing test data
public protocol FuzzGenerator {
/// Generate initial state for fuzzing
/// - Parameters:
/// - timeslot: The timeslot for which to generate the state
/// - config: Protocol configuration
/// - Returns: Array of fuzz key-value pairs representing the state
func generateState(timeslot: TimeslotIndex, config: ProtocolConfigRef) async throws -> [FuzzKeyValue]

/// Generate a block for the given timeslot and state
/// - Parameters:
/// - timeslot: The timeslot for which to generate the block
/// - currentStateRef: Current state reference
/// - config: Protocol configuration
/// - Returns: A valid block reference
func generateBlock(timeslot: UInt32, currentStateRef: StateRef, config: ProtocolConfigRef) async throws -> BlockRef
}

/// Error types for fuzz generators
public enum FuzzGeneratorError: Error {
case stateGenerationFailed(String)
case blockGenerationFailed(String)
case invalidTestData(String)
}
Loading