Skip to content

Commit

Permalink
Cli refactor (#189)
Browse files Browse the repository at this point in the history
* refactor cli handling

* script to launch network

* update

* fix schema

* fix test
  • Loading branch information
xlc authored Oct 23, 2024
1 parent 1bb83f2 commit 6b754db
Show file tree
Hide file tree
Showing 12 changed files with 225 additions and 168 deletions.
38 changes: 38 additions & 0 deletions Blockchain/Sources/Blockchain/Config/ProtocolConfig+Preset.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,44 @@
import Utils

extension Ref where T == ProtocolConfig {
// TODO: pick some good numbers for dev env
public static let minimal = Ref(ProtocolConfig(
auditTranchePeriod: 8,
additionalMinBalancePerStateItem: 10,
additionalMinBalancePerStateByte: 1,
serviceMinBalance: 100,
totalNumberOfCores: 1,
preimagePurgePeriod: 28800,
epochLength: 6,
auditBiasFactor: 2,
coreAccumulationGas: Gas(10_000_000), // TODO: check this
workPackageAuthorizerGas: Gas(10_000_000), // TODO: check this
workPackageRefineGas: Gas(10_000_000), // TODO: check this
recentHistorySize: 8,
maxWorkItems: 4,
maxTicketsPerExtrinsic: 4,
maxLookupAnchorAge: 14400,
transferMemoSize: 128,
ticketEntriesPerValidator: 2,
maxAuthorizationsPoolItems: 8,
slotPeriodSeconds: 4,
maxAuthorizationsQueueItems: 10,
coreAssignmentRotationPeriod: 6,
maxServiceCodeSize: 4_000_000,
preimageReplacementPeriod: 5,
totalNumberOfValidators: 3,
erasureCodedPieceSize: 684,
maxWorkPackageManifestEntries: 1 << 11,
maxEncodedWorkPackageSize: 12 * 1 << 20,
maxEncodedWorkReportSize: 96 * 1 << 10,
erasureCodedSegmentSize: 6,
ticketSubmissionEndSlot: 2,
pvmDynamicAddressAlignmentFactor: 2,
pvmProgramInitInputDataSize: 1 << 24,
pvmProgramInitPageSize: 1 << 14,
pvmProgramInitSegmentSize: 1 << 16
))

// TODO: pick some good numbers for dev env
public static let dev = Ref(ProtocolConfig(
auditTranchePeriod: 8,
Expand Down
2 changes: 1 addition & 1 deletion Boka/.swiftpm/xcode/xcshareddata/xcschemes/Boka.xcscheme
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
</BuildableProductRunnable>
<CommandLineArguments>
<CommandLineArgument
argument = "--validator --dev-seed 1"
argument = "--validator"
isEnabled = "YES">
</CommandLineArgument>
</CommandLineArguments>
Expand Down
17 changes: 13 additions & 4 deletions Boka/Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Boka/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ let package = Package(
.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"),
.package(url: "https://github.com/apple/swift-testing.git", branch: "0.10.0"),
.package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.5.0"),
],
targets: [
// Targets are the basic building blocks of a package, defining a module or a test suite.
Expand All @@ -28,6 +29,7 @@ let package = Package(
.product(name: "OTel", package: "swift-otel"),
.product(name: "OTLPGRPC", package: "swift-otel"),
.product(name: "ConsoleKit", package: "console-kit"),
.product(name: "ArgumentParser", package: "swift-argument-parser"),
]
),
.testTarget(
Expand Down
201 changes: 93 additions & 108 deletions Boka/Sources/Boka.swift
Original file line number Diff line number Diff line change
@@ -1,147 +1,138 @@
import ArgumentParser
import Blockchain
import ConsoleKit
import Foundation
import Node
import ServiceLifecycle
import TracingUtils
import Utils

enum InvalidArgumentError: Error {
case invalidArgument(String)
extension Genesis: @retroactive ExpressibleByArgument {
public init?(argument: String) {
if let preset = GenesisPreset(rawValue: argument) {
self = .preset(preset)
} else {
self = .file(path: argument)
}
}
}

struct Boka: AsyncCommand {
struct Signature: CommandSignature {
@Option(name: "base-path", short: "d", help: "Base path to database files.")
var basePath: String?
extension NetAddr: @retroactive ExpressibleByArgument {
public init?(argument: String) {
self.init(address: argument)
}
}

@Option(name: "chain", short: "c", help: "Path to chain spec file.")
var chain: String?
enum MaybeEnabled<T: ExpressibleByArgument>: ExpressibleByArgument {
case enabled(T)
case disabled

@Option(name: "config-file", short: "f", help: "Path to config file.")
var configFile: String?
init?(argument: String) {
if argument.lowercased() == "false" {
self = .disabled
} else {
guard let argument = T(argument: argument) else {
return nil
}
self = .enabled(argument)
}
}

@Option(
name: "rpc",
help:
"Listen address for RPC server. Pass 'false' to disable RPC server. Default to 127.0.0.1:9955."
)
var rpc: String?
var asOptional: T? {
switch self {
case let .enabled(value):
value
case .disabled:
nil
}
}
}

@Option(name: "p2p", help: "Listen address for P2P protocol.")
var p2p: String?
@main
struct Boka: AsyncParsableCommand {
static let configuration = CommandConfiguration(
abstract: "JAM built with Swift",
version: "0.0.1"
)

@Option(name: "peers", help: "Specify peer P2P addresses separated by commas.")
var peers: String?
@Option(name: .shortAndLong, help: "Base path to database files.")
var basePath: String?

@Flag(name: "validator", help: "Run as a validator.")
var validator: Bool
@Option(name: .long, help: "A preset config or path to chain config file.")
var chain: Genesis = .preset(.dev)

@Option(
name: "operator-rpc",
help:
"Listen address for operator RPC server. Pass 'false' to disable operator RPC server. Default to false."
)
var operatorRpc: String?
@Option(name: .long, help: "Listen address for RPC server. Pass 'false' to disable RPC server. Default to 127.0.0.1:9955.")
var rpc: MaybeEnabled<NetAddr> = .enabled(NetAddr(address: "127.0.0.1:9955")!)

@Option(name: "dev-seed", help: "For development only. Seed for validator keys.")
var devSeed: String?
@Option(name: .long, help: "Listen address for P2P protocol.")
var p2p: NetAddr = .init(address: "127.0.0.1:19955")!

@Flag(name: "version", help: "Show the version.")
var version: Bool
@Option(name: .long, help: "Specify peer P2P addresses.")
var peers: [NetAddr] = []

@Flag(name: "help", short: "h", help: "Show help information.")
var help: Bool
}
@Flag(name: .long, help: "Run as a validator.")
var validator = false

var help: String {
"A command-line tool for Boka."
}
@Option(name: .long, help: "Listen address for operator RPC server. Pass 'false' to disable operator RPC server. Default to false.")
var operatorRpc: NetAddr?

func run(using context: CommandContext, signature: Signature) async throws {
if signature.help {
context.console.info(help)
return
}
// TODO: fix version number issue #168
if signature.version {
context.console.info("Boka version 1.0.0")
return
}
@Option(name: .long, help: "For development only. Seed for validator keys.")
var devSeed: UInt32?

@Option(name: .long, help: "Node name. For telemetry only.")
var name: String?

mutating func run() async throws {
let services = try await Tracing.bootstrap("Boka", loggerOnly: true)
for service in services {
Task {
try await service.run()
}
}

// Handle other options and flags
if let basePath = signature.basePath {
context.console.info("Base path: \(basePath)")
}
let logger = Logger(label: "cli")

if let peers = signature.peers {
let peerList = peers.split(separator: ",").map {
$0.trimmingCharacters(in: .whitespaces)
}
context.console.info("Peers: \(peerList.joined(separator: ", "))")
}
logger.info("Starting Boka. Chain: \(chain)")

if signature.validator {
context.console.info("Running as validator")
if let name {
logger.info("Node name: \(name)")
}

if let operatorRpc = signature.operatorRpc {
context.console.info("Operator RPC listen address: \(operatorRpc)")
if let basePath {
logger.info("Base path: \(basePath)")
}

var rpcListenAddress = NetAddr(ipAddress: "127.0.0.1", port: 9955)
if let rpc = signature.rpc {
if rpc.lowercased() == "false" {
context.console.info("RPC server is disabled")
rpcListenAddress = nil
} else {
if let addr = NetAddr(address: rpc) {
rpcListenAddress = addr
} else {
throw InvalidArgumentError.invalidArgument("Invalid RPC address")
}
}
logger.info("Peers: \(peers)")

if validator {
logger.info("Running as validator")
} else {
logger.info("Running as fullnode")
}

let rpcConfig = rpcListenAddress.map { rpcListenAddress in
let (rpcAddress, rpcPort) = rpcListenAddress.getAddressAndPort()
return RPCConfig(listenAddress: rpcAddress, port: Int(rpcPort))
if let operatorRpc {
logger.info("Operator RPC listen address: \(operatorRpc)")
}

var p2pListenAddress = NetAddr(ipAddress: "127.0.0.1", port: 19955)!
if let p2p = signature.p2p {
if let addr = NetAddr(address: p2p) {
p2pListenAddress = addr
} else {
throw InvalidArgumentError.invalidArgument("Invalid P2P address")
}
let rpcConfig = rpc.asOptional.map { addr -> RPCConfig in
logger.info("RPC listen address: \(addr)")
let (address, port) = addr.getAddressAndPort()
return RPCConfig(listenAddress: address, port: Int(port))
}

let keystore = try await DevKeyStore()

var devKey: KeySet?
let networkKey: Ed25519.SecretKey
if let devSeed = signature.devSeed {
guard let val = UInt32(devSeed) else {
throw InvalidArgumentError.invalidArgument("devSeed is not a valid hex string")
let networkKey: Ed25519.SecretKey = try await {
if let devSeed {
let key = try await keystore.addDevKeys(seed: devSeed)
return await keystore.get(Ed25519.self, publicKey: key.ed25519)!
} else {
return try await keystore.generate(Ed25519.self)
}
devKey = try await keystore.addDevKeys(seed: val)
networkKey = await keystore.get(Ed25519.self, publicKey: devKey!.ed25519)!
} else {
// TODO: only generate network key if keystore is empty
networkKey = try await keystore.generate(Ed25519.self)
}
}()

let networkConfig = NetworkConfig(
mode: signature.validator ? .validator : .builder,
listenAddress: p2pListenAddress,
mode: validator ? .validator : .builder,
listenAddress: p2p,
key: networkKey
)

Expand All @@ -153,26 +144,20 @@ struct Boka: AsyncCommand {
handlerMiddleware: .tracing(prefix: "Handler")
)

var genesis: Genesis = .dev
if let configFile = signature.configFile {
context.console.info("Config file: \(configFile)")
genesis = .file(path: configFile)
}

let config = Node.Config(rpc: rpcConfig, network: networkConfig)

let node: Node = if signature.validator {
let node: Node = if validator {
try await ValidatorNode(
config: config, genesis: genesis, eventBus: eventBus, keystore: keystore
config: config, genesis: chain, eventBus: eventBus, keystore: keystore
)
} else {
try await Node(
config: config, genesis: genesis, eventBus: eventBus, keystore: keystore
config: config, genesis: chain, eventBus: eventBus, keystore: keystore
)
}

try await node.wait()

console.info("Shutting down...")
logger.notice("Shutting down...")
}
}
7 changes: 0 additions & 7 deletions Boka/Sources/main.swift

This file was deleted.

Loading

0 comments on commit 6b754db

Please sign in to comment.