Skip to content

Commit

Permalink
Merge branch 'master' into update-pvm-tests
Browse files Browse the repository at this point in the history
  • Loading branch information
qiweiii committed Feb 12, 2025
2 parents 6f69ce2 + 7d7820e commit 7210da0
Show file tree
Hide file tree
Showing 27 changed files with 591 additions and 72 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ name: docker

on:
push:
branches:
- master
tags:
- '*'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,18 @@ extension BlockchainDataProvider {
try await dataProvider.getBlockHash(byNumber: number)
}

public func getFinalizedHead() async throws -> Data32? {
try await dataProvider.getFinalizedHead()
}

public func getKeys(prefix: Data32, count: UInt32, startKey: Data32?, blockHash: Data32?) async throws -> [String] {
try await dataProvider.getKeys(prefix: prefix, count: count, startKey: startKey, blockHash: blockHash)
}

public func getStorage(key: Data32, blockHash: Data32?) async throws -> [String] {
try await dataProvider.getStorage(key: key, blockHash: blockHash)
}

// add forks of finalized head is not allowed
public func add(block: BlockRef) async throws {
logger.debug("adding block: \(block.hash)")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,13 @@ public protocol BlockchainDataProviderProtocol: Sendable {
func getState(hash: Data32) async throws -> StateRef?

func getFinalizedHead() async throws -> Data32?

func getHeads() async throws -> Set<Data32>

func getKeys(prefix: Data32, count: UInt32, startKey: Data32?, blockHash: Data32?) async throws -> [String]

func getStorage(key: Data32, blockHash: Data32?) async throws -> [String]

/// return empty set if not found
func getBlockHash(byTimeslot timeslot: TimeslotIndex) async throws -> Set<Data32>
/// return empty set if not found
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,25 @@ public actor InMemoryDataProvider {
}

extension InMemoryDataProvider: BlockchainDataProviderProtocol {
public func getKeys(prefix: Data32, count: UInt32, startKey: Data32?, blockHash: Data32?) async throws -> [String] {
guard let stateRef = try getState(hash: blockHash ?? genesisBlockHash) else {
return []
}

return try await stateRef.value.backend.getKeys(prefix, startKey, count).map { $0.key.toHexString() }
}

public func getStorage(key: Data32, blockHash: Data32?) async throws -> [String] {
guard let stateRef = try getState(hash: blockHash ?? genesisBlockHash) else {
return []
}

guard let value = try await stateRef.value.backend.readRaw(key) else {
throw StateBackendError.missingState(key: key)
}
return [value.toHexString()]
}

public func hasBlock(hash: Data32) -> Bool {
blockByHash[hash] != nil
}
Expand Down
13 changes: 13 additions & 0 deletions Blockchain/Sources/Blockchain/Config/ProtocolConfig+Preset.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
import Utils

extension ProtocolConfig {
private static let presetMapping: [String: ProtocolConfig] = [
"minimal": ProtocolConfigRef.minimal.value,
"dev": ProtocolConfigRef.dev.value,
"tiny": ProtocolConfigRef.tiny.value,
"mainnet": ProtocolConfigRef.mainnet.value,
]

public func presetName() -> String? {
ProtocolConfig.presetMapping.first(where: { $0.value == self })?.key
}
}

extension Ref where T == ProtocolConfig {
// TODO: pick some good numbers for dev env
public static let minimal = Ref(ProtocolConfig(
Expand Down
4 changes: 4 additions & 0 deletions Blockchain/Sources/Blockchain/State/StateBackend.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ public final class StateBackend: Sendable {
throw StateBackendError.missingState(key: key)
}

public func getKeys(_ prefix: Data32, _ startKey: Data32?, _ limit: UInt32?) async throws -> [(key: Data, value: Data)] {
try await impl.readAll(prefix: prefix.data, startKey: startKey?.data, limit: limit)
}

public func batchRead(_ keys: [any StateKey]) async throws -> [(key: any StateKey, value: (Codable & Sendable)?)] {
var ret = [(key: any StateKey, value: (Codable & Sendable)?)]()
ret.reserveCapacity(keys.count)
Expand Down
25 changes: 25 additions & 0 deletions Database/Sources/Database/RocksDBBackend.swift
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,31 @@ public final class RocksDBBackend: Sendable {
}

extension RocksDBBackend: BlockchainDataProviderProtocol {
public func getKeys(prefix: Data32, count: UInt32, startKey: Data32?, blockHash: Data32?) async throws -> [String] {
logger.trace("""
getKeys() prefix: \(prefix), count: \(count),
startKey: \(String(describing: startKey)), blockHash: \(String(describing: blockHash))
""")

guard let stateRef = try await getState(hash: blockHash ?? genesisBlockHash) else {
return []
}
return try await stateRef.value.backend.getKeys(prefix, startKey, count).map { $0.key.toHexString() }
}

public func getStorage(key: Data32, blockHash: Data32?) async throws -> [String] {
logger.trace("getStorage() key: \(key), blockHash: \(String(describing: blockHash))")

guard let stateRef = try await getState(hash: blockHash ?? genesisBlockHash) else {
return []
}

guard let value = try await stateRef.value.backend.readRaw(key) else {
throw StateBackendError.missingState(key: key)
}
return [value.toHexString()]
}

public func hasBlock(hash: Data32) async throws -> Bool {
try blocks.exists(key: hash)
}
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ format-clang:

.PHONY: run
run: githooks
swift run --package-path Boka Boka --validator
SWIFT_BACKTRACE=enable=yes swift run --package-path Boka Boka --validator

.PHONY: devnet
devnet:
Expand Down
4 changes: 4 additions & 0 deletions Node/Sources/Node/NetworkingProtocol/Network.swift
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ public final class Network: Sendable {
public var peersCount: Int {
peer.peersCount
}

public var networkKey: String {
peer.publicKey.description
}
}

struct HandlerDef: StreamHandler {
Expand Down
18 changes: 16 additions & 2 deletions Node/Sources/Node/NetworkingProtocol/SyncManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,12 @@ public actor SyncManager {

private let subscriptions: EventSubscriptions

private var status = SyncStatus.discovering
private var status = SyncStatus.discovering {
didSet {
logger.trace("status changed", metadata: ["status": "\(status)"])
}
}

private var syncContinuation: [CheckedContinuation<Void, Never>] = []

private var networkBest: HashAndSlot?
Expand Down Expand Up @@ -61,6 +66,15 @@ public actor SyncManager {

private func on(peerUpdated info: PeerInfo, newBlockHeader: HeaderRef?) async {
// TODO: improve this to handle the case misbehaved peers seding us the wrong best
logger.trace(
"on peer updated",
metadata: [
"peer": "\(info.id)",
"best": "\(String(describing: info.best))",
"finalized": "\(info.finalized)",
"newBlockHeader": "\(String(describing: newBlockHeader))",
]
)
if let networkBest {
if let peerBest = info.best, peerBest.timeslot > networkBest.timeslot {
self.networkBest = peerBest
Expand All @@ -78,7 +92,7 @@ public actor SyncManager {
}

let currentHead = await blockchain.dataProvider.bestHead
if currentHead.timeslot >= networkBest!.timeslot {
if let networkBest, currentHead.timeslot >= networkBest.timeslot {
syncCompleted()
return
}
Expand Down
55 changes: 54 additions & 1 deletion Node/Sources/Node/NodeDataSource.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,46 @@ public final class NodeDataSource: Sendable {
}
}

extension NodeDataSource: SystemDataSource {}
extension NodeDataSource: SystemDataSource {
public func getProperties() async throws -> JSON {
// TODO: Get a custom set of properties as a JSON object, defined in the chain spec
JSON.array([])
}

public func getChainName() async throws -> String {
blockchain.config.value.presetName() ?? ""
}

public func getNodeRoles() async throws -> [String] {
// TODO: Returns the roles the node is running as.
[]
}

public func getVersion() async throws -> String {
// TODO: From spec or config
"0.0.1"
}

public func getHealth() async throws -> Bool {
// TODO: Check health status
true
}

public func getImplementation() async throws -> String {
name
}
}

extension NodeDataSource: ChainDataSource {
public func getKeys(prefix: Data32, count: UInt32, startKey: Data32?, blockHash: Data32?) async throws -> [String] {
// TODO:
try await chainDataProvider.getKeys(prefix: prefix, count: count, startKey: startKey, blockHash: blockHash)
}

public func getStorage(key: Data32, blockHash: Utils.Data32?) async throws -> [String] {
try await chainDataProvider.getStorage(key: key, blockHash: blockHash)
}

public func getBestBlock() async throws -> BlockRef {
try await chainDataProvider.getBlock(hash: chainDataProvider.bestHead.hash)
}
Expand All @@ -37,6 +74,18 @@ extension NodeDataSource: ChainDataSource {
let state = try await chainDataProvider.getState(hash: blockHash)
return try await state.value.read(key: key)
}

public func getBlockHash(byTimeslot timeslot: TimeslotIndex) async throws -> Set<Data32> {
try await chainDataProvider.getBlockHash(byTimeslot: timeslot)
}

public func getHeader(hash: Data32) async throws -> HeaderRef? {
try await chainDataProvider.getHeader(hash: hash)
}

public func getFinalizedHead() async throws -> Data32? {
try await chainDataProvider.getFinalizedHead()
}
}

extension NodeDataSource: TelemetryDataSource {
Expand All @@ -47,4 +96,8 @@ extension NodeDataSource: TelemetryDataSource {
public func getPeersCount() async throws -> Int {
networkManager.peersCount
}

public func getNetworkKey() async throws -> String {
networkManager.network.networkKey
}
}
5 changes: 5 additions & 0 deletions Node/Tests/NodeTests/NodeTests.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Blockchain
import Database
import Foundation
import Testing
import Utils
Expand Down Expand Up @@ -79,6 +80,10 @@ final class NodeTests {
// Verify block was produced
#expect(newTimeslot > initialTimeslot)
#expect(try await validatorNode.blockchain.dataProvider.hasBlock(hash: newBestHead.hash))
#expect(try await validatorNode.blockchain.dataProvider.getKeys(prefix: Data32(), count: 0, startKey: nil, blockHash: nil).isEmpty)
await #expect(throws: StateBackendError.self) {
_ = try await validatorNode.blockchain.dataProvider.getStorage(key: Data32.random(), blockHash: nil)
}
}

@Test func sync() async throws {
Expand Down
2 changes: 0 additions & 2 deletions Node/Tests/NodeTests/Topology.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ struct Topology {
}

func build(genesis: Genesis) async throws -> ([(Node, StoreMiddleware)], MockScheduler) {
// setupTestLogger()

let timeProvider = MockTimeProvider(time: 1000)
let scheduler = MockScheduler(timeProvider: timeProvider)
var ret: [(Node, StoreMiddleware)] = []
Expand Down
15 changes: 14 additions & 1 deletion RPC/Sources/RPC/DataSource/DataSource.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,30 @@ import Blockchain
import Foundation
import Utils

public protocol SystemDataSource: Sendable {}
public protocol SystemDataSource: Sendable {
func getNodeRoles() async throws -> [String]
func getVersion() async throws -> String
func getHealth() async throws -> Bool
func getImplementation() async throws -> String
func getProperties() async throws -> JSON
func getChainName() async throws -> String
}

public protocol ChainDataSource: Sendable {
func getBestBlock() async throws -> BlockRef
func getBlock(hash: Data32) async throws -> BlockRef?
func getState(blockHash: Data32, key: Data32) async throws -> Data?
func getBlockHash(byTimeslot timeslot: TimeslotIndex) async throws -> Set<Data32>
func getHeader(hash: Data32) async throws -> HeaderRef?
func getFinalizedHead() async throws -> Data32?
func getKeys(prefix: Data32, count: UInt32, startKey: Data32?, blockHash: Data32?) async throws -> [String]
func getStorage(key: Data32, blockHash: Data32?) async throws -> [String]
}

public protocol TelemetryDataSource: Sendable {
func name() async throws -> String
func getPeersCount() async throws -> Int
func getNetworkKey() async throws -> String
}

public typealias DataSource = ChainDataSource & SystemDataSource & TelemetryDataSource
22 changes: 14 additions & 8 deletions RPC/Sources/RPC/Handlers/ChainHandlers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,12 @@ public enum ChainHandlers {
self.source = source
}

public func handle(request _: Request) async throws -> Response? {
// TODO: implement
nil
public func handle(request: Request) async throws -> Response? {
if let timeslot = request.value {
let blocks = try await source.getBlockHash(byTimeslot: timeslot)
return blocks.first?.data
}
return nil
}
}

Expand All @@ -78,8 +81,7 @@ public enum ChainHandlers {
}

public func handle(request _: Request) async throws -> Response? {
// TODO: implement
nil
try await source.getFinalizedHead()
}
}

Expand All @@ -97,9 +99,13 @@ public enum ChainHandlers {
self.source = source
}

public func handle(request _: Request) async throws -> Response? {
// TODO: implement
nil
public func handle(request: Request) async throws -> Response? {
let header = if let hash = request.value {
try await source.getHeader(hash: hash)?.value
} else {
try await source.getBestBlock().header
}
return try header.map { try JamEncoder.encode($0) }
}
}
}
10 changes: 4 additions & 6 deletions RPC/Sources/RPC/Handlers/StateHandlers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,8 @@ public enum StateHandlers {
self.source = source
}

public func handle(request _: Request) async throws -> Response? {
// TODO: implement
[]
public func handle(request: Request) async throws -> Response? {
try await source.getKeys(prefix: request.value.0, count: request.value.1, startKey: request.value.2, blockHash: request.value.3)
}
}

Expand All @@ -49,9 +48,8 @@ public enum StateHandlers {
self.source = source
}

public func handle(request _: Request) async throws -> Response? {
// TODO: implement
[]
public func handle(request: Request) async throws -> Response? {
try await source.getStorage(key: request.value.0, blockHash: request.value.1)
}
}
}
Loading

0 comments on commit 7210da0

Please sign in to comment.