-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* codec tests passing * pass safrole * fix * assurances tests
- Loading branch information
Showing
16 changed files
with
289 additions
and
82 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
87 changes: 87 additions & 0 deletions
87
Blockchain/Sources/Blockchain/RuntimeProtocols/Assurances.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import Utils | ||
|
||
public enum AssurancesError: Error { | ||
case invalidAssuranceSignature | ||
case assuranceForEmptyCore | ||
case invalidAssuranceParentHash | ||
} | ||
|
||
public protocol Assurances { | ||
var reports: | ||
ConfigFixedSizeArray< | ||
ReportItem?, | ||
ProtocolConfig.TotalNumberOfCores | ||
> | ||
{ get } | ||
|
||
var currentValidators: | ||
ConfigFixedSizeArray< | ||
ValidatorKey, | ||
ProtocolConfig.TotalNumberOfValidators | ||
> | ||
{ get } | ||
} | ||
|
||
extension Assurances { | ||
public func update( | ||
config: ProtocolConfigRef, | ||
timeslot: TimeslotIndex, | ||
extrinsic: ExtrinsicAvailability, | ||
parentHash: Data32 | ||
) throws -> ( | ||
newReports: ConfigFixedSizeArray< | ||
ReportItem?, | ||
ProtocolConfig.TotalNumberOfCores | ||
>, | ||
availableReports: [WorkReport] | ||
) { | ||
var newReports = reports | ||
|
||
for i in 0 ..< newReports.count { | ||
if let report = newReports[i] { | ||
if (report.timeslot + UInt32(config.value.preimageReplacementPeriod)) <= timeslot { | ||
newReports[i] = nil | ||
} | ||
} | ||
} | ||
|
||
for assurance in extrinsic.assurances { | ||
guard assurance.parentHash == parentHash else { | ||
throw AssurancesError.invalidAssuranceParentHash | ||
} | ||
|
||
let hash = Blake2b256.hash(assurance.parentHash, assurance.assurance) | ||
let payload = SigningContext.available + hash.data | ||
let validatorKey = try currentValidators.at(Int(assurance.validatorIndex)) | ||
let pubkey = try Ed25519.PublicKey(from: validatorKey.ed25519) | ||
guard pubkey.verify(signature: assurance.signature, message: payload) else { | ||
throw AssurancesError.invalidAssuranceSignature | ||
} | ||
} | ||
|
||
var availabilityCount = Array(repeating: 0, count: config.value.totalNumberOfCores) | ||
for assurance in extrinsic.assurances { | ||
for (coreIdx, bit) in assurance.assurance.enumerated() where bit { | ||
// ExtrinsicAvailability.validate() ensures that validatorIndex is in range | ||
availabilityCount[coreIdx] += 1 | ||
} | ||
} | ||
|
||
var availableReports = [WorkReport]() | ||
|
||
for (idx, count) in availabilityCount.enumerated() where count > 0 { | ||
guard let report = reports[idx] else { | ||
throw AssurancesError.assuranceForEmptyCore | ||
} | ||
if count >= ProtocolConfig.TwoThirdValidatorsPlusOne.read(config: config) { | ||
availableReports.append(report.workReport) | ||
newReports[idx] = nil // remove available report from pending reports | ||
} | ||
} | ||
|
||
return ( | ||
newReports: newReports, | ||
availableReports: availableReports | ||
) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
import Blockchain | ||
import Codec | ||
import Foundation | ||
import Testing | ||
import Utils | ||
|
||
@testable import JAMTests | ||
|
||
struct AssurancesInput: Codable { | ||
var assurances: ExtrinsicAvailability | ||
var timeslot: TimeslotIndex | ||
var parentHash: Data32 | ||
} | ||
|
||
struct AssuranceState: Equatable, Codable, Assurances { | ||
var reports: ConfigFixedSizeArray<ReportItem?, ProtocolConfig.TotalNumberOfCores> | ||
var currentValidators: | ||
ConfigFixedSizeArray<ValidatorKey, ProtocolConfig.TotalNumberOfValidators> | ||
} | ||
|
||
struct AssurancesTestcase: Codable { | ||
var input: AssurancesInput | ||
var preState: AssuranceState | ||
var output: Either<[WorkReport], UInt8> | ||
var postState: AssuranceState | ||
} | ||
|
||
enum AssurancesTestVariants: String, CaseIterable { | ||
case tiny | ||
case full | ||
|
||
var config: ProtocolConfigRef { | ||
switch self { | ||
case .tiny: | ||
ProtocolConfigRef.tiny | ||
case .full: | ||
ProtocolConfigRef.mainnet | ||
} | ||
} | ||
} | ||
|
||
struct AssurancesTests { | ||
static func loadTests(variant: AssurancesTestVariants) throws -> [Testcase] { | ||
try TestLoader.getTestcases(path: "assurances/\(variant)", extension: "bin") | ||
} | ||
|
||
func assurancesTests(_ testcase: Testcase, variant: AssurancesTestVariants) throws { | ||
let config = variant.config | ||
let decoder = JamDecoder(data: testcase.data, config: config) | ||
let testcase = try decoder.decode(AssurancesTestcase.self) | ||
|
||
var state = testcase.preState | ||
let result = Result { | ||
try testcase.input.assurances.validate(config: config) | ||
return try state.update( | ||
config: config, timeslot: testcase.input.timeslot, | ||
extrinsic: testcase.input.assurances, | ||
parentHash: testcase.input.parentHash | ||
) | ||
} | ||
switch result { | ||
case let .success((newReports, availableReports)): | ||
switch testcase.output { | ||
case let .left(reports): | ||
state.reports = newReports | ||
#expect(state == testcase.postState) | ||
#expect(availableReports == reports) | ||
case .right: | ||
Issue.record("Expected error, got \(result)") | ||
} | ||
case .failure: | ||
switch testcase.output { | ||
case .left: | ||
Issue.record("Expected success, got \(result)") | ||
case .right: | ||
// ignore error code because it is unspecified | ||
break | ||
} | ||
} | ||
} | ||
|
||
@Test(arguments: try AssurancesTests.loadTests(variant: .tiny)) | ||
func tinyTests(_ testcase: Testcase) throws { | ||
try assurancesTests(testcase, variant: .tiny) | ||
} | ||
|
||
@Test(arguments: try AssurancesTests.loadTests(variant: .full)) | ||
func fullTests(_ testcase: Testcase) throws { | ||
try assurancesTests(testcase, variant: .full) | ||
} | ||
} |
Oops, something went wrong.