-
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.
Merge branch 'master' of github.com:AcalaNetwork/boka
* 'master' of github.com:AcalaNetwork/boka: implements accumulation (#111) pvm memory optimization (#109)
- Loading branch information
Showing
17 changed files
with
581 additions
and
202 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
import Foundation | ||
import Utils | ||
|
||
public struct AccumulateArguments { | ||
public var result: WorkResult | ||
public var paylaodHash: Data32 | ||
public var packageHash: Data32 | ||
public var authorizationOutput: Data | ||
|
||
public init(result: WorkResult, paylaodHash: Data32, packageHash: Data32, authorizationOutput: Data) { | ||
self.result = result | ||
self.paylaodHash = paylaodHash | ||
self.packageHash = packageHash | ||
self.authorizationOutput = authorizationOutput | ||
} | ||
} | ||
|
||
public struct DeferredTransfers { | ||
// s | ||
public var sender: ServiceIndex | ||
// d | ||
public var destination: ServiceIndex | ||
// a | ||
public var amount: Balance | ||
// m | ||
public var memo: Data64 | ||
// g | ||
public var gasLimit: Gas | ||
|
||
public init(sender: ServiceIndex, destination: ServiceIndex, amount: Balance, memo: Data64, gasLimit: Gas) { | ||
self.sender = sender | ||
self.destination = destination | ||
self.amount = amount | ||
self.memo = memo | ||
self.gasLimit = gasLimit | ||
} | ||
} | ||
|
||
public struct AccumlateResultContext { | ||
// s: updated current account | ||
public var account: ServiceAccount? | ||
// c | ||
public var authorizationQueue: ConfigFixedSizeArray< | ||
ConfigFixedSizeArray< | ||
Data32, | ||
ProtocolConfig.MaxAuthorizationsQueueItems | ||
>, | ||
ProtocolConfig.TotalNumberOfCores | ||
> | ||
// v | ||
public var validatorQueue: ConfigFixedSizeArray< | ||
ValidatorKey, ProtocolConfig.TotalNumberOfValidators | ||
> | ||
// i | ||
public var serviceIndex: ServiceIndex | ||
// t | ||
public var transfers: [DeferredTransfers] | ||
// n | ||
public var newAccounts: [ServiceIndex: ServiceAccount] | ||
// p | ||
public var privilegedServices: PrivilegedServices | ||
} | ||
|
||
public protocol AccumulateFunction { | ||
func invoke( | ||
config: ProtocolConfigRef, | ||
service: ServiceIndex, | ||
code: Data, | ||
serviceAccounts: [ServiceIndex: ServiceAccount], | ||
gas: Gas, | ||
arguments: [AccumulateArguments] | ||
) throws -> (ctx: AccumlateResultContext, result: Data32?) | ||
} |
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,161 @@ | ||
import Utils | ||
|
||
public enum AccumulationError: Error { | ||
case invalidServiceIndex | ||
case duplicatedServiceIndex | ||
} | ||
|
||
public struct AccumulationOutput { | ||
public var commitments: [(ServiceIndex, Data32)] | ||
public var privilegedServices: PrivilegedServices | ||
public var validatorQueue: ConfigFixedSizeArray< | ||
ValidatorKey, ProtocolConfig.TotalNumberOfValidators | ||
> | ||
public var authorizationQueue: ConfigFixedSizeArray< | ||
ConfigFixedSizeArray< | ||
Data32, | ||
ProtocolConfig.MaxAuthorizationsQueueItems | ||
>, | ||
ProtocolConfig.TotalNumberOfCores | ||
> | ||
public var serviceAccounts: [ServiceIndex: ServiceAccount] | ||
} | ||
|
||
public protocol Accumulation { | ||
var privilegedServices: PrivilegedServices { get } | ||
var serviceAccounts: [ServiceIndex: ServiceAccount] { get } | ||
var accumlateFunction: AccumulateFunction { get } | ||
var onTransferFunction: OnTransferFunction { get } | ||
} | ||
|
||
extension Accumulation { | ||
public func update(config: ProtocolConfigRef, workReports: [WorkReport]) throws -> AccumulationOutput { | ||
var servicesGasRatio: [ServiceIndex: Gas] = [:] | ||
var servicesGas: [ServiceIndex: Gas] = [:] | ||
|
||
// privileged gas | ||
for (service, gas) in privilegedServices.basicGas { | ||
servicesGas[service] = gas | ||
} | ||
|
||
let totalGasRatio = workReports.flatMap(\.results).reduce(0) { $0 + $1.gasRatio } | ||
let totalMinimalGas = try workReports.flatMap(\.results) | ||
.reduce(0) { try $0 + serviceAccounts[$1.serviceIndex].unwrap(orError: AccumulationError.invalidServiceIndex).minAccumlateGas } | ||
for report in workReports { | ||
for result in report.results { | ||
servicesGasRatio[result.serviceIndex, default: 0] += result.gasRatio | ||
servicesGas[result.serviceIndex, default: 0] += try serviceAccounts[result.serviceIndex] | ||
.unwrap(orError: AccumulationError.invalidServiceIndex).minAccumlateGas | ||
} | ||
} | ||
let remainingGas = config.value.coreAccumulationGas - totalMinimalGas | ||
|
||
for (service, gas) in servicesGas { | ||
servicesGas[service] = gas + servicesGasRatio[service, default: 0] * remainingGas / totalGasRatio | ||
} | ||
|
||
var serviceArguments: [ServiceIndex: [AccumulateArguments]] = [:] | ||
|
||
// ensure privileged services will be called | ||
for service in privilegedServices.basicGas.keys { | ||
serviceArguments[service] = [] | ||
} | ||
|
||
for report in workReports { | ||
for result in report.results { | ||
serviceArguments[result.serviceIndex, default: []].append(AccumulateArguments( | ||
result: result, | ||
paylaodHash: result.payloadHash, | ||
packageHash: report.packageSpecification.workPackageHash, | ||
authorizationOutput: report.authorizationOutput | ||
)) | ||
} | ||
} | ||
|
||
var commitments = [(ServiceIndex, Data32)]() | ||
var newPrivilegedServices: PrivilegedServices? | ||
var newValidatorQueue: ConfigFixedSizeArray< | ||
ValidatorKey, ProtocolConfig.TotalNumberOfValidators | ||
>? | ||
var newAuthorizationQueue: ConfigFixedSizeArray< | ||
ConfigFixedSizeArray< | ||
Data32, | ||
ProtocolConfig.MaxAuthorizationsQueueItems | ||
>, | ||
ProtocolConfig.TotalNumberOfCores | ||
>? | ||
|
||
var newServiceAccounts = serviceAccounts | ||
|
||
var transferReceivers = [ServiceIndex: [DeferredTransfers]]() | ||
|
||
for (service, arguments) in serviceArguments { | ||
guard let gas = servicesGas[service] else { | ||
assertionFailure("unreachable: service not found") | ||
throw AccumulationError.invalidServiceIndex | ||
} | ||
let acc = try serviceAccounts[service].unwrap(orError: AccumulationError.invalidServiceIndex) | ||
guard let code = acc.preimages[acc.codeHash] else { | ||
continue | ||
} | ||
let (ctx, commitment) = try accumlateFunction.invoke( | ||
config: config, | ||
service: service, | ||
code: code, | ||
serviceAccounts: serviceAccounts, | ||
gas: gas, | ||
arguments: arguments | ||
) | ||
if let commitment { | ||
commitments.append((service, commitment)) | ||
} | ||
|
||
for (service, account) in ctx.newAccounts { | ||
guard newServiceAccounts[service] == nil else { | ||
throw AccumulationError.duplicatedServiceIndex | ||
} | ||
newServiceAccounts[service] = account | ||
} | ||
|
||
newServiceAccounts[service] = ctx.account | ||
|
||
switch service { | ||
case privilegedServices.empower: | ||
newPrivilegedServices = ctx.privilegedServices | ||
case privilegedServices.assign: | ||
newAuthorizationQueue = ctx.authorizationQueue | ||
case privilegedServices.designate: | ||
newValidatorQueue = ctx.validatorQueue | ||
default: | ||
break | ||
} | ||
|
||
for transfer in ctx.transfers { | ||
transferReceivers[transfer.sender, default: []].append(transfer) | ||
} | ||
} | ||
|
||
for (service, transfers) in transferReceivers { | ||
let acc = try serviceAccounts[service].unwrap(orError: AccumulationError.invalidServiceIndex) | ||
guard let code = acc.preimages[acc.codeHash] else { | ||
continue | ||
} | ||
newServiceAccounts[service] = try onTransferFunction.invoke( | ||
config: config, | ||
service: service, | ||
code: code, | ||
serviceAccounts: newServiceAccounts, | ||
transfers: transfers | ||
) | ||
} | ||
|
||
return .init( | ||
commitments: commitments, | ||
// those cannot be nil because priviledge services are always called | ||
privilegedServices: newPrivilegedServices!, | ||
validatorQueue: newValidatorQueue!, | ||
authorizationQueue: newAuthorizationQueue!, | ||
serviceAccounts: newServiceAccounts | ||
) | ||
} | ||
} |
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,11 @@ | ||
import Foundation | ||
|
||
public protocol OnTransferFunction { | ||
func invoke( | ||
config: ProtocolConfigRef, | ||
service: ServiceIndex, | ||
code: Data, | ||
serviceAccounts: [ServiceIndex: ServiceAccount], | ||
transfers: [DeferredTransfers] | ||
) throws -> ServiceAccount | ||
} |
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
Oops, something went wrong.