diff --git a/appleApp/FTWKit/Sources/FTWKit/Carrier/CloudView.swift b/appleApp/FTWKit/Sources/FTWKit/Carrier/CloudView.swift index 9ec2899..0381317 100644 --- a/appleApp/FTWKit/Sources/FTWKit/Carrier/CloudView.swift +++ b/appleApp/FTWKit/Sources/FTWKit/Carrier/CloudView.swift @@ -80,6 +80,12 @@ public struct CloudView: Sendable { /// The newest of each class this phone shows. public private(set) var live: CloudWire.Live? public private(set) var lp: CloudWire.LP? + /// Each car's level and goal. A charger's `carId` joins it; where the + /// two disagree, this wins. + public private(set) var cars: CloudWire.Cars? + /// When the box sent the newest lp and car, by the phone's clock, when + /// a rule anchors it. + private var sentAt: [String: Int64] = [:] /// Kept for the screens and left out of `grade`, as in Go: a price /// window ages by its own slots, not by the box's sources. public private(set) var prices: CloudWire.Prices? @@ -115,6 +121,45 @@ public struct CloudView: Sendable { m.map { ($0.uptimeMs, $0.ages) } } + /// A charger's level and goal as its session's car has them, when the + /// car class holds that car; the charger as sent otherwise. The box + /// fills a charger's level fields from its car, so the two agree; where + /// they do not, as when one class arrived after the other, the car + /// wins (srcfl/ftw `docs/protocol-cloud.md`, lp). `socAgeS` is then the + /// car's, counted to the car class's send; `socReadAtMs` dates it. + public func joined(_ l: CloudWire.Loadpoint) -> CloudWire.Loadpoint { + guard let car = cars?.car(l.carId) else { return l } + var out = l + out.socPermille = car.socPermille + out.socAgeS = nil + switch true { + case car.socPermille == nil: out.socSource = "unknown" + case car.socSource == "assumed": out.socSource = "assumed" + case car.socEstimate || car.socSource != "vehicle": out.socSource = "inferred" + default: + out.socSource = "vehicle" + out.socAgeS = car.socAgeS + } + out.targetPermille = car.goal.flatMap { $0.targetPermille > 0 ? $0.targetPermille : nil } + out.recurring = (car.goal?.days ?? 0) != 0 + if car.goal == nil { out.readyByMs = nil } + out.surplusOnly = car.surplusOnly + return out + } + + /// When a charger's level was read, by the phone's clock: from its + /// car's class and that class's own send time when the class holds the + /// car, from the charger's message and its send otherwise. Nil without + /// an age, or when no rule anchors that send. + public func socReadAtMs(_ l: CloudWire.Loadpoint) -> Int64? { + if let car = cars?.car(l.carId) { + guard let age = car.socAgeS, let sent = sentAt[CloudWire.msgCar] else { return nil } + return sent &- age &* 1000 + } + guard let age = l.socAgeS, let sent = sentAt[CloudWire.msgLP] else { return nil } + return sent &- age &* 1000 + } + /// The boot the view holds, nil before the first message. public var boot: Bytes? { currentBoot.isEmpty ? nil : currentBoot } @@ -195,6 +240,8 @@ public struct CloudView: Sendable { } case .lp(let p): lp = p + case .cars(let c): + cars = c case .prices(let p): prices = p case .hb(let b): @@ -207,6 +254,7 @@ public struct CloudView: Sendable { break } seq[envelope.t] = h.seq + if envelope.t == CloudWire.msgLP || envelope.t == CloudWire.msgCar { sentAt[envelope.t] = send } if let send, send > newest { newest = send } } @@ -319,6 +367,7 @@ public struct CloudView: Sendable { private enum Message { case live(CloudWire.Live) case lp(CloudWire.LP) + case cars(CloudWire.Cars) case prices(CloudWire.Prices) case hb(CloudWire.HB) /// Classes the view only orders by seq. @@ -328,6 +377,7 @@ public struct CloudView: Sendable { switch t { case CloudWire.msgLive: self = .live(try CloudWire.Live(body: b)) case CloudWire.msgLP: self = .lp(try CloudWire.LP(body: b)) + case CloudWire.msgCar: self = .cars(try CloudWire.Cars(body: b)) case CloudWire.msgPrices: self = .prices(try CloudWire.Prices(body: b)) case CloudWire.msgHB: self = .hb(try CloudWire.HB(body: b)) default: self = .other(try CloudWire.Header(body: b)) @@ -338,6 +388,7 @@ public struct CloudView: Sendable { switch self { case .live(let l): return l.header case .lp(let p): return p.header + case .cars(let c): return c.header case .prices(let p): return p.header case .hb(let b): return b.header case .other(let h): return h @@ -360,6 +411,8 @@ public struct CloudView: Sendable { fieldSources = [:] live = nil lp = nil + cars = nil + sentAt = [:] prices = nil hb = nil m = nil diff --git a/appleApp/FTWKit/Sources/FTWKit/Carrier/CloudWire.swift b/appleApp/FTWKit/Sources/FTWKit/Carrier/CloudWire.swift index 4169334..81dc2ae 100644 --- a/appleApp/FTWKit/Sources/FTWKit/Carrier/CloudWire.swift +++ b/appleApp/FTWKit/Sources/FTWKit/Carrier/CloudWire.swift @@ -54,10 +54,12 @@ public enum CloudWire { public static let msgEvent = "event" public static let msgTiles = "tiles" public static let msgHB = "hb" + /// Each car's level and goal, by opaque id (srcfl/ftw `docs/cars.md`). + public static let msgCar = "car" - /// Everything else the box sends (hello, roster, bye) is the service's - /// alone and never reaches a phone. - public static let phoneClasses: Set = [msgLive, msgLP, msgPrices, msgPlan, msgHealth, msgEvent, msgTiles, msgHB] + /// Everything else the box sends (hello, roster, bye, notice) is the + /// service's alone and never reaches a phone as a class. + public static let phoneClasses: Set = [msgLive, msgLP, msgCar, msgPrices, msgPlan, msgHealth, msgEvent, msgTiles, msgHB] // The app session's own types. All unsigned. /// Handshake message 1, `{hz}`. @@ -251,6 +253,12 @@ public enum CloudWire { opaqueID(grantKey, "lp\0", loadpoint) } + /// A car's opaque id, cut the same way with "car\0" from the id the + /// owner's app minted, so it differs per home. + static func carID(grantKey: Bytes, car: String) -> String { + opaqueID(grantKey, "car\0", car) + } + private static func opaqueID(_ key: Bytes, _ tag: String, _ name: String) -> String { Base64url.encode(Array(Primitives.hmacSHA256(key: key, Bytes(tag.utf8) + Bytes(name.utf8)).prefix(8))) } @@ -453,6 +461,57 @@ public enum CloudWire { public var nextStartMs: Int64? public var nextEndMs: Int64? public var nextWh: Int64? + /// Opaque id of the session's car, the join to `Cars`. Empty while + /// nothing is plugged in or the box does not know the car. With it, + /// the level and goal fields above are views of that car, and where + /// the two disagree the car class wins. + public var carId: String = "" + /// charger, car, only, tap, asking or none (`docs/cars.md`). + public var carIdentifiedBy: String = "" + public var carCorrected: Bool = false + public var carCorrectedFrom: String = "" + /// While asking: the cars a tap may pick, at most four. + public var choose: [String] = [] + } + + /// Each car's level and goal on this box. Nothing here names a car; the + /// names come from the box over Noise. + public struct Cars: Equatable, Sendable { + public var header: Header + public var cars: [Car] + + /// The car with this opaque id, as a charger's `carId` names it. + public func car(_ id: String) -> Car? { + id.isEmpty ? nil : cars.first { $0.id == id } + } + } + + public struct Car: Equatable, Sendable { + /// Opaque car id. + public var id: String + /// The car's level; the car's or a person's own number while + /// `socEstimate` is false. Nil when the box has none. + public var socPermille: Int64? + /// vehicle, manual or assumed; empty without a level. + public var socSource: String + /// Seconds since the car reported the level or someone set it, at + /// send. + public var socAgeS: Int64? + /// Energy into the car since then; negative after V2X. + public var energySinceWh: Int64 + public var socEstimate: Bool + public var chargeLimitPermille: Int64? + public var goal: CarGoal? + public var surplusOnly: Bool + } + + public struct CarGoal: Equatable, Sendable { + public var targetPermille: Int64 + /// Minutes after midnight in the box's time zone. + public var readyByMin: Int64 + /// ISO weekday mask, Monday in bit 0; 0 is once, 127 every day. + public var days: Int64 + public var finishAtLimit: Bool } /// A manual charging hold. @@ -581,7 +640,39 @@ extension CloudWire.Loadpoint { boost: try f.bool("boost"), nextStartMs: try f.optionalInt("nextStartMs"), nextEndMs: try f.optionalInt("nextEndMs"), - nextWh: try f.optionalInt("nextWh") + nextWh: try f.optionalInt("nextWh"), + carId: try f.text("carId"), + carIdentifiedBy: try f.text("carIdentifiedBy"), + carCorrected: try f.bool("carCorrected"), + carCorrectedFrom: try f.text("carCorrectedFrom"), + choose: try CloudWire.texts(f["choose"], "choose") + ) + } +} + +extension CloudWire.Cars { + public init(body: Bytes) throws { + let f = try CloudWire.Fields(body: body) + self.init(header: try CloudWire.Header(f), + cars: try f.list("cars") { try CloudWire.Car(CloudWire.Fields($0, "cars")) }) + } +} + +extension CloudWire.Car { + init(_ f: CloudWire.Fields) throws { + self.init( + id: try f.text("id"), + socPermille: try f.optionalInt("socPermille"), + socSource: try f.text("socSource"), + socAgeS: try f.optionalInt("socAgeS"), + energySinceWh: try f.int("energySinceWh"), + socEstimate: try f.bool("socEstimate"), + chargeLimitPermille: try f.optionalInt("chargeLimitPermille"), + goal: try f.optional("goal").map { g in + CloudWire.CarGoal(targetPermille: try g.int("targetPermille"), readyByMin: try g.int("readyByMin"), + days: try g.int("days"), finishAtLimit: try g.bool("finishAtLimit")) + }, + surplusOnly: try f.bool("surplusOnly") ) } } diff --git a/appleApp/FTWKit/Sources/FTWKit/State/SiteModel.swift b/appleApp/FTWKit/Sources/FTWKit/State/SiteModel.swift index 04e3983..c81462c 100644 --- a/appleApp/FTWKit/Sources/FTWKit/State/SiteModel.swift +++ b/appleApp/FTWKit/Sources/FTWKit/State/SiteModel.swift @@ -1138,7 +1138,7 @@ extension SiteModel { cloud = c cloudHome = home cloudStatus = c.status - if cloudOn { installPushSink() } + if takesAlerts { installPushSink() } c.start() } @@ -1194,11 +1194,18 @@ extension SiteModel { cloudDirty = true persistNow() startCloud() - // Alerts come with the readable copy, and only then is the person - // asked to allow them. - if cloudOn, cloud != nil { installPushSink() } else if !cloudOn { uninstallPushSink() } + // Alerts come with the readable copy, or to an owner's phone for the + // security notices whenever the box is linked, and only then is the + // person asked to allow them. + if takesAlerts, cloud != nil { installPushSink() } else if !takesAlerts { uninstallPushSink() } } + /// Whether this phone registers for FTW Cloud's alerts: an owner's + /// whenever the box is linked, since security notices go to every owner + /// with the readable copy on or off; another phone only while the copy + /// carries its events. + private var takesAlerts: Bool { cloudOn || canConfigure } + // MARK: Pushes /// The App Group files the notification extension reads. None in the @@ -1288,7 +1295,7 @@ extension SiteModel { guard var view = cloudView, (try? view.accept(env, arrivalMs: Int64(at))) != nil else { return false } cloudView = view switch env.t { - case CloudWire.msgLP: takeCloudCars(at) + case CloudWire.msgLP, CloudWire.msgCar: takeCloudCars(at) case CloudWire.msgPrices: takeCloudPrices(at) default: break } @@ -1397,13 +1404,16 @@ extension SiteModel { guard let view = cloudView, let lp = view.lp, let identity = cloudIdentity else { return } let now = scheduler.nowMs let g = view.grade(nowMs: Int64(now)) - let cars = lp.loadpoints.map { l in + let cars = lp.loadpoints.map(view.joined).map { l in CarEntry.Car( loadpointID: identity.loadpoints[l.id] ?? l.id, pluggedIn: l.plugged, socPct: l.socPermille.map { (Double($0) / 10).rounded() }, socSource: l.socSource, - socReadAtMs: l.socAgeS.map { now - Double($0) * 1000 }, + // Dated from the message that carried the age: the car + // class's own send for a car. Without an anchor, a charger + // without a car is dated from now, as before cars. + socReadAtMs: view.socReadAtMs(l).map(Double.init) ?? (l.carId.isEmpty ? l.socAgeS.map { now - Double($0) * 1000 } : nil), powerW: Double(l.powerW), targetSocPct: l.targetPermille.map { (Double($0) / 10).rounded() } ) diff --git a/appleApp/FTWKit/Sources/FTWShared/PushAlert.swift b/appleApp/FTWKit/Sources/FTWShared/PushAlert.swift index 72e681a..b793d5c 100644 --- a/appleApp/FTWKit/Sources/FTWShared/PushAlert.swift +++ b/appleApp/FTWKit/Sources/FTWShared/PushAlert.swift @@ -27,7 +27,8 @@ public enum PushKind { public static let cloudGranted = "cloud.granted" public static let cloudRevoked = "cloud.revoked" /// Access from outside the house, for every owner (docs/agents.md in - /// srcfl/ftw). They come as the box's `notice`, never as an event. + /// srcfl/ftw). They come as the box's `notice`, never as an event, and + /// FTW Cloud brings them with the readable copy on or off. public static let agentAdded = "agent.added" public static let agentRequest = "agent.request" public static let agentExpiring = "agent.expiring" @@ -98,6 +99,8 @@ public struct PushAlert: Sendable, Equatable { static let clockLeadMs: Int64 = 2_000 /// How old an event may be and still get its text and buttons. static let maxAgeMs: Int64 = 10 * 60_000 + /// How old a notice may be: a day. + static let noticeMaxAgeMs: Int64 = 24 * 60 * 60_000 /// How long rule U's anchor stays usable after it was last learned. static let ruleULifetimeMs: Int64 = 24 * 60 * 60_000 /// Rule U's drift margin: 0.01 % of the time since it was learned. @@ -138,16 +141,22 @@ public struct PushAlert: Sendable, Equatable { guard let event = a.event, !event.isEmpty else { return a.kind == PushKind.boxUnreachable ? PushAlert(.unreachable, home: home) : generic } + // A notice reads as an event of its kind, with the client's name as + // its one argument when it carries one. guard a.kind == nil || a.kind == "", - let env = Envelope(Array(event)), env.t == "event", + let env = Envelope(Array(event)), env.t == "event" || env.t == "notice", let pk = boxKey(home), verify(pk, home: home, env), - let ev = Event(env.b), var at = sendTime(ev, offset: offset(home), nowMs: nowMs) else { + let ev = env.t == "event" ? Event(env.b) : Event(notice: env.b), + var at = sendTime(ev, offset: offset(home), nowMs: nowMs) else { return generic } if let ts = ev.ts { at = min(at, ts) } let age = nowMs.subtractingReportingOverflow(at) - guard PushKind.all.contains(ev.kind), ev.kind != PushKind.boxUnreachable, !PushKind.notices.contains(ev.kind), - !age.overflow, age.partialValue <= maxAgeMs else { + // A notice kind comes only as a notice, and a notice states a + // lasting fact, so it lasts a day. + let notice = env.t == "notice" + guard PushKind.all.contains(ev.kind), ev.kind != PushKind.boxUnreachable, PushKind.notices.contains(ev.kind) == notice, + !age.overflow, age.partialValue <= (notice ? noticeMaxAgeMs : maxAgeMs) else { return generic } return PushAlert(.event, home: home, kind: ev.kind, args: ev.args, src: ev.src, lp: ev.lp, boot: ev.boot.map { Data($0) }, seq: ev.seq, atMs: at) @@ -202,6 +211,17 @@ public struct PushAlert: Sendable, Equatable { } } + /// A notice's client name may be at most this many characters: the + /// consent page keeps 64 and marks a cut name with "…", as in Go. + static let noticeClientMaxRunes = 65 + + /// A client name as Go's NoticeClientOK takes it: no control or format + /// character, and not too long. + static func clientOK(_ c: String) -> Bool { + !c.isEmpty && c.unicodeScalars.count <= noticeClientMaxRunes && + !c.unicodeScalars.contains { $0.properties.generalCategory == .control || $0.properties.generalCategory == .format } + } + /// The event body. A field may be missing or null; one of the wrong /// type makes the whole body unreadable, as it does in Go. struct Event { @@ -238,6 +258,32 @@ public struct PushAlert: Sendable, Equatable { return nil } } + + /// A notice body, read as ReadNotice in Go reads it: the header, a + /// notice kind and, on agent.added only, the client's name. Any + /// other field makes it no notice, so it cannot carry a reading. + init?(notice b: [UInt8]) { + let allowed: Set = ["seq", "boot", "uptimeMs", "wallMs", "kind", "client"] + guard let m = (try? PushCBOR.decode(b))?.textMap, Set(m.keys).isSubset(of: allowed) else { return nil } + func field(_ key: String, _ read: (PushCBOR) -> T?) throws -> T? { + guard let v = m[key], v != .null else { return nil } + guard let out = read(v) else { throw PushCBOR.Malformed() } + return out + } + do { + seq = try field("seq", \.uint) ?? 0 + boot = try field("boot", \.byteString) + uptimeMs = try field("uptimeMs", \.int64) ?? 0 + wallMs = try field("wallMs", \.int64) + kind = try field("kind", \.text) ?? "" + let client = try field("client", \.text) ?? "" + guard PushKind.notices.contains(kind), boot?.count == 16, uptimeMs >= 0, + client.isEmpty || (kind == PushKind.agentAdded && PushAlert.clientOK(client)) else { return nil } + if !client.isEmpty { args = ["client": client] } + } catch { + return nil + } + } } } diff --git a/appleApp/FTWKit/Sources/FTWShared/PushSeen.swift b/appleApp/FTWKit/Sources/FTWShared/PushSeen.swift index af67a2b..b3e12b9 100644 --- a/appleApp/FTWKit/Sources/FTWShared/PushSeen.swift +++ b/appleApp/FTWKit/Sources/FTWShared/PushSeen.swift @@ -4,15 +4,21 @@ import Synchronization /// Events this phone has shown, so a push that arrives twice is shown once. /// /// The box numbers what it sends: `seq` rises by one per message within a -/// boot. Per home and boot the extension keeps the highest `seq` it showed; -/// an event at or below it is a duplicate or older than one already shown. -/// A new boot starts its own count. The file keeps the most recent few -/// boots and is in the App Group, beside the cache. +/// boot. Events and notices share the count, and a notice can arrive after +/// a later event, so per home and boot the extension keeps the numbers it +/// showed, the newest `recent` of them, and a floor below which everything +/// counts as shown: the numbers that fell out, and for a file from before +/// this list, the highest shown. A new boot starts its own count. The file +/// keeps the most recent few boots and is in the App Group, beside the +/// cache. public struct PushSeen: Sendable { public let file: URL /// Homes times boots worth remembering. Each box starts a new boot on /// every restart; the oldest goes first. public static let limit = 32 + /// Numbers kept per boot. Far more than a box raises in a day under + /// FTW Cloud's caps; a notice lives a day. + public static let recent = 64 public init(file: URL) { self.file = file @@ -25,7 +31,11 @@ public struct PushSeen: Sendable { struct Entry: Codable, Equatable { var home: String var boot: Data + /// The floor: anything at or below it counts as shown. var seq: UInt64 + /// The numbers shown above the floor, rising. Absent in a file from + /// before them, whose `seq` is the highest shown. + var shown: [UInt64]? /// The phone's clock when it was last moved, to drop the oldest. var atMs: Double } @@ -41,11 +51,16 @@ public struct PushSeen: Sendable { return Self.lock.withLock { _ in var entries = load() if let i = entries.firstIndex(where: { $0.home == alert.home && $0.boot == boot }) { - guard alert.seq > entries[i].seq else { return false } - entries[i].seq = alert.seq + var shown = entries[i].shown ?? [] + guard alert.seq > entries[i].seq, !shown.contains(alert.seq) else { return false } + shown.insert(alert.seq, at: shown.firstIndex { $0 > alert.seq } ?? shown.endIndex) + if shown.count > Self.recent { + entries[i].seq = max(entries[i].seq, shown.removeFirst()) + } + entries[i].shown = shown entries[i].atMs = nowMs } else { - entries.append(Entry(home: alert.home, boot: boot, seq: alert.seq, atMs: nowMs)) + entries.append(Entry(home: alert.home, boot: boot, seq: 0, shown: [alert.seq], atMs: nowMs)) } entries.sort { $0.atMs > $1.atMs } try? save(Array(entries.prefix(Self.limit))) diff --git a/appleApp/FTWKit/Sources/FTWShared/PushText.swift b/appleApp/FTWKit/Sources/FTWShared/PushText.swift index e5beca7..9c3b52f 100644 --- a/appleApp/FTWKit/Sources/FTWShared/PushText.swift +++ b/appleApp/FTWKit/Sources/FTWShared/PushText.swift @@ -29,11 +29,14 @@ public enum PushText { } /// One kind's sentence. `{name}` marks an argument; `without` is the - /// body to show when an argument is missing or will not do. + /// body to show when an argument is missing or will not do. `named` is + /// the phone's own body for a notice that names the client given + /// access; without the name, `body` shows. struct Sentence { var title: String var body: String var without: String? + var named: String? } /// The line for anything that did not check out: no signature, an old @@ -57,6 +60,7 @@ public enum PushText { } guard let s = sentences(language)[kind] else { return generic(language) } let values = arguments(alert.args, name: sourceName, decimalSeparator: decimalSeparator) + if let named = s.named, let body = fill(named, values) { return Content(title: s.title, body: body) } guard let body = fill(s.body, values) else { return Content(title: s.title, body: s.without ?? s.body) } return Content(title: s.title, body: body) } @@ -76,7 +80,8 @@ public enum PushText { PushKind.boxUnreachable: Sentence(title: "Your box is out of reach", body: "It stopped answering. Power cut, or the internet at home is down."), PushKind.cloudGranted: Sentence(title: "FTW Cloud is on", body: "Your box now shares a copy of its readings with FTW Cloud. You can turn it off in the app or on the box."), PushKind.cloudRevoked: Sentence(title: "FTW Cloud is off", body: "Your box stopped sharing with FTW Cloud and asked it to delete its copy."), - PushKind.agentAdded: Sentence(title: "New access to your box", body: "You gave an assistant or Sourceful access. See who has access in the app."), + PushKind.agentAdded: Sentence(title: "New access to your box", body: "An assistant or Sourceful was given access to your box. See who has access in the app.", + named: "{client} was given access to your box. See who has access in the app."), PushKind.agentRequest: Sentence(title: "Waiting for your answer", body: "An assistant asks to change a setting. Open FTW to confirm or decline."), PushKind.agentExpiring: Sentence(title: "Access ends tomorrow", body: "An assistant's or Sourceful's access to your box runs out in a day. Renew it in the app if it should go on."), PushKind.approvalKeySet: Sentence(title: "A phone can now approve access", body: "A phone set up the key it uses to approve access to your box. If that was not you, remove the phone in the app."), @@ -95,7 +100,8 @@ public enum PushText { PushKind.boxUnreachable: Sentence(title: "Din box går inte att nå", body: "Den har slutat svara. Strömavbrott, eller så är internet hemma nere."), PushKind.cloudGranted: Sentence(title: "FTW Cloud är på", body: "Din box delar nu en kopia av sina mätvärden med FTW Cloud. Du kan stänga av det i appen eller på boxen."), PushKind.cloudRevoked: Sentence(title: "FTW Cloud är av", body: "Din box har slutat dela med FTW Cloud och bett den radera sin kopia."), - PushKind.agentAdded: Sentence(title: "Ny åtkomst till din box", body: "En assistent eller Sourceful har fått åtkomst. Se vem som har åtkomst i appen."), + PushKind.agentAdded: Sentence(title: "Ny åtkomst till din box", body: "En assistent eller Sourceful har fått åtkomst till din box. Se vem som har åtkomst i appen.", + named: "{client} har fått åtkomst till din box. Se vem som har åtkomst i appen."), PushKind.agentRequest: Sentence(title: "Väntar på ditt svar", body: "En assistent vill ändra en inställning. Öppna FTW för att godkänna eller avböja."), PushKind.agentExpiring: Sentence(title: "Åtkomsten upphör i morgon", body: "En assistents eller Sourcefuls åtkomst till din box går ut om ett dygn. Förnya den i appen om den ska fortsätta."), PushKind.approvalKeySet: Sentence(title: "En telefon kan nu godkänna åtkomst", body: "En telefon har satt upp nyckeln den använder för att godkänna åtkomst till din box. Var det inte du, ta bort telefonen i appen."), @@ -119,16 +125,19 @@ public enum PushText { out["by"] = by } for key in ["version", "phase"] { - if let v = args[key].flatMap(plain) { out[key] = v } + if let v = args[key].flatMap({ plain($0) }) { out[key] = v } } - if let name = name.flatMap(plain) { out["name"] = name } + // A client's name as the consent page cut it: up to 64 characters + // and "…". + if let c = args["client"].flatMap({ plain($0, upTo: PushAlert.noticeClientMaxRunes) }) { out["client"] = c } + if let name = name.flatMap({ plain($0) }) { out["name"] = name } return out } /// A short line of text, or nil. - static func plain(_ s: String) -> String? { + static func plain(_ s: String, upTo most: Int = 40) -> String? { let t = s.trimmingCharacters(in: .whitespacesAndNewlines) - guard (1...40).contains(t.count), t.unicodeScalars.allSatisfy({ !CharacterSet.controlCharacters.contains($0) && !CharacterSet.newlines.contains($0) }) else { return nil } + guard (1...most).contains(t.count), t.unicodeScalars.allSatisfy({ $0.properties.generalCategory != .format }), t.unicodeScalars.allSatisfy({ !CharacterSet.controlCharacters.contains($0) && !CharacterSet.newlines.contains($0) }) else { return nil } return t } diff --git a/appleApp/FTWKit/Tests/FTWKitTests/CloudViewTests.swift b/appleApp/FTWKit/Tests/FTWKitTests/CloudViewTests.swift index eef8b00..3753c28 100644 --- a/appleApp/FTWKit/Tests/FTWKitTests/CloudViewTests.swift +++ b/appleApp/FTWKit/Tests/FTWKitTests/CloudViewTests.swift @@ -464,3 +464,55 @@ private func header(_ box: inout FakeCloudBox, at: Int64) throws -> CloudWire.He #expect(v.sentMs(ahead, arrivalMs: 3_000_100) == 3_000_000) } } + +/// The car class (srcfl/ftw docs/cars.md): the view holds it like the other +/// classes, and a charger's level and goal read from its session's car, the +/// car winning where the two disagree. +@Suite struct CloudViewCarTests { + @Test func aChargerReadsItsCarAndTheCarWins() throws { + let vectors = try CloudVectors.load() + var v = CloudView(home: vectors.home, boxKey: Bytes(hex: vectors.boxKey.pk)) + let arrival: Int64 = 1_791_820_900_000 + for name in ["lp car asking", "car", "lp car tapped"] { + try v.accept(vectors.envelope(name), arrivalMs: arrival) + } + let cars = try #require(v.cars) + #expect(cars.cars.count == 2) + let tapped = try #require(v.lp?.loadpoints.first) + let tesla = try #require(cars.car(tapped.carId)) + // The box filled the charger from the car: joined, nothing moves + // but the age, which each class counts to its own send. + var same = tapped + same.socAgeS = tesla.socAgeS + #expect(v.joined(tapped) == same) + #expect(tesla.socPermille == tapped.socPermille) + // Dated from the car class's own send, not from the lp that came + // later, nor from now. + let carSent = try #require(v.sentMs(cars.header, arrivalMs: arrival)) + #expect(v.socReadAtMs(tapped) == carSent - 540_000) + + // A newer car class says otherwise: the car wins. + var box = try FakeCloudBox(boot: 0xb0) + box.seq = 20 + let at = arrival - 1_000 + let car = CBOR.map([ + ("id", .text(tesla.id)), ("socPermille", .int(300)), ("socSource", .text("manual")), ("socAgeS", .int(5)), + ("energySinceWh", .int(0)), ("socEstimate", .bool(false)), ("surplusOnly", .bool(true)), + ("goal", .map([("targetPermille", .int(600)), ("readyByMin", .int(390)), ("days", .int(0)), ("finishAtLimit", .bool(false))])), + ]) + try v.accept(box.seal(CloudWire.msgCar, box.header(at) + [("cars", .array([car]))]), arrivalMs: arrival) + let joined = v.joined(tapped) + #expect(joined.socPermille == 300 && joined.socSource == "inferred" && joined.socAgeS == nil) + #expect(v.socReadAtMs(tapped) == at - 5_000) + #expect(joined.targetPermille == 600 && !joined.recurring && joined.surplusOnly) + #expect(joined.powerW == tapped.powerW && joined.carId == tapped.carId) + + // A charger with no car, or a car the class does not hold, is as + // sent. + var visitor = tapped + visitor.carId = "" + #expect(v.joined(visitor) == visitor) + visitor.carId = "unknown-car" + #expect(v.joined(visitor) == visitor) + } +} diff --git a/appleApp/FTWKit/Tests/FTWKitTests/CloudWireTests.swift b/appleApp/FTWKit/Tests/FTWKitTests/CloudWireTests.swift index f55c563..c2d246a 100644 --- a/appleApp/FTWKit/Tests/FTWKitTests/CloudWireTests.swift +++ b/appleApp/FTWKit/Tests/FTWKitTests/CloudWireTests.swift @@ -118,7 +118,7 @@ func cloudSign(_ key: P256.Signing.PrivateKey, home: String, t: String, b: Bytes let v = try CloudVectors.load() let boxPK = Bytes(hex: v.boxKey.pk) let servicePK = Bytes(hex: v.serviceKey.pk) - #expect(v.messages.count == 22) + #expect(v.messages.count == 27) for m in v.messages { let frame = Bytes(hex: m.frame) @@ -156,11 +156,13 @@ func cloudSign(_ key: P256.Signing.PrivateKey, home: String, t: String, b: Bytes #expect(Bytes(service.publicKey.x963Representation).hex == v.serviceKey.pk) let grantKey = Bytes(hex: v.grantKey) - #expect(v.ids.count == 4) + #expect(v.ids.count == 6) for id in v.ids { - let got = id.kind == "src" - ? CloudWire.sourceID(grantKey: grantKey, driver: id.name) - : CloudWire.loadpointID(grantKey: grantKey, loadpoint: id.name) + let got = switch id.kind { + case "src": CloudWire.sourceID(grantKey: grantKey, driver: id.name) + case "car": CloudWire.carID(grantKey: grantKey, car: id.name) + default: CloudWire.loadpointID(grantKey: grantKey, loadpoint: id.name) + } #expect(got == id.id, "\(id.kind) \(id.name)") } // A loadpoint and a source with one name do not share an id. @@ -209,6 +211,26 @@ func cloudSign(_ key: P256.Signing.PrivateKey, home: String, t: String, b: Bytes #expect(lp.feedback == [CloudWire.Feedback(kind: "ev", src: charger, status: "following", reason: "plan", severity: "ok", evidence: "measured", fresh: true, actualW: 2100, ageMs: 900)]) + // Cars (srcfl/ftw docs/cars.md): the class by opaque id, and a + // tapped charger that names its car. + let tesla = CloudWire.carID(grantKey: grantKey, car: "5f0c1e2d3a4b5c6d7e8f90a1b2c3d4e5") + let volvo = CloudWire.carID(grantKey: grantKey, car: "0123456789abcdef0123456789abcdef") + let cars = try CloudWire.Cars(body: v.envelope("car").b) + #expect(cars.header.seq == 16) + #expect(cars.cars == [ + CloudWire.Car(id: tesla, socPermille: 140, socSource: "vehicle", socAgeS: 540, energySinceWh: 0, socEstimate: false, + chargeLimitPermille: 800, goal: CloudWire.CarGoal(targetPermille: 800, readyByMin: 420, days: 0x7f, finishAtLimit: false), + surplusOnly: false), + CloudWire.Car(id: volvo, socPermille: 620, socSource: "vehicle", socAgeS: 30_600, energySinceWh: 0, socEstimate: true, + chargeLimitPermille: nil, goal: nil, surplusOnly: true), + ]) + let asking = try #require(try CloudWire.LP(body: v.envelope("lp car asking").b).loadpoints.first) + #expect(asking.carId.isEmpty && asking.carIdentifiedBy == "asking" && asking.choose == [tesla, volvo]) + let tapped = try #require(try CloudWire.LP(body: v.envelope("lp car tapped").b).loadpoints.first) + #expect(tapped.carId == tesla && tapped.carIdentifiedBy == "tap" && !tapped.carCorrected && tapped.carCorrectedFrom.isEmpty) + #expect(cars.car(tapped.carId)?.socPermille == tapped.socPermille) + #expect(car.carId.isEmpty && car.choose.isEmpty) + let prices = try CloudWire.Prices(body: v.envelope("prices").b) #expect(prices.zone == "SE3" && prices.currency == "SEK") #expect(prices.t0Ms == 1_791_820_800_000 && prices.slotMs == 900_000) diff --git a/appleApp/FTWKit/Tests/FTWKitTests/Resources/cloud-vectors.json b/appleApp/FTWKit/Tests/FTWKitTests/Resources/cloud-vectors.json index 7a9dd78..52a05b7 100644 --- a/appleApp/FTWKit/Tests/FTWKitTests/Resources/cloud-vectors.json +++ b/appleApp/FTWKit/Tests/FTWKitTests/Resources/cloud-vectors.json @@ -29,6 +29,16 @@ "kind": "lp", "name": "garage", "id": "j3HZORNCWHs" + }, + { + "kind": "car", + "name": "5f0c1e2d3a4b5c6d7e8f90a1b2c3d4e5", + "id": "FNKibitfeU0" + }, + { + "kind": "car", + "name": "0123456789abcdef0123456789abcdef", + "id": "Dm4Qg3Dz3bc" } ], "messages": [ @@ -66,11 +76,11 @@ "name": "roster", "signer": "box", "t": "roster", - "body": "a9637365710264626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474746c531a00015180656772616e74a462696450444444444444444444444444444444446673636f706573826d6674772e6c6976652e726561646f6674772e6576656e74732e7265616468707572706f736573816573657276656a636f6e74726f6c6c657269736f7572636566756c6677616c6c4d731b000001a130d7a00a676465766963657382a66269646871354c77305a6231636b6579582055555555555555555555555555555555555555555555555555555555555555556374756ef464726f6c65656f776e65726565706f6368016673636f706573856d6674772e6c6976652e72656164706674772e686973746f72792e726561646d6674772e706c616e2e726561646f6674772e6865616c74682e726561646f6674772e6576656e74732e72656164a6626964685a66303978513261636b6579582066666666666666666666666666666666666666666666666666666666666666666374756ef464726f6c65667669657765726565706f6368026673636f706573816d6674772e6c6976652e726561646770656e64696e678068757074696d654d7319ea6a69726f7374657253657104", - "signedInputSha256": "54a45d8ab064d1d9fda8dfd3d43708dca9294daefe67078b15ab17ae1c3c086a", - "sig": "26de4f6b491496569361fce489167aa3a445c91235689a79f5499f5a65d4ed5e35ecd601251013d345f8171904bd5b508e0a05d543dc25f2335b2d7a743ad916", - "envelope": "a361625901c5a9637365710264626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474746c531a00015180656772616e74a462696450444444444444444444444444444444446673636f706573826d6674772e6c6976652e726561646f6674772e6576656e74732e7265616468707572706f736573816573657276656a636f6e74726f6c6c657269736f7572636566756c6677616c6c4d731b000001a130d7a00a676465766963657382a66269646871354c77305a6231636b6579582055555555555555555555555555555555555555555555555555555555555555556374756ef464726f6c65656f776e65726565706f6368016673636f706573856d6674772e6c6976652e72656164706674772e686973746f72792e726561646d6674772e706c616e2e726561646f6674772e6865616c74682e726561646f6674772e6576656e74732e72656164a6626964685a66303978513261636b6579582066666666666666666666666666666666666666666666666666666666666666666374756ef464726f6c65667669657765726565706f6368026673636f706573816d6674772e6c6976652e726561646770656e64696e678068757074696d654d7319ea6a69726f7374657253657104617466726f7374657263736967584026de4f6b491496569361fce489167aa3a445c91235689a79f5499f5a65d4ed5e35ecd601251013d345f8171904bd5b508e0a05d543dc25f2335b2d7a743ad916", - "frame": "01010000021aa361625901c5a9637365710264626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474746c531a00015180656772616e74a462696450444444444444444444444444444444446673636f706573826d6674772e6c6976652e726561646f6674772e6576656e74732e7265616468707572706f736573816573657276656a636f6e74726f6c6c657269736f7572636566756c6677616c6c4d731b000001a130d7a00a676465766963657382a66269646871354c77305a6231636b6579582055555555555555555555555555555555555555555555555555555555555555556374756ef464726f6c65656f776e65726565706f6368016673636f706573856d6674772e6c6976652e72656164706674772e686973746f72792e726561646d6674772e706c616e2e726561646f6674772e6865616c74682e726561646f6674772e6576656e74732e72656164a6626964685a66303978513261636b6579582066666666666666666666666666666666666666666666666666666666666666666374756ef464726f6c65667669657765726565706f6368026673636f706573816d6674772e6c6976652e726561646770656e64696e678068757074696d654d7319ea6a69726f7374657253657104617466726f7374657263736967584026de4f6b491496569361fce489167aa3a445c91235689a79f5499f5a65d4ed5e35ecd601251013d345f8171904bd5b508e0a05d543dc25f2335b2d7a743ad916" + "body": "aa637365710264626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474746c531a00015180656772616e74a462696450444444444444444444444444444444446673636f706573826d6674772e6c6976652e726561646f6674772e6576656e74732e7265616468707572706f736573816573657276656a636f6e74726f6c6c657269736f7572636566756c656e6f697365582077777777777777777777777777777777777777777777777777777777777777776677616c6c4d731b000001a130d7a00a676465766963657382a66269646871354c77305a6231636b6579582055555555555555555555555555555555555555555555555555555555555555556374756ef464726f6c65656f776e65726565706f6368016673636f706573856d6674772e6c6976652e72656164706674772e686973746f72792e726561646d6674772e706c616e2e726561646f6674772e6865616c74682e726561646f6674772e6576656e74732e72656164a6626964685a66303978513261636b6579582066666666666666666666666666666666666666666666666666666666666666666374756ef464726f6c65667669657765726565706f6368026673636f706573816d6674772e6c6976652e726561646770656e64696e678068757074696d654d7319ea6a69726f7374657253657104", + "signedInputSha256": "f47b850d31bfffb3f6352ab20c3ba7788cc95f0df22a38f1b8b44184a383399e", + "sig": "96a9ec9cde7b6b2b62bf55ed8d74aeaba0a8d0ded9e5ec32d1f868f758051c146e323ee9aca2c7a009b78190cde63c4b9c882ec29843f07fd15608ed9cf4dd11", + "envelope": "a361625901edaa637365710264626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474746c531a00015180656772616e74a462696450444444444444444444444444444444446673636f706573826d6674772e6c6976652e726561646f6674772e6576656e74732e7265616468707572706f736573816573657276656a636f6e74726f6c6c657269736f7572636566756c656e6f697365582077777777777777777777777777777777777777777777777777777777777777776677616c6c4d731b000001a130d7a00a676465766963657382a66269646871354c77305a6231636b6579582055555555555555555555555555555555555555555555555555555555555555556374756ef464726f6c65656f776e65726565706f6368016673636f706573856d6674772e6c6976652e72656164706674772e686973746f72792e726561646d6674772e706c616e2e726561646f6674772e6865616c74682e726561646f6674772e6576656e74732e72656164a6626964685a66303978513261636b6579582066666666666666666666666666666666666666666666666666666666666666666374756ef464726f6c65667669657765726565706f6368026673636f706573816d6674772e6c6976652e726561646770656e64696e678068757074696d654d7319ea6a69726f7374657253657104617466726f7374657263736967584096a9ec9cde7b6b2b62bf55ed8d74aeaba0a8d0ded9e5ec32d1f868f758051c146e323ee9aca2c7a009b78190cde63c4b9c882ec29843f07fd15608ed9cf4dd11", + "frame": "010100000242a361625901edaa637365710264626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474746c531a00015180656772616e74a462696450444444444444444444444444444444446673636f706573826d6674772e6c6976652e726561646f6674772e6576656e74732e7265616468707572706f736573816573657276656a636f6e74726f6c6c657269736f7572636566756c656e6f697365582077777777777777777777777777777777777777777777777777777777777777776677616c6c4d731b000001a130d7a00a676465766963657382a66269646871354c77305a6231636b6579582055555555555555555555555555555555555555555555555555555555555555556374756ef464726f6c65656f776e65726565706f6368016673636f706573856d6674772e6c6976652e72656164706674772e686973746f72792e726561646d6674772e706c616e2e726561646f6674772e6865616c74682e726561646f6674772e6576656e74732e72656164a6626964685a66303978513261636b6579582066666666666666666666666666666666666666666666666666666666666666666374756ef464726f6c65667669657765726565706f6368026673636f706573816d6674772e6c6976652e726561646770656e64696e678068757074696d654d7319ea6a69726f7374657253657104617466726f7374657263736967584096a9ec9cde7b6b2b62bf55ed8d74aeaba0a8d0ded9e5ec32d1f868f758051c146e323ee9aca2c7a009b78190cde63c4b9c882ec29843f07fd15608ed9cf4dd11" }, { "name": "live full", @@ -196,21 +206,21 @@ "name": "roster tunnel only", "signer": "box", "t": "roster", - "body": "a9637365710c64626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474746c531a00015180656772616e74a4626964406673636f7065738068707572706f736573806a636f6e74726f6c6c657269736f7572636566756c6677616c6c4d731b000001a130d7d6b0676465766963657382a66269646871354c77305a6231636b6579582055555555555555555555555555555555555555555555555555555555555555556374756ef564726f6c65656f776e65726565706f6368016673636f70657380a6626964685a66303978513261636b6579582066666666666666666666666666666666666666666666666666666666666666666374756ef564726f6c65667669657765726565706f6368026673636f706573806770656e64696e6781a264686173685820b4e707c0ac2241df0e0325b42b951b3c71bbd5099bc3c6f2894a48ee23fa35636474746c5319025868757074696d654d731a0001211069726f7374657253657105", - "signedInputSha256": "30ae9e4de78ae0f21421f8a7b172759f3b244da825c0072f2781cb49a73a907c", - "sig": "f9b4b3e144492cf784a07fce0272fe0252d3d71164645b092ebc086a4eab2f0f4e8c11e20aec3567e454a5641a06a97166eaf20ccdd8a022f1eaaa2a18a52e8b", - "envelope": "a36162590168a9637365710c64626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474746c531a00015180656772616e74a4626964406673636f7065738068707572706f736573806a636f6e74726f6c6c657269736f7572636566756c6677616c6c4d731b000001a130d7d6b0676465766963657382a66269646871354c77305a6231636b6579582055555555555555555555555555555555555555555555555555555555555555556374756ef564726f6c65656f776e65726565706f6368016673636f70657380a6626964685a66303978513261636b6579582066666666666666666666666666666666666666666666666666666666666666666374756ef564726f6c65667669657765726565706f6368026673636f706573806770656e64696e6781a264686173685820b4e707c0ac2241df0e0325b42b951b3c71bbd5099bc3c6f2894a48ee23fa35636474746c5319025868757074696d654d731a0001211069726f7374657253657105617466726f73746572637369675840f9b4b3e144492cf784a07fce0272fe0252d3d71164645b092ebc086a4eab2f0f4e8c11e20aec3567e454a5641a06a97166eaf20ccdd8a022f1eaaa2a18a52e8b", - "frame": "0101000001bda36162590168a9637365710c64626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474746c531a00015180656772616e74a4626964406673636f7065738068707572706f736573806a636f6e74726f6c6c657269736f7572636566756c6677616c6c4d731b000001a130d7d6b0676465766963657382a66269646871354c77305a6231636b6579582055555555555555555555555555555555555555555555555555555555555555556374756ef564726f6c65656f776e65726565706f6368016673636f70657380a6626964685a66303978513261636b6579582066666666666666666666666666666666666666666666666666666666666666666374756ef564726f6c65667669657765726565706f6368026673636f706573806770656e64696e6781a264686173685820b4e707c0ac2241df0e0325b42b951b3c71bbd5099bc3c6f2894a48ee23fa35636474746c5319025868757074696d654d731a0001211069726f7374657253657105617466726f73746572637369675840f9b4b3e144492cf784a07fce0272fe0252d3d71164645b092ebc086a4eab2f0f4e8c11e20aec3567e454a5641a06a97166eaf20ccdd8a022f1eaaa2a18a52e8b" + "body": "aa637365710c64626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474746c531a00015180656772616e74a4626964406673636f7065738068707572706f736573806a636f6e74726f6c6c657269736f7572636566756c656e6f697365582077777777777777777777777777777777777777777777777777777777777777776677616c6c4d731b000001a130d7d6b0676465766963657382a66269646871354c77305a6231636b6579582055555555555555555555555555555555555555555555555555555555555555556374756ef564726f6c65656f776e65726565706f6368016673636f70657380a6626964685a66303978513261636b6579582066666666666666666666666666666666666666666666666666666666666666666374756ef564726f6c65667669657765726565706f6368026673636f706573806770656e64696e6781a264686173685820b4e707c0ac2241df0e0325b42b951b3c71bbd5099bc3c6f2894a48ee23fa35636474746c5319025868757074696d654d731a0001211069726f7374657253657105", + "signedInputSha256": "03e4ceaeb5d8abcbd48e0d4a30d53d4145df97c2986d8029e71b1823effd4d3c", + "sig": "729ecdf8ab1a41194cc3da99bf86d5e3593f9bb9e417e70512905e0a6c664f2bfa390316d794493370c9beb8da904e8a01f11af360503b1705cf463834502ac7", + "envelope": "a36162590190aa637365710c64626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474746c531a00015180656772616e74a4626964406673636f7065738068707572706f736573806a636f6e74726f6c6c657269736f7572636566756c656e6f697365582077777777777777777777777777777777777777777777777777777777777777776677616c6c4d731b000001a130d7d6b0676465766963657382a66269646871354c77305a6231636b6579582055555555555555555555555555555555555555555555555555555555555555556374756ef564726f6c65656f776e65726565706f6368016673636f70657380a6626964685a66303978513261636b6579582066666666666666666666666666666666666666666666666666666666666666666374756ef564726f6c65667669657765726565706f6368026673636f706573806770656e64696e6781a264686173685820b4e707c0ac2241df0e0325b42b951b3c71bbd5099bc3c6f2894a48ee23fa35636474746c5319025868757074696d654d731a0001211069726f7374657253657105617466726f73746572637369675840729ecdf8ab1a41194cc3da99bf86d5e3593f9bb9e417e70512905e0a6c664f2bfa390316d794493370c9beb8da904e8a01f11af360503b1705cf463834502ac7", + "frame": "0101000001e5a36162590190aa637365710c64626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474746c531a00015180656772616e74a4626964406673636f7065738068707572706f736573806a636f6e74726f6c6c657269736f7572636566756c656e6f697365582077777777777777777777777777777777777777777777777777777777777777776677616c6c4d731b000001a130d7d6b0676465766963657382a66269646871354c77305a6231636b6579582055555555555555555555555555555555555555555555555555555555555555556374756ef564726f6c65656f776e65726565706f6368016673636f70657380a6626964685a66303978513261636b6579582066666666666666666666666666666666666666666666666666666666666666666374756ef564726f6c65667669657765726565706f6368026673636f706573806770656e64696e6781a264686173685820b4e707c0ac2241df0e0325b42b951b3c71bbd5099bc3c6f2894a48ee23fa35636474746c5319025868757074696d654d731a0001211069726f7374657253657105617466726f73746572637369675840729ecdf8ab1a41194cc3da99bf86d5e3593f9bb9e417e70512905e0a6c664f2bfa390316d794493370c9beb8da904e8a01f11af360503b1705cf463834502ac7" }, { "name": "roster revoking", "signer": "box", "t": "roster", - "body": "a9637365710d64626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474746c531a00015180656772616e74a462696450444444444444444444444444444444446673636f7065738068707572706f736573806a636f6e74726f6c6c657269736f7572636566756c6677616c6c4d731b000001a130d7da98676465766963657381a66269646871354c77305a6231636b6579582055555555555555555555555555555555555555555555555555555555555555556374756ef564726f6c65656f776e65726565706f6368016673636f706573806770656e64696e678068757074696d654d731a000124f869726f7374657253657106", - "signedInputSha256": "d8775b10218f6c69d9b7c9064d1cea8713058eaa9d91aeeba2e2f29419b7f9cc", - "sig": "4332ec4bf6f166175f50eecea08ecbc981d762c10b37f9e676d7e0a763b5d73e58533683fd6b566cf0a33feff903af40d4bf70bedc5245b2696fa85a3191186f", - "envelope": "a3616258f5a9637365710d64626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474746c531a00015180656772616e74a462696450444444444444444444444444444444446673636f7065738068707572706f736573806a636f6e74726f6c6c657269736f7572636566756c6677616c6c4d731b000001a130d7da98676465766963657381a66269646871354c77305a6231636b6579582055555555555555555555555555555555555555555555555555555555555555556374756ef564726f6c65656f776e65726565706f6368016673636f706573806770656e64696e678068757074696d654d731a000124f869726f7374657253657106617466726f737465726373696758404332ec4bf6f166175f50eecea08ecbc981d762c10b37f9e676d7e0a763b5d73e58533683fd6b566cf0a33feff903af40d4bf70bedc5245b2696fa85a3191186f", - "frame": "010100000149a3616258f5a9637365710d64626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474746c531a00015180656772616e74a462696450444444444444444444444444444444446673636f7065738068707572706f736573806a636f6e74726f6c6c657269736f7572636566756c6677616c6c4d731b000001a130d7da98676465766963657381a66269646871354c77305a6231636b6579582055555555555555555555555555555555555555555555555555555555555555556374756ef564726f6c65656f776e65726565706f6368016673636f706573806770656e64696e678068757074696d654d731a000124f869726f7374657253657106617466726f737465726373696758404332ec4bf6f166175f50eecea08ecbc981d762c10b37f9e676d7e0a763b5d73e58533683fd6b566cf0a33feff903af40d4bf70bedc5245b2696fa85a3191186f" + "body": "aa637365710d64626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474746c531a00015180656772616e74a462696450444444444444444444444444444444446673636f7065738068707572706f736573806a636f6e74726f6c6c657269736f7572636566756c656e6f697365582077777777777777777777777777777777777777777777777777777777777777776677616c6c4d731b000001a130d7da98676465766963657381a66269646871354c77305a6231636b6579582055555555555555555555555555555555555555555555555555555555555555556374756ef564726f6c65656f776e65726565706f6368016673636f706573806770656e64696e678068757074696d654d731a000124f869726f7374657253657106", + "signedInputSha256": "322a030b3148bdb71722a8b7ddc7e54d4feff792bb06467125ea6a81d98e387c", + "sig": "c96e88f759f66bb580111e4d63cb45bb4b160d9cd17e211a394df3901b07c2e5317aab96aad398b1a1aca1827a68ba3fe7632ba3b12e886a9968d2c617b70d3c", + "envelope": "a3616259011daa637365710d64626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474746c531a00015180656772616e74a462696450444444444444444444444444444444446673636f7065738068707572706f736573806a636f6e74726f6c6c657269736f7572636566756c656e6f697365582077777777777777777777777777777777777777777777777777777777777777776677616c6c4d731b000001a130d7da98676465766963657381a66269646871354c77305a6231636b6579582055555555555555555555555555555555555555555555555555555555555555556374756ef564726f6c65656f776e65726565706f6368016673636f706573806770656e64696e678068757074696d654d731a000124f869726f7374657253657106617466726f73746572637369675840c96e88f759f66bb580111e4d63cb45bb4b160d9cd17e211a394df3901b07c2e5317aab96aad398b1a1aca1827a68ba3fe7632ba3b12e886a9968d2c617b70d3c", + "frame": "010100000172a3616259011daa637365710d64626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474746c531a00015180656772616e74a462696450444444444444444444444444444444446673636f7065738068707572706f736573806a636f6e74726f6c6c657269736f7572636566756c656e6f697365582077777777777777777777777777777777777777777777777777777777777777776677616c6c4d731b000001a130d7da98676465766963657381a66269646871354c77305a6231636b6579582055555555555555555555555555555555555555555555555555555555555555556374756ef564726f6c65656f776e65726565706f6368016673636f706573806770656e64696e678068757074696d654d731a000124f869726f7374657253657106617466726f73746572637369675840c96e88f759f66bb580111e4d63cb45bb4b160d9cd17e211a394df3901b07c2e5317aab96aad398b1a1aca1827a68ba3fe7632ba3b12e886a9968d2c617b70d3c" }, { "name": "purged copy", @@ -251,6 +261,56 @@ "sig": "", "envelope": "a3616256a3616440636465766871354c77305a6231637369640761746374756e6373696740", "frame": "010100000025a3616256a3616440636465766871354c77305a6231637369640761746374756e6373696740" + }, + { + "name": "roster with an agent", + "signer": "box", + "t": "roster", + "body": "aa637365710e64626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474746c531a00015180656772616e74a4626964406673636f7065738068707572706f736573806a636f6e74726f6c6c657269736f7572636566756c656e6f697365582077777777777777777777777777777777777777777777777777777777777777776677616c6c4d731b000001a130d7de80676465766963657382a66269646871354c77305a6231636b6579582055555555555555555555555555555555555555555555555555555555555555556374756ef564726f6c65656f776e65726565706f6368016673636f70657380a7626964686f4b47696f36536c636b65795820a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a06374756ef464726f6c6567737570706f7274656167656e74a5636f7073816e6c6f6164706f696e742e686f6c646463617073a3646d617857191ce865686f6c6453190384667065724d696e066673636f70657383716674772e64697370617463682e686f6c646d6674772e6c6976652e726561646d6674772e6c6f67732e72656164697072696e636970616ca366636c69656e7471536f7572636566756c20737570706f7274667469636b6574674654572d31323368636c69656e744964783268747470733a2f2f636c6f75642e6674772e656e657267792f636c69656e74732f736f7572636566756c2d737570706f72746b6578706972657341744d731b000001a17720ca006565706f6368016673636f706573806770656e64696e678068757074696d654d731a000128e069726f7374657253657107", + "signedInputSha256": "24ade7fd185b91e8c5c954c34805ebed51cbbf8d6e36097c7a903ebbfe5db7aa", + "sig": "f2f15bfcafd92e077fb41218fb5eca3735d45484cab4074541f0908b8e6a1107a31f8bf3caf605405bd7cbd02963ff4466a40de915aa9d9af96166e5fc5965a7", + "envelope": "a36162590256aa637365710e64626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474746c531a00015180656772616e74a4626964406673636f7065738068707572706f736573806a636f6e74726f6c6c657269736f7572636566756c656e6f697365582077777777777777777777777777777777777777777777777777777777777777776677616c6c4d731b000001a130d7de80676465766963657382a66269646871354c77305a6231636b6579582055555555555555555555555555555555555555555555555555555555555555556374756ef564726f6c65656f776e65726565706f6368016673636f70657380a7626964686f4b47696f36536c636b65795820a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a06374756ef464726f6c6567737570706f7274656167656e74a5636f7073816e6c6f6164706f696e742e686f6c646463617073a3646d617857191ce865686f6c6453190384667065724d696e066673636f70657383716674772e64697370617463682e686f6c646d6674772e6c6976652e726561646d6674772e6c6f67732e72656164697072696e636970616ca366636c69656e7471536f7572636566756c20737570706f7274667469636b6574674654572d31323368636c69656e744964783268747470733a2f2f636c6f75642e6674772e656e657267792f636c69656e74732f736f7572636566756c2d737570706f72746b6578706972657341744d731b000001a17720ca006565706f6368016673636f706573806770656e64696e678068757074696d654d731a000128e069726f7374657253657107617466726f73746572637369675840f2f15bfcafd92e077fb41218fb5eca3735d45484cab4074541f0908b8e6a1107a31f8bf3caf605405bd7cbd02963ff4466a40de915aa9d9af96166e5fc5965a7", + "frame": "0101000002aba36162590256aa637365710e64626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474746c531a00015180656772616e74a4626964406673636f7065738068707572706f736573806a636f6e74726f6c6c657269736f7572636566756c656e6f697365582077777777777777777777777777777777777777777777777777777777777777776677616c6c4d731b000001a130d7de80676465766963657382a66269646871354c77305a6231636b6579582055555555555555555555555555555555555555555555555555555555555555556374756ef564726f6c65656f776e65726565706f6368016673636f70657380a7626964686f4b47696f36536c636b65795820a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a06374756ef464726f6c6567737570706f7274656167656e74a5636f7073816e6c6f6164706f696e742e686f6c646463617073a3646d617857191ce865686f6c6453190384667065724d696e066673636f70657383716674772e64697370617463682e686f6c646d6674772e6c6976652e726561646d6674772e6c6f67732e72656164697072696e636970616ca366636c69656e7471536f7572636566756c20737570706f7274667469636b6574674654572d31323368636c69656e744964783268747470733a2f2f636c6f75642e6674772e656e657267792f636c69656e74732f736f7572636566756c2d737570706f72746b6578706972657341744d731b000001a17720ca006565706f6368016673636f706573806770656e64696e678068757074696d654d731a000128e069726f7374657253657107617466726f73746572637369675840f2f15bfcafd92e077fb41218fb5eca3735d45484cab4074541f0908b8e6a1107a31f8bf3caf605405bd7cbd02963ff4466a40de915aa9d9af96166e5fc5965a7" + }, + { + "name": "lp car asking", + "signer": "box", + "t": "lp", + "body": "a7637365710f64626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06677616c6c4d731b000001a130d7de8068666565646261636b8068757074696d654d731a000128e06a636f6e74726f6c52657619032d6a6c6f6164706f696e747381b8186269646b6a33485a4f524e43574873637372636b4455595757516d6e2d586765626f6f7374f46663686f6f7365826b464e4b69626974666555306b446d34516733447a336263666d616e75616cf6666e6578745768f666706f776572570067706c7567676564f56773657373696f6e703764316630633261396233653466353067736f6341676553f6696e657874456e644d73f669726561647942794d73f669726563757272696e67f46973657373696f6e57680069736f63536f7572636567756e6b6e6f776e6a636f6d6d616e64656457f66b6e65787453746172744d73f66b706c616e50656e64696e67f46b736f635065726d696c6c65f66b737572706c75734f6e6c79f56c677269644465666572726564f46e7461726765745065726d696c6c65f66f6361724964656e74696669656442796661736b696e676f636f6d6d616e646564526561736f6e7070765f737572706c75735f7061757365", + "signedInputSha256": "895b15e9dd2992642f09e346998147e6c89162a1c6a84e2c1fe9aba9349f9a82", + "sig": "fa1b746e25aa7f02deb3fbc543e233315f26f31e0325f4921fb0d5a9ef01e249c129184c88d23396bcc1dc3193bdf78fa2f5805760dd9bea3d05137343bbe420", + "envelope": "a361625901bda7637365710f64626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06677616c6c4d731b000001a130d7de8068666565646261636b8068757074696d654d731a000128e06a636f6e74726f6c52657619032d6a6c6f6164706f696e747381b8186269646b6a33485a4f524e43574873637372636b4455595757516d6e2d586765626f6f7374f46663686f6f7365826b464e4b69626974666555306b446d34516733447a336263666d616e75616cf6666e6578745768f666706f776572570067706c7567676564f56773657373696f6e703764316630633261396233653466353067736f6341676553f6696e657874456e644d73f669726561647942794d73f669726563757272696e67f46973657373696f6e57680069736f63536f7572636567756e6b6e6f776e6a636f6d6d616e64656457f66b6e65787453746172744d73f66b706c616e50656e64696e67f46b736f635065726d696c6c65f66b737572706c75734f6e6c79f56c677269644465666572726564f46e7461726765745065726d696c6c65f66f6361724964656e74696669656442796661736b696e676f636f6d6d616e646564526561736f6e7070765f737572706c75735f70617573656174626c70637369675840fa1b746e25aa7f02deb3fbc543e233315f26f31e0325f4921fb0d5a9ef01e249c129184c88d23396bcc1dc3193bdf78fa2f5805760dd9bea3d05137343bbe420", + "frame": "01010000020ea361625901bda7637365710f64626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06677616c6c4d731b000001a130d7de8068666565646261636b8068757074696d654d731a000128e06a636f6e74726f6c52657619032d6a6c6f6164706f696e747381b8186269646b6a33485a4f524e43574873637372636b4455595757516d6e2d586765626f6f7374f46663686f6f7365826b464e4b69626974666555306b446d34516733447a336263666d616e75616cf6666e6578745768f666706f776572570067706c7567676564f56773657373696f6e703764316630633261396233653466353067736f6341676553f6696e657874456e644d73f669726561647942794d73f669726563757272696e67f46973657373696f6e57680069736f63536f7572636567756e6b6e6f776e6a636f6d6d616e64656457f66b6e65787453746172744d73f66b706c616e50656e64696e67f46b736f635065726d696c6c65f66b737572706c75734f6e6c79f56c677269644465666572726564f46e7461726765745065726d696c6c65f66f6361724964656e74696669656442796661736b696e676f636f6d6d616e646564526561736f6e7070765f737572706c75735f70617573656174626c70637369675840fa1b746e25aa7f02deb3fbc543e233315f26f31e0325f4921fb0d5a9ef01e249c129184c88d23396bcc1dc3193bdf78fa2f5805760dd9bea3d05137343bbe420" + }, + { + "name": "car", + "signer": "box", + "t": "car", + "body": "a5637365711064626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0646361727382a96269646b464e4b696269746665553064676f616ca46464617973187f6a726561647942794d696e1901a46d66696e69736841744c696d6974f46e7461726765745065726d696c6c6519032067736f634167655319021c69736f63536f757263656776656869636c656b736f63457374696d617465f46b736f635065726d696c6c65188c6b737572706c75734f6e6c79f46d656e6572677953696e6365576800736368617267654c696d69745065726d696c6c65190320a96269646b446d34516733447a33626364676f616cf667736f634167655319778869736f63536f757263656776656869636c656b736f63457374696d617465f56b736f635065726d696c6c6519026c6b737572706c75734f6e6c79f56d656e6572677953696e6365576800736368617267654c696d69745065726d696c6c65f66677616c6c4d731b000001a130d7de8a68757074696d654d731a000128ea", + "signedInputSha256": "855da2d9302e228ac2acc0c4d900d6a3074675c8b5fb1350c2942bce438b9fc4", + "sig": "7ee438570fc0f1072201594add376235e687cb87168f882c88e94e1e087b595be184505d295ecc901cb8c8a424a8fa3df0b65b28b1c2352a7ecfc4cb0b53ae01", + "envelope": "a36162590177a5637365711064626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0646361727382a96269646b464e4b696269746665553064676f616ca46464617973187f6a726561647942794d696e1901a46d66696e69736841744c696d6974f46e7461726765745065726d696c6c6519032067736f634167655319021c69736f63536f757263656776656869636c656b736f63457374696d617465f46b736f635065726d696c6c65188c6b737572706c75734f6e6c79f46d656e6572677953696e6365576800736368617267654c696d69745065726d696c6c65190320a96269646b446d34516733447a33626364676f616cf667736f634167655319778869736f63536f757263656776656869636c656b736f63457374696d617465f56b736f635065726d696c6c6519026c6b737572706c75734f6e6c79f56d656e6572677953696e6365576800736368617267654c696d69745065726d696c6c65f66677616c6c4d731b000001a130d7de8a68757074696d654d731a000128ea6174636361726373696758407ee438570fc0f1072201594add376235e687cb87168f882c88e94e1e087b595be184505d295ecc901cb8c8a424a8fa3df0b65b28b1c2352a7ecfc4cb0b53ae01", + "frame": "0101000001c9a36162590177a5637365711064626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0646361727382a96269646b464e4b696269746665553064676f616ca46464617973187f6a726561647942794d696e1901a46d66696e69736841744c696d6974f46e7461726765745065726d696c6c6519032067736f634167655319021c69736f63536f757263656776656869636c656b736f63457374696d617465f46b736f635065726d696c6c65188c6b737572706c75734f6e6c79f46d656e6572677953696e6365576800736368617267654c696d69745065726d696c6c65190320a96269646b446d34516733447a33626364676f616cf667736f634167655319778869736f63536f757263656776656869636c656b736f63457374696d617465f56b736f635065726d696c6c6519026c6b737572706c75734f6e6c79f56d656e6572677953696e6365576800736368617267654c696d69745065726d696c6c65f66677616c6c4d731b000001a130d7de8a68757074696d654d731a000128ea6174636361726373696758407ee438570fc0f1072201594add376235e687cb87168f882c88e94e1e087b595be184505d295ecc901cb8c8a424a8fa3df0b65b28b1c2352a7ecfc4cb0b53ae01" + }, + { + "name": "event car which", + "signer": "box", + "t": "event", + "body": "ac626c706b6a33485a4f524e435748736274731b000001a130d7de306373657111637372636b4455595757516d6e2d58676461726773a16773657373696f6e703764316630633261396233653466353064626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06463617273826b464e4b69626974666555306b446d34516733447a336263646b696e64696361722e77686963686677616c6c4d731b000001a130d7de94676576656e7449646c643268705932686a5958497868736576657269747964696e666f68757074696d654d731a000128f4", + "signedInputSha256": "e576ea700f4abd28075bef81b88d28414de2979912ea333d3503ec45a31cb27d", + "sig": "70a21cc4e288f019d1ffeb59456f8555b07c9f450f6182e6d2cddb5dcca37c31a4a3465c5080c429d6f01063c571edf44e4012dd0198b6bdc1fdfcc6ca3e4b88", + "envelope": "a3616258d4ac626c706b6a33485a4f524e435748736274731b000001a130d7de306373657111637372636b4455595757516d6e2d58676461726773a16773657373696f6e703764316630633261396233653466353064626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06463617273826b464e4b69626974666555306b446d34516733447a336263646b696e64696361722e77686963686677616c6c4d731b000001a130d7de94676576656e7449646c643268705932686a5958497868736576657269747964696e666f68757074696d654d731a000128f46174656576656e7463736967584070a21cc4e288f019d1ffeb59456f8555b07c9f450f6182e6d2cddb5dcca37c31a4a3465c5080c429d6f01063c571edf44e4012dd0198b6bdc1fdfcc6ca3e4b88", + "frame": "010100000127a3616258d4ac626c706b6a33485a4f524e435748736274731b000001a130d7de306373657111637372636b4455595757516d6e2d58676461726773a16773657373696f6e703764316630633261396233653466353064626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06463617273826b464e4b69626974666555306b446d34516733447a336263646b696e64696361722e77686963686677616c6c4d731b000001a130d7de94676576656e7449646c643268705932686a5958497868736576657269747964696e666f68757074696d654d731a000128f46174656576656e7463736967584070a21cc4e288f019d1ffeb59456f8555b07c9f450f6182e6d2cddb5dcca37c31a4a3465c5080c429d6f01063c571edf44e4012dd0198b6bdc1fdfcc6ca3e4b88" + }, + { + "name": "lp car tapped", + "signer": "box", + "t": "lp", + "body": "a7637365711264626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06677616c6c4d731b000001a130d7f20868666565646261636b8068757074696d654d731a00013c686a636f6e74726f6c52657619032e6a6c6f6164706f696e747381b8186269646b6a33485a4f524e43574873637372636b4455595757516d6e2d586765626f6f7374f46563617249646b464e4b6962697466655530666d616e75616cf6666e6578745768f666706f776572570067706c7567676564f56773657373696f6e703764316630633261396233653466353067736f6341676553190221696e657874456e644d73f669726561647942794d731b000001a133a1bc8069726563757272696e67f56973657373696f6e57680069736f63536f757263656776656869636c656a636f6d6d616e64656457f66b6e65787453746172744d73f66b706c616e50656e64696e67f46b736f635065726d696c6c65188c6b737572706c75734f6e6c79f46c677269644465666572726564f46e7461726765745065726d696c6c651903206f6361724964656e7469666965644279637461706f636f6d6d616e646564526561736f6e64706c616e", + "signedInputSha256": "daabad621023f8fb32b67179de6f06ec1ed7b72dd9c83de533aa3b625fa7b439", + "sig": "89b4a6faa2af341cdc4da9ad0247aa2135f40e1aec94554cf1855392d22412cb2191b2a594084e4490faada200f68abbdcdd80b7b0bc3e7fb4ff65e5572f3fcf", + "envelope": "a361625901ada7637365711264626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06677616c6c4d731b000001a130d7f20868666565646261636b8068757074696d654d731a00013c686a636f6e74726f6c52657619032e6a6c6f6164706f696e747381b8186269646b6a33485a4f524e43574873637372636b4455595757516d6e2d586765626f6f7374f46563617249646b464e4b6962697466655530666d616e75616cf6666e6578745768f666706f776572570067706c7567676564f56773657373696f6e703764316630633261396233653466353067736f6341676553190221696e657874456e644d73f669726561647942794d731b000001a133a1bc8069726563757272696e67f56973657373696f6e57680069736f63536f757263656776656869636c656a636f6d6d616e64656457f66b6e65787453746172744d73f66b706c616e50656e64696e67f46b736f635065726d696c6c65188c6b737572706c75734f6e6c79f46c677269644465666572726564f46e7461726765745065726d696c6c651903206f6361724964656e7469666965644279637461706f636f6d6d616e646564526561736f6e64706c616e6174626c7063736967584089b4a6faa2af341cdc4da9ad0247aa2135f40e1aec94554cf1855392d22412cb2191b2a594084e4490faada200f68abbdcdd80b7b0bc3e7fb4ff65e5572f3fcf", + "frame": "0101000001fea361625901ada7637365711264626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06677616c6c4d731b000001a130d7f20868666565646261636b8068757074696d654d731a00013c686a636f6e74726f6c52657619032e6a6c6f6164706f696e747381b8186269646b6a33485a4f524e43574873637372636b4455595757516d6e2d586765626f6f7374f46563617249646b464e4b6962697466655530666d616e75616cf6666e6578745768f666706f776572570067706c7567676564f56773657373696f6e703764316630633261396233653466353067736f6341676553190221696e657874456e644d73f669726561647942794d731b000001a133a1bc8069726563757272696e67f56973657373696f6e57680069736f63536f757263656776656869636c656a636f6d6d616e64656457f66b6e65787453746172744d73f66b706c616e50656e64696e67f46b736f635065726d696c6c65188c6b737572706c75734f6e6c79f46c677269644465666572726564f46e7461726765745065726d696c6c651903206f6361724964656e7469666965644279637461706f636f6d6d616e646564526561736f6e64706c616e6174626c7063736967584089b4a6faa2af341cdc4da9ad0247aa2135f40e1aec94554cf1855392d22412cb2191b2a594084e4490faada200f68abbdcdd80b7b0bc3e7fb4ff65e5572f3fcf" } ] } diff --git a/appleApp/FTWKit/Tests/FTWSharedTests/PushAlertTests.swift b/appleApp/FTWKit/Tests/FTWSharedTests/PushAlertTests.swift index 5c5568a..3f544d0 100644 --- a/appleApp/FTWKit/Tests/FTWSharedTests/PushAlertTests.swift +++ b/appleApp/FTWKit/Tests/FTWSharedTests/PushAlertTests.swift @@ -64,7 +64,7 @@ import Testing @Test func everyPushVectorReadsAsTheGoReferenceReadsIt() throws { let file = try Self.load("push-vectors", as: PushFile.self) - #expect(file.cases.count == 18) + #expect(file.cases.count == 28) let keys = Dictionary(uniqueKeysWithValues: file.keys.map { ($0.home, Data(hex: $0.pk)) }) for c in file.cases { let payload = Data(c.payload.utf8) @@ -100,7 +100,7 @@ import Testing let file = try Self.load("cloud-vectors", as: CloudFile.self) let home = try #require(file.boxKey.home) let keys = ["box": Data(hex: file.boxKey.pk), "service": Data(hex: file.serviceKey.pk)] - #expect(file.messages.count == 22) + #expect(file.messages.count == 27) for m in file.messages { let env = try #require(PushAlert.Envelope(Array(Data(hex: m.envelope))), "\(m.name)") #expect(env.t == m.t, "\(m.name)") diff --git a/appleApp/FTWKit/Tests/FTWSharedTests/PushStoreTests.swift b/appleApp/FTWKit/Tests/FTWSharedTests/PushStoreTests.swift index af563aa..94a71d0 100644 --- a/appleApp/FTWKit/Tests/FTWSharedTests/PushStoreTests.swift +++ b/appleApp/FTWKit/Tests/FTWSharedTests/PushStoreTests.swift @@ -68,7 +68,8 @@ import Testing let seen = PushSeen(file: dir.appendingPathComponent("push-seen")) #expect(seen.accept(Self.event(seq: 9), nowMs: 1)) #expect(!seen.accept(Self.event(seq: 9), nowMs: 2)) - // Older than one shown. + // A notice numbered before an event shown, and late: shown once. + #expect(seen.accept(Self.event(seq: 8), nowMs: 3)) #expect(!seen.accept(Self.event(seq: 8), nowMs: 3)) #expect(seen.accept(Self.event(seq: 10), nowMs: 4)) // A restart counts again; another home counts on its own. @@ -82,6 +83,30 @@ import Testing #expect(seen.accept(PushAlert(.generic, home: Self.home), nowMs: 10)) } + /// Past `recent` numbers the oldest fall below the floor, which counts + /// as shown; a file from before the list keeps its highest as the + /// floor. + @Test func theNumbersKeptAreBounded() throws { + let dir = Self.temp() + defer { try? FileManager.default.removeItem(at: dir) } + let seen = PushSeen(file: dir.appendingPathComponent("push-seen")) + for seq in UInt64(1)...UInt64(PushSeen.recent + 1) { + #expect(seen.accept(Self.event(seq: seq * 2), nowMs: Double(seq))) + } + let entry = try #require(seen.load().first) + #expect(entry.shown?.count == PushSeen.recent && entry.seq == 2) + #expect(!seen.accept(Self.event(seq: 1), nowMs: 100)) + #expect(seen.accept(Self.event(seq: 5), nowMs: 101)) + + let legacy = PushSeen(file: dir.appendingPathComponent("legacy")) + try FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true) + let boot = try #require(Self.event(seq: 1).boot) + try JSONSerialization.data(withJSONObject: [["home": Self.home, "boot": boot.base64EncodedString(), "seq": 9, "atMs": 1]]) + .write(to: legacy.file) + #expect(!legacy.accept(Self.event(seq: 8), nowMs: 2)) + #expect(legacy.accept(Self.event(seq: 10), nowMs: 3)) + } + @Test func theFileStaysSmall() { let dir = Self.temp() defer { try? FileManager.default.removeItem(at: dir) } diff --git a/appleApp/FTWKit/Tests/FTWSharedTests/PushTextTests.swift b/appleApp/FTWKit/Tests/FTWSharedTests/PushTextTests.swift index c892f7d..a998d11 100644 --- a/appleApp/FTWKit/Tests/FTWSharedTests/PushTextTests.swift +++ b/appleApp/FTWKit/Tests/FTWSharedTests/PushTextTests.swift @@ -241,4 +241,27 @@ import Testing #expect(again.thread == "fjzyr67buzpyncqmjliwgezvde") #expect(connected.sound && unreachable.sound) } + + /// A notice that names the client says so; without a name that will + /// do, the catalogue's sentence shows. No notice gets buttons. + @Test func aNoticeNamesTheClientWhenItCan() { + let named = Self.event(PushKind.agentAdded, args: ["client": "Claude"]) + #expect(PushText.content(named, language: .en) == .init(title: "New access to your box", + body: "Claude was given access to your box. See who has access in the app.")) + #expect(PushText.content(named, language: .sv) == .init(title: "Ny åtkomst till din box", + body: "Claude har fått åtkomst till din box. Se vem som har åtkomst i appen.")) + // A name the consent page cut: 64 characters and "…". + let long = String(repeating: "ö", count: 64) + "…" + #expect(PushText.content(Self.event(PushKind.agentAdded, args: ["client": long]), language: .en).body + == "\(long) was given access to your box. See who has access in the app.") + for client in ["", "Claude\nPay here", "Claude\u{202E}", String(repeating: "c", count: 66)] { + #expect(PushText.content(Self.event(PushKind.agentAdded, args: ["client": client]), language: .en).body + == "An assistant or Sourceful was given access to your box. See who has access in the app.", "\(client)") + } + #expect(PushText.content(Self.event(PushKind.ownerAdded, args: ["client": "Claude"]), language: .sv) + == .init(title: "En ny ägare", body: "En telefon har parats som ägare av din box. Var det inte du, ta bort den i appen.")) + for kind in PushKind.notices { + #expect(PushNotice(Self.event(kind), home: nil, language: .en).category == "", "\(kind)") + } + } } diff --git a/appleApp/FTWKit/Tests/FTWSharedTests/Resources/cloud-vectors.json b/appleApp/FTWKit/Tests/FTWSharedTests/Resources/cloud-vectors.json index 7a9dd78..52a05b7 100644 --- a/appleApp/FTWKit/Tests/FTWSharedTests/Resources/cloud-vectors.json +++ b/appleApp/FTWKit/Tests/FTWSharedTests/Resources/cloud-vectors.json @@ -29,6 +29,16 @@ "kind": "lp", "name": "garage", "id": "j3HZORNCWHs" + }, + { + "kind": "car", + "name": "5f0c1e2d3a4b5c6d7e8f90a1b2c3d4e5", + "id": "FNKibitfeU0" + }, + { + "kind": "car", + "name": "0123456789abcdef0123456789abcdef", + "id": "Dm4Qg3Dz3bc" } ], "messages": [ @@ -66,11 +76,11 @@ "name": "roster", "signer": "box", "t": "roster", - "body": "a9637365710264626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474746c531a00015180656772616e74a462696450444444444444444444444444444444446673636f706573826d6674772e6c6976652e726561646f6674772e6576656e74732e7265616468707572706f736573816573657276656a636f6e74726f6c6c657269736f7572636566756c6677616c6c4d731b000001a130d7a00a676465766963657382a66269646871354c77305a6231636b6579582055555555555555555555555555555555555555555555555555555555555555556374756ef464726f6c65656f776e65726565706f6368016673636f706573856d6674772e6c6976652e72656164706674772e686973746f72792e726561646d6674772e706c616e2e726561646f6674772e6865616c74682e726561646f6674772e6576656e74732e72656164a6626964685a66303978513261636b6579582066666666666666666666666666666666666666666666666666666666666666666374756ef464726f6c65667669657765726565706f6368026673636f706573816d6674772e6c6976652e726561646770656e64696e678068757074696d654d7319ea6a69726f7374657253657104", - "signedInputSha256": "54a45d8ab064d1d9fda8dfd3d43708dca9294daefe67078b15ab17ae1c3c086a", - "sig": "26de4f6b491496569361fce489167aa3a445c91235689a79f5499f5a65d4ed5e35ecd601251013d345f8171904bd5b508e0a05d543dc25f2335b2d7a743ad916", - "envelope": "a361625901c5a9637365710264626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474746c531a00015180656772616e74a462696450444444444444444444444444444444446673636f706573826d6674772e6c6976652e726561646f6674772e6576656e74732e7265616468707572706f736573816573657276656a636f6e74726f6c6c657269736f7572636566756c6677616c6c4d731b000001a130d7a00a676465766963657382a66269646871354c77305a6231636b6579582055555555555555555555555555555555555555555555555555555555555555556374756ef464726f6c65656f776e65726565706f6368016673636f706573856d6674772e6c6976652e72656164706674772e686973746f72792e726561646d6674772e706c616e2e726561646f6674772e6865616c74682e726561646f6674772e6576656e74732e72656164a6626964685a66303978513261636b6579582066666666666666666666666666666666666666666666666666666666666666666374756ef464726f6c65667669657765726565706f6368026673636f706573816d6674772e6c6976652e726561646770656e64696e678068757074696d654d7319ea6a69726f7374657253657104617466726f7374657263736967584026de4f6b491496569361fce489167aa3a445c91235689a79f5499f5a65d4ed5e35ecd601251013d345f8171904bd5b508e0a05d543dc25f2335b2d7a743ad916", - "frame": "01010000021aa361625901c5a9637365710264626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474746c531a00015180656772616e74a462696450444444444444444444444444444444446673636f706573826d6674772e6c6976652e726561646f6674772e6576656e74732e7265616468707572706f736573816573657276656a636f6e74726f6c6c657269736f7572636566756c6677616c6c4d731b000001a130d7a00a676465766963657382a66269646871354c77305a6231636b6579582055555555555555555555555555555555555555555555555555555555555555556374756ef464726f6c65656f776e65726565706f6368016673636f706573856d6674772e6c6976652e72656164706674772e686973746f72792e726561646d6674772e706c616e2e726561646f6674772e6865616c74682e726561646f6674772e6576656e74732e72656164a6626964685a66303978513261636b6579582066666666666666666666666666666666666666666666666666666666666666666374756ef464726f6c65667669657765726565706f6368026673636f706573816d6674772e6c6976652e726561646770656e64696e678068757074696d654d7319ea6a69726f7374657253657104617466726f7374657263736967584026de4f6b491496569361fce489167aa3a445c91235689a79f5499f5a65d4ed5e35ecd601251013d345f8171904bd5b508e0a05d543dc25f2335b2d7a743ad916" + "body": "aa637365710264626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474746c531a00015180656772616e74a462696450444444444444444444444444444444446673636f706573826d6674772e6c6976652e726561646f6674772e6576656e74732e7265616468707572706f736573816573657276656a636f6e74726f6c6c657269736f7572636566756c656e6f697365582077777777777777777777777777777777777777777777777777777777777777776677616c6c4d731b000001a130d7a00a676465766963657382a66269646871354c77305a6231636b6579582055555555555555555555555555555555555555555555555555555555555555556374756ef464726f6c65656f776e65726565706f6368016673636f706573856d6674772e6c6976652e72656164706674772e686973746f72792e726561646d6674772e706c616e2e726561646f6674772e6865616c74682e726561646f6674772e6576656e74732e72656164a6626964685a66303978513261636b6579582066666666666666666666666666666666666666666666666666666666666666666374756ef464726f6c65667669657765726565706f6368026673636f706573816d6674772e6c6976652e726561646770656e64696e678068757074696d654d7319ea6a69726f7374657253657104", + "signedInputSha256": "f47b850d31bfffb3f6352ab20c3ba7788cc95f0df22a38f1b8b44184a383399e", + "sig": "96a9ec9cde7b6b2b62bf55ed8d74aeaba0a8d0ded9e5ec32d1f868f758051c146e323ee9aca2c7a009b78190cde63c4b9c882ec29843f07fd15608ed9cf4dd11", + "envelope": "a361625901edaa637365710264626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474746c531a00015180656772616e74a462696450444444444444444444444444444444446673636f706573826d6674772e6c6976652e726561646f6674772e6576656e74732e7265616468707572706f736573816573657276656a636f6e74726f6c6c657269736f7572636566756c656e6f697365582077777777777777777777777777777777777777777777777777777777777777776677616c6c4d731b000001a130d7a00a676465766963657382a66269646871354c77305a6231636b6579582055555555555555555555555555555555555555555555555555555555555555556374756ef464726f6c65656f776e65726565706f6368016673636f706573856d6674772e6c6976652e72656164706674772e686973746f72792e726561646d6674772e706c616e2e726561646f6674772e6865616c74682e726561646f6674772e6576656e74732e72656164a6626964685a66303978513261636b6579582066666666666666666666666666666666666666666666666666666666666666666374756ef464726f6c65667669657765726565706f6368026673636f706573816d6674772e6c6976652e726561646770656e64696e678068757074696d654d7319ea6a69726f7374657253657104617466726f7374657263736967584096a9ec9cde7b6b2b62bf55ed8d74aeaba0a8d0ded9e5ec32d1f868f758051c146e323ee9aca2c7a009b78190cde63c4b9c882ec29843f07fd15608ed9cf4dd11", + "frame": "010100000242a361625901edaa637365710264626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474746c531a00015180656772616e74a462696450444444444444444444444444444444446673636f706573826d6674772e6c6976652e726561646f6674772e6576656e74732e7265616468707572706f736573816573657276656a636f6e74726f6c6c657269736f7572636566756c656e6f697365582077777777777777777777777777777777777777777777777777777777777777776677616c6c4d731b000001a130d7a00a676465766963657382a66269646871354c77305a6231636b6579582055555555555555555555555555555555555555555555555555555555555555556374756ef464726f6c65656f776e65726565706f6368016673636f706573856d6674772e6c6976652e72656164706674772e686973746f72792e726561646d6674772e706c616e2e726561646f6674772e6865616c74682e726561646f6674772e6576656e74732e72656164a6626964685a66303978513261636b6579582066666666666666666666666666666666666666666666666666666666666666666374756ef464726f6c65667669657765726565706f6368026673636f706573816d6674772e6c6976652e726561646770656e64696e678068757074696d654d7319ea6a69726f7374657253657104617466726f7374657263736967584096a9ec9cde7b6b2b62bf55ed8d74aeaba0a8d0ded9e5ec32d1f868f758051c146e323ee9aca2c7a009b78190cde63c4b9c882ec29843f07fd15608ed9cf4dd11" }, { "name": "live full", @@ -196,21 +206,21 @@ "name": "roster tunnel only", "signer": "box", "t": "roster", - "body": "a9637365710c64626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474746c531a00015180656772616e74a4626964406673636f7065738068707572706f736573806a636f6e74726f6c6c657269736f7572636566756c6677616c6c4d731b000001a130d7d6b0676465766963657382a66269646871354c77305a6231636b6579582055555555555555555555555555555555555555555555555555555555555555556374756ef564726f6c65656f776e65726565706f6368016673636f70657380a6626964685a66303978513261636b6579582066666666666666666666666666666666666666666666666666666666666666666374756ef564726f6c65667669657765726565706f6368026673636f706573806770656e64696e6781a264686173685820b4e707c0ac2241df0e0325b42b951b3c71bbd5099bc3c6f2894a48ee23fa35636474746c5319025868757074696d654d731a0001211069726f7374657253657105", - "signedInputSha256": "30ae9e4de78ae0f21421f8a7b172759f3b244da825c0072f2781cb49a73a907c", - "sig": "f9b4b3e144492cf784a07fce0272fe0252d3d71164645b092ebc086a4eab2f0f4e8c11e20aec3567e454a5641a06a97166eaf20ccdd8a022f1eaaa2a18a52e8b", - "envelope": "a36162590168a9637365710c64626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474746c531a00015180656772616e74a4626964406673636f7065738068707572706f736573806a636f6e74726f6c6c657269736f7572636566756c6677616c6c4d731b000001a130d7d6b0676465766963657382a66269646871354c77305a6231636b6579582055555555555555555555555555555555555555555555555555555555555555556374756ef564726f6c65656f776e65726565706f6368016673636f70657380a6626964685a66303978513261636b6579582066666666666666666666666666666666666666666666666666666666666666666374756ef564726f6c65667669657765726565706f6368026673636f706573806770656e64696e6781a264686173685820b4e707c0ac2241df0e0325b42b951b3c71bbd5099bc3c6f2894a48ee23fa35636474746c5319025868757074696d654d731a0001211069726f7374657253657105617466726f73746572637369675840f9b4b3e144492cf784a07fce0272fe0252d3d71164645b092ebc086a4eab2f0f4e8c11e20aec3567e454a5641a06a97166eaf20ccdd8a022f1eaaa2a18a52e8b", - "frame": "0101000001bda36162590168a9637365710c64626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474746c531a00015180656772616e74a4626964406673636f7065738068707572706f736573806a636f6e74726f6c6c657269736f7572636566756c6677616c6c4d731b000001a130d7d6b0676465766963657382a66269646871354c77305a6231636b6579582055555555555555555555555555555555555555555555555555555555555555556374756ef564726f6c65656f776e65726565706f6368016673636f70657380a6626964685a66303978513261636b6579582066666666666666666666666666666666666666666666666666666666666666666374756ef564726f6c65667669657765726565706f6368026673636f706573806770656e64696e6781a264686173685820b4e707c0ac2241df0e0325b42b951b3c71bbd5099bc3c6f2894a48ee23fa35636474746c5319025868757074696d654d731a0001211069726f7374657253657105617466726f73746572637369675840f9b4b3e144492cf784a07fce0272fe0252d3d71164645b092ebc086a4eab2f0f4e8c11e20aec3567e454a5641a06a97166eaf20ccdd8a022f1eaaa2a18a52e8b" + "body": "aa637365710c64626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474746c531a00015180656772616e74a4626964406673636f7065738068707572706f736573806a636f6e74726f6c6c657269736f7572636566756c656e6f697365582077777777777777777777777777777777777777777777777777777777777777776677616c6c4d731b000001a130d7d6b0676465766963657382a66269646871354c77305a6231636b6579582055555555555555555555555555555555555555555555555555555555555555556374756ef564726f6c65656f776e65726565706f6368016673636f70657380a6626964685a66303978513261636b6579582066666666666666666666666666666666666666666666666666666666666666666374756ef564726f6c65667669657765726565706f6368026673636f706573806770656e64696e6781a264686173685820b4e707c0ac2241df0e0325b42b951b3c71bbd5099bc3c6f2894a48ee23fa35636474746c5319025868757074696d654d731a0001211069726f7374657253657105", + "signedInputSha256": "03e4ceaeb5d8abcbd48e0d4a30d53d4145df97c2986d8029e71b1823effd4d3c", + "sig": "729ecdf8ab1a41194cc3da99bf86d5e3593f9bb9e417e70512905e0a6c664f2bfa390316d794493370c9beb8da904e8a01f11af360503b1705cf463834502ac7", + "envelope": "a36162590190aa637365710c64626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474746c531a00015180656772616e74a4626964406673636f7065738068707572706f736573806a636f6e74726f6c6c657269736f7572636566756c656e6f697365582077777777777777777777777777777777777777777777777777777777777777776677616c6c4d731b000001a130d7d6b0676465766963657382a66269646871354c77305a6231636b6579582055555555555555555555555555555555555555555555555555555555555555556374756ef564726f6c65656f776e65726565706f6368016673636f70657380a6626964685a66303978513261636b6579582066666666666666666666666666666666666666666666666666666666666666666374756ef564726f6c65667669657765726565706f6368026673636f706573806770656e64696e6781a264686173685820b4e707c0ac2241df0e0325b42b951b3c71bbd5099bc3c6f2894a48ee23fa35636474746c5319025868757074696d654d731a0001211069726f7374657253657105617466726f73746572637369675840729ecdf8ab1a41194cc3da99bf86d5e3593f9bb9e417e70512905e0a6c664f2bfa390316d794493370c9beb8da904e8a01f11af360503b1705cf463834502ac7", + "frame": "0101000001e5a36162590190aa637365710c64626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474746c531a00015180656772616e74a4626964406673636f7065738068707572706f736573806a636f6e74726f6c6c657269736f7572636566756c656e6f697365582077777777777777777777777777777777777777777777777777777777777777776677616c6c4d731b000001a130d7d6b0676465766963657382a66269646871354c77305a6231636b6579582055555555555555555555555555555555555555555555555555555555555555556374756ef564726f6c65656f776e65726565706f6368016673636f70657380a6626964685a66303978513261636b6579582066666666666666666666666666666666666666666666666666666666666666666374756ef564726f6c65667669657765726565706f6368026673636f706573806770656e64696e6781a264686173685820b4e707c0ac2241df0e0325b42b951b3c71bbd5099bc3c6f2894a48ee23fa35636474746c5319025868757074696d654d731a0001211069726f7374657253657105617466726f73746572637369675840729ecdf8ab1a41194cc3da99bf86d5e3593f9bb9e417e70512905e0a6c664f2bfa390316d794493370c9beb8da904e8a01f11af360503b1705cf463834502ac7" }, { "name": "roster revoking", "signer": "box", "t": "roster", - "body": "a9637365710d64626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474746c531a00015180656772616e74a462696450444444444444444444444444444444446673636f7065738068707572706f736573806a636f6e74726f6c6c657269736f7572636566756c6677616c6c4d731b000001a130d7da98676465766963657381a66269646871354c77305a6231636b6579582055555555555555555555555555555555555555555555555555555555555555556374756ef564726f6c65656f776e65726565706f6368016673636f706573806770656e64696e678068757074696d654d731a000124f869726f7374657253657106", - "signedInputSha256": "d8775b10218f6c69d9b7c9064d1cea8713058eaa9d91aeeba2e2f29419b7f9cc", - "sig": "4332ec4bf6f166175f50eecea08ecbc981d762c10b37f9e676d7e0a763b5d73e58533683fd6b566cf0a33feff903af40d4bf70bedc5245b2696fa85a3191186f", - "envelope": "a3616258f5a9637365710d64626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474746c531a00015180656772616e74a462696450444444444444444444444444444444446673636f7065738068707572706f736573806a636f6e74726f6c6c657269736f7572636566756c6677616c6c4d731b000001a130d7da98676465766963657381a66269646871354c77305a6231636b6579582055555555555555555555555555555555555555555555555555555555555555556374756ef564726f6c65656f776e65726565706f6368016673636f706573806770656e64696e678068757074696d654d731a000124f869726f7374657253657106617466726f737465726373696758404332ec4bf6f166175f50eecea08ecbc981d762c10b37f9e676d7e0a763b5d73e58533683fd6b566cf0a33feff903af40d4bf70bedc5245b2696fa85a3191186f", - "frame": "010100000149a3616258f5a9637365710d64626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474746c531a00015180656772616e74a462696450444444444444444444444444444444446673636f7065738068707572706f736573806a636f6e74726f6c6c657269736f7572636566756c6677616c6c4d731b000001a130d7da98676465766963657381a66269646871354c77305a6231636b6579582055555555555555555555555555555555555555555555555555555555555555556374756ef564726f6c65656f776e65726565706f6368016673636f706573806770656e64696e678068757074696d654d731a000124f869726f7374657253657106617466726f737465726373696758404332ec4bf6f166175f50eecea08ecbc981d762c10b37f9e676d7e0a763b5d73e58533683fd6b566cf0a33feff903af40d4bf70bedc5245b2696fa85a3191186f" + "body": "aa637365710d64626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474746c531a00015180656772616e74a462696450444444444444444444444444444444446673636f7065738068707572706f736573806a636f6e74726f6c6c657269736f7572636566756c656e6f697365582077777777777777777777777777777777777777777777777777777777777777776677616c6c4d731b000001a130d7da98676465766963657381a66269646871354c77305a6231636b6579582055555555555555555555555555555555555555555555555555555555555555556374756ef564726f6c65656f776e65726565706f6368016673636f706573806770656e64696e678068757074696d654d731a000124f869726f7374657253657106", + "signedInputSha256": "322a030b3148bdb71722a8b7ddc7e54d4feff792bb06467125ea6a81d98e387c", + "sig": "c96e88f759f66bb580111e4d63cb45bb4b160d9cd17e211a394df3901b07c2e5317aab96aad398b1a1aca1827a68ba3fe7632ba3b12e886a9968d2c617b70d3c", + "envelope": "a3616259011daa637365710d64626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474746c531a00015180656772616e74a462696450444444444444444444444444444444446673636f7065738068707572706f736573806a636f6e74726f6c6c657269736f7572636566756c656e6f697365582077777777777777777777777777777777777777777777777777777777777777776677616c6c4d731b000001a130d7da98676465766963657381a66269646871354c77305a6231636b6579582055555555555555555555555555555555555555555555555555555555555555556374756ef564726f6c65656f776e65726565706f6368016673636f706573806770656e64696e678068757074696d654d731a000124f869726f7374657253657106617466726f73746572637369675840c96e88f759f66bb580111e4d63cb45bb4b160d9cd17e211a394df3901b07c2e5317aab96aad398b1a1aca1827a68ba3fe7632ba3b12e886a9968d2c617b70d3c", + "frame": "010100000172a3616259011daa637365710d64626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474746c531a00015180656772616e74a462696450444444444444444444444444444444446673636f7065738068707572706f736573806a636f6e74726f6c6c657269736f7572636566756c656e6f697365582077777777777777777777777777777777777777777777777777777777777777776677616c6c4d731b000001a130d7da98676465766963657381a66269646871354c77305a6231636b6579582055555555555555555555555555555555555555555555555555555555555555556374756ef564726f6c65656f776e65726565706f6368016673636f706573806770656e64696e678068757074696d654d731a000124f869726f7374657253657106617466726f73746572637369675840c96e88f759f66bb580111e4d63cb45bb4b160d9cd17e211a394df3901b07c2e5317aab96aad398b1a1aca1827a68ba3fe7632ba3b12e886a9968d2c617b70d3c" }, { "name": "purged copy", @@ -251,6 +261,56 @@ "sig": "", "envelope": "a3616256a3616440636465766871354c77305a6231637369640761746374756e6373696740", "frame": "010100000025a3616256a3616440636465766871354c77305a6231637369640761746374756e6373696740" + }, + { + "name": "roster with an agent", + "signer": "box", + "t": "roster", + "body": "aa637365710e64626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474746c531a00015180656772616e74a4626964406673636f7065738068707572706f736573806a636f6e74726f6c6c657269736f7572636566756c656e6f697365582077777777777777777777777777777777777777777777777777777777777777776677616c6c4d731b000001a130d7de80676465766963657382a66269646871354c77305a6231636b6579582055555555555555555555555555555555555555555555555555555555555555556374756ef564726f6c65656f776e65726565706f6368016673636f70657380a7626964686f4b47696f36536c636b65795820a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a06374756ef464726f6c6567737570706f7274656167656e74a5636f7073816e6c6f6164706f696e742e686f6c646463617073a3646d617857191ce865686f6c6453190384667065724d696e066673636f70657383716674772e64697370617463682e686f6c646d6674772e6c6976652e726561646d6674772e6c6f67732e72656164697072696e636970616ca366636c69656e7471536f7572636566756c20737570706f7274667469636b6574674654572d31323368636c69656e744964783268747470733a2f2f636c6f75642e6674772e656e657267792f636c69656e74732f736f7572636566756c2d737570706f72746b6578706972657341744d731b000001a17720ca006565706f6368016673636f706573806770656e64696e678068757074696d654d731a000128e069726f7374657253657107", + "signedInputSha256": "24ade7fd185b91e8c5c954c34805ebed51cbbf8d6e36097c7a903ebbfe5db7aa", + "sig": "f2f15bfcafd92e077fb41218fb5eca3735d45484cab4074541f0908b8e6a1107a31f8bf3caf605405bd7cbd02963ff4466a40de915aa9d9af96166e5fc5965a7", + "envelope": "a36162590256aa637365710e64626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474746c531a00015180656772616e74a4626964406673636f7065738068707572706f736573806a636f6e74726f6c6c657269736f7572636566756c656e6f697365582077777777777777777777777777777777777777777777777777777777777777776677616c6c4d731b000001a130d7de80676465766963657382a66269646871354c77305a6231636b6579582055555555555555555555555555555555555555555555555555555555555555556374756ef564726f6c65656f776e65726565706f6368016673636f70657380a7626964686f4b47696f36536c636b65795820a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a06374756ef464726f6c6567737570706f7274656167656e74a5636f7073816e6c6f6164706f696e742e686f6c646463617073a3646d617857191ce865686f6c6453190384667065724d696e066673636f70657383716674772e64697370617463682e686f6c646d6674772e6c6976652e726561646d6674772e6c6f67732e72656164697072696e636970616ca366636c69656e7471536f7572636566756c20737570706f7274667469636b6574674654572d31323368636c69656e744964783268747470733a2f2f636c6f75642e6674772e656e657267792f636c69656e74732f736f7572636566756c2d737570706f72746b6578706972657341744d731b000001a17720ca006565706f6368016673636f706573806770656e64696e678068757074696d654d731a000128e069726f7374657253657107617466726f73746572637369675840f2f15bfcafd92e077fb41218fb5eca3735d45484cab4074541f0908b8e6a1107a31f8bf3caf605405bd7cbd02963ff4466a40de915aa9d9af96166e5fc5965a7", + "frame": "0101000002aba36162590256aa637365710e64626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474746c531a00015180656772616e74a4626964406673636f7065738068707572706f736573806a636f6e74726f6c6c657269736f7572636566756c656e6f697365582077777777777777777777777777777777777777777777777777777777777777776677616c6c4d731b000001a130d7de80676465766963657382a66269646871354c77305a6231636b6579582055555555555555555555555555555555555555555555555555555555555555556374756ef564726f6c65656f776e65726565706f6368016673636f70657380a7626964686f4b47696f36536c636b65795820a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a06374756ef464726f6c6567737570706f7274656167656e74a5636f7073816e6c6f6164706f696e742e686f6c646463617073a3646d617857191ce865686f6c6453190384667065724d696e066673636f70657383716674772e64697370617463682e686f6c646d6674772e6c6976652e726561646d6674772e6c6f67732e72656164697072696e636970616ca366636c69656e7471536f7572636566756c20737570706f7274667469636b6574674654572d31323368636c69656e744964783268747470733a2f2f636c6f75642e6674772e656e657267792f636c69656e74732f736f7572636566756c2d737570706f72746b6578706972657341744d731b000001a17720ca006565706f6368016673636f706573806770656e64696e678068757074696d654d731a000128e069726f7374657253657107617466726f73746572637369675840f2f15bfcafd92e077fb41218fb5eca3735d45484cab4074541f0908b8e6a1107a31f8bf3caf605405bd7cbd02963ff4466a40de915aa9d9af96166e5fc5965a7" + }, + { + "name": "lp car asking", + "signer": "box", + "t": "lp", + "body": "a7637365710f64626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06677616c6c4d731b000001a130d7de8068666565646261636b8068757074696d654d731a000128e06a636f6e74726f6c52657619032d6a6c6f6164706f696e747381b8186269646b6a33485a4f524e43574873637372636b4455595757516d6e2d586765626f6f7374f46663686f6f7365826b464e4b69626974666555306b446d34516733447a336263666d616e75616cf6666e6578745768f666706f776572570067706c7567676564f56773657373696f6e703764316630633261396233653466353067736f6341676553f6696e657874456e644d73f669726561647942794d73f669726563757272696e67f46973657373696f6e57680069736f63536f7572636567756e6b6e6f776e6a636f6d6d616e64656457f66b6e65787453746172744d73f66b706c616e50656e64696e67f46b736f635065726d696c6c65f66b737572706c75734f6e6c79f56c677269644465666572726564f46e7461726765745065726d696c6c65f66f6361724964656e74696669656442796661736b696e676f636f6d6d616e646564526561736f6e7070765f737572706c75735f7061757365", + "signedInputSha256": "895b15e9dd2992642f09e346998147e6c89162a1c6a84e2c1fe9aba9349f9a82", + "sig": "fa1b746e25aa7f02deb3fbc543e233315f26f31e0325f4921fb0d5a9ef01e249c129184c88d23396bcc1dc3193bdf78fa2f5805760dd9bea3d05137343bbe420", + "envelope": "a361625901bda7637365710f64626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06677616c6c4d731b000001a130d7de8068666565646261636b8068757074696d654d731a000128e06a636f6e74726f6c52657619032d6a6c6f6164706f696e747381b8186269646b6a33485a4f524e43574873637372636b4455595757516d6e2d586765626f6f7374f46663686f6f7365826b464e4b69626974666555306b446d34516733447a336263666d616e75616cf6666e6578745768f666706f776572570067706c7567676564f56773657373696f6e703764316630633261396233653466353067736f6341676553f6696e657874456e644d73f669726561647942794d73f669726563757272696e67f46973657373696f6e57680069736f63536f7572636567756e6b6e6f776e6a636f6d6d616e64656457f66b6e65787453746172744d73f66b706c616e50656e64696e67f46b736f635065726d696c6c65f66b737572706c75734f6e6c79f56c677269644465666572726564f46e7461726765745065726d696c6c65f66f6361724964656e74696669656442796661736b696e676f636f6d6d616e646564526561736f6e7070765f737572706c75735f70617573656174626c70637369675840fa1b746e25aa7f02deb3fbc543e233315f26f31e0325f4921fb0d5a9ef01e249c129184c88d23396bcc1dc3193bdf78fa2f5805760dd9bea3d05137343bbe420", + "frame": "01010000020ea361625901bda7637365710f64626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06677616c6c4d731b000001a130d7de8068666565646261636b8068757074696d654d731a000128e06a636f6e74726f6c52657619032d6a6c6f6164706f696e747381b8186269646b6a33485a4f524e43574873637372636b4455595757516d6e2d586765626f6f7374f46663686f6f7365826b464e4b69626974666555306b446d34516733447a336263666d616e75616cf6666e6578745768f666706f776572570067706c7567676564f56773657373696f6e703764316630633261396233653466353067736f6341676553f6696e657874456e644d73f669726561647942794d73f669726563757272696e67f46973657373696f6e57680069736f63536f7572636567756e6b6e6f776e6a636f6d6d616e64656457f66b6e65787453746172744d73f66b706c616e50656e64696e67f46b736f635065726d696c6c65f66b737572706c75734f6e6c79f56c677269644465666572726564f46e7461726765745065726d696c6c65f66f6361724964656e74696669656442796661736b696e676f636f6d6d616e646564526561736f6e7070765f737572706c75735f70617573656174626c70637369675840fa1b746e25aa7f02deb3fbc543e233315f26f31e0325f4921fb0d5a9ef01e249c129184c88d23396bcc1dc3193bdf78fa2f5805760dd9bea3d05137343bbe420" + }, + { + "name": "car", + "signer": "box", + "t": "car", + "body": "a5637365711064626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0646361727382a96269646b464e4b696269746665553064676f616ca46464617973187f6a726561647942794d696e1901a46d66696e69736841744c696d6974f46e7461726765745065726d696c6c6519032067736f634167655319021c69736f63536f757263656776656869636c656b736f63457374696d617465f46b736f635065726d696c6c65188c6b737572706c75734f6e6c79f46d656e6572677953696e6365576800736368617267654c696d69745065726d696c6c65190320a96269646b446d34516733447a33626364676f616cf667736f634167655319778869736f63536f757263656776656869636c656b736f63457374696d617465f56b736f635065726d696c6c6519026c6b737572706c75734f6e6c79f56d656e6572677953696e6365576800736368617267654c696d69745065726d696c6c65f66677616c6c4d731b000001a130d7de8a68757074696d654d731a000128ea", + "signedInputSha256": "855da2d9302e228ac2acc0c4d900d6a3074675c8b5fb1350c2942bce438b9fc4", + "sig": "7ee438570fc0f1072201594add376235e687cb87168f882c88e94e1e087b595be184505d295ecc901cb8c8a424a8fa3df0b65b28b1c2352a7ecfc4cb0b53ae01", + "envelope": "a36162590177a5637365711064626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0646361727382a96269646b464e4b696269746665553064676f616ca46464617973187f6a726561647942794d696e1901a46d66696e69736841744c696d6974f46e7461726765745065726d696c6c6519032067736f634167655319021c69736f63536f757263656776656869636c656b736f63457374696d617465f46b736f635065726d696c6c65188c6b737572706c75734f6e6c79f46d656e6572677953696e6365576800736368617267654c696d69745065726d696c6c65190320a96269646b446d34516733447a33626364676f616cf667736f634167655319778869736f63536f757263656776656869636c656b736f63457374696d617465f56b736f635065726d696c6c6519026c6b737572706c75734f6e6c79f56d656e6572677953696e6365576800736368617267654c696d69745065726d696c6c65f66677616c6c4d731b000001a130d7de8a68757074696d654d731a000128ea6174636361726373696758407ee438570fc0f1072201594add376235e687cb87168f882c88e94e1e087b595be184505d295ecc901cb8c8a424a8fa3df0b65b28b1c2352a7ecfc4cb0b53ae01", + "frame": "0101000001c9a36162590177a5637365711064626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0646361727382a96269646b464e4b696269746665553064676f616ca46464617973187f6a726561647942794d696e1901a46d66696e69736841744c696d6974f46e7461726765745065726d696c6c6519032067736f634167655319021c69736f63536f757263656776656869636c656b736f63457374696d617465f46b736f635065726d696c6c65188c6b737572706c75734f6e6c79f46d656e6572677953696e6365576800736368617267654c696d69745065726d696c6c65190320a96269646b446d34516733447a33626364676f616cf667736f634167655319778869736f63536f757263656776656869636c656b736f63457374696d617465f56b736f635065726d696c6c6519026c6b737572706c75734f6e6c79f56d656e6572677953696e6365576800736368617267654c696d69745065726d696c6c65f66677616c6c4d731b000001a130d7de8a68757074696d654d731a000128ea6174636361726373696758407ee438570fc0f1072201594add376235e687cb87168f882c88e94e1e087b595be184505d295ecc901cb8c8a424a8fa3df0b65b28b1c2352a7ecfc4cb0b53ae01" + }, + { + "name": "event car which", + "signer": "box", + "t": "event", + "body": "ac626c706b6a33485a4f524e435748736274731b000001a130d7de306373657111637372636b4455595757516d6e2d58676461726773a16773657373696f6e703764316630633261396233653466353064626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06463617273826b464e4b69626974666555306b446d34516733447a336263646b696e64696361722e77686963686677616c6c4d731b000001a130d7de94676576656e7449646c643268705932686a5958497868736576657269747964696e666f68757074696d654d731a000128f4", + "signedInputSha256": "e576ea700f4abd28075bef81b88d28414de2979912ea333d3503ec45a31cb27d", + "sig": "70a21cc4e288f019d1ffeb59456f8555b07c9f450f6182e6d2cddb5dcca37c31a4a3465c5080c429d6f01063c571edf44e4012dd0198b6bdc1fdfcc6ca3e4b88", + "envelope": "a3616258d4ac626c706b6a33485a4f524e435748736274731b000001a130d7de306373657111637372636b4455595757516d6e2d58676461726773a16773657373696f6e703764316630633261396233653466353064626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06463617273826b464e4b69626974666555306b446d34516733447a336263646b696e64696361722e77686963686677616c6c4d731b000001a130d7de94676576656e7449646c643268705932686a5958497868736576657269747964696e666f68757074696d654d731a000128f46174656576656e7463736967584070a21cc4e288f019d1ffeb59456f8555b07c9f450f6182e6d2cddb5dcca37c31a4a3465c5080c429d6f01063c571edf44e4012dd0198b6bdc1fdfcc6ca3e4b88", + "frame": "010100000127a3616258d4ac626c706b6a33485a4f524e435748736274731b000001a130d7de306373657111637372636b4455595757516d6e2d58676461726773a16773657373696f6e703764316630633261396233653466353064626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06463617273826b464e4b69626974666555306b446d34516733447a336263646b696e64696361722e77686963686677616c6c4d731b000001a130d7de94676576656e7449646c643268705932686a5958497868736576657269747964696e666f68757074696d654d731a000128f46174656576656e7463736967584070a21cc4e288f019d1ffeb59456f8555b07c9f450f6182e6d2cddb5dcca37c31a4a3465c5080c429d6f01063c571edf44e4012dd0198b6bdc1fdfcc6ca3e4b88" + }, + { + "name": "lp car tapped", + "signer": "box", + "t": "lp", + "body": "a7637365711264626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06677616c6c4d731b000001a130d7f20868666565646261636b8068757074696d654d731a00013c686a636f6e74726f6c52657619032e6a6c6f6164706f696e747381b8186269646b6a33485a4f524e43574873637372636b4455595757516d6e2d586765626f6f7374f46563617249646b464e4b6962697466655530666d616e75616cf6666e6578745768f666706f776572570067706c7567676564f56773657373696f6e703764316630633261396233653466353067736f6341676553190221696e657874456e644d73f669726561647942794d731b000001a133a1bc8069726563757272696e67f56973657373696f6e57680069736f63536f757263656776656869636c656a636f6d6d616e64656457f66b6e65787453746172744d73f66b706c616e50656e64696e67f46b736f635065726d696c6c65188c6b737572706c75734f6e6c79f46c677269644465666572726564f46e7461726765745065726d696c6c651903206f6361724964656e7469666965644279637461706f636f6d6d616e646564526561736f6e64706c616e", + "signedInputSha256": "daabad621023f8fb32b67179de6f06ec1ed7b72dd9c83de533aa3b625fa7b439", + "sig": "89b4a6faa2af341cdc4da9ad0247aa2135f40e1aec94554cf1855392d22412cb2191b2a594084e4490faada200f68abbdcdd80b7b0bc3e7fb4ff65e5572f3fcf", + "envelope": "a361625901ada7637365711264626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06677616c6c4d731b000001a130d7f20868666565646261636b8068757074696d654d731a00013c686a636f6e74726f6c52657619032e6a6c6f6164706f696e747381b8186269646b6a33485a4f524e43574873637372636b4455595757516d6e2d586765626f6f7374f46563617249646b464e4b6962697466655530666d616e75616cf6666e6578745768f666706f776572570067706c7567676564f56773657373696f6e703764316630633261396233653466353067736f6341676553190221696e657874456e644d73f669726561647942794d731b000001a133a1bc8069726563757272696e67f56973657373696f6e57680069736f63536f757263656776656869636c656a636f6d6d616e64656457f66b6e65787453746172744d73f66b706c616e50656e64696e67f46b736f635065726d696c6c65188c6b737572706c75734f6e6c79f46c677269644465666572726564f46e7461726765745065726d696c6c651903206f6361724964656e7469666965644279637461706f636f6d6d616e646564526561736f6e64706c616e6174626c7063736967584089b4a6faa2af341cdc4da9ad0247aa2135f40e1aec94554cf1855392d22412cb2191b2a594084e4490faada200f68abbdcdd80b7b0bc3e7fb4ff65e5572f3fcf", + "frame": "0101000001fea361625901ada7637365711264626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06677616c6c4d731b000001a130d7f20868666565646261636b8068757074696d654d731a00013c686a636f6e74726f6c52657619032e6a6c6f6164706f696e747381b8186269646b6a33485a4f524e43574873637372636b4455595757516d6e2d586765626f6f7374f46563617249646b464e4b6962697466655530666d616e75616cf6666e6578745768f666706f776572570067706c7567676564f56773657373696f6e703764316630633261396233653466353067736f6341676553190221696e657874456e644d73f669726561647942794d731b000001a133a1bc8069726563757272696e67f56973657373696f6e57680069736f63536f757263656776656869636c656a636f6d6d616e64656457f66b6e65787453746172744d73f66b706c616e50656e64696e67f46b736f635065726d696c6c65188c6b737572706c75734f6e6c79f46c677269644465666572726564f46e7461726765745065726d696c6c651903206f6361724964656e7469666965644279637461706f636f6d6d616e646564526561736f6e64706c616e6174626c7063736967584089b4a6faa2af341cdc4da9ad0247aa2135f40e1aec94554cf1855392d22412cb2191b2a594084e4490faada200f68abbdcdd80b7b0bc3e7fb4ff65e5572f3fcf" } ] } diff --git a/appleApp/FTWKit/Tests/FTWSharedTests/Resources/push-vectors.json b/appleApp/FTWKit/Tests/FTWSharedTests/Resources/push-vectors.json index e6b4ac6..55b8145 100644 --- a/appleApp/FTWKit/Tests/FTWSharedTests/Resources/push-vectors.json +++ b/appleApp/FTWKit/Tests/FTWSharedTests/Resources/push-vectors.json @@ -252,6 +252,125 @@ "verdict": "generic", "home": "fjzyr67buzpyncqmjliwgezvde" } + }, + { + "name": "notice: agent added", + "payload": "{\"aps\":{\"alert\":{\"title-loc-key\":\"push.title\",\"loc-key\":\"push.generic\"},\"mutable-content\":1,\"sound\":\"default\"},\"ftw\":{\"v\":1,\"home\":\"fjzyr67buzpyncqmjliwgezvde\",\"event\":\"o2FiWEulY3NlcRFkYm9vdFCwsLCwsLCwsLCwsLCwsLCwZGtpbmRrYWdlbnQuYWRkZWRmd2FsbE1zGwAAAaEw19qpaHVwdGltZU1zGgABJQlhdGZub3RpY2Vjc2lnWEAbAyFDum/1cvsa/UZ6W4Ycr9Vi0fi4Za3eybeuZVPnU2TKhbb8isxPSpwOBLfLQVgHhoY+bkI6oQUgQQ2R9+3v\"}}", + "sig": "1b032143ba6ff572fb1afd467a5b861cafd562d1f8b865addec9b7ae6553e75364ca85b6fc8acc4f4a9c0e04b7cb41580786863e6e423aa10520410d91f7edef", + "nowMs": 1791820816217, + "want": { + "verdict": "event", + "home": "fjzyr67buzpyncqmjliwgezvde", + "kind": "agent.added", + "boot": "b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0", + "seq": 17, + "atMs": 1791820815017 + } + }, + { + "name": "notice: agent added, with the client's name", + "payload": "{\"aps\":{\"alert\":{\"title-loc-key\":\"push.title\",\"loc-key\":\"push.generic\"},\"mutable-content\":1,\"sound\":\"default\"},\"ftw\":{\"v\":1,\"home\":\"fjzyr67buzpyncqmjliwgezvde\",\"event\":\"o2FiWFmmY3NlcRJkYm9vdFCwsLCwsLCwsLCwsLCwsLCwZGtpbmRrYWdlbnQuYWRkZWRmY2xpZW50ZkNsYXVkZWZ3YWxsTXMbAAABoTDX2qpodXB0aW1lTXMaAAElCmF0Zm5vdGljZWNzaWdYQE4+GuWl/LzZJLZfra214MRS7FcNFraI3uxiFd8KifedDll34e209yxx4ShX4EnuMw3pZ/saTXZbanwGvvq7OwA=\"}}", + "sig": "4e3e1ae5a5fcbcd924b65fadadb5e0c452ec570d16b688deec6215df0a89f79d0e5977e1edb4f72c71e12857e049ee330de967fb1a4d765b6a7c06befabb3b00", + "nowMs": 1791820816218, + "want": { + "verdict": "event", + "home": "fjzyr67buzpyncqmjliwgezvde", + "kind": "agent.added", + "args": { + "client": "Claude" + }, + "boot": "b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0", + "seq": 18, + "atMs": 1791820815018 + } + }, + { + "name": "notice: owner added", + "payload": "{\"aps\":{\"alert\":{\"title-loc-key\":\"push.title\",\"loc-key\":\"push.generic\"},\"mutable-content\":1,\"sound\":\"default\"},\"ftw\":{\"v\":1,\"home\":\"fjzyr67buzpyncqmjliwgezvde\",\"event\":\"o2FiWEulY3NlcRNkYm9vdFCwsLCwsLCwsLCwsLCwsLCwZGtpbmRrb3duZXIuYWRkZWRmd2FsbE1zGwAAAaEw19qraHVwdGltZU1zGgABJQthdGZub3RpY2Vjc2lnWEA1UrmPsIrWKWG9fjxWr25VeJlNItvdxLisM+pnQm6hiBlJP04wSC+YWHHEc7ONCX7RaG9AuXgy9YYIC861ofPC\"}}", + "sig": "3552b98fb08ad62961bd7e3c56af6e5578994d22dbddc4b8ac33ea67426ea18819493f4e30482f985871c473b38d097ed1686f40b97832f586080bceb5a1f3c2", + "nowMs": 1791820816219, + "want": { + "verdict": "event", + "home": "fjzyr67buzpyncqmjliwgezvde", + "kind": "owner.added", + "boot": "b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0", + "seq": 19, + "atMs": 1791820815019 + } + }, + { + "name": "notice of a kind that is not a notice", + "payload": "{\"aps\":{\"alert\":{\"title-loc-key\":\"push.title\",\"loc-key\":\"push.generic\"},\"mutable-content\":1,\"sound\":\"default\"},\"ftw\":{\"v\":1,\"home\":\"fjzyr67buzpyncqmjliwgezvde\",\"event\":\"o2FiWFKlY3NlcRRkYm9vdFCwsLCwsLCwsLCwsLCwsLCwZGtpbmRyY2hhcmdpbmcuY29ubmVjdGVkZndhbGxNcxsAAAGhMNfarGh1cHRpbWVNcxoAASUMYXRmbm90aWNlY3NpZ1hAEgkMM7TKQ7T+9OGA0ghvgzsbClOTmAZDKyjx5+2GbmhLfEnPDyrf8j7+/XJtPUpRAYtG08d0fJwFx4N25Fj7ig==\"}}", + "sig": "12090c33b4ca43b4fef4e180d2086f833b1b0a53939806432b28f1e7ed866e684b7c49cf0f2adff23efefd726d3d4a51018b46d3c7747c9c05c78376e458fb8a", + "nowMs": 1791820816220, + "want": { + "verdict": "generic", + "home": "fjzyr67buzpyncqmjliwgezvde" + } + }, + { + "name": "notice with a client's name on another kind", + "payload": "{\"aps\":{\"alert\":{\"title-loc-key\":\"push.title\",\"loc-key\":\"push.generic\"},\"mutable-content\":1,\"sound\":\"default\"},\"ftw\":{\"v\":1,\"home\":\"fjzyr67buzpyncqmjliwgezvde\",\"event\":\"o2FiWFumY3NlcRVkYm9vdFCwsLCwsLCwsLCwsLCwsLCwZGtpbmRtYWdlbnQucmVxdWVzdGZjbGllbnRmQ2xhdWRlZndhbGxNcxsAAAGhMNfarWh1cHRpbWVNcxoAASUNYXRmbm90aWNlY3NpZ1hAs6NjZVEBVln2Oa0fswegEGi/WY4oZE+ttBlos/7klFaXvtNQ0LhlWjs1WsNg6F5A1SSN5nruJ1Djpl7T/SaG8w==\"}}", + "sig": "b3a3636551015659f639ad1fb307a01068bf598e28644fadb41968b3fee4945697bed350d0b8655a3b355ac360e85e40d5248de67aee2750e3a65ed3fd2686f3", + "nowMs": 1791820816221, + "want": { + "verdict": "generic", + "home": "fjzyr67buzpyncqmjliwgezvde" + } + }, + { + "name": "notice that carries a reading", + "payload": "{\"aps\":{\"alert\":{\"title-loc-key\":\"push.title\",\"loc-key\":\"push.generic\"},\"mutable-content\":1,\"sound\":\"default\"},\"ftw\":{\"v\":1,\"home\":\"fjzyr67buzpyncqmjliwgezvde\",\"event\":\"o2FiWFmmY3NlcRZkYm9vdFCwsLCwsLCwsLCwsLCwsLCwZGtpbmRtYWdlbnQucmVxdWVzdGZmaWVsZHOhARkJYGZ3YWxsTXMbAAABoTDX2q5odXB0aW1lTXMaAAElDmF0Zm5vdGljZWNzaWdYQLAkL9JgkAuamo13qXA+F1jPe7kikT0U7zv65Audx/TaPylfZhJ+A5ozM0YLiSoi8bceFQJeYGVP9Qtl/y1WStM=\"}}", + "sig": "b0242fd260900b9a9a8d77a9703e1758cf7bb922913d14ef3bfae40b9dc7f4da3f295f66127e039a3333460b892a22f1b71e15025e60654ff50b65ff2d564ad3", + "nowMs": 1791820816222, + "want": { + "verdict": "generic", + "home": "fjzyr67buzpyncqmjliwgezvde" + } + }, + { + "name": "a notice kind sent as an event", + "payload": "{\"aps\":{\"alert\":{\"title-loc-key\":\"push.title\",\"loc-key\":\"push.generic\"},\"mutable-content\":1,\"sound\":\"default\"},\"ftw\":{\"v\":1,\"home\":\"fjzyr67buzpyncqmjliwgezvde\",\"event\":\"o2FiWHSoY3NlcRdkYXJnc6BkYm9vdFCwsLCwsLCwsLCwsLCwsLCwZGtpbmRrYWdlbnQuYWRkZWRmd2FsbE1zGwAAAaEw19qvZ2V2ZW50SWRsYm05MGFXTmxaWFl4aHNldmVyaXR5ZGluZm9odXB0aW1lTXMaAAElD2F0ZWV2ZW50Y3NpZ1hAdYhzcPtaXW1REa8z71XlON6LANr0PDuWzx2axo7CkNzZSYNx4OF/juTHPf4LKSjQW+dwwkAeE2vIjFjpLhJjEw==\"}}", + "sig": "75887370fb5a5d6d5111af33ef55e538de8b00daf43c3b96cf1d9ac68ec290dcd9498371e0e17f8ee4c73dfe0b2928d05be770c2401e136bc88c58e92e126313", + "nowMs": 1791820816223, + "want": { + "verdict": "generic", + "home": "fjzyr67buzpyncqmjliwgezvde" + } + }, + { + "name": "notice: twenty hours old is still shown", + "payload": "{\"aps\":{\"alert\":{\"title-loc-key\":\"push.title\",\"loc-key\":\"push.generic\"},\"mutable-content\":1,\"sound\":\"default\"},\"ftw\":{\"v\":1,\"home\":\"fjzyr67buzpyncqmjliwgezvde\",\"event\":\"o2FiWFGlY3NlcRgYZGJvb3RQsLCwsLCwsLCwsLCwsLCwsGRraW5kcGFwcHJvdmFsLmtleS5zZXRmd2FsbE1zGwAAAaEw19qwaHVwdGltZU1zGgABJRBhdGZub3RpY2Vjc2lnWEBslMBAqiznLiaedLlM28EGe661MxcbIVAadoY7xwF5cpHybeQU8pFgluWy6XoaOX7zvtEPOxBLFj0qzgfEaBLE\"}}", + "sig": "6c94c040aa2ce72e269e74b94cdbc1067baeb533171b21501a76863bc701797291f26de414f2916096e5b2e97a1a397ef3bed10f3b104b163d2ace07c46812c4", + "nowMs": 1791892815024, + "want": { + "verdict": "event", + "home": "fjzyr67buzpyncqmjliwgezvde", + "kind": "approval.key.set", + "boot": "b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0", + "seq": 24, + "atMs": 1791820815024 + } + }, + { + "name": "notice: over a day old", + "payload": "{\"aps\":{\"alert\":{\"title-loc-key\":\"push.title\",\"loc-key\":\"push.generic\"},\"mutable-content\":1,\"sound\":\"default\"},\"ftw\":{\"v\":1,\"home\":\"fjzyr67buzpyncqmjliwgezvde\",\"event\":\"o2FiWFGlY3NlcRgYZGJvb3RQsLCwsLCwsLCwsLCwsLCwsGRraW5kcGFwcHJvdmFsLmtleS5zZXRmd2FsbE1zGwAAAaEw19qwaHVwdGltZU1zGgABJRBhdGZub3RpY2Vjc2lnWEDhgeh5sDs7qhAO/vie0bMhUWZJeajyBGqqql6TIH2c2i4nNqnWOFdaTnGigUzmM+Vvw13M9baTp+8wqH6gnRcg\"}}", + "sig": "e181e879b03b3baa100efef89ed1b32151664979a8f2046aaaaa5e93207d9cda2e2736a9d638575a4e71a2814ce633e56fc35dccf5b693a7ef30a87ea09d1720", + "nowMs": 1791907215025, + "want": { + "verdict": "generic", + "home": "fjzyr67buzpyncqmjliwgezvde" + } + }, + { + "name": "notice with a short boot", + "payload": "{\"aps\":{\"alert\":{\"title-loc-key\":\"push.title\",\"loc-key\":\"push.generic\"},\"mutable-content\":1,\"sound\":\"default\"},\"ftw\":{\"v\":1,\"home\":\"fjzyr67buzpyncqmjliwgezvde\",\"event\":\"o2FiWESlY3NlcRgZZGJvb3RIsLCwsLCwsLBka2luZGtvd25lci5hZGRlZGZ3YWxsTXMbAAABoTDX2rFodXB0aW1lTXMaAAElEWF0Zm5vdGljZWNzaWdYQB0GZm45ROf59oQ4Kl/xD8+TQaF3B+mNoBN3uHrYHTvqGTD1378qCy/FI34or24G6uAl7HB4/RN3QQTNMdDU0KI=\"}}", + "sig": "1d06666e3944e7f9f684382a5ff10fcf9341a17707e98da01377b87ad81d3bea1930f5dfbf2a0b2fc5237e28af6e06eae025ec7078fd13774104cd31d0d4d0a2", + "nowMs": 1791820816225, + "want": { + "verdict": "generic", + "home": "fjzyr67buzpyncqmjliwgezvde" + } } ] } diff --git a/protocol/push-catalogue.yaml b/protocol/push-catalogue.yaml index 0c9e8c5..3a56367 100644 --- a/protocol/push-catalogue.yaml +++ b/protocol/push-catalogue.yaml @@ -65,7 +65,7 @@ events: # the release check access to the box. Access from outside the house is # news every owner should hear, as with cloud.granted. title: New access to your box - body: "You gave an assistant or Sourceful access. See who has access in the app." + body: "An assistant or Sourceful was given access to your box. See who has access in the app." - kind: agent.request # To every owner, when an agent asks for a durable change. It waits for # one of them to confirm it, and runs out after twelve hours.