Skip to content

Commit

Permalink
Merge branch 'master' of github.com:AcalaNetwork/boka
Browse files Browse the repository at this point in the history
* 'master' of github.com:AcalaNetwork/boka:
  fix warning (#117)
  keystore (#115)
  setup host calls and is-authorized invocation (#114)
  • Loading branch information
MacOMNI committed Sep 17, 2024
2 parents a59766e + f25bec7 commit 5b66d0d
Show file tree
Hide file tree
Showing 26 changed files with 506 additions and 157 deletions.
38 changes: 38 additions & 0 deletions Blockchain/Sources/Blockchain/IsAuthorizedFunction.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import Codec
import Foundation
import PolkaVM

public protocol IsAuthorizedFunction {
func invoke(
config: ProtocolConfigRef,
package: WorkPackage,
coreIndex: CoreIndex
) throws -> Result<Data, WorkResultError>
}

extension IsAuthorizedFunction {
public func invoke(config: ProtocolConfigRef, package: WorkPackage, coreIndex: CoreIndex) throws -> Result<Data, WorkResultError> {
var ctx = IsAuthorizedContext()
let args = try JamEncoder.encode(package) + JamEncoder.encode(coreIndex)
let (exitReason, _, _, output) = invokePVM(
config: config,
blob: package.authorizationCodeHash.data,
pc: 0,
gas: config.value.workPackageAuthorizerGas,
argumentData: args,
ctx: &ctx
)
switch exitReason {
case .outOfGas:
return .failure(.outOfGas)
case .panic(.trap):
return .failure(.panic)
default:
if let output {
return .success(output)
} else {
return .failure(.panic)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public enum DisputeError: Error {
case invalidJudgementsCount
case expectInFaults
case expectInCulprits
case invalidPublicKey
}

public struct ReportItem: Sendable, Equatable, Codable {
Expand Down Expand Up @@ -99,7 +100,10 @@ extension Disputes {

let prefix = judgement.isValid ? SigningContext.valid : SigningContext.invalid
let payload = prefix + verdict.reportHash.data
guard Ed25519.verify(signature: judgement.signature, message: payload, publicKey: signer) else {
let pubkey = try Result { try Ed25519.PublicKey(from: signer) }
.mapError { _ in DisputeError.invalidPublicKey }
.get()
guard pubkey.verify(signature: judgement.signature, message: payload) else {
throw .invalidJudgementSignature
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public enum GuaranteeingError: Error {
case duplicatedWorkPackage
case prerequistieNotFound
case invalidResultCodeHash
case invalidPublicKey
}

public protocol Guaranteeing {
Expand Down Expand Up @@ -104,7 +105,10 @@ extension Guaranteeing {
let reportHash = report.hash()
workReportHashes.insert(reportHash)
let payload = SigningContext.guarantee + reportHash.data
guard Ed25519.verify(signature: credential.signature, message: payload, publicKey: key) else {
let pubkey = try Result { try Ed25519.PublicKey(from: key) }
.mapError { _ in GuaranteeingError.invalidPublicKey }
.get()
guard pubkey.verify(signature: credential.signature, message: payload) else {
throw .invalidGuaranteeSignature
}

Expand Down
3 changes: 2 additions & 1 deletion Blockchain/Sources/Blockchain/RuntimeProtocols/Runtime.swift
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,8 @@ public final class Runtime {
let hash = Blake2b256.hash(assurance.parentHash, assurance.assurance)
let payload = SigningContext.available + hash.data
let validatorKey = try newState.currentValidators.at(Int(assurance.validatorIndex))
guard Ed25519.verify(signature: assurance.signature, message: payload, publicKey: validatorKey.ed25519) else {
let pubkey = try Ed25519.PublicKey(from: validatorKey.ed25519)
guard pubkey.verify(signature: assurance.signature, message: payload) else {
throw Error.invalidAssuranceSignature
}
}
Expand Down
11 changes: 9 additions & 2 deletions Blockchain/Sources/Blockchain/Types/ExtrinsicDisputes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ extension ExtrinsicDisputes: Validate {
case judgementsNotSorted
case invalidCulpritSignature
case invalidFaultSignature
case invalidPublicKey
}

public func validate(config _: Config) throws(Error) {
Expand All @@ -130,15 +131,21 @@ extension ExtrinsicDisputes: Validate {

for culprit in culprits {
let payload = SigningContext.guarantee + culprit.reportHash.data
guard Ed25519.verify(signature: culprit.signature, message: payload, publicKey: culprit.validatorKey) else {
let pubkey = try Result { try Ed25519.PublicKey(from: culprit.validatorKey) }
.mapError { _ in Error.invalidPublicKey }
.get()
guard pubkey.verify(signature: culprit.signature, message: payload) else {
throw .invalidCulpritSignature
}
}

for fault in faults {
let prefix = fault.vote ? SigningContext.valid : SigningContext.invalid
let payload = prefix + fault.reportHash.data
guard Ed25519.verify(signature: fault.signature, message: payload, publicKey: fault.validatorKey) else {
let pubkey = try Result { try Ed25519.PublicKey(from: fault.validatorKey) }
.mapError { _ in Error.invalidPublicKey }
.get()
guard pubkey.verify(signature: fault.signature, message: payload) else {
throw .invalidFaultSignature
}
}
Expand Down
1 change: 0 additions & 1 deletion Blockchain/Sources/Blockchain/Validator/KeyStore.swift

This file was deleted.

2 changes: 2 additions & 0 deletions Blockchain/Sources/Blockchain/Validator/Validator.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import Utils

public class Validator {
private let blockchain: Blockchain
private var keystore: KeyStore
Expand Down
2 changes: 1 addition & 1 deletion Blockchain/Tests/BlockchainTests/ValidateTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import Testing

// @retroactive to slient Equtable warning
extension ValidateError: @retroactive Equatable {
extension ValidateError: Swift.Equatable {
public static func == (lhs: ValidateError, rhs: ValidateError) -> Bool {
String(describing: lhs) == String(describing: rhs)
}
Expand Down
39 changes: 36 additions & 3 deletions PolkaVM/Sources/PolkaVM/Engine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ private let logger = Logger(label: "Engine")

public class Engine {
let config: PvmConfig
let hostCallContext: (any HostCallContext)?

public init(config: PvmConfig) {
public init(config: PvmConfig, hostCallContext: (any HostCallContext)? = nil) {
self.config = config
self.hostCallContext = hostCallContext
}

public func execute(program: ProgramCode, state: VMState) -> ExitReason {
Expand All @@ -17,12 +19,43 @@ public class Engine {
return .outOfGas
}
if case let .exit(reason) = step(program: program, context: context) {
return reason
switch reason {
case let .hostCall(callIndex):
if case let .exit(hostExitReason) = hostCall(state: state, callIndex: callIndex) {
return hostExitReason
}
default:
return reason
}
}
}
}

public func step(program: ProgramCode, context: ExecutionContext) -> ExecOutcome {
func hostCall(state: VMState, callIndex: UInt32) -> ExecOutcome {
guard let hostCallContext else {
return .exit(.panic(.trap))
}

let result = hostCallContext.dispatch(index: callIndex, state: state)
switch result {
case let .exit(reason):
switch reason {
case let .pageFault(address):
return .exit(.pageFault(address))
case let .hostCall(callIndexInner):
let pc = state.pc
let skip = state.program.skip(pc)
state.increasePC(skip + 1)
return hostCall(state: state, callIndex: callIndexInner)
default:
return .exit(reason)
}
case .continued:
return .continued
}
}

func step(program: ProgramCode, context: ExecutionContext) -> ExecOutcome {
let pc = context.state.pc
let skip = program.skip(pc)
let startIndex = program.code.startIndex + Int(pc)
Expand Down
8 changes: 8 additions & 0 deletions PolkaVM/Sources/PolkaVM/HostCall/Context.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
public protocol HostCallContext<ContextType> {
associatedtype ContextType

var context: ContextType { get set }

/// host-call dispatch function
func dispatch(index: UInt32, state: VMState) -> ExecOutcome
}
9 changes: 9 additions & 0 deletions PolkaVM/Sources/PolkaVM/HostCall/Function.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
public protocol HostCallFunction {
static var identifier: UInt8 { get }
static var gasCost: UInt8 { get }

associatedtype Input
associatedtype Output

static func call(state: VMState, input: Input) throws -> Output
}
13 changes: 13 additions & 0 deletions PolkaVM/Sources/PolkaVM/HostCall/Functions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
public class Gas: HostCallFunction {
public static var identifier: UInt8 { 0 }
public static var gasCost: UInt8 { 10 }

public typealias Input = Void

public typealias Output = Void

public static func call(state: VMState, input _: Input) -> Output {
state.writeRegister(Registers.Index(raw: 0), UInt32(bitPattern: Int32(state.getGas() & 0xFFFF_FFFF)))
state.writeRegister(Registers.Index(raw: 1), UInt32(bitPattern: Int32(state.getGas() >> 32)))
}
}
36 changes: 36 additions & 0 deletions PolkaVM/Sources/PolkaVM/HostCall/ResultConstants.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
public enum HostCallResultCode: UInt32 {
/// NONE = 2^32 − 1: The return value indicating an item does not exist.
case NONE = 0xFFFF_FFFF
/// WHAT = 2^32 − 2: Name unknown.
case WHAT = 0xFFFF_FFFE
/// OOB = 2^32 − 3: The return value for when a memory index is provided for reading/writing which is not accessible.
case OOB = 0xFFFF_FFFD
/// WHO = 2^32 − 4: Index unknown.
case WHO = 0xFFFF_FFFC
/// FULL = 2^32 − 5: Storage full.
case FULL = 0xFFFF_FFFB
/// CORE = 2^32 − 6: Core index unknown.
case CORE = 0xFFFF_FFFA
/// CASH = 2^32 − 7: Insufficient funds.
case CASH = 0xFFFF_FFF9
/// LOW = 2^32 − 8: Gas limit too low.
case LOW = 0xFFFF_FFF8
/// HIGH = 2^32 − 9: Gas limit too high.
case HIGH = 0xFFFF_FFF7
/// HUH = 2^32 − 10: The item is already solicited or cannot be forgotten.
case HUH = 0xFFFF_FFF6
/// OK = 0: The return value indicating general success.
case OK = 0
}

// Inner pvm invocations have their own set of result codes👇
public enum HostCallResultCodeInner: UInt32 {
/// HALT = 0: The invocation completed and halted normally.
case HALT = 0
/// PANIC = 1: The invocation completed with a panic.
case PANIC = 1
/// FAULT = 2: The invocation completed with a page fault.
case FAULT = 2
/// HOST = 3: The invocation completed with a host-call fault.
case HOST = 3
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import Foundation

public class IsAuthorizedContext: HostCallContext {
public typealias ContextType = Void

public var context: ContextType = ()

public init() {}

public func dispatch(index: UInt32, state: VMState) -> ExecOutcome {
if index == Gas.identifier {
Gas.call(state: state, input: ())
} else {
state.consumeGas(10)
state.writeRegister(Registers.Index(raw: 0), HostCallResultCode.WHAT.rawValue)
}
return .continued
}
}
2 changes: 1 addition & 1 deletion PolkaVM/Sources/PolkaVM/Memory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ public class Memory {
try getSection(forAddress: address).read(address: address, length: 1).first ?? 0
}

public func read(address: UInt32, length: Int) throws -> Data {
public func read(address: UInt32, length: Int) throws(Error) -> Data {
try getSection(forAddress: address).read(address: address, length: length)
}

Expand Down
2 changes: 1 addition & 1 deletion PolkaVM/Sources/PolkaVM/ProgramCode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ public class ProgramCode {

/// traverse the program code and collect basic block indices
private static func getBasicBlockIndices(code: Data, bitmask: Data) -> Set<UInt32> {
// TODO: parse the instructions here and so we don't need to do skip calculation twice
// TODO: parse the instructions here and so we don't need to do skip calculation multiple times
var res: Set<UInt32> = [0]
var i = UInt32(0)
while i < code.count {
Expand Down
4 changes: 4 additions & 0 deletions PolkaVM/Sources/PolkaVM/Registers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ public struct Registers: Equatable {
public init(rd: UInt8) {
value = min(rd, 12)
}

public init(raw: UInt8) {
value = raw
}
}

public var reg1: UInt32 = 0
Expand Down
33 changes: 33 additions & 0 deletions PolkaVM/Sources/PolkaVM/invokePVM.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import Foundation
import TracingUtils

private let logger = Logger(label: "invokePVM")

/// common PVM program-argument invocation function
public func invokePVM(config: PvmConfig, blob: Data, pc: UInt32, gas: UInt64, argumentData: Data?,
ctx: inout some HostCallContext) -> (ExitReason, VMState?, UInt64?, Data?)
{
do {
let state = try VMState(standardProgramBlob: blob, pc: pc, gas: gas, argumentData: argumentData)
let engine = Engine(config: config, hostCallContext: ctx)
let exitReason = engine.execute(program: state.program, state: state)

switch exitReason {
case .outOfGas:
return (.outOfGas, state, nil, nil)
case .halt:
let (reg10, reg11) = state.readRegister(Registers.Index(raw: 10), Registers.Index(raw: 11))
// TODO: check if this is correct
let output = try? state.readMemory(address: reg10, length: Int(reg11 - reg10))
return (.halt, state, UInt64(state.getGas()), output ?? Data())
default:
return (.panic(.trap), state, nil, nil)
}
} catch let e as StandardProgram.Error {
logger.error("standard program initialization failed: \(e)")
return (.panic(.trap), nil, nil, nil)
} catch let e {
logger.error("unknown error: \(e)")
return (.panic(.trap), nil, nil, nil)
}
}
Loading

0 comments on commit 5b66d0d

Please sign in to comment.