proof-of-portfolio 0.0.46__py3-none-any.whl → 0.0.48__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.
- proof_of_portfolio/__init__.py +8 -6
- proof_of_portfolio/_version.py +1 -1
- proof_of_portfolio/demos/calmar.py +3 -11
- proof_of_portfolio/demos/omega.py +4 -12
- proof_of_portfolio/demos/sharpe.py +4 -12
- proof_of_portfolio/demos/sortino.py +3 -12
- proof_of_portfolio/demos/tstat.py +5 -12
- proof_of_portfolio/parsing_utils.py +201 -0
- proof_of_portfolio/proof_generator.py +36 -20
- {proof_of_portfolio-0.0.46.dist-info → proof_of_portfolio-0.0.48.dist-info}/METADATA +2 -1
- {proof_of_portfolio-0.0.46.dist-info → proof_of_portfolio-0.0.48.dist-info}/RECORD +14 -13
- {proof_of_portfolio-0.0.46.dist-info → proof_of_portfolio-0.0.48.dist-info}/WHEEL +0 -0
- {proof_of_portfolio-0.0.46.dist-info → proof_of_portfolio-0.0.48.dist-info}/entry_points.txt +0 -0
- {proof_of_portfolio-0.0.46.dist-info → proof_of_portfolio-0.0.48.dist-info}/top_level.txt +0 -0
proof_of_portfolio/__init__.py
CHANGED
@@ -52,14 +52,14 @@ def requires_dependencies(func):
|
|
52
52
|
return wrapper
|
53
53
|
|
54
54
|
|
55
|
-
def _prove_worker(miner_data, hotkey):
|
55
|
+
def _prove_worker(miner_data, hotkey, verbose=False):
|
56
56
|
"""
|
57
57
|
Worker function to run proof generation in a separate process.
|
58
58
|
"""
|
59
59
|
try:
|
60
60
|
from .proof_generator import generate_proof
|
61
61
|
|
62
|
-
result = generate_proof(miner_data, hotkey)
|
62
|
+
result = generate_proof(miner_data, hotkey, verbose=verbose)
|
63
63
|
|
64
64
|
return {
|
65
65
|
"status": "success",
|
@@ -81,13 +81,14 @@ def _prove_worker(miner_data, hotkey):
|
|
81
81
|
|
82
82
|
|
83
83
|
@requires_dependencies
|
84
|
-
async def prove(miner_data, hotkey):
|
84
|
+
async def prove(miner_data, hotkey, verbose=False):
|
85
85
|
"""
|
86
86
|
Generate zero-knowledge proof for miner portfolio data asynchronously.
|
87
87
|
|
88
88
|
Args:
|
89
89
|
miner_data: Dictionary containing perf_ledgers and positions for the miner
|
90
90
|
hotkey: Miner's hotkey
|
91
|
+
verbose: Boolean to control logging verbosity
|
91
92
|
|
92
93
|
Returns:
|
93
94
|
Dictionary with proof results including status, portfolio_metrics, etc.
|
@@ -97,7 +98,7 @@ async def prove(miner_data, hotkey):
|
|
97
98
|
with ProcessPoolExecutor(max_workers=1) as executor:
|
98
99
|
try:
|
99
100
|
result = await loop.run_in_executor(
|
100
|
-
executor, _prove_worker, miner_data, hotkey
|
101
|
+
executor, _prove_worker, miner_data, hotkey, verbose
|
101
102
|
)
|
102
103
|
return result
|
103
104
|
except Exception as e:
|
@@ -108,15 +109,16 @@ async def prove(miner_data, hotkey):
|
|
108
109
|
}
|
109
110
|
|
110
111
|
|
111
|
-
def prove_sync(miner_data, hotkey):
|
112
|
+
def prove_sync(miner_data, hotkey, verbose=False):
|
112
113
|
"""
|
113
114
|
Synchronous wrapper for the prove function for backward compatibility.
|
114
115
|
|
115
116
|
Args:
|
116
117
|
miner_data: Dictionary containing perf_ledgers and positions for the miner
|
117
118
|
hotkey: Miner's hotkey
|
119
|
+
verbose: Boolean to control logging verbosity
|
118
120
|
|
119
121
|
Returns:
|
120
122
|
Dictionary with proof results including status, portfolio_metrics, etc.
|
121
123
|
"""
|
122
|
-
return _prove_worker(miner_data, hotkey)
|
124
|
+
return _prove_worker(miner_data, hotkey, verbose)
|
proof_of_portfolio/_version.py
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
# This file is auto-generated during build
|
2
|
-
__version__ = "0.0.
|
2
|
+
__version__ = "0.0.48"
|
@@ -4,6 +4,7 @@ import subprocess
|
|
4
4
|
import os
|
5
5
|
import time
|
6
6
|
from . import utils
|
7
|
+
from ..parsing_utils import parse_demo_output
|
7
8
|
|
8
9
|
SCALE = 10_000_000
|
9
10
|
WEIGHTED_AVERAGE_DECAY_RATE = 0.08
|
@@ -127,21 +128,12 @@ def run_calmar_nargo(
|
|
127
128
|
f"DEBUG: Prover.toml after nargo execute - first line of log_returns: {content.split('log_returns = [')[1].split(',')[0] if 'log_returns = [' in content else 'NOT_FOUND'}"
|
128
129
|
)
|
129
130
|
|
130
|
-
fp = 0
|
131
|
-
if "Field" in result.stdout:
|
132
|
-
unsigned_i = int(result.stdout.split("Field(")[1].split(")")[0])
|
133
|
-
if unsigned_i >= 2**63:
|
134
|
-
i = unsigned_i - 2**64
|
135
|
-
else:
|
136
|
-
i = unsigned_i
|
137
|
-
if i == CALMAR_NOCONFIDENCE_VALUE:
|
138
|
-
fp = float(i)
|
139
|
-
else:
|
140
|
-
fp = i / SCALE
|
141
131
|
if result.returncode != 0:
|
142
132
|
print(result.stderr)
|
143
133
|
raise RuntimeError("nargo execute failed")
|
144
134
|
|
135
|
+
fp = parse_demo_output(result.stdout, SCALE, CALMAR_NOCONFIDENCE_VALUE)
|
136
|
+
|
145
137
|
# Debug: Final check of Prover.toml at end of function
|
146
138
|
with open(prover_path, "r") as f:
|
147
139
|
content = f.read()
|
@@ -3,6 +3,7 @@ import matplotlib.pyplot as plt
|
|
3
3
|
import numpy as np
|
4
4
|
import subprocess
|
5
5
|
from . import utils
|
6
|
+
from ..parsing_utils import parse_demo_output
|
6
7
|
|
7
8
|
SCALE = 10_000_000
|
8
9
|
WEIGHTED_AVERAGE_DECAY_RATE = 0.08
|
@@ -114,21 +115,12 @@ def run_omega_nargo(log_returns: list[float], bypass_confidence: bool, weighting
|
|
114
115
|
text=True,
|
115
116
|
cwd=prover_path.rsplit("/", 1)[0],
|
116
117
|
)
|
117
|
-
|
118
|
-
if "Field" in result.stdout:
|
119
|
-
unsigned_i = int(result.stdout.split("Field(")[1].split(")")[0])
|
120
|
-
if unsigned_i >= 2**63:
|
121
|
-
i = unsigned_i - 2**64
|
122
|
-
else:
|
123
|
-
i = unsigned_i
|
124
|
-
if i == OMEGA_NOCONFIDENCE_VALUE:
|
125
|
-
fp = float(i)
|
126
|
-
else:
|
127
|
-
fp = i / SCALE
|
118
|
+
|
128
119
|
if result.returncode != 0:
|
129
120
|
print(result.stderr)
|
130
121
|
raise RuntimeError("nargo execute failed")
|
131
|
-
|
122
|
+
|
123
|
+
return parse_demo_output(result.stdout, SCALE, OMEGA_NOCONFIDENCE_VALUE)
|
132
124
|
|
133
125
|
|
134
126
|
def compare_implementations(test_data: dict, bypass_confidence: bool, weighting: bool):
|
@@ -4,6 +4,7 @@ import numpy as np
|
|
4
4
|
import math
|
5
5
|
import subprocess
|
6
6
|
from . import utils
|
7
|
+
from ..parsing_utils import parse_demo_output
|
7
8
|
|
8
9
|
SCALE = 10_000_000
|
9
10
|
WEIGHTED_AVERAGE_DECAY_RATE = 0.08
|
@@ -126,21 +127,12 @@ def run_sharpe_nargo(
|
|
126
127
|
text=True,
|
127
128
|
cwd=prover_path.rsplit("/", 1)[0],
|
128
129
|
)
|
129
|
-
|
130
|
-
if "Field" in result.stdout:
|
131
|
-
unsigned_i = int(result.stdout.split("Field(")[1].split(")")[0])
|
132
|
-
if unsigned_i >= 2**63:
|
133
|
-
i = unsigned_i - 2**64
|
134
|
-
else:
|
135
|
-
i = unsigned_i
|
136
|
-
if i == SHARPE_NOCONFIDENCE_VALUE:
|
137
|
-
fp = float(i)
|
138
|
-
else:
|
139
|
-
fp = i / SCALE
|
130
|
+
|
140
131
|
if result.returncode != 0:
|
141
132
|
print(result.stderr)
|
142
133
|
raise RuntimeError("nargo execute failed")
|
143
|
-
|
134
|
+
|
135
|
+
return parse_demo_output(result.stdout, SCALE, SHARPE_NOCONFIDENCE_VALUE)
|
144
136
|
|
145
137
|
|
146
138
|
def compare_implementations(test_data: dict, bypass_confidence: bool, weighting: bool):
|
@@ -4,6 +4,7 @@ import numpy as np
|
|
4
4
|
import math
|
5
5
|
import subprocess
|
6
6
|
from . import utils
|
7
|
+
from ..parsing_utils import parse_demo_output
|
7
8
|
|
8
9
|
SCALE = 10_000_000
|
9
10
|
WEIGHTED_AVERAGE_DECAY_RATE = 0.08
|
@@ -140,21 +141,11 @@ def run_sortino_nargo(
|
|
140
141
|
text=True,
|
141
142
|
cwd=prover_path.rsplit("/", 1)[0],
|
142
143
|
)
|
143
|
-
fp = 0
|
144
|
-
if "Field" in result.stdout:
|
145
|
-
unsigned_i = int(result.stdout.split("Field(")[1].split(")")[0])
|
146
|
-
if unsigned_i >= 2**63:
|
147
|
-
i = unsigned_i - 2**64
|
148
|
-
else:
|
149
|
-
i = unsigned_i
|
150
|
-
if i == SORTINO_NOCONFIDENCE_VALUE:
|
151
|
-
fp = float(i)
|
152
|
-
else:
|
153
|
-
fp = i / SCALE
|
154
144
|
if result.returncode != 0:
|
155
145
|
print(result.stderr)
|
156
146
|
raise RuntimeError("nargo execute failed")
|
157
|
-
|
147
|
+
|
148
|
+
return parse_demo_output(result.stdout, SCALE, SORTINO_NOCONFIDENCE_VALUE)
|
158
149
|
|
159
150
|
|
160
151
|
def compare_implementations(test_data: dict, bypass_confidence: bool, weighting: bool):
|
@@ -3,6 +3,7 @@ import numpy as np
|
|
3
3
|
import subprocess
|
4
4
|
from scipy.stats import ttest_1samp
|
5
5
|
from . import utils
|
6
|
+
from ..parsing_utils import parse_demo_output
|
6
7
|
|
7
8
|
SCALE = 10_000_000
|
8
9
|
WEIGHTED_AVERAGE_DECAY_RATE = 0.08
|
@@ -59,21 +60,13 @@ def run_tstat_nargo(log_returns: list[float], bypass_confidence: bool, weighting
|
|
59
60
|
text=True,
|
60
61
|
cwd=prover_path.rsplit("/", 1)[0],
|
61
62
|
)
|
62
|
-
fp = 0
|
63
|
-
if "Field" in result.stdout:
|
64
|
-
unsigned_i = int(result.stdout.split("Field(")[1].split(")")[0])
|
65
|
-
if unsigned_i >= 2**63:
|
66
|
-
i = unsigned_i - 2**64
|
67
|
-
else:
|
68
|
-
i = unsigned_i
|
69
|
-
if i == STATISTICAL_CONFIDENCE_NOCONFIDENCE_VALUE:
|
70
|
-
fp = float(i)
|
71
|
-
else:
|
72
|
-
fp = i / SCALE
|
73
63
|
if result.returncode != 0:
|
74
64
|
print(result.stderr)
|
75
65
|
raise RuntimeError("nargo execute failed")
|
76
|
-
|
66
|
+
|
67
|
+
return parse_demo_output(
|
68
|
+
result.stdout, SCALE, STATISTICAL_CONFIDENCE_NOCONFIDENCE_VALUE
|
69
|
+
)
|
77
70
|
|
78
71
|
|
79
72
|
def compare_implementations(test_data: dict, bypass_confidence: bool, weighting: bool):
|
@@ -0,0 +1,201 @@
|
|
1
|
+
"""
|
2
|
+
Shared parsing utilities for nargo output across all demos.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import re
|
6
|
+
|
7
|
+
|
8
|
+
def parse_single_field_output(output):
|
9
|
+
"""
|
10
|
+
Parses nargo output that contains a single field value.
|
11
|
+
Returns the integer value, or None if no field found.
|
12
|
+
"""
|
13
|
+
if "0x" in output:
|
14
|
+
hex_match = output.split("0x")[1].split()[0]
|
15
|
+
return int(hex_match, 16)
|
16
|
+
|
17
|
+
if "Field(" in output:
|
18
|
+
return int(output.split("Field(")[1].split(")")[0])
|
19
|
+
|
20
|
+
if "Circuit output:" in output:
|
21
|
+
output_line = output.split("Circuit output:")[1].strip().split()[0]
|
22
|
+
return int(output_line)
|
23
|
+
|
24
|
+
lines = output.strip().split("\n")
|
25
|
+
for line in lines:
|
26
|
+
line = line.strip()
|
27
|
+
if (
|
28
|
+
line
|
29
|
+
and not any(char.isalpha() for char in line)
|
30
|
+
and line.replace("-", "").replace(".", "").isdigit()
|
31
|
+
):
|
32
|
+
return int(float(line))
|
33
|
+
|
34
|
+
return None
|
35
|
+
|
36
|
+
|
37
|
+
def field_to_signed_int(field_str):
|
38
|
+
"""
|
39
|
+
Convert a field string to a signed integer, handling two's complement.
|
40
|
+
"""
|
41
|
+
if isinstance(field_str, str) and field_str.startswith("0x"):
|
42
|
+
val = int(field_str, 16)
|
43
|
+
else:
|
44
|
+
val = int(field_str)
|
45
|
+
|
46
|
+
# Noir's i64 as u64 casting uses standard two's complement
|
47
|
+
# Convert from u64 back to i64 using two's complement
|
48
|
+
if val >= 2**63: # If the high bit is set, it's negative
|
49
|
+
return val - 2**64 # Convert from unsigned to signed
|
50
|
+
else:
|
51
|
+
return val # Positive values unchanged
|
52
|
+
|
53
|
+
|
54
|
+
def parse_demo_output(output, scale=10_000_000, no_confidence_value=-100):
|
55
|
+
"""
|
56
|
+
Standardized parsing for demo output.
|
57
|
+
|
58
|
+
Args:
|
59
|
+
output: Raw stdout
|
60
|
+
scale: Scale factor used in the circuit (default 10M)
|
61
|
+
no_confidence_value: Value indicating no confidence result
|
62
|
+
|
63
|
+
Returns:
|
64
|
+
Float value scaled appropriately, or the no_confidence_value as-is
|
65
|
+
"""
|
66
|
+
field_value = parse_single_field_output(output)
|
67
|
+
|
68
|
+
if field_value is None:
|
69
|
+
raise ValueError(f"Could not parse field value from nargo output: {output}")
|
70
|
+
|
71
|
+
signed_value = field_to_signed_int(field_value)
|
72
|
+
|
73
|
+
# If it's the no confidence marker, return as-is
|
74
|
+
if signed_value == no_confidence_value:
|
75
|
+
return float(signed_value)
|
76
|
+
|
77
|
+
# Otherwise scale it back
|
78
|
+
return signed_value / scale
|
79
|
+
|
80
|
+
|
81
|
+
def parse_nargo_struct_output(output):
|
82
|
+
"""
|
83
|
+
Parses the raw output of a nargo execute command that returns a struct.
|
84
|
+
"""
|
85
|
+
if (
|
86
|
+
"[" in output
|
87
|
+
and "]" in output
|
88
|
+
and not ("MerkleTree" in output or "ReturnsData" in output)
|
89
|
+
):
|
90
|
+
array_matches = re.findall(r"\[([^\]]+)\]", output)
|
91
|
+
if array_matches:
|
92
|
+
array_content = array_matches[-1]
|
93
|
+
values = []
|
94
|
+
for item in array_content.split(","):
|
95
|
+
item = item.strip()
|
96
|
+
if item.startswith("0x"):
|
97
|
+
try:
|
98
|
+
values.append(str(int(item, 16)))
|
99
|
+
except ValueError:
|
100
|
+
continue
|
101
|
+
elif item.lstrip("-").isdigit():
|
102
|
+
values.append(item)
|
103
|
+
if values:
|
104
|
+
return values
|
105
|
+
|
106
|
+
struct_start = output.find("{")
|
107
|
+
struct_end = output.rfind("}")
|
108
|
+
|
109
|
+
if struct_start == -1 or struct_end == -1:
|
110
|
+
return re.findall(r"Field\(([-0-9]+)\)", output)
|
111
|
+
|
112
|
+
struct_content = output[struct_start : struct_end + 1]
|
113
|
+
|
114
|
+
if "MerkleTree" in output:
|
115
|
+
tree = {}
|
116
|
+
try:
|
117
|
+
if "leaf_hashes:" in struct_content:
|
118
|
+
start = struct_content.find("leaf_hashes:") + len("leaf_hashes:")
|
119
|
+
end = struct_content.find(", path_elements:")
|
120
|
+
leaf_section = struct_content[start:end].strip()
|
121
|
+
if leaf_section.startswith("[") and leaf_section.endswith("]"):
|
122
|
+
leaf_content = leaf_section[1:-1]
|
123
|
+
tree["leaf_hashes"] = [
|
124
|
+
x.strip() for x in leaf_content.split(",") if x.strip()
|
125
|
+
]
|
126
|
+
|
127
|
+
# Parse path_elements
|
128
|
+
if "path_elements:" in struct_content:
|
129
|
+
start = struct_content.find("path_elements:") + len("path_elements:")
|
130
|
+
end = struct_content.find(", path_indices:")
|
131
|
+
path_elem_section = struct_content[start:end].strip()
|
132
|
+
tree["path_elements"] = parse_nested_arrays(path_elem_section)
|
133
|
+
|
134
|
+
# Parse path_indices
|
135
|
+
if "path_indices:" in struct_content:
|
136
|
+
start = struct_content.find("path_indices:") + len("path_indices:")
|
137
|
+
end = struct_content.find(", root:")
|
138
|
+
path_idx_section = struct_content[start:end].strip()
|
139
|
+
tree["path_indices"] = parse_nested_arrays(path_idx_section)
|
140
|
+
|
141
|
+
# Parse root
|
142
|
+
if "root:" in struct_content:
|
143
|
+
start = struct_content.find("root:") + len("root:")
|
144
|
+
root_section = struct_content[start:].strip().rstrip("}")
|
145
|
+
tree["root"] = root_section.strip()
|
146
|
+
|
147
|
+
return tree
|
148
|
+
except Exception:
|
149
|
+
pass
|
150
|
+
|
151
|
+
values = []
|
152
|
+
|
153
|
+
parts = re.split(r"[,\s]+", struct_content)
|
154
|
+
for part in parts:
|
155
|
+
part = part.strip("{}[](), \t\n\r")
|
156
|
+
if not part:
|
157
|
+
continue
|
158
|
+
|
159
|
+
if part.startswith("0x") and len(part) > 2:
|
160
|
+
try:
|
161
|
+
values.append(str(int(part, 16)))
|
162
|
+
continue
|
163
|
+
except ValueError:
|
164
|
+
pass
|
165
|
+
|
166
|
+
if part.lstrip("-").isdigit():
|
167
|
+
values.append(part)
|
168
|
+
|
169
|
+
return values
|
170
|
+
|
171
|
+
|
172
|
+
def parse_nested_arrays(section):
|
173
|
+
"""Helper function to parse nested array structures like [[...], [...]]"""
|
174
|
+
if not section.strip().startswith("["):
|
175
|
+
return []
|
176
|
+
|
177
|
+
arrays = []
|
178
|
+
depth = 0
|
179
|
+
current_array = ""
|
180
|
+
|
181
|
+
for char in section:
|
182
|
+
if char == "[":
|
183
|
+
depth += 1
|
184
|
+
if depth == 2: # Start of inner array
|
185
|
+
current_array = ""
|
186
|
+
elif depth == 1: # Start of outer array
|
187
|
+
continue
|
188
|
+
elif char == "]":
|
189
|
+
depth -= 1
|
190
|
+
if depth == 1: # End of inner array
|
191
|
+
if current_array.strip():
|
192
|
+
arrays.append(
|
193
|
+
[x.strip() for x in current_array.split(",") if x.strip()]
|
194
|
+
)
|
195
|
+
current_array = ""
|
196
|
+
elif depth == 0: # End of outer array
|
197
|
+
break
|
198
|
+
elif depth == 2: # Inside inner array
|
199
|
+
current_array += char
|
200
|
+
|
201
|
+
return arrays
|
@@ -5,6 +5,7 @@ import os
|
|
5
5
|
import time
|
6
6
|
from .min_metrics import MinMetrics
|
7
7
|
import math
|
8
|
+
import bittensor as bt
|
8
9
|
|
9
10
|
# Constants for the circuit
|
10
11
|
MAX_CHECKPOINTS = 200
|
@@ -19,16 +20,16 @@ def run_command(command, cwd, verbose=True):
|
|
19
20
|
"""Executes a command in a given directory and returns the output."""
|
20
21
|
result = subprocess.run(command, capture_output=True, text=True, cwd=cwd)
|
21
22
|
if verbose:
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
23
|
+
bt.logging.info("--- nargo stdout ---")
|
24
|
+
bt.logging.info(result.stdout)
|
25
|
+
bt.logging.info("--- nargo stderr ---")
|
26
|
+
bt.logging.info(result.stderr)
|
27
|
+
bt.logging.info("--------------------")
|
27
28
|
if result.returncode != 0:
|
28
29
|
if verbose:
|
29
|
-
|
30
|
-
|
31
|
-
|
30
|
+
bt.logging.error("Error:")
|
31
|
+
bt.logging.error(result.stdout)
|
32
|
+
bt.logging.error(result.stderr)
|
32
33
|
raise RuntimeError(
|
33
34
|
f"Command {' '.join(command)} failed with exit code {result.returncode}"
|
34
35
|
)
|
@@ -265,25 +266,39 @@ def generate_proof(data=None, miner_hotkey=None, verbose=None):
|
|
265
266
|
Returns:
|
266
267
|
Dictionary with proof results including status, portfolio_metrics, etc.
|
267
268
|
"""
|
269
|
+
bt.logging.info(
|
270
|
+
f"generate_proof called with verbose={verbose}, miner_hotkey={miner_hotkey[:8] if miner_hotkey else None}"
|
271
|
+
)
|
272
|
+
|
268
273
|
# Auto-detect mode: demo mode if reading from file, production if data provided
|
269
274
|
is_demo_mode = data is None
|
270
275
|
if verbose is None:
|
271
276
|
verbose = is_demo_mode
|
272
277
|
|
273
|
-
|
274
|
-
|
275
|
-
|
278
|
+
bt.logging.info(
|
279
|
+
f"After auto-detect: verbose={verbose}, is_demo_mode={is_demo_mode}"
|
280
|
+
)
|
281
|
+
|
282
|
+
try:
|
283
|
+
if data is None:
|
284
|
+
if verbose:
|
285
|
+
bt.logging.info("Loading data from validator_checkpoint.json...")
|
276
286
|
import json
|
277
287
|
|
278
288
|
with open("validator_checkpoint.json", "r") as f:
|
279
289
|
data = json.load(f)
|
290
|
+
except Exception as e:
|
291
|
+
bt.logging.error(f"Failed to load data {e}")
|
280
292
|
|
281
293
|
if miner_hotkey is None:
|
282
294
|
miner_hotkey = list(data["perf_ledgers"].keys())[0]
|
283
295
|
if verbose:
|
284
|
-
|
296
|
+
bt.logging.info(
|
297
|
+
f"No hotkey specified, using first available: {miner_hotkey}"
|
298
|
+
)
|
285
299
|
else:
|
286
|
-
|
300
|
+
if verbose:
|
301
|
+
bt.logging.info(f"Using specified hotkey: {miner_hotkey}")
|
287
302
|
|
288
303
|
if miner_hotkey not in data["perf_ledgers"]:
|
289
304
|
raise ValueError(
|
@@ -293,13 +308,13 @@ def generate_proof(data=None, miner_hotkey=None, verbose=None):
|
|
293
308
|
perf_ledger = data["perf_ledgers"][miner_hotkey]
|
294
309
|
positions = data["positions"][miner_hotkey]["positions"]
|
295
310
|
if verbose:
|
296
|
-
|
311
|
+
bt.logging.info("Preparing circuit inputs...")
|
297
312
|
|
298
313
|
cps = perf_ledger["cps"]
|
299
314
|
checkpoint_count = len(cps)
|
300
315
|
if checkpoint_count > MAX_CHECKPOINTS:
|
301
316
|
if verbose:
|
302
|
-
|
317
|
+
bt.logging.warning(
|
303
318
|
f"Warning: Miner has {checkpoint_count} checkpoints, but circuit only supports {MAX_CHECKPOINTS}. Truncating."
|
304
319
|
)
|
305
320
|
cps = cps[:MAX_CHECKPOINTS]
|
@@ -324,7 +339,7 @@ def generate_proof(data=None, miner_hotkey=None, verbose=None):
|
|
324
339
|
signals_count = len(all_orders)
|
325
340
|
if signals_count > MAX_SIGNALS:
|
326
341
|
if verbose:
|
327
|
-
|
342
|
+
bt.logging.warning(
|
328
343
|
f"Warning: Miner has {signals_count} signals, but circuit only supports {MAX_SIGNALS}. Truncating."
|
329
344
|
)
|
330
345
|
all_orders = all_orders[:MAX_SIGNALS]
|
@@ -376,12 +391,14 @@ def generate_proof(data=None, miner_hotkey=None, verbose=None):
|
|
376
391
|
] * (MAX_SIGNALS - len(signals))
|
377
392
|
|
378
393
|
if verbose:
|
379
|
-
|
394
|
+
bt.logging.info(
|
395
|
+
f"Prepared {checkpoint_count} checkpoints and {signals_count} signals."
|
396
|
+
)
|
380
397
|
|
381
398
|
if verbose:
|
382
|
-
|
399
|
+
bt.logging.info("Running tree_generator circuit...")
|
383
400
|
else:
|
384
|
-
|
401
|
+
bt.logging.info(f"Generating tree for hotkey {miner_hotkey}...")
|
385
402
|
current_dir = os.path.dirname(os.path.abspath(__file__))
|
386
403
|
tree_generator_dir = os.path.join(current_dir, "tree_generator")
|
387
404
|
|
@@ -574,7 +591,6 @@ def generate_proof(data=None, miner_hotkey=None, verbose=None):
|
|
574
591
|
|
575
592
|
# Calculate MinMetrics (Python) for comparison with ZK circuit
|
576
593
|
try:
|
577
|
-
|
578
594
|
# Extract daily log returns from checkpoint data (same as ZK circuit)
|
579
595
|
daily_log_returns = []
|
580
596
|
for cp in cps:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: proof-of-portfolio
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.48
|
4
4
|
Summary: Zero-Knowledge Proof framework for verifiable, private portfolio performance metrics
|
5
5
|
Author-email: "Inference Labs, Inc." <info@inferencelabs.com>
|
6
6
|
Requires-Python: >=3.10
|
@@ -10,6 +10,7 @@ Requires-Dist: colorama
|
|
10
10
|
Requires-Dist: matplotlib
|
11
11
|
Requires-Dist: toml
|
12
12
|
Requires-Dist: scipy
|
13
|
+
Requires-Dist: bittensor
|
13
14
|
Provides-Extra: dev
|
14
15
|
Requires-Dist: black; extra == "dev"
|
15
16
|
Requires-Dist: flake8; extra == "dev"
|
@@ -1,11 +1,12 @@
|
|
1
|
-
proof_of_portfolio/__init__.py,sha256=
|
2
|
-
proof_of_portfolio/_version.py,sha256=
|
1
|
+
proof_of_portfolio/__init__.py,sha256=cb8b_zhdFvxvpq7I5g3bP5RM7yAmD8kv4lrcrhD3-1w,3580
|
2
|
+
proof_of_portfolio/_version.py,sha256=7uSHG9iByhMK7vMNxzZ24YxAef2krt6H9bFOyiw4gQg,66
|
3
3
|
proof_of_portfolio/analyze_data.py,sha256=t80ueFtBUzcWTrPiamiC1HXvw5Em-151zXJx0cCk8sQ,3709
|
4
4
|
proof_of_portfolio/main.py,sha256=JbvdAK7B6hw2sQdjT0EVCiglCQZgN3urKphdeWcW43w,30994
|
5
5
|
proof_of_portfolio/min_metrics.py,sha256=BAEpJdqYoR6RjkqX-M5BUTWlurSADvxAHuLFznH3AT8,13196
|
6
6
|
proof_of_portfolio/miner.py,sha256=eGXcPMRQVAepzXJj1ePbbDAf-72E9Yj9n-yfP7GohLw,17177
|
7
|
+
proof_of_portfolio/parsing_utils.py,sha256=Hx61Sby5mEbC6uzhG9G69K7YPCLbjU6vK8fazpNBeLc,6463
|
7
8
|
proof_of_portfolio/post_install.py,sha256=e-57Z_h-svQdjA8ibsM985MXmdV6-KLyymQm3Cgu8YM,5654
|
8
|
-
proof_of_portfolio/proof_generator.py,sha256=
|
9
|
+
proof_of_portfolio/proof_generator.py,sha256=Cpc8Lk1R9tqCd7E7PrCEX_reqm6ReKo9WxPk65Zux8I,27943
|
9
10
|
proof_of_portfolio/signal_processor.py,sha256=JQjnkMJEbv_MWIgKNrjKjrBIcIT5pAkAlCneEOGsqT0,6702
|
10
11
|
proof_of_portfolio/validator.py,sha256=kt3BeaXOffv-h52PZBKV1YHUWtiGsnPte16m3EJpITY,3839
|
11
12
|
proof_of_portfolio/circuits/Nargo.toml,sha256=D6ycN7H3xiTcWHH5wz4qXYTXn7Ht0WgPr9w4B7d8ZGw,141
|
@@ -63,15 +64,15 @@ proof_of_portfolio/demo/just_tstat/target/just_tstat.json,sha256=Oc1jZxI8eLvxESc
|
|
63
64
|
proof_of_portfolio/demo/merkle_generator/Nargo.toml,sha256=CDsvYcN3fq_PxTKLPp9_pYF8Vyx3vXLlRVUDIsN7VQU,94
|
64
65
|
proof_of_portfolio/demo/merkle_generator/src/main.nr,sha256=-EkIH4rTaXcxF4uiqXU29t6MY-mtlmlJ_3JA9fADP68,1243
|
65
66
|
proof_of_portfolio/demos/all.py,sha256=kiI2nbVNUUBUPcTKNYpcBu8IXn3xnXKOtH5sNSLJCOw,3430
|
66
|
-
proof_of_portfolio/demos/calmar.py,sha256=
|
67
|
+
proof_of_portfolio/demos/calmar.py,sha256=Inc9GOa6GDU0jO-5y9_Fzf7IS8ntS6XlibUx06F2Jro,8634
|
67
68
|
proof_of_portfolio/demos/drawdown.py,sha256=O85B54YziXtnFcPbrtCg2UVqWSalfqceMcL2THQqgyI,4728
|
68
69
|
proof_of_portfolio/demos/generate_input_data.py,sha256=tw4ijtvj3fh7l0nlxEeJ4vD8i0J9ZYSV9LDTIdwvEqE,4560
|
69
70
|
proof_of_portfolio/demos/log_returns.py,sha256=LAwB36xNWv1nIUqe84kk-AW7QSr11KvVo7vRN_QoM1c,5621
|
70
71
|
proof_of_portfolio/demos/main.py,sha256=3o46VM_YqG_j01DpPFfYMDDkQpTFjInVhOyz4gEZSMU,1015
|
71
|
-
proof_of_portfolio/demos/omega.py,sha256=
|
72
|
-
proof_of_portfolio/demos/sharpe.py,sha256=
|
73
|
-
proof_of_portfolio/demos/sortino.py,sha256=
|
74
|
-
proof_of_portfolio/demos/tstat.py,sha256=
|
72
|
+
proof_of_portfolio/demos/omega.py,sha256=3vL7oucVvoqbimmkiDqEgSPzcEdipGQqNaHsrPBQPm0,7167
|
73
|
+
proof_of_portfolio/demos/sharpe.py,sha256=1Is5hMYk0C29llzlQVhhs1HZKE8rWWZxH0tVJCTafjU,7821
|
74
|
+
proof_of_portfolio/demos/sortino.py,sha256=2DJjW-BkQHPkCCDIwbxLINGbE907IKHKzoDZBPbudqE,8230
|
75
|
+
proof_of_portfolio/demos/tstat.py,sha256=t8avL5pxAxmMrSqYACPHl71plRvY3PWSFNIttZWmJLQ,5333
|
75
76
|
proof_of_portfolio/demos/utils.py,sha256=dgpNCxmXSRxPY8MN0Pd-oYL-_sqi7I-rXqtraCejqlc,6985
|
76
77
|
proof_of_portfolio/returns_generator/Nargo.toml,sha256=IbhsdutLFwRdIkXZN0EWFVySFW_tmVfpZSMSmqbUS48,130
|
77
78
|
proof_of_portfolio/returns_generator/src/main.nr,sha256=WIiDyCxdtbH1VomFOAOdYeEbgJr8dOQb7EpVMalmK2g,5315
|
@@ -80,8 +81,8 @@ proof_of_portfolio/tree_generator/Nargo.toml,sha256=O6iSvb-EpV0XcETiDxNgSp7XKNiY
|
|
80
81
|
proof_of_portfolio/tree_generator/target.gz,sha256=7LPzAb8JvKWSDOzyW5vI_6NKQ0aB9cb31q4CWbchFSw,308614
|
81
82
|
proof_of_portfolio/tree_generator/src/main.nr,sha256=zHG_0OphqSROyeVc7yTSEULg4bYS8B-LsmvTzTl8aW4,2393
|
82
83
|
proof_of_portfolio/tree_generator/target/tree_generator.json,sha256=M_bI5et5ncgILJ_Czen4ZsGfWHwComEVxMLQpIWmN1k,1540889
|
83
|
-
proof_of_portfolio-0.0.
|
84
|
-
proof_of_portfolio-0.0.
|
85
|
-
proof_of_portfolio-0.0.
|
86
|
-
proof_of_portfolio-0.0.
|
87
|
-
proof_of_portfolio-0.0.
|
84
|
+
proof_of_portfolio-0.0.48.dist-info/METADATA,sha256=CsVXIrXZbw7fufYF3dGwzS5Y0TcRqOQuLScQsFAKuPc,7076
|
85
|
+
proof_of_portfolio-0.0.48.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
86
|
+
proof_of_portfolio-0.0.48.dist-info/entry_points.txt,sha256=KeLSJT_UJtr1WiLTkhlGqWQuPKbO_ylgj6OXOC2gJV4,53
|
87
|
+
proof_of_portfolio-0.0.48.dist-info/top_level.txt,sha256=sY5xZnE6YuiISK1IuRHPfl71NV0vXO3N3YA2li_SPXU,19
|
88
|
+
proof_of_portfolio-0.0.48.dist-info/RECORD,,
|
File without changes
|
{proof_of_portfolio-0.0.46.dist-info → proof_of_portfolio-0.0.48.dist-info}/entry_points.txt
RENAMED
File without changes
|
File without changes
|