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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions fabric/go/cmd/fabric/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ func main() {
seeds = fabric.DefaultSeeds
}

f := fabric.New(seeds)
f := fabric.New()
f.SetSeeds(seeds)
if devMode {
f.SetDevMode(true)
}
Expand All @@ -62,14 +63,14 @@ func main() {
}
}

batch, err := f.ResolveAll(handles)
zones, err := f.ResolveAll(handles)
if err != nil {
fmt.Fprintf(os.Stderr, "error: %s\n", err)
os.Exit(1)
}

zoneMap := make(map[string]libveritas.Zone)
for _, z := range batch.Zones {
for _, z := range zones {
if _, exists := zoneMap[z.Handle]; !exists {
zoneMap[z.Handle] = z
}
Expand Down
61 changes: 39 additions & 22 deletions fabric/go/fabric.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,36 @@ func (p *anchorPool) merged() (string, error) {
}
allEntries = append(allEntries, entries...)
}
data, err := json.Marshal(allEntries)

type entryWithHeight struct {
raw json.RawMessage
height uint64
}
parsed := make([]entryWithHeight, 0, len(allEntries))
seen := make(map[uint64]struct{}, len(allEntries))
for _, e := range allEntries {
var meta struct {
Block struct {
Height uint64 `json:"height"`
} `json:"block"`
}
if err := json.Unmarshal(e, &meta); err != nil {
continue
}
if _, ok := seen[meta.Block.Height]; ok {
continue
}
seen[meta.Block.Height] = struct{}{}
parsed = append(parsed, entryWithHeight{raw: e, height: meta.Block.Height})
}
sort.Slice(parsed, func(i, j int) bool {
return parsed[i].height > parsed[j].height
})
out := make([]json.RawMessage, len(parsed))
for i, e := range parsed {
out[i] = e.raw
}
data, err := json.Marshal(out)
return string(data), err
}

Expand Down Expand Up @@ -242,7 +271,7 @@ func (f *Fabric) Badge(zone libveritas.Zone) VerificationBadge {
}

// BadgeFor returns the verification badge given sovereignty and an anchor hash.
func (f *Fabric) BadgeFor(sovereignty string, anchorHash string) VerificationBadge {
func (f *Fabric) BadgeFor(sovereignty string, anchorHash []byte) VerificationBadge {
f.mu.Lock()
hasAny := f.trusted != nil || f.observed != nil || f.semiTrusted != nil
f.mu.Unlock()
Expand All @@ -263,43 +292,31 @@ func (f *Fabric) BadgeFor(sovereignty string, anchorHash string) VerificationBad
return BadgeNone
}

func (f *Fabric) isRootTrusted(anchorHash string) bool {
func (f *Fabric) isRootTrusted(anchorHash []byte) bool {
f.mu.Lock()
defer f.mu.Unlock()
if f.trusted == nil {
return false
}
rootBytes, err := hex.DecodeString(anchorHash)
if err != nil {
return false
}
return containsRoot(f.trusted.Roots, rootBytes)
return containsRoot(f.trusted.Roots, anchorHash)
}

func (f *Fabric) isRootObserved(anchorHash string) bool {
func (f *Fabric) isRootObserved(anchorHash []byte) bool {
f.mu.Lock()
defer f.mu.Unlock()
if f.observed == nil {
return false
}
rootBytes, err := hex.DecodeString(anchorHash)
if err != nil {
return false
}
return containsRoot(f.observed.Roots, rootBytes)
return containsRoot(f.observed.Roots, anchorHash)
}

func (f *Fabric) isRootSemiTrusted(anchorHash string) bool {
func (f *Fabric) isRootSemiTrusted(anchorHash []byte) bool {
f.mu.Lock()
defer f.mu.Unlock()
if f.semiTrusted == nil {
return false
}
rootBytes, err := hex.DecodeString(anchorHash)
if err != nil {
return false
}
return containsRoot(f.semiTrusted.Roots, rootBytes)
return containsRoot(f.semiTrusted.Roots, anchorHash)
}

func containsRoot(roots [][]byte, target []byte) bool {
Expand Down Expand Up @@ -530,8 +547,8 @@ func (f *Fabric) SearchAddr(name, addr string) ([]libveritas.Zone, error) {
// Filter to zones that actually contain the matching addr record
var matching []libveritas.Zone
for _, z := range zones {
if z.Records != nil {
rs := libveritas.NewRecordSet(*z.Records)
if len(z.Records) > 0 {
rs := libveritas.NewRecordSet(z.Records)
records, err := rs.Unpack()
if err != nil {
continue
Expand Down
1 change: 1 addition & 0 deletions fabric/js/fabric-core/src/fabric.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ export class Fabric {
seen.add(h);
return true;
});
deduped.sort((a, b) => (b.block?.height ?? b.height) - (a.block?.height ?? a.height));
const anchors = this.provider.createAnchors(deduped);
this.veritas = this.provider.createVeritas(anchors);
}
Expand Down
42 changes: 25 additions & 17 deletions fabric/kotlin/src/main/kotlin/org/spacesprotocol/fabric/Fabric.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import org.spacesprotocol.libveritas.*
import java.io.InputStreamReader
import java.net.HttpURLConnection
Expand Down Expand Up @@ -60,15 +63,23 @@ private class AnchorPool {
var observed: String = "" // raw entries JSON array string

fun merged(): String {
val parts = mutableListOf<String>()
val all = mutableListOf<JsonElement>()
for (src in listOf(trusted, semiTrusted, observed)) {
if (src.isNotEmpty()) {
// Strip outer brackets and add contents
val inner = src.trim().removePrefix("[").removeSuffix("]").trim()
if (inner.isNotEmpty()) parts.add(inner)
}
if (src.isEmpty()) continue
try {
all.addAll(json.parseToJsonElement(src).jsonArray)
} catch (_: Exception) {}
}
val seen = mutableSetOf<Long>()
val deduped = mutableListOf<Pair<Long, JsonElement>>()
for (e in all) {
val h = (e as? kotlinx.serialization.json.JsonObject)
?.get("block")?.jsonObject
?.get("height")?.jsonPrimitive?.content?.toLongOrNull() ?: 0L
if (seen.add(h)) deduped.add(h to e)
}
return "[${parts.joinToString(",")}]"
deduped.sortByDescending { it.first }
return "[${deduped.joinToString(",") { it.second.toString() }}]"
}
}

Expand Down Expand Up @@ -126,7 +137,7 @@ class Fabric(
fun badge(zone: Zone): VerificationBadge =
badgeFor(zone.sovereignty, zone.anchorHash)

fun badgeFor(sovereignty: String, anchorHash: String): VerificationBadge {
fun badgeFor(sovereignty: String, anchorHash: ByteArray): VerificationBadge {
val hasAny = synchronized(lock) { trusted != null || observed != null || semiTrusted != null }
if (!hasAny) return VerificationBadge.Unverified

Expand Down Expand Up @@ -671,22 +682,19 @@ class Fabric(

// -- Private trust helpers --

private fun isRootTrusted(anchorHash: String): Boolean {
private fun isRootTrusted(anchorHash: ByteArray): Boolean {
val ts = trusted ?: return false
val rootBytes = anchorHash.hexToByteArray()
return ts.roots.any { it.contentEquals(rootBytes) }
return ts.roots.any { it.contentEquals(anchorHash) }
}

private fun isRootObserved(anchorHash: String): Boolean {
private fun isRootObserved(anchorHash: ByteArray): Boolean {
val ts = observed ?: return false
val rootBytes = anchorHash.hexToByteArray()
return ts.roots.any { it.contentEquals(rootBytes) }
return ts.roots.any { it.contentEquals(anchorHash) }
}

private fun isRootSemiTrusted(anchorHash: String): Boolean {
private fun isRootSemiTrusted(anchorHash: ByteArray): Boolean {
val ts = semiTrusted ?: return false
val rootBytes = anchorHash.hexToByteArray()
return ts.roots.any { it.contentEquals(rootBytes) }
return ts.roots.any { it.contentEquals(anchorHash) }
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,15 @@ fun main(args: Array<String>) {
}
}

val batch = try {
val zones = try {
fabric.resolveAll(handles)
} catch (e: Exception) {
System.err.println("error: $e")
exitProcess(1)
}

for (handle in handles) {
val zone = batch.zones.find { it.handle == handle }
val zone = zones.find { it.handle == handle }
if (zone == null) {
System.err.println("$handle: not found")
continue
Expand Down
4 changes: 4 additions & 0 deletions fabric/python/fabric/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ def merged(self) -> list:
if h not in seen:
seen.add(h)
deduped.append(e)
deduped.sort(
key=lambda e: e.get("block", {}).get("height", 0) if isinstance(e, dict) else 0,
reverse=True,
)
return deduped


Expand Down
44 changes: 25 additions & 19 deletions fabric/swift/Sources/Fabric/Fabric.swift
Original file line number Diff line number Diff line change
Expand Up @@ -84,19 +84,28 @@ private struct AnchorPool {
var observed: String = "" // raw entries JSON array string

func merged() -> String? {
var parts = [String]()
var all: [Any] = []
for src in [trusted, semiTrusted, observed] {
if src.isEmpty { continue }
let inner = src.trimmingCharacters(in: .whitespaces)
.dropFirst() // remove [
.dropLast() // remove ]
.trimmingCharacters(in: .whitespaces)
if !inner.isEmpty {
parts.append(String(inner))
guard let data = src.data(using: .utf8),
let arr = try? JSONSerialization.jsonObject(with: data) as? [Any] else { continue }
all.append(contentsOf: arr)
}
if all.isEmpty { return nil }
var seen = Set<UInt64>()
var deduped: [(UInt64, Any)] = []
for e in all {
let h = ((e as? [String: Any])?["block"] as? [String: Any])?["height"] as? UInt64
?? UInt64(((e as? [String: Any])?["block"] as? [String: Any])?["height"] as? Int ?? 0)
if seen.insert(h).inserted {
deduped.append((h, e))
}
}
if parts.isEmpty { return nil }
return "[\(parts.joined(separator: ","))]"
deduped.sort { $0.0 > $1.0 }
let sorted = deduped.map { $0.1 }
guard let data = try? JSONSerialization.data(withJSONObject: sorted),
let str = String(data: data, encoding: .utf8) else { return nil }
return str
}
}

Expand Down Expand Up @@ -248,7 +257,7 @@ public final class Fabric: @unchecked Sendable {
}

/// Badge given sovereignty and an anchor hash.
public func badgeFor(sovereignty: String, anchorHash: String) -> VerificationBadge {
public func badgeFor(sovereignty: String, anchorHash: Data) -> VerificationBadge {
lock.lock()
let hasAny = trusted != nil || observed != nil || semiTrusted != nil
lock.unlock()
Expand Down Expand Up @@ -701,25 +710,22 @@ public final class Fabric: @unchecked Sendable {

// MARK: - Trust helpers (private)

private func isRootTrusted(_ anchorHash: String) -> Bool {
private func isRootTrusted(_ anchorHash: Data) -> Bool {
lock.lock(); defer { lock.unlock() }
guard let ts = trusted else { return false }
guard let rootBytes = Data(hexString: anchorHash) else { return false }
return ts.roots.contains { Data($0) == rootBytes }
return ts.roots.contains { Data($0) == anchorHash }
}

private func isRootObserved(_ anchorHash: String) -> Bool {
private func isRootObserved(_ anchorHash: Data) -> Bool {
lock.lock(); defer { lock.unlock() }
guard let ts = observed else { return false }
guard let rootBytes = Data(hexString: anchorHash) else { return false }
return ts.roots.contains { Data($0) == rootBytes }
return ts.roots.contains { Data($0) == anchorHash }
}

private func isRootSemiTrusted(_ anchorHash: String) -> Bool {
private func isRootSemiTrusted(_ anchorHash: Data) -> Bool {
lock.lock(); defer { lock.unlock() }
guard let ts = semiTrusted else { return false }
guard let rootBytes = Data(hexString: anchorHash) else { return false }
return ts.roots.contains { Data($0) == rootBytes }
return ts.roots.contains { Data($0) == anchorHash }
}

// MARK: - Internal fetch helpers
Expand Down
4 changes: 2 additions & 2 deletions fabric/swift/Sources/FabricCLI/FabricCLI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ struct FabricCLI {
try await fabric.trust(trustId)
}

let batch = try await fabric.resolveAll(handles)
let zones = try await fabric.resolveAll(handles)

for handle in handles {
guard let zone = batch.zones.first(where: { $0.handle == handle }) else {
guard let zone = zones.first(where: { $0.handle == handle }) else {
fputs("\(handle): not found\n", stderr)
continue
}
Expand Down
Loading