Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 45 additions & 57 deletions Blockchain/Sources/Blockchain/RuntimeProtocols/Accumulation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,37 +77,53 @@ public struct AccumulationResult: Sendable {
}

public struct AccountChanges: Sendable {
public enum UpdateKind: Sendable {
case newAccount(ServiceIndex, ServiceAccount)
case removeAccount(ServiceIndex)
case updateAccount(ServiceIndex, ServiceAccountDetails)
case updateStorage(ServiceIndex, Data, Data?)
case updatePreimage(ServiceIndex, Data32, Data?)
case updatePreimageInfo(ServiceIndex, Data32, UInt32, StateKeys.ServiceAccountPreimageInfoKey.Value?)
}

// records for checking conflicts
public var newAccounts: [ServiceIndex: ServiceAccount]
public var altered: Set<ServiceIndex>
public var accountUpdates: [ServiceIndex: ServiceAccountDetails]
public var storageUpdates: [ServiceIndex: [Data: Data?]]
public var preimageUpdates: [ServiceIndex: [Data32: Data?]]
public var preimageInfoUpdates: [ServiceIndex: [Data32: (UInt32, StateKeys.ServiceAccountPreimageInfoKey.Value?)]]
public var removed: Set<ServiceIndex>

// array for apply sequential updates
public var updates: [UpdateKind]

public init() {
newAccounts = [:]
altered = []
accountUpdates = [:]
storageUpdates = [:]
preimageUpdates = [:]
preimageInfoUpdates = [:]
removed = []
updates = []
}

public mutating func addNewAccount(index: ServiceIndex, account: ServiceAccount) {
newAccounts[index] = account
updates.append(.newAccount(index, account))
}

public mutating func addRemovedAccount(index: ServiceIndex) {
removed.insert(index)
updates.append(.removeAccount(index))
}

public mutating func addAccountUpdate(index: ServiceIndex, account: ServiceAccountDetails) {
accountUpdates[index] = account
altered.insert(index)
updates.append(.updateAccount(index, account))
}

public mutating func addStorageUpdate(index: ServiceIndex, key: Data, value: Data?) {
storageUpdates[index, default: [:]][key] = value
altered.insert(index)
updates.append(.updateStorage(index, key, value))
}

public mutating func addPreimageUpdate(index: ServiceIndex, hash: Data32, value: Data?) {
preimageUpdates[index, default: [:]][hash] = value
altered.insert(index)
updates.append(.updatePreimage(index, hash, value))
}

public mutating func addPreimageInfoUpdate(
Expand All @@ -116,32 +132,24 @@ public struct AccountChanges: Sendable {
length: UInt32,
value: StateKeys.ServiceAccountPreimageInfoKey.Value?
) {
preimageInfoUpdates[index, default: [:]][hash] = (length, value)
altered.insert(index)
updates.append(.updatePreimageInfo(index, hash, length, value))
}

public func apply(to accounts: ServiceAccountsMutRef) async throws {
for (index, account) in newAccounts {
try await accounts.addNew(serviceAccount: index, account: account)
}
for index in removed {
accounts.remove(serviceAccount: index)
}
for (index, account) in accountUpdates {
accounts.set(serviceAccount: index, account: account)
}
for (index, storage) in storageUpdates {
for (key, value) in storage {
for update in updates {
switch update {
case let .newAccount(index, account):
try await accounts.addNew(serviceAccount: index, account: account)
case let .removeAccount(index):
accounts.remove(serviceAccount: index)
case let .updateAccount(index, account):
accounts.set(serviceAccount: index, account: account)
case let .updateStorage(index, key, value):
try await accounts.set(serviceAccount: index, storageKey: key, value: value)
}
}
for (index, preimages) in preimageUpdates {
for (hash, value) in preimages {
case let .updatePreimage(index, hash, value):
accounts.set(serviceAccount: index, preimageHash: hash, value: value)
}
}
for (index, preimageInfos) in preimageInfoUpdates {
for (hash, (length, value)) in preimageInfos {
case let .updatePreimageInfo(index, hash, length, value):
try await accounts.set(serviceAccount: index, preimageHash: hash, length: length, value: value)
}
}
Expand All @@ -166,25 +174,7 @@ public struct AccountChanges: Sendable {
}
altered.formUnion(other.altered)
removed.formUnion(other.removed)

for (index, account) in other.accountUpdates {
accountUpdates[index] = account
}
for (index, storage) in other.storageUpdates {
for (key, value) in storage {
storageUpdates[index, default: [:]][key] = value
}
}
for (index, preimages) in other.preimageUpdates {
for (hash, value) in preimages {
preimageUpdates[index, default: [:]][hash] = value
}
}
for (index, preimageInfos) in other.preimageInfoUpdates {
for (hash, info) in preimageInfos {
preimageInfoUpdates[index, default: [:]][hash] = info
}
}
updates.append(contentsOf: other.updates)
}
}

Expand Down Expand Up @@ -255,7 +245,7 @@ extension Accumulation {
}

/// parallelized accumulate function ∆*
private mutating func parallelizedAccumulate(
private func parallelizedAccumulate(
config: ProtocolConfigRef,
state: AccumulateState,
workReports: [WorkReport],
Expand Down Expand Up @@ -297,11 +287,9 @@ extension Accumulation {
) { group in
for service in serviceBatch {
group.addTask { [batchState] in
let parallelState = batchState.copy()

return try await Self.singleAccumulate(
config: config,
state: parallelState,
state: batchState.copy(),
workReports: workReports,
service: service,
alwaysAcc: alwaysAcc,
Expand Down Expand Up @@ -377,7 +365,7 @@ extension Accumulation {
return batches
}

private mutating func mergeParallelBatchResults(
private func mergeParallelBatchResults(
batchResults: [(ServiceIndex, AccumulationResult)],
currentState: inout AccumulateState,
overallAccountChanges: inout AccountChanges,
Expand Down Expand Up @@ -437,7 +425,7 @@ extension Accumulation {
}

/// outer accumulate function ∆+
private mutating func outerAccumulate(
private func outerAccumulate(
config: ProtocolConfigRef,
state: AccumulateState,
workReports: [WorkReport],
Expand Down Expand Up @@ -589,7 +577,7 @@ extension Accumulation {
}

// accumulate execution
private mutating func execution(
private func execution(
config: ProtocolConfigRef,
workReports: [WorkReport],
state: AccumulateState,
Expand Down
4 changes: 2 additions & 2 deletions Blockchain/Sources/Blockchain/State/ServiceAccounts.swift
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,12 @@ public class ServiceAccountsMutRef: @unchecked Sendable {
for (key, value) in account.preimageInfos {
try await ref.value.set(serviceAccount: index, preimageHash: key.hash, length: key.length, value: value)
}
changes.newAccounts[index] = account
changes.addNewAccount(index: index, account: account)
}

public func remove(serviceAccount index: ServiceIndex) {
ref.value.set(serviceAccount: index, account: nil)
changes.removed.insert(index)
changes.addRemovedAccount(index: index)
}

public func clearRecordedChanges() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@ public protocol HostCall {
extension HostCall {
public func call(config: ProtocolConfigRef, state: VMState) async -> ExecOutcome {
logger.debug("===== host call: \(Self.self) =====")
state.consumeGas(gasCost(state: state))
logger.debug("consumed \(gasCost(state: state)) gas, \(state.getGas()) left")

guard hasEnoughGas(state: state) else {
logger.debug("not enough gas")
return .exit(.outOfGas)
}
state.consumeGas(gasCost(state: state))
logger.debug("consumed \(gasCost(state: state)) gas, \(state.getGas()) left")

do {
try await _callImpl(config: config, state: state)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -294,15 +294,11 @@ public class Read: HostCall {
nil
}

logger.debug("value: \(value?.toDebugHexString() ?? "nil")")

guard let value else {
state.writeRegister(Registers.Index(raw: 7), HostCallResultCode.NONE.rawValue)
return
}

logger.debug("raw val: \(value.toDebugHexString())")

let reg11: UInt64 = state.readRegister(Registers.Index(raw: 11))
let reg12: UInt64 = state.readRegister(Registers.Index(raw: 12))

Expand Down
7 changes: 6 additions & 1 deletion Codec/Sources/Codec/JamEncoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,12 @@ private class EncodeContext: Encoder {
data.append(UInt8(1)) // Encode presence flag
try encode(value, key: key)
} else {
data.append(UInt8(0)) // Encode absence flag
if key == nil, codingPath.isEmpty {
// top-level nil encoding: do nothing (empty data)
return
} else {
data.append(UInt8(0)) // Encode absence flag
}
}
}

Expand Down
18 changes: 10 additions & 8 deletions Codec/Tests/CodecTests/EncoderTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,7 @@ struct EncoderTests {
let encodedNone = try JamEncoder.encode(Int?.none)

#expect(encodedSome == Data([1, 1, 0, 0, 0, 0, 0, 0, 0])) // Optional with value encoded
#expect(encodedNone == Data([0])) // None encoded as 1 byte (0)
#expect(encodedNone == Data()) // None encoded as empty data
}

@Test func encodeOptionalData() throws {
Expand All @@ -386,7 +386,7 @@ struct EncoderTests {
let encodedNone = try JamEncoder.encode(Data?.none)

#expect(encodedSome == Data([1, 1, 2, 3])) // Optional with value encoded
#expect(encodedNone == Data([0])) // None encoded as 1 byte (0)
#expect(encodedNone == Data()) // None encoded as empty data
}

@Test func encodeInt() throws {
Expand All @@ -413,11 +413,6 @@ struct EncoderTests {
let int64Value: Int64 = 1
let uintValue: UInt = 65535
let uint16Value: UInt16 = 65535
let nilValue: Int64? = nil

let encodedNil = try JamEncoder.encode(nilValue)
#expect(encodedNil == Data([0]))
try #expect(JamDecoder.decode(Int64?.self, from: encodedNil) == nilValue)

let encodedInt16 = try JamEncoder.encode(int16Value)
#expect(encodedInt16 == Data([1, 0]))
Expand All @@ -443,12 +438,19 @@ struct EncoderTests {
@Test func encodeArrayOfNils() throws {
let arrayOfNils: [UInt8?] = [nil, nil, nil]
let encoded = try JamEncoder.encode(arrayOfNils)
#expect(encoded == Data([3, 0, 0, 0]))
#expect(encoded == Data([3]))
}

@Test func encodeEmptyArray() throws {
let emptyArray: [Int] = []
let encoded = try JamEncoder.encode(emptyArray)
#expect(encoded == Data([0]))
}

@Test func encodeSingleNil() throws {
let nilValue: Int64? = nil

let encodedNil = try JamEncoder.encode(nilValue)
#expect(encodedNil == Data([]))
}
}
2 changes: 2 additions & 0 deletions JAMTests/Tests/JAMTests/jamtestnet/FuzzTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ struct FuzzTests {
expectFailure: [
],
ignore: [
("0.7.0/1756548583", "00000009"), // TODO: one storage / preimage mismatch
("0.7.0/1756548706", "00000094"), // TODO: backend
]
))
func v070(_ input: TestInput) async throws {
Expand Down
Binary file added JAMTests/fuzz/0.7.0/1756548459/00000041.bin
Binary file not shown.
660 changes: 660 additions & 0 deletions JAMTests/fuzz/0.7.0/1756548459/00000041.json

Large diffs are not rendered by default.

Binary file added JAMTests/fuzz/0.7.0/1756548459/00000042.bin
Binary file not shown.
672 changes: 672 additions & 0 deletions JAMTests/fuzz/0.7.0/1756548459/00000042.json

Large diffs are not rendered by default.

Binary file added JAMTests/fuzz/0.7.0/1756548583/00000008.bin
Binary file not shown.
469 changes: 469 additions & 0 deletions JAMTests/fuzz/0.7.0/1756548583/00000008.json

Large diffs are not rendered by default.

Binary file added JAMTests/fuzz/0.7.0/1756548583/00000009.bin
Binary file not shown.
442 changes: 442 additions & 0 deletions JAMTests/fuzz/0.7.0/1756548583/00000009.json

Large diffs are not rendered by default.

Binary file not shown.
Loading