package host import ( "context" "encoding/json" "strings" "github.com/carlosframework/platform/internal/hostactors" "github.com/carlosframework/platform/internal/hostbox" "github.com/carlosframework/platform/internal/hostevidence" "github.com/carlosframework/platform/internal/release" ) func readMigrationStop(ctx context.Context, store release.Store, directory hostactors.Directory, launches *hostbox.LaunchJournal, inventory *hostbox.StopInventoryDB, launch hostbox.LaunchInventory) (StopResult, error) { target := launch.Target in, err := inventory.ReadExactStopInventory(ctx, target) if err != nil { return StopResult{}, err } journal, err := launches.ReadStopKernelJournal(ctx, target) if err != nil { return StopResult{}, err } if err := legacyJournalProof(store, directory)(ctx, launch, in, journal); err != nil { return StopResult{}, err } roster, err := directory.Read(ctx) if err != nil { return StopResult{}, err } actor, ok := roster.Roster.Boxes[target.Box] if !ok || actor.Enrollment != target.Enrollment { return StopResult{}, hostbox.ErrLegacyJournalProof } prefix := strings.TrimSuffix(stopEvidenceKey(target.Host, target.Op, target.Box, ""), ".json") keys, err := store.List(ctx, prefix) if err != nil { return StopResult{}, err } wanted := StopReceipt{Host: target.Host, Incarnation: target.Incarnation, InstallationStamp: target.Stamp, WriterSession: target.Session, Box: target.Box, Enrollment: target.Enrollment, BootID: target.BootID, ControllerID: target.ControllerID, OperationID: target.Op, Region: target.Region, Binding: in.DirectoryRevision, InventorySHA256: digestStop(in), LocalJournalSHA256: digestStop(journal)} for _, key := range keys { if !strings.HasPrefix(key, prefix) || !strings.HasSuffix(key, ".json") { continue } raw, _, err := store.Get(ctx, key) if err != nil { return StopResult{}, err } signature, _, err := store.Get(ctx, key+".sig") if err != nil { continue } if hostevidence.Verify(actor.CensusPublicKey, key, raw, signature) != nil { continue } var p StopReceipt if json.Unmarshal(raw, &p) != nil || p.V != 1 || p.Kind != "stopped" || p.ID == "" || p.FenceID == "" || p.ClaimGen == 0 || p.ObservedBootID == "" || p.EvidenceAt.IsZero() || len(p.CgroupIdentities) == 0 || p.RequestLimitNS != int64(hostbox.RequestLimit) || key != prefix+p.ID+".json" || legacyJournalProofMatch(p) != legacyJournalProofMatch(wanted) || p.Phase != target.Phase || p.ObligationID != target.ObligationID { continue } numeral, ok := receiptRosterRevision(p.DirectoryRevision) if !ok { continue } target.FenceID, target.ClaimGen, target.ClaimETag, target.RosterRevision = p.FenceID, p.ClaimGen, p.ClaimETag, numeral return StopResult{Target: target, Key: key, Digest: digestRawStop(raw), Bytes: raw, EvidenceAt: p.EvidenceAt}, nil } return StopResult{}, hostbox.ErrLegacyJournalProof }