diff --git a/.changeset/price-vat-rate.md b/.changeset/price-vat-rate.md new file mode 100644 index 000000000..a2cf2ee46 --- /dev/null +++ b/.changeset/price-vat-rate.md @@ -0,0 +1,5 @@ +--- +"ftw": patch +--- + +The box's price answer to the app, and FTW Cloud's price tile, now carry the VAT rate the box taxes the total at, as `vatPermille` (250 is 25 %). The rate is the one in force after the default, so a box that never set `vat_percent` says 250. With it the iPhone app can show the spot price with VAT at the box's own rate instead of a guess. diff --git a/docs/protocol-cloud.md b/docs/protocol-cloud.md index bde98a520..5992bfb0f 100644 --- a/docs/protocol-cloud.md +++ b/docs/protocol-cloud.md @@ -564,13 +564,14 @@ unknown `t`. | `slotMs` | uint | Slot length. Where stored slots differ in length, the box repeats a longer slot's price across `slotMs` slots | | `spotMinor` | [int or null] | Market price, minor units per kWh | | `totalMinor` | [int or null] | What the household pays, tariff and VAT included | +| `vatPermille` | int | The VAT rate in `totalMinor`, in permille: 250 is 25 %. 0 means no VAT; an older box leaves it out, and the phone reads that as unknown | `totalMinor` shows the household's tariff, and the privacy notice says so. The phone sees how far the slots reach. The window runs from the start of today to the end of tomorrow. Because it starts at midnight and not now, its hash moves -only when new prices land. The box looks at prices once a minute and sends -`prices` only when a hash of `t0Ms`, `slotMs`, `spotMinor` and `totalMinor` -changes. A gap between stored slots is `null`. +only when new prices land or the VAT rate changes. The box looks at prices once a minute and sends +`prices` only when a hash of `t0Ms`, `slotMs`, `spotMinor`, `totalMinor` and +`vatPermille` changes. A gap between stored slots is `null`. ### `plan`: `ftw.plan.read` diff --git a/go/cmd/ftw/app_link.go b/go/cmd/ftw/app_link.go index 61e3cf1d6..d54bcee49 100644 --- a/go/cmd/ftw/app_link.go +++ b/go/cmd/ftw/app_link.go @@ -405,6 +405,11 @@ const maxPriceSlotMin = 120 func (a *appPrices) Zone() string { return a.svc.Zone } func (a *appPrices) Currency() string { return a.svc.Currency } +// VATPercent is the rate the service's Applier holds, the one the stored +// totals were taxed at, and not the config's own field: the service has +// already turned an unset rate into its default. +func (a *appPrices) VATPercent() float64 { return a.svc.VATPercent() } + func (a *appPrices) Slots(_ context.Context, fromMs, toMs int64) ([]appproto.PricePoint, error) { // The store selects on slot start, inclusive at both ends. Widening the // lower bound by one slot catches the slot in progress at fromMs — the diff --git a/go/cmd/ftw/app_prices_test.go b/go/cmd/ftw/app_prices_test.go index e4a17c12d..344911c85 100644 --- a/go/cmd/ftw/app_prices_test.go +++ b/go/cmd/ftw/app_prices_test.go @@ -6,6 +6,7 @@ import ( "testing" "time" + "github.com/srcfl/ftw/go/internal/config" "github.com/srcfl/ftw/go/internal/prices" "github.com/srcfl/ftw/go/internal/state" ) @@ -148,3 +149,18 @@ func TestTheLookbackCoversTheLongestSlot(t *testing.T) { t.Fatalf("the slot in progress was missed: %+v", got) } } + +// The app and FTW Cloud get the rate the service taxes at, after its default. +// A box that never set vat_percent taxes at 25 % and must say so, not send +// the config's 0 as "no VAT". +func TestAppPricesSendsTheServicesVATRate(t *testing.T) { + svc := prices.FromConfig(&config.Price{Provider: "sourceful", Zone: "SE3"}, nil, nil) + a := &appPrices{svc: svc} + if got := a.VATPercent(); got != 25 { + t.Fatalf("unset VAT went out as %v, want the default 25", got) + } + svc.SetApplier(prices.Applier{VATPercent: 25.5}) + if got := a.VATPercent(); got != 25.5 { + t.Fatalf("after a reload VAT went out as %v, want 25.5", got) + } +} diff --git a/go/cmd/ftw/cloud_link.go b/go/cmd/ftw/cloud_link.go index 7040ce015..be766de4b 100644 --- a/go/cmd/ftw/cloud_link.go +++ b/go/cmd/ftw/cloud_link.go @@ -343,18 +343,20 @@ func (c *cloudSite) Prices() (cloudwire.Prices, bool) { if err != nil || len(slots) == 0 { return cloudwire.Prices{}, false } - return priceColumns(c.prices.Zone(), c.prices.Currency(), slots), true + return priceColumns(c.prices.Zone(), c.prices.Currency(), c.prices.VATPercent(), slots), true } // priceColumns lays slots out at the shortest slot length. A longer slot's -// price repeats across the columns it covers; a gap is null. -func priceColumns(zone, currency string, slots []appproto.PricePoint) cloudwire.Prices { +// price repeats across the columns it covers; a gap is null. The VAT rate +// crosses in permille, rounded once, as in the app protocol's price answer. +func priceColumns(zone, currency string, vatPercent float64, slots []appproto.PricePoint) cloudwire.Prices { sort.Slice(slots, func(a, b int) bool { return slots[a].StartMs < slots[b].StartMs }) slotMs := int64(math.MaxInt64) for _, s := range slots { slotMs = min(slotMs, int64(s.LenMin)*60_000) } - p := cloudwire.Prices{Zone: zone, Currency: currency, T0Ms: slots[0].StartMs, SlotMs: slotMs} + p := cloudwire.Prices{Zone: zone, Currency: currency, T0Ms: slots[0].StartMs, SlotMs: slotMs, + VATPermille: int64(math.Round(vatPercent * 10))} for _, s := range slots { for at := s.StartMs; at < s.StartMs+int64(s.LenMin)*60_000; at += slotMs { i := int((at - p.T0Ms) / slotMs) diff --git a/go/cmd/ftw/cloud_link_test.go b/go/cmd/ftw/cloud_link_test.go index 4943dd6e1..fc2a3ca92 100644 --- a/go/cmd/ftw/cloud_link_test.go +++ b/go/cmd/ftw/cloud_link_test.go @@ -27,7 +27,7 @@ import ( func TestPriceColumnsRepeatLongSlotsAndLeaveGapsNull(t *testing.T) { t0 := int64(1_791_763_200_000) q := int64(15 * 60_000) - p := priceColumns("SE3", "SEK", []appproto.PricePoint{ + p := priceColumns("SE3", "SEK", 25.5, []appproto.PricePoint{ {StartMs: t0 + 4*q, LenMin: 60, SpotMinor: 40.4, TotalMinor: 110.6}, {StartMs: t0, LenMin: 15, SpotMinor: 41.5, TotalMinor: 112}, {StartMs: t0 + q, LenMin: 15, SpotMinor: -2.4, TotalMinor: 60}, @@ -35,6 +35,9 @@ func TestPriceColumnsRepeatLongSlotsAndLeaveGapsNull(t *testing.T) { if p.T0Ms != t0 || p.SlotMs != q || len(p.SpotMinor) != 8 || len(p.TotalMinor) != 8 { t.Fatalf("columns %+v", p) } + if p.VATPermille != 255 { + t.Fatalf("VAT 25.5 %% went out as %d permille, want 255", p.VATPermille) + } want := []any{int64(42), int64(-2), nil, nil, int64(40), int64(40), int64(40), int64(40)} for i, w := range want { got := p.SpotMinor[i] diff --git a/go/internal/appproto/ports.go b/go/internal/appproto/ports.go index 2fa92bb9b..f6d725f59 100644 --- a/go/internal/appproto/ports.go +++ b/go/internal/appproto/ports.go @@ -319,6 +319,9 @@ type PriceReader interface { // Currency is the ISO code their minor units are in. Without it the app // would have to guess whether 45 is öre or cents. Currency() string + // VATPercent is the VAT rate the box applies to TotalMinor, after any + // default. Still a float, as the config keeps it; the wire rounds it once. + VATPercent() float64 // Slots returns every stored slot overlapping [fromMs, toMs), oldest // first. Overlapping, not contained: the slot in progress at fromMs is // the price right now, and a window starting at "now" that left it out diff --git a/go/internal/appproto/price.go b/go/internal/appproto/price.go index eaa2964c7..cc8b44fcc 100644 --- a/go/internal/appproto/price.go +++ b/go/internal/appproto/price.go @@ -38,11 +38,16 @@ const maxPriceWindowMs = 8 * 24 * 60 * 60 * 1000 // asked for at breakfast genuinely ends early. // // Staleness covers the whole window, both edges and the middle — see below. -func priceFrom(zone, currency string, points []PricePoint, fromMs, toMs int64) Price { +// +// vatPercent is the rate the box applies to the totals. It becomes integer +// permille here, once, by the same rule as the money: every rate in use has at +// most one decimal in percent, so 25.5 % crosses as 255 and nothing is lost. +func priceFrom(zone, currency string, vatPercent float64, points []PricePoint, fromMs, toMs int64) Price { out := Price{ - Zone: zone, - Currency: currency, - Slots: make([]PriceSlot, 0, len(points)), + Zone: zone, + Currency: currency, + Slots: make([]PriceSlot, 0, len(points)), + VATPermille: int64(math.Round(vatPercent * 10)), } for _, p := range points { @@ -136,6 +141,7 @@ func (h *Handler) onPriceGet(ctx context.Context, env Envelope) error { return h.sendPrice(env.ID, priceFrom( h.cfg.Prices.Zone(), h.cfg.Prices.Currency(), + h.cfg.Prices.VATPercent(), points, q.FromMs, q.ToMs, diff --git a/go/internal/appproto/price_test.go b/go/internal/appproto/price_test.go index a08400efb..eadce7d3d 100644 --- a/go/internal/appproto/price_test.go +++ b/go/internal/appproto/price_test.go @@ -20,6 +20,7 @@ import ( type fakePrices struct { zone string currency string + vat float64 points []PricePoint err error @@ -27,8 +28,9 @@ type fakePrices struct { askedTo int64 } -func (f *fakePrices) Zone() string { return f.zone } -func (f *fakePrices) Currency() string { return f.currency } +func (f *fakePrices) Zone() string { return f.zone } +func (f *fakePrices) Currency() string { return f.currency } +func (f *fakePrices) VATPercent() float64 { return f.vat } func (f *fakePrices) Slots(_ context.Context, fromMs, toMs int64) ([]PricePoint, error) { f.askedFrom, f.askedTo = fromMs, toMs @@ -154,6 +156,37 @@ func TestPriceRoundsToWholeMinorUnits(t *testing.T) { } } +// The answer carries the VAT rate the box taxed the totals at, so an app can +// show spot with VAT at the box's rate rather than its own guess. Integer +// permille, rounded once like the money, and sent at zero too: 0 is a rate, +// and only a box without the field leaves it out. +func TestPriceCarriesTheAppliedVATRate(t *testing.T) { + at := int64(1_760_000_000_000) + for _, tc := range []struct { + vat float64 + want int64 + }{ + {25, 250}, + {25.5, 255}, // Finland since September 2024 + {0, 0}, + } { + h, rec, _ := newPriceRig(t, &fakePrices{ + zone: "SE3", currency: "SEK", vat: tc.vat, points: quarterHours(at, 4, 42, 91), + }) + + deliver(t, h, MsgPriceGet, ptrU32(7), PriceGet{FromMs: at, ToMs: at + 3_600_000}) + + f := rec.only(t, MsgPrice) + if p := body[Price](t, f); p.VATPermille != tc.want { + t.Errorf("VAT %v %% went out as %d permille, want %d", tc.vat, p.VATPermille, tc.want) + } + keys := body[map[string]any](t, f) + if _, ok := keys["vatPermille"]; !ok { + t.Errorf("VAT %v %%: no vatPermille key; an absent rate reads as unknown, not as zero", tc.vat) + } + } +} + // Tomorrow's rates arrive in the afternoon. Until they do, the honest answer // ends early and says so — the app stops drawing rather than showing a cliff // the market did not have. diff --git a/go/internal/cloudlink/sender.go b/go/internal/cloudlink/sender.go index 03aa4725e..8f2757e6f 100644 --- a/go/internal/cloudlink/sender.go +++ b/go/internal/cloudlink/sender.go @@ -694,7 +694,7 @@ func abs(v int64) int64 { } func pricesKey(p cloudwire.Prices) string { - return digest([]any{p.T0Ms, p.SlotMs, p.SpotMinor, p.TotalMinor}) + return digest([]any{p.T0Ms, p.SlotMs, p.SpotMinor, p.TotalMinor, p.VATPermille}) } // rosterNumber builds the roster and its number. The number rises when the diff --git a/go/internal/prices/prices.go b/go/internal/prices/prices.go index 01cabd451..d67fae3ff 100644 --- a/go/internal/prices/prices.go +++ b/go/internal/prices/prices.go @@ -600,6 +600,15 @@ func (s *Service) SetApplier(a Applier) { s.applierMu.Unlock() } +// VATPercent is the VAT rate the next fetch applies to the consumer total, +// after the default. Read under the same lock SetApplier takes, so a reader +// never sees a rate halfway through a config reload. +func (s *Service) VATPercent() float64 { + s.applierMu.Lock() + defer s.applierMu.Unlock() + return s.Applier.VATPercent +} + // FXConverter abstracts currency conversion so the prices package // doesn't need to import currency/ directly (and FromConfig callers // can pass a test stub). When it is nil, or has no rate for the pair, a diff --git a/go/internal/prices/prices_test.go b/go/internal/prices/prices_test.go index 5a1ffe59d..29af5ba20 100644 --- a/go/internal/prices/prices_test.go +++ b/go/internal/prices/prices_test.go @@ -363,6 +363,23 @@ func TestFromConfigDefaultsZoneAndVAT(t *testing.T) { } } +// VATPercent is the rate the totals are taxed at: the default when the config +// sets none, and whatever a reload swaps in after that. The app's price answer +// and FTW Cloud's tile both send it, so it must never be the config's 0. +func TestVATPercentIsTheRateTheTotalsAreTaxedAt(t *testing.T) { + s := FromConfig(&config.Price{Provider: "sourceful", GridTariffOreKwh: 30}, nil, nil) + if got := s.VATPercent(); got != 25 { + t.Fatalf("unset VAT reads as %v, want the default 25", got) + } + if _, total := s.Applier.Apply(1.00); total != (100+30)*(1+s.VATPercent()/100) { + t.Fatalf("total %v is not taxed at the rate VATPercent reports", total) + } + s.SetApplier(Applier{GridTariffOreKwh: 30, VATPercent: 25.5}) + if got := s.VATPercent(); got != 25.5 { + t.Fatalf("after a reload VAT reads as %v, want 25.5", got) + } +} + // A base_url reads that server only: a day it does not have stays empty // instead of falling back to live Nord Pool prices. func TestFromConfigBaseURLReadsOnlyThatServer(t *testing.T) { diff --git a/go/protocol/cloudwire/allowlist.go b/go/protocol/cloudwire/allowlist.go index c9653865c..8001dcb4e 100644 --- a/go/protocol/cloudwire/allowlist.go +++ b/go/protocol/cloudwire/allowlist.go @@ -35,7 +35,7 @@ var Fields = map[string][]string{ }, "CarGoal": {"targetPermille", "readyByMin", "days", "finishAtLimit"}, - MsgPrices: {"zone", "currency", "t0Ms", "slotMs", "spotMinor", "totalMinor"}, + MsgPrices: {"zone", "currency", "t0Ms", "slotMs", "spotMinor", "totalMinor", "vatPermille"}, MsgHB: {"clock", "srcRev", "liveSeq", "lpSeq", "ages"}, diff --git a/go/protocol/cloudwire/messages.go b/go/protocol/cloudwire/messages.go index 335ea26a1..4160e3135 100644 --- a/go/protocol/cloudwire/messages.go +++ b/go/protocol/cloudwire/messages.go @@ -307,6 +307,9 @@ type Prices struct { // has no price. TotalMinor includes tariff and VAT. SpotMinor []*int64 `cbor:"spotMinor"` TotalMinor []*int64 `cbor:"totalMinor"` + // VATPermille is the VAT rate in TotalMinor, as in the app protocol's + // price answer: 250 is 25 %, and 0 means no VAT. + VATPermille int64 `cbor:"vatPermille"` } // HB says the box is there and how old its readings are. It never vouches diff --git a/go/protocol/cloudwire/testdata/cloud-app-vectors.json b/go/protocol/cloudwire/testdata/cloud-app-vectors.json index feb481ec0..8449ef1e7 100644 --- a/go/protocol/cloudwire/testdata/cloud-app-vectors.json +++ b/go/protocol/cloudwire/testdata/cloud-app-vectors.json @@ -9,9 +9,9 @@ "prologue": "6674772e636c6f75642e76313a666a7a7972363762757a70796e63716d6a6c697767657a766465", "msg1Payload": "a3616245a162687a016174636e6f776373696740", "msg1": "38869cb4f47eb17ca6a251b657a79943bca4b3a22813caac1578296e837a0273dd35e67fe141738f375ec80128d8e3c18dae31dcc5fe42a0273b9cbe300164e9e903d823a7c1bab23d562d1bf2879379e0a974150000f0b93b6e0c0fe2d0f58b1d814d5427606b9ab58d548696d231eab9369a86", - "msg2Payload": "a36162590771a4646d6f726500666f6e6c696e65f569656e76656c6f70657385a361625901fdaa63736571036461676573a36b41436648743444574c37671903986b4455595757516d6e2d586719e68c6b662d6f484d4471562d6d41190a3c64626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0666669656c6473a76131006132190730613339090561341903b66135190264613619044c62313019083466737263526576016677616c6c4d731b000001a130d7a01467736f7572636573a36b41436648743444574c3767a4646b696e6466647269766572657374617465646c697665686c6173744f6b4d7319e6dc6c7374616c6541667465724d731927106b4455595757516d6e2d5867a4646b696e6466647269766572657374617465676c616767696e67686c6173744f6b4d731903e86c7374616c6541667465724d731975306b662d6f484d4471562d6d41a4646b696e6466647269766572657374617465646c697665686c6173744f6b4d7319e0386c7374616c6541667465724d73193a9868757074696d654d7319ea746c6669656c64536f7572636573a66132816b41436648743444574c37676133816b662d6f484d4471562d6d416134816b662d6f484d4471562d6d416135816b662d6f484d4471562d6d416136836b4455595757516d6e2d58676b662d6f484d4471562d6d416b41436648743444574c3767623130816b4455595757516d6e2d5867716469737061746368426c6f636b65644279806174646c69766563736967584002a8fa0091fbaa932e9ce91bf214e7b1e09f1b17767f49e330bebfdec31c0d7ae738f0a34e3c3f9331a1a8ddf7195152821318c815a61891dc3cd91610516b95a3616258a1a863736571046461676573a26b41436648743444574c37671901906b662d6f484d4471562d6d4119083464626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0666669656c6473a7613100613219070361333908f161341903b66135190264613619040b62313019083466737263526576016677616c6c4d731b000001a130d7b19468757074696d654d7319fbf4716469737061746368426c6f636b65644279806174646c6976656373696758400dc02a9f9207e8694654a8e64125db8b165f7302b8b952c1d5f3d90e8d8de7f9d7969ad8006334681b9b20732e24efddbdf46904686dd01092dd2cf7edfa49f6a361625901ffa7637365710564626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06677616c6c4d731b000001a130d7b19e68666565646261636b81a9637372636b4455595757516d6e2d5867646b696e64626576656167654d73190384656672657368f566726561736f6e64706c616e6673746174757369666f6c6c6f77696e676761637475616c571908346865766964656e6365686d65617375726564687365766572697479626f6b68757074696d654d7319fbfe6a636f6e74726f6c52657619032c6a6c6f6164706f696e747381b66269646b6a33485a4f524e43574873637372636b4455595757516d6e2d586765626f6f7374f4666d616e75616cf6666e6578745768191ce866706f7765725719083467706c7567676564f56773657373696f6e62733167736f6341676553190708696e657874456e644d731b000001a133030b8069726561647942794d731b000001a133a1bc8069726563757272696e67f56973657373696f6e5768190d4869736f63536f7572636568696e6665727265646a636f6d6d616e646564571908166b6e65787453746172744d731b000001a132952e806b706c616e50656e64696e67f46b736f635065726d696c6c651901ae6b737572706c75734f6e6c79f46c677269644465666572726564f46e7461726765745065726d696c6c651903206f636f6d6d616e646564526561736f6e64706c616e6174626c70637369675840b3754b61b1e615d66e94d1e78bbb04f06d466710276ff5807be02499af3863bb27bd183f773095e145e3415b54ec49991c7f6e4e70e2f2db235cd7d80c379c79a36162588caa637365710664626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474304d731b000001a130d7a000647a6f6e656353453366736c6f744d731a000dbba06677616c6c4d731b000001a130d7b1a86863757272656e63796353454b68757074696d654d7319fc086973706f744d696e6f7284182a1828f6226a746f74616c4d696e6f728418761873f61847617466707269636573637369675840e53619e522b5665e9599d2e04c4a62a1040f797a5742f39fde5b56b250b60c5426e008ae623bcaf0def871c596b371763f589cf280cd7dee1381064fdca2c7e3a361625880a963736571076461676573a26b41436648743444574c37671905786b662d6f484d4471562d6d41190c1c64626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b065636c6f636b636e7470656c705365710566737263526576016677616c6c4d731b000001a130d7c33c676c6976655365710468757074696d654d731a00010d9c6174626862637369675840e82b54b463aaf1c315cb0a049e304609482ecf9b3f6a1f5ea9c727d618d9f69ff7c256f1ac88cef858d5168ccec7528951f987e360891aacb00dd03370403fd86a6c6173745365656e4d731b000001a130d8ad88617468736e617073686f746373696740", - "msg2": "a0719d669e99f8ba82522bf6f2e6614d99bccb5616ae3e2af7e4635267531c148d96093e3a658c9843d6ba1321e2fc10ea6f9dc9a4ff98256ab2edf4dd8f9d8d29fc50ed8ce63ab3d409e5740f425a84b8331523b6aebb7bc37080cf41080144beee9311a8089e341d3fb4886247e9114f2990333e5e29e7f3d91f338d0fdb89bb0ff5424c533b8a6cb800dd263eb08c5995540efdad9b729ff8d63e1c15df4cfe7079a8354fee4141d7ac43a0d44cc6b820444920c760983413caefbd1a31037003228381c291226f4cead6db97fe6023e8c0c1a503e8467c6726d1a045b456971a2ba19df457439481c218b95a5a94628ca3a813ee741501e475b6039f5614ab8a5e71ffe8377d3e06fc253f106a4133709fbfdccb6c6ca754ba375e12c924cb5c52351cf78ee6645f007a439ef55774f69b8e458d93cec2a1b369759ba67772b8c4f45737a82aef39f434c956b34a8a1978e261999904b80d39cd619cda27cbc983b45385b8c2569677f76d888439e7dae4df77a4e1d05ee11526f1a6276de5a53266d13fcc620e77594115f90d1b9e802ee942c5705aecb8bcd7ca2d410474dcd89d368f5411645a69bf364e67abc04e7638028117f577c016d008ba9eea84215a921b67dab3e16ec3c11b716574c332a5047e824b0232b97a1bdd210056b4a936cf70221aef0e40fd7ae1341abf7ab9b09a4424203c2f821ca2e9360b2f2130b28fa7d28cf7ac89d7013dd086f4f7aa88f3667cd25d279b0697224504f05bb5dd15098759a86cf046521fddf4010323fa2bfd88bd58b3e6babaf32d77a607b220aa613b4a0dacc1141556d8aae189d218f9f402e57b3ededb3f8b46d5d18267566fa0c309ef988333c416218579247abd47634d6631a4c6e81ecb0c5946370b1686b08509ff35aee5a6b4aed4ff56bb1c4fd7742377f8da760867a8c8e482abd062326085f62b2343957c6826e15d8a5025a584ca3b1eb31d2f48c5c0234ec9f63b68d589d4b7ad4727bb5141e2e2d3a3a7cfbe15eae129a6cdaaab7df28eb05d8ce4cf5d65761c4f5132dc717cae2c7045860224743780aca762c3e77fec18db5c7bb73bfd66f90df934afaf0b6f4f7d6102bae17b083e47a4aa63db614aa0441c0fd45193d7bc02194d9d93241f6ce83feda0f6f16d79cd13a3aadfb1fad0e8db8a40a1434f85634e923759363a71b37579f28c3795fba377a59c719937b56d44aed8bad1fd3a7be959b80ae0e12fcf1d8f2a016a64663a9f197eb35c1d1948dea05b88ff7cf25aa794a98e01383ab4dd5e4de1aceaa0dae29b90c776b4b8c97ef95201f1b466b5a19841a427a4b74123341f763770661c4fb8b01837a785c86f065d258801fac607d886d0775b32c8856e47f05be7c0f672c886c218371104c237c95c9e149bf2a5ab68882737919351429a0982da7304bb3d2643a11f12105ac393a5167fdf30a163b175c3bfe2aea7cb89cf13a60fac27fc447e98c530726d5ed4ec0d2355488a5c0ee8b265c95a3ec9381a2e1f156740bfe1346143e227d4ed6a78adbd1447f9e2690fb878f3c7bdbaefa366edbdb4c2b99c4735847beaaeada651140497d4dc82133797dd1f3333579b93aeef3a485dc3a658a20d22584b54bd5efac1d1542e37212465b51c0ccebb55d81f68729e77db8ea5eb001abd46fd4ea13d2e67114be82976a748368948bd9f3bb44784830eb8723d37e1bb12785fdcd2b02ce60d7ad655000d5abbdbbae9db3f215308e56f378b736cd101236221dbfb5c00a15c97161b53c144de4fdfac0f1e349536a37065259b86630b2f7351568878832c2a8c8c5669f70d8430b6a22c80c898da82ccd6e0b0cb902eda4b5294981a3d037cdf4da8ea31e511fdd142546e4c01e1c0154d1c098f5b36dee0d4732ad19032e9416eed93addea024bc422e5dad3783af218534b255d5dbde9202d0931230b7c6678c66165e05f0e98099b9ebe45ae7d83a1e0f4bc15845cbe4012a807632819d8d5762286c2868056b0670c26b7faa765047b42f89d7fe6e012a262c354a485419b0fb236a4332130707728e25b323f76dfcd6d89fcff4ffa274f988bcd09207f0e24f12f69a83f448b814c5eb0d7d87f993593f27ea3189a61f111d89c9d9e6668abd495253340741b31b34234e3d89106cda2a4dd06e3bc96ad3ed9dede19f1d387b1c7e094733b9e85571d9393fa23efdef0e64af8a53d4ab314b0c0f2a0f15d16bb2db9f8e57816b10d0e8a3d3406a08a68d24d0fdb1589ef951c7e98f77f9ad558da0a9a927ad9d85779bec3376f51188d12accf7fe20a019861a3c8bf0cf5135a093a7ab2cb7b2db0ca548b5e677a3bf8e0add303e3567d71826c4fd38a65a95aeeb8c166846090ee9a173fe0a753041f9df779f693ae686b75fe9854f76d0405bd2e4e5d98f3d0ee74747229c73f2964e72861a4b8df60fe638e8c450b80de929b1d612afc9c8fe537ffad8eed8f176c4d53cd1149e9566da2ff321905ea640938df4f972cb887c66c1a7828da36a014b09f111c1464ffe85f74d6e50a8428c4384579d0180ecd00209012545755e9b1c584817446d5a930e775460bb6edb1f19f6185e63685debd622b293657d3a560ef47c53aff422488f8cb09013290f08433e0f8284fa0e5105f84a2aa2882e75b7b846f19bf6dc89894a3fd773f1804f8cfa5e8a134ba15003472bacb2e5ccae391e8f08ae52a05ee1d4c2f850f94f096087bb4d26c5a6dacde4189ce9b76aeb738021092857b36c84037356d7f3431ac041c41072c57377ea", - "handshakeHash": "572f4062a19964d6417d27e070137f6e012ac705c073841669685158a36a0de7", + "msg2Payload": "a3616259077fa4646d6f726500666f6e6c696e65f569656e76656c6f70657385a361625901fdaa63736571036461676573a36b41436648743444574c37671903986b4455595757516d6e2d586719e68c6b662d6f484d4471562d6d41190a3c64626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0666669656c6473a76131006132190730613339090561341903b66135190264613619044c62313019083466737263526576016677616c6c4d731b000001a130d7a01467736f7572636573a36b41436648743444574c3767a4646b696e6466647269766572657374617465646c697665686c6173744f6b4d7319e6dc6c7374616c6541667465724d731927106b4455595757516d6e2d5867a4646b696e6466647269766572657374617465676c616767696e67686c6173744f6b4d731903e86c7374616c6541667465724d731975306b662d6f484d4471562d6d41a4646b696e6466647269766572657374617465646c697665686c6173744f6b4d7319e0386c7374616c6541667465724d73193a9868757074696d654d7319ea746c6669656c64536f7572636573a66132816b41436648743444574c37676133816b662d6f484d4471562d6d416134816b662d6f484d4471562d6d416135816b662d6f484d4471562d6d416136836b4455595757516d6e2d58676b662d6f484d4471562d6d416b41436648743444574c3767623130816b4455595757516d6e2d5867716469737061746368426c6f636b65644279806174646c69766563736967584002a8fa0091fbaa932e9ce91bf214e7b1e09f1b17767f49e330bebfdec31c0d7ae738f0a34e3c3f9331a1a8ddf7195152821318c815a61891dc3cd91610516b95a3616258a1a863736571046461676573a26b41436648743444574c37671901906b662d6f484d4471562d6d4119083464626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0666669656c6473a7613100613219070361333908f161341903b66135190264613619040b62313019083466737263526576016677616c6c4d731b000001a130d7b19468757074696d654d7319fbf4716469737061746368426c6f636b65644279806174646c6976656373696758400dc02a9f9207e8694654a8e64125db8b165f7302b8b952c1d5f3d90e8d8de7f9d7969ad8006334681b9b20732e24efddbdf46904686dd01092dd2cf7edfa49f6a361625901ffa7637365710564626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06677616c6c4d731b000001a130d7b19e68666565646261636b81a9637372636b4455595757516d6e2d5867646b696e64626576656167654d73190384656672657368f566726561736f6e64706c616e6673746174757369666f6c6c6f77696e676761637475616c571908346865766964656e6365686d65617375726564687365766572697479626f6b68757074696d654d7319fbfe6a636f6e74726f6c52657619032c6a6c6f6164706f696e747381b66269646b6a33485a4f524e43574873637372636b4455595757516d6e2d586765626f6f7374f4666d616e75616cf6666e6578745768191ce866706f7765725719083467706c7567676564f56773657373696f6e62733167736f6341676553190708696e657874456e644d731b000001a133030b8069726561647942794d731b000001a133a1bc8069726563757272696e67f56973657373696f6e5768190d4869736f63536f7572636568696e6665727265646a636f6d6d616e646564571908166b6e65787453746172744d731b000001a132952e806b706c616e50656e64696e67f46b736f635065726d696c6c651901ae6b737572706c75734f6e6c79f46c677269644465666572726564f46e7461726765745065726d696c6c651903206f636f6d6d616e646564526561736f6e64706c616e6174626c70637369675840b3754b61b1e615d66e94d1e78bbb04f06d466710276ff5807be02499af3863bb27bd183f773095e145e3415b54ec49991c7f6e4e70e2f2db235cd7d80c379c79a36162589aab637365710664626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474304d731b000001a130d7a000647a6f6e656353453366736c6f744d731a000dbba06677616c6c4d731b000001a130d7b1a86863757272656e63796353454b68757074696d654d7319fc086973706f744d696e6f7284182a1828f6226a746f74616c4d696e6f728418761873f618476b7661745065726d696c6c6518fa61746670726963657363736967584044a6561b11c9adc34368d839f5e763ccadf5f22a1f536a906e4a222832a2f5178f80b1ea71bb635cf52c8efa9479503b4cad74eb63fa4a757bb3c3539b327dcba361625880a963736571076461676573a26b41436648743444574c37671905786b662d6f484d4471562d6d41190c1c64626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b065636c6f636b636e7470656c705365710566737263526576016677616c6c4d731b000001a130d7c33c676c6976655365710468757074696d654d731a00010d9c6174626862637369675840e82b54b463aaf1c315cb0a049e304609482ecf9b3f6a1f5ea9c727d618d9f69ff7c256f1ac88cef858d5168ccec7528951f987e360891aacb00dd03370403fd86a6c6173745365656e4d731b000001a130d8ad88617468736e617073686f746373696740", + "msg2": "a0719d669e99f8ba82522bf6f2e6614d99bccb5616ae3e2af7e4635267531c148d96093e3a6b8c9843d6ba1321e2fc10ea6f9dc9a4ff98256ab2edf4dd8f9d8d29fc50ed8ce63ab3d409e5740f425a84b8331523b6aebb7bc37080cf41080144beee9311a8089e341d3fb4886247e9114f2990333e5e29e7f3d91f338d0fdb89bb0ff5424c533b8a6cb800dd263eb08c5995540efdad9b729ff8d63e1c15df4cfe7079a8354fee4141d7ac43a0d44cc6b820444920c760983413caefbd1a31037003228381c291226f4cead6db97fe6023e8c0c1a503e8467c6726d1a045b456971a2ba19df457439481c218b95a5a94628ca3a813ee741501e475b6039f5614ab8a5e71ffe8377d3e06fc253f106a4133709fbfdccb6c6ca754ba375e12c924cb5c52351cf78ee6645f007a439ef55774f69b8e458d93cec2a1b369759ba67772b8c4f45737a82aef39f434c956b34a8a1978e261999904b80d39cd619cda27cbc983b45385b8c2569677f76d888439e7dae4df77a4e1d05ee11526f1a6276de5a53266d13fcc620e77594115f90d1b9e802ee942c5705aecb8bcd7ca2d410474dcd89d368f5411645a69bf364e67abc04e7638028117f577c016d008ba9eea84215a921b67dab3e16ec3c11b716574c332a5047e824b0232b97a1bdd210056b4a936cf70221aef0e40fd7ae1341abf7ab9b09a4424203c2f821ca2e9360b2f2130b28fa7d28cf7ac89d7013dd086f4f7aa88f3667cd25d279b0697224504f05bb5dd15098759a86cf046521fddf4010323fa2bfd88bd58b3e6babaf32d77a607b220aa613b4a0dacc1141556d8aae189d218f9f402e57b3ededb3f8b46d5d18267566fa0c309ef988333c416218579247abd47634d6631a4c6e81ecb0c5946370b1686b08509ff35aee5a6b4aed4ff56bb1c4fd7742377f8da760867a8c8e482abd062326085f62b2343957c6826e15d8a5025a584ca3b1eb31d2f48c5c0234ec9f63b68d589d4b7ad4727bb5141e2e2d3a3a7cfbe15eae129a6cdaaab7df28eb05d8ce4cf5d65761c4f5132dc717cae2c7045860224743780aca762c3e77fec18db5c7bb73bfd66f90df934afaf0b6f4f7d6102bae17b083e47a4aa63db614aa0441c0fd45193d7bc02194d9d93241f6ce83feda0f6f16d79cd13a3aadfb1fad0e8db8a40a1434f85634e923759363a71b37579f28c3795fba377a59c719937b56d44aed8bad1fd3a7be959b80ae0e12fcf1d8f2a016a64663a9f197eb35c1d1948dea05b88ff7cf25aa794a98e01383ab4dd5e4de1aceaa0dae29b90c776b4b8c97ef95201f1b466b5a19841a427a4b74123341f763770661c4fb8b01837a785c86f065d258801fac607d886d0775b32c8856e47f05be7c0f672c886c218371104c237c95c9e149bf2a5ab68882737919351429a0982da7304bb3d2643a11f12105ac393a5167fdf30a163b175c3bfe2aea7cb89cf13a60fac27fc447e98c530726d5ed4ec0d2355488a5c0ee8b265c95a3ec9381a2e1f156740bfe1346143e227d4ed6a78adbd1447f9e2690fb878f3c7bdbaefa366edbdb4c2b99c4735847beaaeada651140497d4dc82133797dd1f3333579b93aeef3a485dc3a658a20d22584b54bd5efac1d1542e37212465b51c0ccebb55d81f68729e77db8ea5eb001abd46fd4ea13d2e67114be82976a748368948bd9f3bb44784830eb8723d37e1bb12785fdcd2b02ce60d7ad655000d5abbdbbae9db3f215308e56f378b736cd101236221dbfb5c00a15c97161b53c144de4fdfac0f1e349536a37065259b86630b2f7351568878832c2a8c8c5669f70d8430b6a22c80c898da82ccd6e0b0cb902eda4b5294981a3d037cdf4da8ea31e511fdd142546e4c01e1c0154d1c098f5b36dee0d4732ad19032e9416eed93addea024bc422e5dad3783af218534b255d5dbde9202d0931230b7c6678c66165e05f0e98099b9ebe45ae7d83a1e0f4bc15845cbe4012a807632819d8d5762286c2868056b0670c26b7faa765047b42f89d7fe6e012a262c354a485419b0fb236a4332130707728e25b323f76dfcd6d89fcff4ffa274f988bcd09207f0e24f12f69a83f448b814c5fd0c7d87f993593f27ea3189a61f111d89c9d9e6668abd495253340741b31b34234e3d89106cda2a4dd06e3bc96ad3ed9dede19f1d387b1c7e094733b9e85571d9393fa23efdef0e64af8a53d4ab314b0c0f2a0f15d16bb2db9f8e57816b10d0e8a3d3406a08a68d24d0fdb1589ef951c7e98f77f9ad558da0a9a927ad9d85779bec3376f51188d12accf7fe20aa1b811e1e87e1c74b3abf9fd8090d26e2b29bee9eb6cb814904091ed716396773bfabdc454dcfa03836fbd8f6b7c992c31914a9d85ab089e0029ebcd3619fb4546bfebc780d8debabd26d5e7d6e9758b70f458d9b3a5cd5d00a186e2adc594dc5ce0c3dd3d515fb3ec479b3bcb5bce3edb5744383d8c8c2eb7c9cc948a616d19541e33b32c70d8357e92f21202bad144c9c66c17251517cb9da98d735d1149a8f1d85e5246740b142b05282339706b2e5cd4a1c695726269addcafd03114468618546122565722b7ca84cfc117a66170e8faababb927761315818d06811ef1d04805a9b7809b25fb76b20f87f2ca6e694fd3436e320372bedcc51e6964f2ace85e012f9e9b04c8e8341d9c2062f584ca97b68e95aeecb434db0cd3c46668fbf2f5f1bba3d2d3847310a3bb86326eb940d0316e0099ad7aa109b00e9ef4d9389526ab0a8e7a866f13cfbfd0f7e516d27178e2beefa69eba1e73514fb5681bfa022", + "handshakeHash": "91874de4e74130cf7829c03cdf7f3d5f99220e08b7112a394f9ea304d60c78e1", "snapshot": [ "live full", "live delta", diff --git a/go/protocol/cloudwire/testdata/cloud-once-vectors.json b/go/protocol/cloudwire/testdata/cloud-once-vectors.json index 10b870716..90f754e93 100644 --- a/go/protocol/cloudwire/testdata/cloud-once-vectors.json +++ b/go/protocol/cloudwire/testdata/cloud-once-vectors.json @@ -14,8 +14,8 @@ "live full", "lp" ], - "msg2Payload": "a36162590201a4646d6f726501666f6e6c696e65f569656e76656c6f70657382a3616258a1a863736571046461676573a26b41436648743444574c37671901906b662d6f484d4471562d6d4119083464626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0666669656c6473a7613100613219070361333908f161341903b66135190264613619040b62313019083466737263526576016677616c6c4d731b000001a130d7b19468757074696d654d7319fbf4716469737061746368426c6f636b65644279806174646c6976656373696758400dc02a9f9207e8694654a8e64125db8b165f7302b8b952c1d5f3d90e8d8de7f9d7969ad8006334681b9b20732e24efddbdf46904686dd01092dd2cf7edfa49f6a36162588caa637365710664626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474304d731b000001a130d7a000647a6f6e656353453366736c6f744d731a000dbba06677616c6c4d731b000001a130d7b1a86863757272656e63796353454b68757074696d654d7319fc086973706f744d696e6f7284182a1828f6226a746f74616c4d696e6f728418761873f61847617466707269636573637369675840e53619e522b5665e9599d2e04c4a62a1040f797a5742f39fde5b56b250b60c5426e008ae623bcaf0def871c596b371763f589cf280cd7dee1381064fdca2c7e36a6c6173745365656e4d731b000001a130d8ad88617468736e617073686f746373696740", - "msg2": "a2cae50b4966bef1eea596266dfcc3584d94e754455e094c30b0c1c3f3bc4833a46345c2a3d9fb673acb3e7bfbcb42a2825985e009adb5e7175f87221293984c0254ca43900b4dbe564cecab7a425fc525249114b2fbc38abfc6c65ad7a70c1caa1af5dca675b13b9f71ecfc47583e82dd0bd5ccd57ce439044b4050aa3ba57c1845fa4a59faf9dbc01aeabcc1e1f2414558b69c0b18b1c94658a95688d7321b693e12d30b6799f51765f5537d4ca194747ae042f09f7de64562a2ad131379de592d76bd7d8db5659d0b3888100b65fcdce6bbc2eb1ec41b8f1e6af6f5fe0a7730d8f3b99b0e26ed3f9ea3f1b053de1a052e54f0249152adeb39f811c8d0c2fd9c930cf8efbc84dede94f0ef400b7fdf908925998f37e2be06da99ec82975ef31c7c5fbbaf1ec76e67ab73ce28cb35279756276e131aa2737c88be3db111b97187785c81a60ff8b0c6708870273f081ea4ef12922eae00183a71840507dbfe94daadcfdc7a88e93aaaacb1ee916ade56ca7a068a064a631252d0f9f7e91a5d23de54946a8fbfaa35621f3724eee7b53b93a8fd83e3125b976d7fdd0dca9d50cc57dc64548b8ec3a36865a58553573d643182a51d780fd48599626739cb2c1bb4c32e138b94686f6161469965ed34c22e9ff2e28af028e0b7c24094ec33b9247f9e18978e298eb6295bcf485875bc911e189241fbbaeafcdb7c24d9ea31342df1e07f46be4b7149a1ca20cd18af637384fe9d493e6142cce517ffda102996478381d98b29ea2ea666fdbcf85d2ebbf7f6145f83cfa0546c0a94f228926cc654c1a0e6b6a5fd27d8", + "msg2Payload": "a3616259020fa4646d6f726501666f6e6c696e65f569656e76656c6f70657382a3616258a1a863736571046461676573a26b41436648743444574c37671901906b662d6f484d4471562d6d4119083464626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0666669656c6473a7613100613219070361333908f161341903b66135190264613619040b62313019083466737263526576016677616c6c4d731b000001a130d7b19468757074696d654d7319fbf4716469737061746368426c6f636b65644279806174646c6976656373696758400dc02a9f9207e8694654a8e64125db8b165f7302b8b952c1d5f3d90e8d8de7f9d7969ad8006334681b9b20732e24efddbdf46904686dd01092dd2cf7edfa49f6a36162589aab637365710664626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474304d731b000001a130d7a000647a6f6e656353453366736c6f744d731a000dbba06677616c6c4d731b000001a130d7b1a86863757272656e63796353454b68757074696d654d7319fc086973706f744d696e6f7284182a1828f6226a746f74616c4d696e6f728418761873f618476b7661745065726d696c6c6518fa61746670726963657363736967584044a6561b11c9adc34368d839f5e763ccadf5f22a1f536a906e4a222832a2f5178f80b1ea71bb635cf52c8efa9479503b4cad74eb63fa4a757bb3c3539b327dcb6a6c6173745365656e4d731b000001a130d8ad88617468736e617073686f746373696740", + "msg2": "a2cae50b4966bef1eea596266dfcc3584d94e754455e094c30b0c1c3f3bc4833a46345c2a3d7fb673acb3e7bfbcb42a2825985e009adb5e7175f87221293984c0254ca43900b4dbe564cecab7a425fc525249114b2fbc38abfc6c65ad7a70c1caa1af5dca675b13b9f71ecfc47583e82dd0bd5ccd57ce439044b4050aa3ba57c1845fa4a59faf9dbc01aeabcc1e1f2414558b69c0b18b1c94658a95688d7321b693e12d30b6799f51765f5537d4ca194747ae042f09f7de64562a2ad131379de592d76bd7d8db5659d0b3888100b65fcdce6bbc2eb1ec41b8f1e6af6f5fe0a7730d8f3b99b0e26ed3f9ea3f1b053de1a052e54f0249152adeb39f811c8d0c2fd9c930cf8efbc84dede94f0ef400b7fdf908925998f37e2be06da99ec82975ef31c7c5fbbaf1ec76e67ab73ce28cb35279756276e131aa2657d88be3db111b97187785c81a60ff8b0c6708870273f081ea4ef12922eae00183a71840507dbfe94daadcfdc7a88e93aaaacb1ee916ade56ca7a068a064a631252d0f9f7e91a5d23de54946a8fbfaa35621f3724eee7b53b93a8fd83e3125b976d7fdd0dca9d50cc57dc64548b8ec3a36865a58553573d643182a51d780fd48599626739cb2c1bb4c32e138b9e6a6865434a886df73bdd22e050c31ba04177fc1443b91ad902a36b943e90dc3de605d3da7fbf5e171fc42d6233e02f70fd01d3d7806316e1774a924481b030f99c00425ba80f85d4f1450867160720e64045db31e10cbd377e6efe4ab2467841f77d62ecbac64054cb849fda1b38114152583165b522861613b8c2b3ff8b70f9cac407a65426ee585e9db11a2d5ffca9", "following": [ { "from": "service", @@ -24,8 +24,8 @@ "message": "00000000000000007309a15213862d5a4d41fd8d09b9adf139ea8648030e7f80827f420068e713425dae48f4b84a55dc51b876ba59ff2aa62f3b0903d894b10db1c5d0e845fe152e7a7ce4fa9d624d4e82cfbaafa8b34b330bdc2c34c231566c5a31b30f15b54d12d93c49e0c1edbc6eba6d449d72318c401c16ca7578fc07bdf45ceb4b9c980f3abcc47b71d60fbc652bf16d25795007f35161e71e392cbb480845b4adf8a2a6639165294b3100b97b639174dbabcced97f18d861f55de63ff732bb1a742d16f61ba26226841ecbffa33a2d049fac1f23a5002835b7775fd56d3575a1b71b5da650af4b9679316" } ], - "answer": "0247a2cae50b4966bef1eea596266dfcc3584d94e754455e094c30b0c1c3f3bc4833a46345c2a3d9fb673acb3e7bfbcb42a2825985e009adb5e7175f87221293984c0254ca43900b4dbe564cecab7a425fc525249114b2fbc38abfc6c65ad7a70c1caa1af5dca675b13b9f71ecfc47583e82dd0bd5ccd57ce439044b4050aa3ba57c1845fa4a59faf9dbc01aeabcc1e1f2414558b69c0b18b1c94658a95688d7321b693e12d30b6799f51765f5537d4ca194747ae042f09f7de64562a2ad131379de592d76bd7d8db5659d0b3888100b65fcdce6bbc2eb1ec41b8f1e6af6f5fe0a7730d8f3b99b0e26ed3f9ea3f1b053de1a052e54f0249152adeb39f811c8d0c2fd9c930cf8efbc84dede94f0ef400b7fdf908925998f37e2be06da99ec82975ef31c7c5fbbaf1ec76e67ab73ce28cb35279756276e131aa2737c88be3db111b97187785c81a60ff8b0c6708870273f081ea4ef12922eae00183a71840507dbfe94daadcfdc7a88e93aaaacb1ee916ade56ca7a068a064a631252d0f9f7e91a5d23de54946a8fbfaa35621f3724eee7b53b93a8fd83e3125b976d7fdd0dca9d50cc57dc64548b8ec3a36865a58553573d643182a51d780fd48599626739cb2c1bb4c32e138b94686f6161469965ed34c22e9ff2e28af028e0b7c24094ec33b9247f9e18978e298eb6295bcf485875bc911e189241fbbaeafcdb7c24d9ea31342df1e07f46be4b7149a1ca20cd18af637384fe9d493e6142cce517ffda102996478381d98b29ea2ea666fdbcf85d2ebbf7f6145f83cfa0546c0a94f228926cc654c1a0e6b6a5fd27d800ee00000000000000007309a15213862d5a4d41fd8d09b9adf139ea8648030e7f80827f420068e713425dae48f4b84a55dc51b876ba59ff2aa62f3b0903d894b10db1c5d0e845fe152e7a7ce4fa9d624d4e82cfbaafa8b34b330bdc2c34c231566c5a31b30f15b54d12d93c49e0c1edbc6eba6d449d72318c401c16ca7578fc07bdf45ceb4b9c980f3abcc47b71d60fbc652bf16d25795007f35161e71e392cbb480845b4adf8a2a6639165294b3100b97b639174dbabcced97f18d861f55de63ff732bb1a742d16f61ba26226841ecbffa33a2d049fac1f23a5002835b7775fd56d3575a1b71b5da650af4b9679316", - "handshakeHash": "d136cb1707c8b872b0a5c81a2fdb871fd5c6df9ea48c6bbbe1c95e96b37d8fd9", + "answer": "0255a2cae50b4966bef1eea596266dfcc3584d94e754455e094c30b0c1c3f3bc4833a46345c2a3d7fb673acb3e7bfbcb42a2825985e009adb5e7175f87221293984c0254ca43900b4dbe564cecab7a425fc525249114b2fbc38abfc6c65ad7a70c1caa1af5dca675b13b9f71ecfc47583e82dd0bd5ccd57ce439044b4050aa3ba57c1845fa4a59faf9dbc01aeabcc1e1f2414558b69c0b18b1c94658a95688d7321b693e12d30b6799f51765f5537d4ca194747ae042f09f7de64562a2ad131379de592d76bd7d8db5659d0b3888100b65fcdce6bbc2eb1ec41b8f1e6af6f5fe0a7730d8f3b99b0e26ed3f9ea3f1b053de1a052e54f0249152adeb39f811c8d0c2fd9c930cf8efbc84dede94f0ef400b7fdf908925998f37e2be06da99ec82975ef31c7c5fbbaf1ec76e67ab73ce28cb35279756276e131aa2657d88be3db111b97187785c81a60ff8b0c6708870273f081ea4ef12922eae00183a71840507dbfe94daadcfdc7a88e93aaaacb1ee916ade56ca7a068a064a631252d0f9f7e91a5d23de54946a8fbfaa35621f3724eee7b53b93a8fd83e3125b976d7fdd0dca9d50cc57dc64548b8ec3a36865a58553573d643182a51d780fd48599626739cb2c1bb4c32e138b9e6a6865434a886df73bdd22e050c31ba04177fc1443b91ad902a36b943e90dc3de605d3da7fbf5e171fc42d6233e02f70fd01d3d7806316e1774a924481b030f99c00425ba80f85d4f1450867160720e64045db31e10cbd377e6efe4ab2467841f77d62ecbac64054cb849fda1b38114152583165b522861613b8c2b3ff8b70f9cac407a65426ee585e9db11a2d5ffca900ee00000000000000007309a15213862d5a4d41fd8d09b9adf139ea8648030e7f80827f420068e713425dae48f4b84a55dc51b876ba59ff2aa62f3b0903d894b10db1c5d0e845fe152e7a7ce4fa9d624d4e82cfbaafa8b34b330bdc2c34c231566c5a31b30f15b54d12d93c49e0c1edbc6eba6d449d72318c401c16ca7578fc07bdf45ceb4b9c980f3abcc47b71d60fbc652bf16d25795007f35161e71e392cbb480845b4adf8a2a6639165294b3100b97b639174dbabcced97f18d861f55de63ff732bb1a742d16f61ba26226841ecbffa33a2d049fac1f23a5002835b7775fd56d3575a1b71b5da650af4b9679316", + "handshakeHash": "e10a20d7495edeedf987a4c675ae484df4113d97c89cf4b299f8df587cf55da0", "notes": { "answer": "Message 2, then each following transport message, every one after its length as u16 big endian.", "held": "The cloud-vectors.json envelopes the read's have covers, which the service leaves out.", diff --git a/go/protocol/cloudwire/testdata/cloud-quic-vectors.json b/go/protocol/cloudwire/testdata/cloud-quic-vectors.json index f9b4c3529..e5446937d 100644 --- a/go/protocol/cloudwire/testdata/cloud-quic-vectors.json +++ b/go/protocol/cloudwire/testdata/cloud-quic-vectors.json @@ -145,7 +145,7 @@ }, { "name": "prices", - "envelope": "a36162588caa637365710664626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474304d731b000001a130d7a000647a6f6e656353453366736c6f744d731a000dbba06677616c6c4d731b000001a130d7b1a86863757272656e63796353454b68757074696d654d7319fc086973706f744d696e6f7284182a1828f6226a746f74616c4d696e6f728418761873f61847617466707269636573637369675840e53619e522b5665e9599d2e04c4a62a1040f797a5742f39fde5b56b250b60c5426e008ae623bcaf0def871c596b371763f589cf280cd7dee1381064fdca2c7e3", + "envelope": "a36162589aab637365710664626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474304d731b000001a130d7a000647a6f6e656353453366736c6f744d731a000dbba06677616c6c4d731b000001a130d7b1a86863757272656e63796353454b68757074696d654d7319fc086973706f744d696e6f7284182a1828f6226a746f74616c4d696e6f728418761873f618476b7661745065726d696c6c6518fa61746670726963657363736967584044a6561b11c9adc34368d839f5e763ccadf5f22a1f536a906e4a222832a2f5178f80b1ea71bb635cf52c8efa9479503b4cad74eb63fa4a757bb3c3539b327dcb", "datagram": false }, { diff --git a/go/protocol/cloudwire/testdata/cloud-vectors.json b/go/protocol/cloudwire/testdata/cloud-vectors.json index ffed95c17..386b1c608 100644 --- a/go/protocol/cloudwire/testdata/cloud-vectors.json +++ b/go/protocol/cloudwire/testdata/cloud-vectors.json @@ -116,11 +116,11 @@ "name": "prices", "signer": "box", "t": "prices", - "body": "aa637365710664626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474304d731b000001a130d7a000647a6f6e656353453366736c6f744d731a000dbba06677616c6c4d731b000001a130d7b1a86863757272656e63796353454b68757074696d654d7319fc086973706f744d696e6f7284182a1828f6226a746f74616c4d696e6f728418761873f61847", - "signedInputSha256": "022b3d2ae277cd3caee80775e0a2fe1c8ecebcce3263f586c0ee5e4af96a00e7", - "sig": "e53619e522b5665e9599d2e04c4a62a1040f797a5742f39fde5b56b250b60c5426e008ae623bcaf0def871c596b371763f589cf280cd7dee1381064fdca2c7e3", - "envelope": "a36162588caa637365710664626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474304d731b000001a130d7a000647a6f6e656353453366736c6f744d731a000dbba06677616c6c4d731b000001a130d7b1a86863757272656e63796353454b68757074696d654d7319fc086973706f744d696e6f7284182a1828f6226a746f74616c4d696e6f728418761873f61847617466707269636573637369675840e53619e522b5665e9599d2e04c4a62a1040f797a5742f39fde5b56b250b60c5426e008ae623bcaf0def871c596b371763f589cf280cd7dee1381064fdca2c7e3", - "frame": "0101000000e0a36162588caa637365710664626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474304d731b000001a130d7a000647a6f6e656353453366736c6f744d731a000dbba06677616c6c4d731b000001a130d7b1a86863757272656e63796353454b68757074696d654d7319fc086973706f744d696e6f7284182a1828f6226a746f74616c4d696e6f728418761873f61847617466707269636573637369675840e53619e522b5665e9599d2e04c4a62a1040f797a5742f39fde5b56b250b60c5426e008ae623bcaf0def871c596b371763f589cf280cd7dee1381064fdca2c7e3" + "body": "ab637365710664626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474304d731b000001a130d7a000647a6f6e656353453366736c6f744d731a000dbba06677616c6c4d731b000001a130d7b1a86863757272656e63796353454b68757074696d654d7319fc086973706f744d696e6f7284182a1828f6226a746f74616c4d696e6f728418761873f618476b7661745065726d696c6c6518fa", + "signedInputSha256": "f6ff0efee7782037c846a7dd9ac9b365a631615f5840ff108c8bc11c3f922b92", + "sig": "44a6561b11c9adc34368d839f5e763ccadf5f22a1f536a906e4a222832a2f5178f80b1ea71bb635cf52c8efa9479503b4cad74eb63fa4a757bb3c3539b327dcb", + "envelope": "a36162589aab637365710664626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474304d731b000001a130d7a000647a6f6e656353453366736c6f744d731a000dbba06677616c6c4d731b000001a130d7b1a86863757272656e63796353454b68757074696d654d7319fc086973706f744d696e6f7284182a1828f6226a746f74616c4d696e6f728418761873f618476b7661745065726d696c6c6518fa61746670726963657363736967584044a6561b11c9adc34368d839f5e763ccadf5f22a1f536a906e4a222832a2f5178f80b1ea71bb635cf52c8efa9479503b4cad74eb63fa4a757bb3c3539b327dcb", + "frame": "0101000000eea36162589aab637365710664626f6f7450b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b06474304d731b000001a130d7a000647a6f6e656353453366736c6f744d731a000dbba06677616c6c4d731b000001a130d7b1a86863757272656e63796353454b68757074696d654d7319fc086973706f744d696e6f7284182a1828f6226a746f74616c4d696e6f728418761873f618476b7661745065726d696c6c6518fa61746670726963657363736967584044a6561b11c9adc34368d839f5e763ccadf5f22a1f536a906e4a222832a2f5178f80b1ea71bb635cf52c8efa9479503b4cad74eb63fa4a757bb3c3539b327dcb" }, { "name": "hb", diff --git a/go/protocol/cloudwire/vectors_test.go b/go/protocol/cloudwire/vectors_test.go index 56876c0e2..4b827298d 100644 --- a/go/protocol/cloudwire/vectors_test.go +++ b/go/protocol/cloudwire/vectors_test.go @@ -155,8 +155,9 @@ func vectorBodies(grantKey []byte, boxPK []byte) []struct { {"prices", "box", MsgPrices, Prices{ Header: h(6, 64_520), Zone: "SE3", Currency: "SEK", T0Ms: 1_791_820_800_000, SlotMs: 900_000, - SpotMinor: []*int64{i64(42), i64(40), nil, i64(-3)}, - TotalMinor: []*int64{i64(118), i64(115), nil, i64(71)}, + SpotMinor: []*int64{i64(42), i64(40), nil, i64(-3)}, + TotalMinor: []*int64{i64(118), i64(115), nil, i64(71)}, + VATPermille: 250, }}, {"hb", "box", MsgHB, HB{ Header: h(7, 69_020), Clock: ClockNTP, SrcRev: 1, LiveSeq: 4, LpSeq: 5, diff --git a/go/protocol/messages.go b/go/protocol/messages.go index b53dbbe46..65703fbb8 100644 --- a/go/protocol/messages.go +++ b/go/protocol/messages.go @@ -613,6 +613,14 @@ type Price struct { // afternoon, so a window asked for at breakfast genuinely ends early, and // saying so beats drawing a cliff the market did not have. Stale bool `cbor:"stale"` + // VATPermille is the VAT rate the box applies to TotalMinor, in permille: + // 250 is 25 %, 255 is 25.5 %. It is the rate after the box's default, so + // 0 means no VAT. A box that predates the field leaves it out, and a + // client reads absent as unknown. It lets an app show spot with VAT at + // the box's own rate instead of a guess. The box applies a rate when it + // stores a slot and rewrites today and tomorrow every hour, so after a + // change older slots keep the rate they were stored with. + VATPermille int64 `cbor:"vatPermille"` } // --------------------------------------------------------------------------