alberta-buck 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- alberta_buck/__init__.py +8 -0
- alberta_buck/oracle.py +286 -0
- alberta_buck/registry/__init__.py +96 -0
- alberta_buck/registry/certificate.py +575 -0
- alberta_buck/registry/client.py +227 -0
- alberta_buck/registry/feature_authority.py +271 -0
- alberta_buck/registry/kernel_vectors.py +511 -0
- alberta_buck/registry/merkle_service.py +643 -0
- alberta_buck/registry/particulars.py +125 -0
- alberta_buck/registry/registry.py +561 -0
- alberta_buck/registry/regulator.py +321 -0
- alberta_buck/registry/tree.py +647 -0
- alberta_buck/registry/vectors.py +511 -0
- alberta_buck/review/__init__.py +2 -0
- alberta_buck/review/examples.py +145 -0
- alberta_buck/review/integration.py +92 -0
- alberta_buck/review/known_log.py +31 -0
- alberta_buck/review/mitigations.py +132 -0
- alberta_buck/sim/__init__.py +9 -0
- alberta_buck/sim/__main__.py +112 -0
- alberta_buck/sim/agents.py +305 -0
- alberta_buck/sim/anvil.py +117 -0
- alberta_buck/sim/basket_flow.py +622 -0
- alberta_buck/sim/basket_model.py +421 -0
- alberta_buck/sim/cast.py +59 -0
- alberta_buck/sim/catalogue.py +327 -0
- alberta_buck/sim/chain.py +27 -0
- alberta_buck/sim/compare_ops.py +291 -0
- alberta_buck/sim/deploy.py +626 -0
- alberta_buck/sim/direct_mint.py +422 -0
- alberta_buck/sim/director_agent.py +285 -0
- alberta_buck/sim/driver.py +216 -0
- alberta_buck/sim/eqmetrics.py +601 -0
- alberta_buck/sim/equilibrium_agents.py +4356 -0
- alberta_buck/sim/experiment.py +524 -0
- alberta_buck/sim/experiments/backdrop-ab.toml +57 -0
- alberta_buck/sim/experiments/backdrop.toml +43 -0
- alberta_buck/sim/experiments/baseline-5yr.toml +13 -0
- alberta_buck/sim/experiments/baseline-capacity.toml +24 -0
- alberta_buck/sim/experiments/baseline-savers2x.toml +19 -0
- alberta_buck/sim/experiments/catalogue-dump.toml +66 -0
- alberta_buck/sim/experiments/catalogue-grind-down.toml +66 -0
- alberta_buck/sim/experiments/catalogue-grind-up.toml +65 -0
- alberta_buck/sim/experiments/catalogue-none.toml +58 -0
- alberta_buck/sim/experiments/catalogue-spike.toml +64 -0
- alberta_buck/sim/experiments/catalogue-spike16.toml +64 -0
- alberta_buck/sim/experiments/catalogue-squeeze.toml +65 -0
- alberta_buck/sim/experiments/catalogue-step.toml +63 -0
- alberta_buck/sim/experiments/catalogue-step16.toml +63 -0
- alberta_buck/sim/experiments/growth-decline.toml +32 -0
- alberta_buck/sim/experiments/growth-scurve.toml +39 -0
- alberta_buck/sim/experiments/growth-steady.toml +37 -0
- alberta_buck/sim/experiments/isolation.toml +42 -0
- alberta_buck/sim/experiments/population-churn.toml +33 -0
- alberta_buck/sim/experiments/realistic.toml +42 -0
- alberta_buck/sim/experiments/rebalancing-eq-5yr-capacity.toml +36 -0
- alberta_buck/sim/experiments/rebalancing-eq-5yr-debtors-mature-basketeers-endog-excursion-raid.toml +89 -0
- alberta_buck/sim/experiments/rebalancing-eq-5yr-debtors-mature-basketeers-endog-excursion.toml +82 -0
- alberta_buck/sim/experiments/rebalancing-eq-5yr-debtors-mature-basketeers-endog-raid.toml +82 -0
- alberta_buck/sim/experiments/rebalancing-eq-5yr-debtors-mature-basketeers-endog.toml +73 -0
- alberta_buck/sim/experiments/rebalancing-eq-5yr-debtors-mature-basketeers-originate.toml +74 -0
- alberta_buck/sim/experiments/rebalancing-eq-5yr-debtors-mature-basketeers.toml +57 -0
- alberta_buck/sim/experiments/rebalancing-eq-5yr-debtors-mature.toml +51 -0
- alberta_buck/sim/experiments/rebalancing-eq-5yr-debtors.toml +46 -0
- alberta_buck/sim/experiments/rebalancing-eq-5yr-deep.toml +22 -0
- alberta_buck/sim/experiments/rebalancing-eq-5yr-endow-deep.toml +34 -0
- alberta_buck/sim/experiments/rebalancing-eq-5yr-endow.toml +34 -0
- alberta_buck/sim/experiments/rebalancing-eq-5yr-portcast.toml +70 -0
- alberta_buck/sim/experiments/rebalancing-eq-5yr.toml +61 -0
- alberta_buck/sim/experiments/retune-mid.toml +17 -0
- alberta_buck/sim/experiments/shock-demand.toml +27 -0
- alberta_buck/sim/experiments/shock-price.toml +25 -0
- alberta_buck/sim/experiments/shock-uptake.toml +25 -0
- alberta_buck/sim/experiments/template.toml +155 -0
- alberta_buck/sim/gen_historical.py +163 -0
- alberta_buck/sim/gen_prices.py +148 -0
- alberta_buck/sim/identity.py +382 -0
- alberta_buck/sim/inspect.py +549 -0
- alberta_buck/sim/loop.py +366 -0
- alberta_buck/sim/mini.py +143 -0
- alberta_buck/sim/monetary_ops.py +468 -0
- alberta_buck/sim/notes_stack.py +674 -0
- alberta_buck/sim/observer.py +191 -0
- alberta_buck/sim/plot_basket_flow.py +123 -0
- alberta_buck/sim/plot_basket_model.py +112 -0
- alberta_buck/sim/plot_basket_split.py +257 -0
- alberta_buck/sim/plot_catalogue.py +129 -0
- alberta_buck/sim/plot_equilibrium.py +307 -0
- alberta_buck/sim/plot_octl.py +141 -0
- alberta_buck/sim/plot_rebalance_article.py +244 -0
- alberta_buck/sim/plot_rebalance_policy.py +193 -0
- alberta_buck/sim/plot_rebalancing.py +555 -0
- alberta_buck/sim/plot_routing.py +231 -0
- alberta_buck/sim/prices/aoil-rev.csv +731 -0
- alberta_buck/sim/prices/aoil.csv +731 -0
- alberta_buck/sim/prices/cbbtc-rev.csv +731 -0
- alberta_buck/sim/prices/cbbtc.csv +731 -0
- alberta_buck/sim/prices/hist-cbbtc.csv +1828 -0
- alberta_buck/sim/prices/hist-cnst.csv +1828 -0
- alberta_buck/sim/prices/hist-food.csv +1828 -0
- alberta_buck/sim/prices/hist-labr.csv +1828 -0
- alberta_buck/sim/prices/hist-nrgc.csv +1828 -0
- alberta_buck/sim/prices/hist-paxg.csv +1828 -0
- alberta_buck/sim/prices/paxg-rev.csv +731 -0
- alberta_buck/sim/prices/paxg.csv +731 -0
- alberta_buck/sim/prices.py +40 -0
- alberta_buck/sim/privacy_world.py +480 -0
- alberta_buck/sim/proxy.py +231 -0
- alberta_buck/sim/pyrevm_backend.py +372 -0
- alberta_buck/sim/quotes/__init__.py +28 -0
- alberta_buck/sim/quotes/data/AU-USD.csv +2322 -0
- alberta_buck/sim/quotes/data/BCPI_MONTHLY-sd-1972-01-01.csv +666 -0
- alberta_buck/sim/quotes/data/BTC-USD.csv +5765 -0
- alberta_buck/sim/quotes/data/CAD_Wages_1997.csv +535 -0
- alberta_buck/sim/quotes/data/CPI-USD.csv +1360 -0
- alberta_buck/sim/quotes/data/M2-USD.csv +810 -0
- alberta_buck/sim/quotes/data/STATIC_INFLATIONCALC.csv +1352 -0
- alberta_buck/sim/quotes/data/USD_CAD_1972.csv +647 -0
- alberta_buck/sim/quotes/data/us/cpi.csv +953 -0
- alberta_buck/sim/quotes/data/us/energy_electricity.csv +570 -0
- alberta_buck/sim/quotes/data/us/energy_gasoline.csv +606 -0
- alberta_buck/sim/quotes/data/us/energy_natgas.csv +570 -0
- alberta_buck/sim/quotes/data/us/food_bananas.csv +555 -0
- alberta_buck/sim/quotes/data/us/food_beef.csv +508 -0
- alberta_buck/sim/quotes/data/us/food_bread.csv +557 -0
- alberta_buck/sim/quotes/data/us/m2.csv +810 -0
- alberta_buck/sim/quotes/data/us/ppi_cement.csv +666 -0
- alberta_buck/sim/quotes/data/us/ppi_gravel.csv +954 -0
- alberta_buck/sim/quotes/data/us/ppi_lumber.csv +1206 -0
- alberta_buck/sim/quotes/data/us/ppi_steel.csv +1206 -0
- alberta_buck/sim/quotes/data/us/wage_ahetpi.csv +750 -0
- alberta_buck/sim/quotes/fetch_feedstock.py +198 -0
- alberta_buck/sim/quotes/ingest.py +237 -0
- alberta_buck/sim/quotes/labour.py +101 -0
- alberta_buck/sim/quotes/metrics.py +47 -0
- alberta_buck/sim/quotes/plot_quotes.py +95 -0
- alberta_buck/sim/quotes/source.py +180 -0
- alberta_buck/sim/quotes/units.py +108 -0
- alberta_buck/sim/quotes/walks.py +119 -0
- alberta_buck/sim/rebalance_policy.py +1283 -0
- alberta_buck/sim/rebalancer.py +148 -0
- alberta_buck/sim/rng.py +117 -0
- alberta_buck/sim/router.py +94 -0
- alberta_buck/sim/scenario.py +314 -0
- alberta_buck/sim/server.py +328 -0
- alberta_buck/sim/snapshot.py +596 -0
- alberta_buck/sim/sweep.py +131 -0
- alberta_buck/sim/telemetry.py +56 -0
- alberta_buck/sim/tui.py +338 -0
- alberta_buck/test/__init__.py +0 -0
- alberta_buck/test/gen_truthstake_plots.py +412 -0
- alberta_buck/test/review/conftest.py +47 -0
- alberta_buck/test/review/test_wallet_failures.py +290 -0
- alberta_buck/test/test_accumulator.py +368 -0
- alberta_buck/test/test_b1_binding.py +193 -0
- alberta_buck/test/test_cards.py +37 -0
- alberta_buck/test/test_contract_binding.py +38 -0
- alberta_buck/test/test_debtor_ledger.py +275 -0
- alberta_buck/test/test_delivery.py +139 -0
- alberta_buck/test/test_domains.py +114 -0
- alberta_buck/test/test_e2e_payload.py +211 -0
- alberta_buck/test/test_escrow_oracle.py +1514 -0
- alberta_buck/test/test_identity_cache_regen.py +166 -0
- alberta_buck/test/test_issuer.py +233 -0
- alberta_buck/test/test_issuer_reenc.py +210 -0
- alberta_buck/test/test_js_join_web3.py +91 -0
- alberta_buck/test/test_kernel_backend.py +110 -0
- alberta_buck/test/test_mini_parity.py +122 -0
- alberta_buck/test/test_notes.py +182 -0
- alberta_buck/test/test_oracle_history.py +159 -0
- alberta_buck/test/test_particulars.py +65 -0
- alberta_buck/test/test_poseidon.py +82 -0
- alberta_buck/test/test_privacy_world.py +177 -0
- alberta_buck/test/test_quotes.py +205 -0
- alberta_buck/test/test_rebalancing_sim_invariants.py +313 -0
- alberta_buck/test/test_receipt.py +288 -0
- alberta_buck/test/test_receipt_core.py +504 -0
- alberta_buck/test/test_receipt_e2e.py +172 -0
- alberta_buck/test/test_receiving_key.py +493 -0
- alberta_buck/test/test_registry.py +1095 -0
- alberta_buck/test/test_render.py +238 -0
- alberta_buck/test/test_routing_sim_web3.py +51 -0
- alberta_buck/test/test_schnorr.py +72 -0
- alberta_buck/test/test_timeseries_oracle.py +930 -0
- alberta_buck/test/test_truthstake.py +1718 -0
- alberta_buck/test/test_unilateral_a1.py +291 -0
- alberta_buck/test/test_unilateral_a2.py +445 -0
- alberta_buck/test/test_vectors.py +175 -0
- alberta_buck/test/test_verifiable_decrypt.py +79 -0
- alberta_buck/test/test_wallet.py +305 -0
- alberta_buck/test/test_wallet_kernel_backend.py +147 -0
- alberta_buck/test/vectors/e2e/a1.json +401 -0
- alberta_buck/test/vectors/e2e/a2.json +464 -0
- alberta_buck/test/vectors/e2e/b1.json +295 -0
- alberta_buck/test/vectors/privacy/world.json +1109 -0
- alberta_buck/test/vectors/receipt-eoa_priv.golden.txt +32 -0
- alberta_buck/test/vectors/receipt-eoa_pub.golden.txt +32 -0
- alberta_buck/test/vectors/receipt-eoa_pub_unicode.golden.txt +32 -0
- alberta_buck/test/vectors/receipt-note_a1.golden.txt +33 -0
- alberta_buck/test/vectors/receipt-note_a1_issuer.golden.txt +33 -0
- alberta_buck/test/vectors/receipt-note_a2.golden.txt +33 -0
- alberta_buck/test/vectors/receipt-note_a2_issuer.golden.txt +33 -0
- alberta_buck/test/vectors/receipt-note_b1.golden.txt +33 -0
- alberta_buck/test/vectors/receipt-note_b1_issuer.golden.txt +33 -0
- alberta_buck/truthstake.py +1685 -0
- alberta_buck/wallet/__init__.py +129 -0
- alberta_buck/wallet/_kernel.py +112 -0
- alberta_buck/wallet/attributes.py +107 -0
- alberta_buck/wallet/b1_binding.py +275 -0
- alberta_buck/wallet/bn254.py +144 -0
- alberta_buck/wallet/build_receipt.py +679 -0
- alberta_buck/wallet/cards.py +284 -0
- alberta_buck/wallet/chaum_pedersen.py +183 -0
- alberta_buck/wallet/cli.py +173 -0
- alberta_buck/wallet/contract_binding.py +86 -0
- alberta_buck/wallet/delivery.py +236 -0
- alberta_buck/wallet/deposit_fold.py +497 -0
- alberta_buck/wallet/domains.py +147 -0
- alberta_buck/wallet/elgamal.py +43 -0
- alberta_buck/wallet/envelope.py +631 -0
- alberta_buck/wallet/identity.py +51 -0
- alberta_buck/wallet/issuer.py +218 -0
- alberta_buck/wallet/issuer_reenc.py +233 -0
- alberta_buck/wallet/kernel_vectors.py +450 -0
- alberta_buck/wallet/nizk.py +292 -0
- alberta_buck/wallet/notes.py +195 -0
- alberta_buck/wallet/nums.py +94 -0
- alberta_buck/wallet/poseidon.py +143 -0
- alberta_buck/wallet/poseidon_constants.json +206 -0
- alberta_buck/wallet/ps.py +167 -0
- alberta_buck/wallet/receipt.py +257 -0
- alberta_buck/wallet/recvkey.py +316 -0
- alberta_buck/wallet/render.py +425 -0
- alberta_buck/wallet/salt.py +90 -0
- alberta_buck/wallet/schnorr.py +103 -0
- alberta_buck/wallet/transcript.py +48 -0
- alberta_buck/wallet/unilateral_a1.py +218 -0
- alberta_buck/wallet/unilateral_a2.py +370 -0
- alberta_buck/wallet/vectors.py +675 -0
- alberta_buck/wallet/verifiable_decrypt.py +175 -0
- alberta_buck/wallet/verify_receipt.py +455 -0
- alberta_buck/wallet/wallet_kernel_vectors.py +804 -0
- alberta_buck-0.2.0.dist-info/METADATA +129 -0
- alberta_buck-0.2.0.dist-info/RECORD +249 -0
- alberta_buck-0.2.0.dist-info/WHEEL +5 -0
- alberta_buck-0.2.0.dist-info/licenses/LICENSE +674 -0
- alberta_buck-0.2.0.dist-info/licenses/NOTICE +74 -0
- alberta_buck-0.2.0.dist-info/top_level.txt +1 -0
- alberta_buck-0.2.0.dist-info/zip-safe +1 -0
alberta_buck/__init__.py
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Alberta Buck -- commodity-basket stablecoin tools
|
|
2
|
+
#
|
|
3
|
+
# buck_core was reached from here by a sys.path shim appending
|
|
4
|
+
# ../core/python, because there was no wheel to depend on. There is now:
|
|
5
|
+
# alberta-buck-core is a hard dependency of this package. A checkout still
|
|
6
|
+
# gets its own copy first, because `make venv` installs core/python
|
|
7
|
+
# editable -- which is the same mechanism, declared rather than improvised.
|
|
8
|
+
# (alberta-buck-platform.org Phase 2; alberta-buck-deployment.org P5.)
|
alberta_buck/oracle.py
ADDED
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
"""Chainlink oracle historical price reading utilities.
|
|
2
|
+
|
|
3
|
+
Two query strategies:
|
|
4
|
+
|
|
5
|
+
get_round_history() -- walks backwards through getRoundData(roundId) at the current
|
|
6
|
+
(fork) block. All calls hit one block height, so Anvil's storage cache is maximally
|
|
7
|
+
effective. Preferred when running against a local Anvil fork.
|
|
8
|
+
|
|
9
|
+
get_daily_prices() -- calls latestRoundData() at estimated block numbers for each day.
|
|
10
|
+
Each call targets a different block height. Works against any RPC but is not
|
|
11
|
+
cache-friendly across blocks.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import logging
|
|
15
|
+
import os
|
|
16
|
+
from datetime import datetime, timezone
|
|
17
|
+
|
|
18
|
+
from web3 import Web3
|
|
19
|
+
|
|
20
|
+
log = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
# Chainlink AggregatorV3Interface -- minimal ABI for price reading
|
|
23
|
+
AGGREGATOR_V3_ABI = [
|
|
24
|
+
{
|
|
25
|
+
"inputs": [],
|
|
26
|
+
"name": "latestRoundData",
|
|
27
|
+
"outputs": [
|
|
28
|
+
{"name": "roundId", "type": "uint80"},
|
|
29
|
+
{"name": "answer", "type": "int256"},
|
|
30
|
+
{"name": "startedAt", "type": "uint256"},
|
|
31
|
+
{"name": "updatedAt", "type": "uint256"},
|
|
32
|
+
{"name": "answeredInRound", "type": "uint80"},
|
|
33
|
+
],
|
|
34
|
+
"stateMutability": "view",
|
|
35
|
+
"type": "function",
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"inputs": [{"name": "_roundId", "type": "uint80"}],
|
|
39
|
+
"name": "getRoundData",
|
|
40
|
+
"outputs": [
|
|
41
|
+
{"name": "roundId", "type": "uint80"},
|
|
42
|
+
{"name": "answer", "type": "int256"},
|
|
43
|
+
{"name": "startedAt", "type": "uint256"},
|
|
44
|
+
{"name": "updatedAt", "type": "uint256"},
|
|
45
|
+
{"name": "answeredInRound", "type": "uint80"},
|
|
46
|
+
],
|
|
47
|
+
"stateMutability": "view",
|
|
48
|
+
"type": "function",
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"inputs": [],
|
|
52
|
+
"name": "decimals",
|
|
53
|
+
"outputs": [{"name": "", "type": "uint8"}],
|
|
54
|
+
"stateMutability": "view",
|
|
55
|
+
"type": "function",
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"inputs": [],
|
|
59
|
+
"name": "description",
|
|
60
|
+
"outputs": [{"name": "", "type": "string"}],
|
|
61
|
+
"stateMutability": "view",
|
|
62
|
+
"type": "function",
|
|
63
|
+
},
|
|
64
|
+
]
|
|
65
|
+
|
|
66
|
+
# Chainlink mainnet feed addresses (from BuckKController.t.sol)
|
|
67
|
+
FEEDS = {
|
|
68
|
+
"XAU/USD": "0x214eD9Da11D2fbe465a6fc601a91E62EbEc1a0D6",
|
|
69
|
+
"XAG/USD": "0x379589227b15F1a12195D3f2d90bBc9F31f95235",
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
# Average Ethereum block time post-merge (seconds)
|
|
73
|
+
AVG_BLOCK_TIME = 12.05
|
|
74
|
+
SECONDS_PER_DAY = 86400
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def get_web3(rpc_url=None):
|
|
78
|
+
"""Connect to Ethereum mainnet via the provided or environment RPC URL."""
|
|
79
|
+
if rpc_url is None:
|
|
80
|
+
rpc_url = os.environ.get("ETH_RPC_URL") or os.environ.get("MAINNET_RPC_URL")
|
|
81
|
+
if not rpc_url:
|
|
82
|
+
raise RuntimeError(
|
|
83
|
+
"No RPC URL: set MAINNET_RPC_URL or ETH_RPC_URL environment variable"
|
|
84
|
+
)
|
|
85
|
+
log.info(
|
|
86
|
+
"Connecting to %s (ETH_RPC_URL=%s, MAINNET_RPC_URL=%s)",
|
|
87
|
+
rpc_url,
|
|
88
|
+
os.environ.get("ETH_RPC_URL", "(unset)"),
|
|
89
|
+
os.environ.get("MAINNET_RPC_URL", "(unset)"),
|
|
90
|
+
)
|
|
91
|
+
w3 = Web3(Web3.HTTPProvider(rpc_url))
|
|
92
|
+
if not w3.is_connected():
|
|
93
|
+
raise RuntimeError(f"Cannot connect to {rpc_url}")
|
|
94
|
+
|
|
95
|
+
# Patch the provider to count actual RPC calls and cache eth_chainId.
|
|
96
|
+
# web3.py's fill_transaction_defaults calls eth_chainId on every
|
|
97
|
+
# contract.functions.foo().call() -- ~2x per eth_call. Caching at
|
|
98
|
+
# the provider level intercepts all code paths (middleware,
|
|
99
|
+
# fill_transaction_defaults, etc.).
|
|
100
|
+
rpc_counts = {} # actual calls that hit the node
|
|
101
|
+
cached_counts = {} # calls served from cache
|
|
102
|
+
_cached = {}
|
|
103
|
+
_original = w3.provider.make_request
|
|
104
|
+
|
|
105
|
+
def _counting_provider(method, params):
|
|
106
|
+
if method == "eth_chainId" and method in _cached:
|
|
107
|
+
cached_counts[method] = cached_counts.get(method, 0) + 1
|
|
108
|
+
return _cached[method]
|
|
109
|
+
rpc_counts[method] = rpc_counts.get(method, 0) + 1
|
|
110
|
+
result = _original(method, params)
|
|
111
|
+
if method == "eth_chainId":
|
|
112
|
+
_cached[method] = result
|
|
113
|
+
return result
|
|
114
|
+
|
|
115
|
+
w3.provider.make_request = _counting_provider
|
|
116
|
+
w3.rpc_counts = rpc_counts
|
|
117
|
+
w3.cached_counts = cached_counts
|
|
118
|
+
|
|
119
|
+
return w3
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def rpc_call_summary(w3, label=""):
|
|
123
|
+
"""Return a human-readable summary of RPC call counts."""
|
|
124
|
+
if not hasattr(w3, "rpc_counts") or not w3.rpc_counts:
|
|
125
|
+
return ""
|
|
126
|
+
total = sum(w3.rpc_counts.values())
|
|
127
|
+
parts = ", ".join(
|
|
128
|
+
f"{method}: {count}" for method, count in sorted(w3.rpc_counts.items())
|
|
129
|
+
)
|
|
130
|
+
prefix = f"RPC calls [{label}]" if label else "RPC calls"
|
|
131
|
+
summary = f"{prefix}: {total} total ({parts})"
|
|
132
|
+
if hasattr(w3, "cached_counts") and w3.cached_counts:
|
|
133
|
+
cached_total = sum(w3.cached_counts.values())
|
|
134
|
+
summary += f" + {cached_total} cached"
|
|
135
|
+
return summary
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def log_call_counts(w3, label=""):
|
|
139
|
+
"""Log a summary of actual (non-cached) RPC call counts."""
|
|
140
|
+
summary = rpc_call_summary(w3, label)
|
|
141
|
+
if summary:
|
|
142
|
+
log.info("%s", summary)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def get_daily_prices(w3, feed_address, days=365):
|
|
146
|
+
"""Query a Chainlink price feed at daily block intervals.
|
|
147
|
+
|
|
148
|
+
Returns (description, decimals, records) where each record is a dict with
|
|
149
|
+
keys: date, price, block, round_id, updated_at.
|
|
150
|
+
"""
|
|
151
|
+
contract = w3.eth.contract(
|
|
152
|
+
address=Web3.to_checksum_address(feed_address),
|
|
153
|
+
abi=AGGREGATOR_V3_ABI,
|
|
154
|
+
)
|
|
155
|
+
decimals = contract.functions.decimals().call()
|
|
156
|
+
description = contract.functions.description().call()
|
|
157
|
+
|
|
158
|
+
latest_block = w3.eth.block_number
|
|
159
|
+
latest = w3.eth.get_block(latest_block)
|
|
160
|
+
now_ts = latest.timestamp
|
|
161
|
+
|
|
162
|
+
log.info(
|
|
163
|
+
"Querying %s (%d decimals) over %d days from block %d",
|
|
164
|
+
description, decimals, days, latest_block,
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
records = []
|
|
168
|
+
for day_offset in range(days, -1, -1):
|
|
169
|
+
blocks_ago = int(day_offset * SECONDS_PER_DAY / AVG_BLOCK_TIME)
|
|
170
|
+
block_num = max(1, latest_block - blocks_ago)
|
|
171
|
+
|
|
172
|
+
try:
|
|
173
|
+
data = contract.functions.latestRoundData().call(
|
|
174
|
+
block_identifier=block_num
|
|
175
|
+
)
|
|
176
|
+
round_id, answer, _started, updated_at, _answered = data
|
|
177
|
+
price = answer / (10**decimals)
|
|
178
|
+
dt = datetime.fromtimestamp(updated_at, tz=timezone.utc)
|
|
179
|
+
|
|
180
|
+
records.append({
|
|
181
|
+
"date": dt,
|
|
182
|
+
"price": price,
|
|
183
|
+
"block": block_num,
|
|
184
|
+
"round_id": round_id,
|
|
185
|
+
"updated_at": updated_at,
|
|
186
|
+
})
|
|
187
|
+
except Exception as exc:
|
|
188
|
+
log.warning(" block %d (day -%d): %s", block_num, day_offset, exc)
|
|
189
|
+
|
|
190
|
+
if day_offset % 30 == 0:
|
|
191
|
+
log.info(" ... %d days remaining", day_offset)
|
|
192
|
+
|
|
193
|
+
return description, decimals, records
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
# ---------------------------------------------------------------------------
|
|
197
|
+
# Round-based history -- cache-friendly (all calls at one block height)
|
|
198
|
+
# ---------------------------------------------------------------------------
|
|
199
|
+
|
|
200
|
+
def get_round_history(w3, feed_address, days=365):
|
|
201
|
+
"""Query a Chainlink feed by walking backwards through getRoundData().
|
|
202
|
+
|
|
203
|
+
All calls execute at the current (fork) block, reading historical round
|
|
204
|
+
storage. This makes every call cacheable by Anvil at a single block height.
|
|
205
|
+
|
|
206
|
+
Walks backwards one round at a time, collecting one record per calendar day.
|
|
207
|
+
|
|
208
|
+
Returns (description, decimals, records) -- same shape as get_daily_prices().
|
|
209
|
+
"""
|
|
210
|
+
contract = w3.eth.contract(
|
|
211
|
+
address=Web3.to_checksum_address(feed_address),
|
|
212
|
+
abi=AGGREGATOR_V3_ABI,
|
|
213
|
+
)
|
|
214
|
+
decimals = contract.functions.decimals().call()
|
|
215
|
+
description = contract.functions.description().call()
|
|
216
|
+
|
|
217
|
+
round_id, _, _, latest_ts, _ = contract.functions.latestRoundData().call()
|
|
218
|
+
phase_id = round_id >> 64
|
|
219
|
+
agg_round = round_id & ((1 << 64) - 1)
|
|
220
|
+
cutoff_ts = latest_ts - days * SECONDS_PER_DAY
|
|
221
|
+
|
|
222
|
+
log.info(
|
|
223
|
+
"Querying %s via getRoundData: phase=%d, latest round=%d",
|
|
224
|
+
description, phase_id, agg_round,
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
records = {} # date -> record (one per calendar day)
|
|
228
|
+
cursor = agg_round
|
|
229
|
+
consecutive_errors = 0
|
|
230
|
+
|
|
231
|
+
while cursor >= 1 and phase_id >= 1:
|
|
232
|
+
rid = (phase_id << 64) | cursor
|
|
233
|
+
try:
|
|
234
|
+
_, answer, _, updated_at, _ = contract.functions.getRoundData(rid).call()
|
|
235
|
+
consecutive_errors = 0
|
|
236
|
+
except Exception:
|
|
237
|
+
consecutive_errors += 1
|
|
238
|
+
if consecutive_errors > 5:
|
|
239
|
+
# Likely hit phase boundary -- try previous phase
|
|
240
|
+
phase_id -= 1
|
|
241
|
+
if phase_id >= 1:
|
|
242
|
+
cursor = _find_phase_last_round(contract, phase_id)
|
|
243
|
+
consecutive_errors = 0
|
|
244
|
+
continue
|
|
245
|
+
break
|
|
246
|
+
cursor -= 1
|
|
247
|
+
continue
|
|
248
|
+
|
|
249
|
+
if updated_at <= cutoff_ts:
|
|
250
|
+
break
|
|
251
|
+
|
|
252
|
+
dt = datetime.fromtimestamp(updated_at, tz=timezone.utc)
|
|
253
|
+
date_key = dt.date()
|
|
254
|
+
if date_key not in records:
|
|
255
|
+
records[date_key] = {
|
|
256
|
+
"date": dt,
|
|
257
|
+
"price": answer / (10**decimals),
|
|
258
|
+
"block": None,
|
|
259
|
+
"round_id": rid,
|
|
260
|
+
"updated_at": updated_at,
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
cursor -= 1
|
|
264
|
+
|
|
265
|
+
if len(records) % 30 == 0 and cursor == agg_round - 1 or len(records) % 60 == 0:
|
|
266
|
+
log.info(" ... %d days collected, round %d", len(records), cursor)
|
|
267
|
+
|
|
268
|
+
result = sorted(records.values(), key=lambda r: r["date"])
|
|
269
|
+
log.info("%s: %d daily records via round walk", description, len(result))
|
|
270
|
+
return description, decimals, result
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def _find_phase_last_round(contract, phase_id, hi=100_000):
|
|
274
|
+
"""Binary search for the highest valid aggregator round in a phase."""
|
|
275
|
+
lo = 1
|
|
276
|
+
last_valid = 0
|
|
277
|
+
while lo <= hi:
|
|
278
|
+
mid = (lo + hi) // 2
|
|
279
|
+
rid = (phase_id << 64) | mid
|
|
280
|
+
try:
|
|
281
|
+
contract.functions.getRoundData(rid).call()
|
|
282
|
+
last_valid = mid
|
|
283
|
+
lo = mid + 1
|
|
284
|
+
except Exception:
|
|
285
|
+
hi = mid - 1
|
|
286
|
+
return last_valid
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"""Alberta Buck Identity Registry -- government identity certification.
|
|
2
|
+
|
|
3
|
+
A federated identity-certification layer for the Identity-axis Notes system.
|
|
4
|
+
Each registry (provincial, state, federal) independently verifies KYC data and
|
|
5
|
+
issues signed, ElGamal-encrypted identity certificates. Feature authorities
|
|
6
|
+
attest that identities possess specific attributes (age>18, has-license, region).
|
|
7
|
+
A central Merkle service aggregates sub-tree roots from all registries and
|
|
8
|
+
feature authorities; the single combined root is posted on chain and consumed
|
|
9
|
+
by Notes spend-path identity-membership proofs.
|
|
10
|
+
|
|
11
|
+
Reference: alberta-buck-notes.org ("Mutual Decryptability", "one gadget" / Identity-M model); see also alberta-buck-notes-flow.org "The Identity-M Spend Path".
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from alberta_buck.registry.certificate import (
|
|
15
|
+
IdentityCertificate,
|
|
16
|
+
SignedCertificate,
|
|
17
|
+
SealedCertificate,
|
|
18
|
+
RegistryKeyPair,
|
|
19
|
+
RegistrySchnorrProof,
|
|
20
|
+
registry_keygen,
|
|
21
|
+
registry_schnorr_sign,
|
|
22
|
+
registry_schnorr_verify,
|
|
23
|
+
registry_sign_certificate,
|
|
24
|
+
registry_verify_certificate,
|
|
25
|
+
seal_certificate,
|
|
26
|
+
unseal_certificate,
|
|
27
|
+
)
|
|
28
|
+
from alberta_buck.registry.tree import (
|
|
29
|
+
IdentityMerkleTree,
|
|
30
|
+
MembershipProof,
|
|
31
|
+
identity_leaf,
|
|
32
|
+
identity_leaf_salted,
|
|
33
|
+
EMPTY_LEAF,
|
|
34
|
+
)
|
|
35
|
+
from alberta_buck.registry.merkle_service import (
|
|
36
|
+
CentralMerkleService,
|
|
37
|
+
SubTreeKind,
|
|
38
|
+
SubTreeRecord,
|
|
39
|
+
AggregatorMembershipProof,
|
|
40
|
+
FullMembershipProof,
|
|
41
|
+
ComposedMembershipProof,
|
|
42
|
+
)
|
|
43
|
+
from alberta_buck.registry.registry import (
|
|
44
|
+
RegistryAgent,
|
|
45
|
+
RegistrationRecord,
|
|
46
|
+
FullRegistrationRecord,
|
|
47
|
+
RegistryState,
|
|
48
|
+
)
|
|
49
|
+
from alberta_buck.registry.feature_authority import (
|
|
50
|
+
FeatureAuthority,
|
|
51
|
+
FeatureRecord,
|
|
52
|
+
)
|
|
53
|
+
from alberta_buck.registry.client import (
|
|
54
|
+
ClientAgent,
|
|
55
|
+
VerifiedIdentity,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
__all__ = [
|
|
59
|
+
# certificate
|
|
60
|
+
"IdentityCertificate",
|
|
61
|
+
"SignedCertificate",
|
|
62
|
+
"SealedCertificate",
|
|
63
|
+
"RegistryKeyPair",
|
|
64
|
+
"RegistrySchnorrProof",
|
|
65
|
+
"registry_keygen",
|
|
66
|
+
"registry_schnorr_sign",
|
|
67
|
+
"registry_schnorr_verify",
|
|
68
|
+
"registry_sign_certificate",
|
|
69
|
+
"registry_verify_certificate",
|
|
70
|
+
"seal_certificate",
|
|
71
|
+
"unseal_certificate",
|
|
72
|
+
# tree
|
|
73
|
+
"IdentityMerkleTree",
|
|
74
|
+
"MembershipProof",
|
|
75
|
+
"identity_leaf",
|
|
76
|
+
"identity_leaf_salted",
|
|
77
|
+
"EMPTY_LEAF",
|
|
78
|
+
# merkle_service
|
|
79
|
+
"CentralMerkleService",
|
|
80
|
+
"SubTreeKind",
|
|
81
|
+
"SubTreeRecord",
|
|
82
|
+
"AggregatorMembershipProof",
|
|
83
|
+
"FullMembershipProof",
|
|
84
|
+
"ComposedMembershipProof",
|
|
85
|
+
# registry
|
|
86
|
+
"RegistryAgent",
|
|
87
|
+
"RegistrationRecord",
|
|
88
|
+
"FullRegistrationRecord",
|
|
89
|
+
"RegistryState",
|
|
90
|
+
# feature_authority
|
|
91
|
+
"FeatureAuthority",
|
|
92
|
+
"FeatureRecord",
|
|
93
|
+
# client
|
|
94
|
+
"ClientAgent",
|
|
95
|
+
"VerifiedIdentity",
|
|
96
|
+
]
|