-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathconsumer.py
More file actions
112 lines (94 loc) · 3.89 KB
/
consumer.py
File metadata and controls
112 lines (94 loc) · 3.89 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#!/usr/bin/env python3
"""Voidly Pay sample: content-moderation CONSUMER.
Hires a `content.classify` provider, submits text, polls for the
verdict, verifies sha256(text) matches what the provider claims it
classified, and accepts (releasing escrow on success).
Run:
pip install pynacl
python3 consumer.py "Click here to buy now! Limited offer!"
Flow:
1. capability_search → find any active content.classify provider
2. hire → atomic open-escrow + record-hire
3. poll hire state → wait for provider's work_claim
4. verify → sha256(input) == receipt.text_sha256
5. accept / dispute → escrow auto-releases on accept
"""
import json
import os
import sys
import time
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "_lib"))
import voidly_pay # noqa: E402
IDENTITY_PATH = os.path.join(os.path.dirname(__file__), ".identity-mod-consumer.json")
CAPABILITY_SLUG = "content.classify"
def verify_receipt(text: str, summary_str: str) -> tuple[bool, dict | None]:
"""Verify the provider classified the same text we submitted."""
try:
deliverable = json.loads(summary_str)
except Exception:
return False, None
expected_hash = voidly_pay.sha256_hex(text)
if deliverable.get("text_sha256") != expected_hash:
return False, deliverable
return True, deliverable
def main():
if len(sys.argv) < 2:
print("Usage: python3 consumer.py <text-to-moderate>")
return 1
text = " ".join(sys.argv[1:])
pay = voidly_pay.bootstrap(IDENTITY_PATH, name_prefix="content-mod-consumer")
caps = pay.capability_search(capability=CAPABILITY_SLUG, limit=20)
caps = [c for c in caps if c["did"] != pay.did and c.get("active")]
if not caps:
print("[consumer] no active content.classify providers. "
"Run moderator.py first.")
return 1
cap = caps[0]
print(f"[consumer] found {cap['id']} @ "
f"{cap['price_per_call_micro'] / voidly_pay.MICRO_PER_CREDIT} cr "
f"from {cap['did']}")
hire = pay.hire(cap["id"], input_obj={"text": text}, delivery_deadline_hours=1)
if not hire.get("ok"):
print(f"[consumer] hire failed: {hire}")
return 1
hire_id = hire["hire_id"]
print(f"[consumer] hire {hire_id} posted, escrow {hire.get('escrow_id')}")
deadline = time.time() + 90
receipt = None
while time.time() < deadline:
time.sleep(3)
h = pay.hire_get(hire_id)
state = h.get("state")
print(f"[consumer] hire state: {state}")
if state == "claimed" and h.get("receipt_id"):
receipt = pay.receipt(h["receipt_id"])
break
if state in ("expired", "completed", "disputed"):
break
if not receipt:
print("[consumer] timed out waiting for moderation result")
return 1
summary = receipt.get("summary") or ""
ok, deliverable = verify_receipt(text, summary)
if ok and deliverable:
verdict = deliverable.get("verdict", {})
print(f"[consumer] verdict: label={verdict.get('label')} "
f"confidence={verdict.get('confidence')} "
f"flagged={verdict.get('flagged')}")
if verdict.get("categories"):
print(f"[consumer] categories: {verdict['categories']}")
ack = pay.work_accept(receipt["id"], rating=5,
feedback="Verdict returned and hash verified")
if ack.get("ok"):
print("[consumer] accepted ✓ escrow released")
else:
print(f"[consumer] accept error: {ack}")
else:
print("[consumer] hash mismatch — disputing")
pay.work_dispute(receipt["id"], dispute_reason="text_sha256_mismatch")
w = pay.wallet().get("wallet", {})
print(f"[consumer] new balance: "
f"{w.get('balance_credits', 0) / voidly_pay.MICRO_PER_CREDIT:.6f} cr")
return 0
if __name__ == "__main__":
sys.exit(main())