Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Network handler #182

Merged
merged 25 commits into from
Oct 21, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 4 additions & 0 deletions Blockchain/Sources/Blockchain/Blockchain.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,8 @@ public final class Blockchain: ServiceBase, @unchecked Sendable {

publish(RuntimeEvents.BlockFinalized(hash: hash))
}

public func publish(event: some Event) {
publish(event)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,15 @@ public final class BlockchainDataProvider: Sendable {
let heads = try await dataProvider.getHeads()
var bestHead: (HeaderRef, Data32)?
for head in heads {
guard let header = try? await dataProvider.getHeader(hash: head) else {
continue
}
let header = try await dataProvider.getHeader(hash: head)
if bestHead == nil || header.value.timeslot > bestHead!.0.value.timeslot {
bestHead = (header, head)
}
}
let finalizedHead = try await dataProvider.getFinalizedHead()

storage = ThreadSafeContainer(.init(
bestHead: bestHead?.1 ?? Data32(),
bestHead: bestHead?.1 ?? dataProvider.genesisBlockHash,
bestHeadTimeslot: bestHead?.0.value.timeslot ?? 0,
finalizedHead: finalizedHead
))
Expand Down Expand Up @@ -129,4 +127,8 @@ extension BlockchainDataProvider {

try await dataProvider.remove(hash: hash)
}

public var genesisBlockHash: Data32 {
dataProvider.genesisBlockHash
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,6 @@ public protocol BlockchainDataProviderProtocol: Sendable {

/// remove header, block and state
func remove(hash: Data32) async throws

var genesisBlockHash: Data32 { get }
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,15 @@ public actor InMemoryDataProvider: Sendable {
private var blockByHash: [Data32: BlockRef] = [:]
private var stateByBlockHash: [Data32: StateRef] = [:]
private var hashByTimeslot: [TimeslotIndex: Set<Data32>] = [:]
public let genesisBlockHash: Data32

public init(genesis: StateRef) async {
heads = [Data32()]
finalizedHead = Data32()
public init(genesisState: StateRef, genesisBlock: BlockRef) async {
genesisBlockHash = genesisBlock.hash
heads = [genesisBlockHash]
finalizedHead = genesisBlockHash

add(state: genesis)
add(block: genesisBlock)
add(state: genesisState)
}
}

Expand Down Expand Up @@ -80,8 +83,7 @@ extension InMemoryDataProvider: BlockchainDataProviderProtocol {
// parent needs to be either
// - existing head
// - known block
// - genesis / all zeros
guard heads.remove(parent) != nil || hasBlock(hash: parent) || parent == Data32() else {
guard heads.remove(parent) != nil || hasBlock(hash: parent) else {
throw BlockchainDataProviderError.noData(hash: parent)
}
heads.insert(hash)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ public enum RuntimeEvents {
// New safrole ticket received from network
public struct SafroleTicketsReceived: Event {
public let items: [ExtrinsicTickets.TicketItem]

public init(items: [ExtrinsicTickets.TicketItem]) {
self.items = items
}
}

// New block authored by BlockAuthor service
Expand Down
10 changes: 9 additions & 1 deletion Blockchain/Sources/Blockchain/Types/RecentHistory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,15 @@ public struct RecentHistory: Sendable, Equatable, Codable {
extension RecentHistory: Dummy {
public typealias Config = ProtocolConfigRef
public static func dummy(config: Config) -> RecentHistory {
RecentHistory(items: try! ConfigLimitedSizeArray(config: config))
RecentHistory(items: try! ConfigLimitedSizeArray(
config: config,
array: [HistoryItem(
headerHash: Data32(),
mmr: MMR([]),
stateRoot: Data32(),
workReportHashes: ConfigLimitedSizeArray(config: config)
)]
))
}
}

Expand Down
12 changes: 10 additions & 2 deletions Blockchain/Sources/Blockchain/Types/State+Genesis.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Utils

extension State {
public static func devGenesis(config: ProtocolConfigRef) throws -> State {
public static func devGenesis(config: ProtocolConfigRef) throws -> (StateRef, BlockRef) {
var devKeys = [ValidatorKey]()

var state = State.dummy(config: config)
Expand Down Expand Up @@ -32,7 +32,15 @@ extension State {
)
state.safroleState.ticketsVerifier = commitment.data

return state
let block = BlockRef(Block.dummy(config: config))
try state.recentHistory.items.append(RecentHistory.HistoryItem(
headerHash: block.hash,
mmr: MMR([]),
stateRoot: Data32(),
workReportHashes: ConfigLimitedSizeArray(config: config)
))

return (StateRef(state), block)
}
// TODO: add file genesis
// public static func fileGenesis(config: ProtocolConfigRef) throws -> State
Expand Down
2 changes: 1 addition & 1 deletion Blockchain/Sources/Blockchain/Types/State.swift
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ public struct State: Sendable, Equatable, Codable {

extension State {
public var lastBlockHash: Data32 {
recentHistory.items.last.map(\.headerHash) ?? Data32()
recentHistory.items.last.map(\.headerHash)!
}
}

Expand Down
9 changes: 5 additions & 4 deletions Blockchain/Tests/BlockchainTests/BlockAuthorTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ struct BlockAuthorTests {
config = ProtocolConfigRef.dev
timeProvider = MockTimeProvider(time: 988)

dataProvider = try await BlockchainDataProvider(InMemoryDataProvider(genesis: StateRef(State.devGenesis(config: config))))
let (genesisState, genesisBlock) = try State.devGenesis(config: config)
dataProvider = try await BlockchainDataProvider(InMemoryDataProvider(genesisState: genesisState, genesisBlock: genesisBlock))

storeMiddleware = StoreMiddleware()
eventBus = EventBus(eventMiddleware: Middleware(storeMiddleware))
Expand All @@ -45,7 +46,7 @@ struct BlockAuthorTests {

@Test
func createNewBlockWithFallbackKey() async throws {
let genesisState = try await dataProvider.getState(hash: Data32())
let genesisState = try await dataProvider.getState(hash: dataProvider.genesisBlockHash)

let timeslot = timeProvider.getTime().timeToTimeslot(config: config)

Expand All @@ -62,7 +63,7 @@ struct BlockAuthorTests {

@Test
func createNewBlockWithTicket() async throws {
let genesisState = try await dataProvider.getState(hash: Data32())
let genesisState = try await dataProvider.getState(hash: dataProvider.genesisBlockHash)
var state = genesisState.value

state.safroleState.ticketsVerifier = try Bandersnatch.RingCommitment(
Expand Down Expand Up @@ -108,7 +109,7 @@ struct BlockAuthorTests {

@Test
func scheduleNewBlocks() async throws {
let genesisState = try await dataProvider.getState(hash: Data32())
let genesisState = try await dataProvider.getState(hash: dataProvider.genesisBlockHash)

await blockAuthor.on(genesis: genesisState)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,23 +38,23 @@ struct DispatchQueueSchedulerTests {

let diff = try #require(end.value).timeIntervalSince(now) - delay
let diffAbs = abs(diff)
#expect(diffAbs < 0.5)
#expect(diffAbs < 1)
}
}

@Test func scheduleRepeatingTask() async throws {
try await confirmation(expectedCount: 3) { confirm in
let delay = 0.5
try await confirmation(expectedCount: 2) { confirm in
let delay = 1.5
let now = Date()
let executionTimes = ThreadSafeContainer<[Date]>([])
let expectedExecutions = 3
let expectedExecutions = 2

let cancel = scheduler.schedule(delay: delay, repeats: true) {
executionTimes.value.append(Date())
confirm()
}

try await Task.sleep(for: .seconds(1.6))
try await Task.sleep(for: .seconds(3.1))

_ = cancel

Expand All @@ -64,7 +64,7 @@ struct DispatchQueueSchedulerTests {
let expectedInterval = delay * Double(index + 1)
let actualInterval = time.timeIntervalSince(now)
let difference = abs(actualInterval - expectedInterval)
#expect(difference < 0.5)
#expect(difference < 1)
}
}
}
Expand All @@ -83,13 +83,13 @@ struct DispatchQueueSchedulerTests {

@Test func cancelRepeatingTask() async throws {
try await confirmation(expectedCount: 2) { confirm in
let delay = 0.5
let delay = 1.0

let cancel = scheduler.schedule(delay: delay, repeats: true) {
confirm()
}

try await Task.sleep(for: .seconds(1.2))
try await Task.sleep(for: .seconds(2.2))

cancel.cancel()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ struct ExtrinsicPoolServiceTests {
}
timeProvider = MockTimeProvider(time: 1000)

dataProvider = try await BlockchainDataProvider(InMemoryDataProvider(genesis: StateRef(State.devGenesis(config: config))))
let (genesisState, genesisBlock) = try State.devGenesis(config: config)
dataProvider = try await BlockchainDataProvider(InMemoryDataProvider(genesisState: genesisState, genesisBlock: genesisBlock))

storeMiddleware = StoreMiddleware()
eventBus = EventBus(eventMiddleware: Middleware(storeMiddleware))
Expand Down Expand Up @@ -186,6 +187,7 @@ struct ExtrinsicPoolServiceTests {

let newBlock = BlockRef.dummy(config: config).mutate {
$0.header.unsigned.timeslot = nextTimeslot
$0.header.unsigned.parentHash = dataProvider.bestHead
}

let oldEntropyPool = state.value.entropyPool
Expand Down
42 changes: 23 additions & 19 deletions Blockchain/Tests/BlockchainTests/InMemoryDataProviderTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,18 @@ struct InMemoryDataProviderTests {
let config = ProtocolConfigRef.mainnet

@Test func testInitialization() async throws {
let genesis = StateRef(State.dummy(config: config))
let provider = await InMemoryDataProvider(genesis: genesis)
let (genesis, block) = try State.devGenesis(config: config)
let provider = await InMemoryDataProvider(genesisState: genesis, genesisBlock: block)

#expect(await (provider.getHeads()) == [Data32()])
#expect(await (provider.getFinalizedHead()) == Data32())
#expect(await (provider.getHeads()) == [block.hash])
#expect(await (provider.getFinalizedHead()) == block.hash)
}

@Test func testAddAndRetrieveBlock() async throws {
let genesis = StateRef(State.dummy(config: config))
let provider = await InMemoryDataProvider(genesis: genesis)
let (genesis, block) = try State.devGenesis(config: config)

let provider = await InMemoryDataProvider(genesisState: genesis, genesisBlock: block)

let block = BlockRef(Block.dummy(config: config))
await provider.add(block: block)

#expect(await (provider.hasBlock(hash: block.hash)) == true)
Expand All @@ -29,8 +29,9 @@ struct InMemoryDataProviderTests {
}

@Test func testAddAndRetrieveState() async throws {
let genesis = StateRef(State.dummy(config: config))
let provider = await InMemoryDataProvider(genesis: genesis)
let (genesis, block) = try State.devGenesis(config: config)

let provider = await InMemoryDataProvider(genesisState: genesis, genesisBlock: block)

let state = StateRef(State.dummy(config: config))
await provider.add(state: state)
Expand All @@ -40,16 +41,18 @@ struct InMemoryDataProviderTests {
}

@Test func testUpdateHead() async throws {
let genesis = StateRef(State.dummy(config: config))
let provider = await InMemoryDataProvider(genesis: genesis)
let (genesis, block) = try State.devGenesis(config: config)
let provider = await InMemoryDataProvider(genesisState: genesis, genesisBlock: block)

let newBlock = BlockRef(Block.dummy(config: config))
let newBlock = BlockRef(Block.dummy(config: config)).mutate {
$0.header.unsigned.timeslot = 123
}

await provider.add(block: newBlock)
try await provider.updateHead(hash: newBlock.hash, parent: Data32())
try await provider.updateHead(hash: newBlock.hash, parent: block.hash)

#expect(await provider.isHead(hash: newBlock.hash) == true)
#expect(await provider.isHead(hash: Data32()) == false)
#expect(await provider.isHead(hash: block.hash) == false)

let hash = Data32.random()
await #expect(throws: BlockchainDataProviderError.noData(hash: hash)) {
Expand All @@ -58,19 +61,20 @@ struct InMemoryDataProviderTests {
}

@Test func testSetFinalizedHead() async throws {
let genesis = StateRef(State.dummy(config: config))
let provider = await InMemoryDataProvider(genesis: genesis)
let (genesis, block) = try State.devGenesis(config: config)

let provider = await InMemoryDataProvider(genesisState: genesis, genesisBlock: block)

let block = BlockRef(Block.dummy(config: config))
await provider.add(block: block)
await provider.setFinalizedHead(hash: block.hash)

#expect(await (provider.getFinalizedHead()) == block.hash)
}

@Test func testRemoveHash() async throws {
let genesis = StateRef(State.dummy(config: config))
let provider = await InMemoryDataProvider(genesis: genesis)
let (genesis, block) = try State.devGenesis(config: config)

let provider = await InMemoryDataProvider(genesisState: genesis, genesisBlock: block)

let state = StateRef(State.dummy(config: ProtocolConfigRef.dev))
let timeslotIndex = state.value.timeslot
Expand Down
2 changes: 1 addition & 1 deletion Blockchain/Tests/BlockchainTests/SafroleServiceTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ struct SafroleServiceTests {
}
timeProvider = MockTimeProvider(time: 1000)

genesisState = try StateRef(State.devGenesis(config: config))
(genesisState, _) = try State.devGenesis(config: config)

storeMiddleware = StoreMiddleware()
eventBus = EventBus(eventMiddleware: Middleware(storeMiddleware))
Expand Down
9 changes: 5 additions & 4 deletions Blockchain/Tests/BlockchainTests/ValidatorServiceTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ struct ValidatorServiceTests {
config = ProtocolConfigRef.dev
timeProvider = MockTimeProvider(time: 988)

dataProvider = try await BlockchainDataProvider(InMemoryDataProvider(genesis: StateRef(State.devGenesis(config: config))))
let (genesisState, genesisBlock) = try State.devGenesis(config: config)
dataProvider = try await BlockchainDataProvider(InMemoryDataProvider(genesisState: genesisState, genesisBlock: genesisBlock))

storeMiddleware = StoreMiddleware()
eventBus = EventBus(eventMiddleware: Middleware(storeMiddleware))
Expand All @@ -48,7 +49,7 @@ struct ValidatorServiceTests {

@Test
func onGenesis() async throws {
let genesisState = try await dataProvider.getState(hash: Data32())
let genesisState = try await dataProvider.getState(hash: dataProvider.genesisBlockHash)

await validatorService.on(genesis: genesisState)

Expand All @@ -64,7 +65,7 @@ struct ValidatorServiceTests {

@Test
func produceBlocks() async throws {
let genesisState = try await dataProvider.getState(hash: Data32())
let genesisState = try await dataProvider.getState(hash: dataProvider.genesisBlockHash)

await validatorService.on(genesis: genesisState)

Expand Down Expand Up @@ -103,7 +104,7 @@ struct ValidatorServiceTests {

@Test
func makeManyBlocks() async throws {
let genesisState = try await dataProvider.getState(hash: Data32())
let genesisState = try await dataProvider.getState(hash: dataProvider.genesisBlockHash)

await validatorService.on(genesis: genesisState)

Expand Down
6 changes: 6 additions & 0 deletions Boka/.swiftpm/xcode/xcshareddata/xcschemes/Boka.xcscheme
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@
ReferencedContainer = "container:">
</BuildableReference>
</BuildableProductRunnable>
<CommandLineArguments>
<CommandLineArgument
argument = "--validator --dev-seed 1"
isEnabled = "YES">
</CommandLineArgument>
</CommandLineArguments>
<EnvironmentVariables>
<EnvironmentVariable
key = "LOG_LEVEL"
Expand Down
Loading