Skip to content

Commit 8b19016

Browse files
authored
RPC to submit work package (#305)
* update chain handlers * update workpackage test * update swiftlint * update BuilderHandlers * update NodeDataSourceTests * delete unused function * update workpackage message
1 parent a0f3171 commit 8b19016

File tree

11 files changed

+190
-4
lines changed

11 files changed

+190
-4
lines changed

Blockchain/Sources/Blockchain/Types/WorkPackage.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,16 @@ extension WorkPackage: Dummy {
8080
}
8181
}
8282

83+
extension WorkPackage {
84+
public func encode() throws -> Data {
85+
try JamEncoder.encode(self)
86+
}
87+
88+
public static func decode(data: Data, withConfig: ProtocolConfigRef) throws -> WorkPackage {
89+
try JamDecoder.decode(WorkPackage.self, from: data, withConfig: withConfig)
90+
}
91+
}
92+
8393
extension WorkPackage {
8494
/// a: work-package’s implied authorizer, the hash of the concatenation of the authorization code
8595
/// and the parameterization
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import Foundation
2+
import Testing
3+
4+
@testable import Blockchain
5+
6+
struct WorkPackageTests {
7+
@Test
8+
func workPackageEncodeAndDecode() throws {
9+
let workPackage = WorkPackage.dummy(config: .minimal)
10+
let data = try workPackage.encode()
11+
#expect(data.count > 0)
12+
let wp = try WorkPackage.decode(data: data, withConfig: ProtocolConfigRef.minimal)
13+
#expect(wp == workPackage)
14+
}
15+
}

Node/Sources/Node/NetworkingProtocol/CommonEphemeral/WorkPackage.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,13 @@ public struct WorkPackageMessage: Codable, Sendable, Equatable, Hashable {
1818
self.extrinsics = extrinsics
1919
}
2020
}
21+
22+
extension WorkPackageMessage {
23+
public func encode() throws -> Data {
24+
try JamEncoder.encode(self)
25+
}
26+
27+
public static func decode(data: Data, withConfig: ProtocolConfigRef) throws -> WorkPackageMessage {
28+
try JamDecoder.decode(WorkPackageMessage.self, from: data, withConfig: withConfig)
29+
}
30+
}

Node/Sources/Node/NodeDataSource.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,20 @@ extension NodeDataSource: SystemDataSource {
5151
}
5252
}
5353

54+
extension NodeDataSource: BuilderDataSource {
55+
public func submitWorkPackage(data: Data) async throws -> Bool {
56+
let workPackageMessage = try WorkPackageMessage.decode(data: data, withConfig: blockchain.config)
57+
// TODO: get guarantor from state etc.
58+
blockchain.publish(event: RuntimeEvents
59+
.WorkPackagesReceived(
60+
coreIndex: workPackageMessage.coreIndex,
61+
workPackage: workPackageMessage.workPackage.asRef(),
62+
extrinsics: workPackageMessage.extrinsics
63+
))
64+
return true
65+
}
66+
}
67+
5468
extension NodeDataSource: ChainDataSource {
5569
public func getKeys(prefix: Data32, count: UInt32, startKey: Data32?, blockHash: Data32?) async throws -> [String] {
5670
try await chainDataProvider.getKeys(prefix: prefix, count: count, startKey: startKey, blockHash: blockHash)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import Blockchain
2+
import Foundation
3+
import Testing
4+
import Utils
5+
6+
@testable import Node
7+
8+
struct NodeDataSourceTests {
9+
var dataSource: NodeDataSource!
10+
var networkManager: NetworkManagerTests!
11+
init() async throws {
12+
networkManager = try! await NetworkManagerTests()
13+
dataSource = await NodeDataSource(
14+
blockchain: networkManager.services.blockchain,
15+
chainDataProvider: networkManager.services.dataProvider,
16+
networkManager: networkManager.networkManager,
17+
name: "submitWorkPackage"
18+
)
19+
}
20+
21+
@Test func submitWorkPackage() async throws {
22+
let workPackage = WorkPackage.dummy(config: networkManager.services.config)
23+
let workPackageMessage = WorkPackageMessage(coreIndex: 0, workPackage: workPackage, extrinsics: [])
24+
#expect(try await dataSource.submitWorkPackage(data: workPackageMessage.encode()) == true)
25+
}
26+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import Blockchain
2+
import Foundation
3+
import Testing
4+
5+
@testable import Node
6+
7+
struct WorkPackageTests {
8+
@Test
9+
func workPackageEncodeAndDecode() throws {
10+
let workPackage = WorkPackage.dummy(config: .minimal)
11+
let workPackageMessage = WorkPackageMessage(coreIndex: 0, workPackage: workPackage, extrinsics: [])
12+
let data = try workPackageMessage.encode()
13+
#expect(data.count > 0)
14+
let message = try WorkPackageMessage.decode(data: data, withConfig: ProtocolConfigRef.minimal)
15+
#expect(workPackageMessage == message)
16+
}
17+
}

RPC/Sources/RPC/DataSource/DataSource.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,14 @@ public protocol ChainDataSource: Sendable {
2222
func getStorage(key: Data32, blockHash: Data32?) async throws -> [String]
2323
}
2424

25+
public protocol BuilderDataSource: Sendable {
26+
func submitWorkPackage(data: Data) async throws -> Bool
27+
}
28+
2529
public protocol TelemetryDataSource: Sendable {
2630
func name() async throws -> String
2731
func getPeersCount() async throws -> Int
2832
func getNetworkKey() async throws -> String
2933
}
3034

31-
public typealias DataSource = ChainDataSource & SystemDataSource & TelemetryDataSource
35+
public typealias DataSource = BuilderDataSource & ChainDataSource & SystemDataSource & TelemetryDataSource

RPC/Sources/RPC/Handlers/AllHandlers.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,17 @@ public enum AllHandlers {
44
SystemHandlers.handlers +
55
StateHandlers.handlers +
66
TelemetryHandlers.handlers +
7-
RPCHandlers.handlers
7+
RPCHandlers.handlers +
8+
BuilderHandlers.handlers
89

9-
public static func getHandlers(source: ChainDataSource & SystemDataSource & TelemetryDataSource) -> [any RPCHandler] {
10+
public static func getHandlers(source: ChainDataSource & SystemDataSource & TelemetryDataSource & BuilderDataSource)
11+
-> [any RPCHandler]
12+
{
1013
var handlers = ChainHandlers.getHandlers(source: source)
1114
handlers.append(contentsOf: SystemHandlers.getHandlers(source: source))
1215
handlers.append(contentsOf: StateHandlers.getHandlers(source: source))
1316
handlers.append(contentsOf: TelemetryHandlers.getHandlers(source: source))
17+
handlers.append(contentsOf: BuilderHandlers.getHandlers(source: source))
1418
handlers.append(contentsOf: RPCHandlers.getHandlers(source: Self.handlers))
1519
return handlers
1620
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import Blockchain
2+
import Codec
3+
import Foundation
4+
import Utils
5+
6+
public enum BuilderHandlers {
7+
public static let handlers: [any RPCHandler.Type] = [
8+
SubmitWorkPackage.self,
9+
]
10+
11+
public static func getHandlers(source: BuilderDataSource) -> [any RPCHandler] {
12+
[
13+
SubmitWorkPackage(source: source),
14+
]
15+
}
16+
17+
public struct SubmitWorkPackage: RPCHandler {
18+
public typealias Request = Request1<Data>
19+
public typealias Response = Bool
20+
21+
public static var method: String { "builder_submitWorkPackage" }
22+
public static var requestNames: [String] { ["workPackage"] }
23+
public static var summary: String? { "Send the work package to other connected nodes" }
24+
25+
private let source: BuilderDataSource
26+
27+
init(source: BuilderDataSource) {
28+
self.source = source
29+
}
30+
31+
public func handle(request: Request) async throws -> Response? {
32+
try await source.submitWorkPackage(data: request.value)
33+
}
34+
}
35+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import Blockchain
2+
@testable import RPC
3+
import Testing
4+
import TracingUtils
5+
@testable import Utils
6+
import Vapor
7+
import XCTVapor
8+
9+
public final class DummyBuildDataSource: Sendable {
10+
public let chainDataProvider: BlockchainDataProvider
11+
public init(
12+
chainDataProvider: BlockchainDataProvider
13+
) {
14+
self.chainDataProvider = chainDataProvider
15+
}
16+
}
17+
18+
extension DummyBuildDataSource: BuilderDataSource {
19+
public func submitWorkPackage(data _: Data) async throws -> Bool {
20+
true
21+
}
22+
}
23+
24+
final class BuilderRPCControllerTests {
25+
var app: Application!
26+
var dataProvider: BlockchainDataProvider!
27+
28+
func setUp() async throws {
29+
app = try await Application.make(.testing)
30+
let (genesisState, genesisBlock) = try! State.devGenesis(config: .minimal)
31+
dataProvider = try! await BlockchainDataProvider(InMemoryDataProvider(genesisState: genesisState, genesisBlock: genesisBlock))
32+
let rpcController = JSONRPCController(handlers: BuilderHandlers
33+
.getHandlers(source: DummyBuildDataSource(chainDataProvider: dataProvider)))
34+
try app.register(collection: rpcController)
35+
}
36+
37+
@Test func submitWorkPackage() async throws {
38+
try await setUp()
39+
let hashHex = await dataProvider.bestHead.hash.toHexString()
40+
let params = JSON.array([.string(hashHex)])
41+
let req = JSONRequest(jsonrpc: "2.0", method: "builder_submitWorkPackage", params: params, id: 0)
42+
var buffer = ByteBuffer()
43+
try buffer.writeJSONEncodable(req)
44+
try await app.test(.POST, "/", headers: ["Content-Type": "application/json"], body: buffer) { res async in
45+
#expect(res.status == .ok)
46+
let resp = try! res.content.decode(JSONResponse.self, using: JSONDecoder())
47+
#expect((resp.result!.value as! Utils.JSON).bool == true)
48+
}
49+
try await app.asyncShutdown()
50+
}
51+
}

0 commit comments

Comments
 (0)