proof-of-portfolio 0.0.47__tar.gz → 0.0.48__tar.gz

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.
Files changed (95) hide show
  1. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/PKG-INFO +2 -1
  2. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/_version.py +1 -1
  3. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/demos/calmar.py +3 -11
  4. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/demos/omega.py +4 -12
  5. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/demos/sharpe.py +4 -12
  6. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/demos/sortino.py +3 -12
  7. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/demos/tstat.py +5 -12
  8. proof_of_portfolio-0.0.48/proof_of_portfolio/parsing_utils.py +201 -0
  9. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/proof_generator.py +36 -20
  10. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio.egg-info/PKG-INFO +2 -1
  11. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio.egg-info/SOURCES.txt +1 -1
  12. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio.egg-info/requires.txt +1 -0
  13. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/pyproject.toml +5 -2
  14. proof_of_portfolio-0.0.48/setup.cfg +4 -0
  15. proof_of_portfolio-0.0.47/setup.cfg +0 -9
  16. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/README.md +0 -0
  17. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/__init__.py +0 -0
  18. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/analyze_data.py +0 -0
  19. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/circuits/Nargo.toml +0 -0
  20. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/circuits/components/Nargo.toml +0 -0
  21. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/circuits/components/src/core/calmar.nr +0 -0
  22. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/circuits/components/src/core/cps_to_log_returns.nr +0 -0
  23. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/circuits/components/src/core/drawdown.nr +0 -0
  24. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/circuits/components/src/core/merkle.nr +0 -0
  25. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/circuits/components/src/core/mod.nr +0 -0
  26. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/circuits/components/src/core/omega.nr +0 -0
  27. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/circuits/components/src/core/position.nr +0 -0
  28. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/circuits/components/src/core/sharpe.nr +0 -0
  29. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/circuits/components/src/core/sortino.nr +0 -0
  30. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/circuits/components/src/core/tstat.nr +0 -0
  31. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/circuits/components/src/float/Nargo.toml +0 -0
  32. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/circuits/components/src/float/Verifier.toml +0 -0
  33. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/circuits/components/src/float/src/lib.nr +0 -0
  34. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/circuits/components/src/lib.nr +0 -0
  35. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/circuits/components/src/nrstat.nr +0 -0
  36. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/circuits/components/src/utils/ann_excess_return.nr +0 -0
  37. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/circuits/components/src/utils/ann_volatility.nr +0 -0
  38. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/circuits/components/src/utils/average.nr +0 -0
  39. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/circuits/components/src/utils/constants.nr +0 -0
  40. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/circuits/components/src/utils/mod.nr +0 -0
  41. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/circuits/components/src/utils/sqrt.nr +0 -0
  42. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/circuits/components/src/utils/variance.nr +0 -0
  43. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/circuits/components/src/utils/weighting_distribution.nr +0 -0
  44. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/circuits/generate_inputs.py +0 -0
  45. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/circuits/proof/proof +0 -0
  46. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/circuits/proof/public_inputs +0 -0
  47. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/circuits/src/main.nr +0 -0
  48. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/circuits/target/circuits.json +0 -0
  49. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/circuits/vk/vk +0 -0
  50. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/demo/just_calmar/Nargo.toml +0 -0
  51. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/demo/just_calmar/src/main.nr +0 -0
  52. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/demo/just_calmar/target/just_calmar.json +0 -0
  53. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/demo/just_cps_to_log_return/Nargo.toml +0 -0
  54. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/demo/just_cps_to_log_return/src/main.nr +0 -0
  55. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/demo/just_cps_to_log_return/target/just_cps_to_log_return.json +0 -0
  56. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/demo/just_drawdown/Nargo.toml +0 -0
  57. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/demo/just_drawdown/src/main.nr +0 -0
  58. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/demo/just_drawdown/target/just_drawdown.json +0 -0
  59. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/demo/just_omega/Nargo.toml +0 -0
  60. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/demo/just_omega/src/main.nr +0 -0
  61. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/demo/just_omega/target/just_omega.json +0 -0
  62. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/demo/just_sharpe/Nargo.toml +0 -0
  63. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/demo/just_sharpe/src/main.nr +0 -0
  64. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/demo/just_sharpe/target/just_sharpe.json +0 -0
  65. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/demo/just_sortino/Nargo.toml +0 -0
  66. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/demo/just_sortino/src/main.nr +0 -0
  67. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/demo/just_sortino/target/just_sortino.json +0 -0
  68. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/demo/just_tstat/Nargo.toml +0 -0
  69. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/demo/just_tstat/src/main.nr +0 -0
  70. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/demo/just_tstat/target/just_tstat.json +0 -0
  71. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/demo/merkle_generator/Nargo.toml +0 -0
  72. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/demo/merkle_generator/src/main.nr +0 -0
  73. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/demos/all.py +0 -0
  74. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/demos/drawdown.py +0 -0
  75. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/demos/generate_input_data.py +0 -0
  76. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/demos/log_returns.py +0 -0
  77. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/demos/main.py +0 -0
  78. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/demos/utils.py +0 -0
  79. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/main.py +0 -0
  80. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/min_metrics.py +0 -0
  81. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/miner.py +0 -0
  82. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/post_install.py +0 -0
  83. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/returns_generator/Nargo.toml +0 -0
  84. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/returns_generator/src/main.nr +0 -0
  85. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/returns_generator/target/returns_generator.json +0 -0
  86. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/signal_processor.py +0 -0
  87. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/tree_generator/Nargo.toml +0 -0
  88. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/tree_generator/src/main.nr +0 -0
  89. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/tree_generator/target/tree_generator.json +0 -0
  90. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/tree_generator/target.gz +0 -0
  91. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio/validator.py +0 -0
  92. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio.egg-info/dependency_links.txt +0 -0
  93. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio.egg-info/entry_points.txt +0 -0
  94. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/proof_of_portfolio.egg-info/top_level.txt +0 -0
  95. {proof_of_portfolio-0.0.47 → proof_of_portfolio-0.0.48}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: proof-of-portfolio
3
- Version: 0.0.47
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,2 +1,2 @@
1
1
  # This file is auto-generated during build
2
- __version__ = "0.0.47"
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
- fp = 0
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
- return fp
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
- fp = 0
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
- return fp
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
- return fp
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
- return fp
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
- print("--- nargo stdout ---")
23
- print(result.stdout)
24
- print("--- nargo stderr ---")
25
- print(result.stderr)
26
- print("--------------------")
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
- print("Error:")
30
- print(result.stdout)
31
- print(result.stderr)
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
- if data is None:
274
- if verbose:
275
- print("Loading data from validator_checkpoint.json...")
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
- print(f"No hotkey specified, using first available: {miner_hotkey}")
296
+ bt.logging.info(
297
+ f"No hotkey specified, using first available: {miner_hotkey}"
298
+ )
285
299
  else:
286
- print(f"Using specified hotkey: {miner_hotkey}")
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
- print("Preparing circuit inputs...")
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
- print(
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
- print(
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
- print(f"Prepared {checkpoint_count} checkpoints and {signals_count} signals.")
394
+ bt.logging.info(
395
+ f"Prepared {checkpoint_count} checkpoints and {signals_count} signals."
396
+ )
380
397
 
381
398
  if verbose:
382
- print("Running tree_generator circuit...")
399
+ bt.logging.info("Running tree_generator circuit...")
383
400
  else:
384
- print(f"Generating tree for hotkey {miner_hotkey}...")
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.47
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,6 +1,5 @@
1
1
  README.md
2
2
  pyproject.toml
3
- setup.cfg
4
3
  setup.py
5
4
  proof_of_portfolio/__init__.py
6
5
  proof_of_portfolio/_version.py
@@ -8,6 +7,7 @@ proof_of_portfolio/analyze_data.py
8
7
  proof_of_portfolio/main.py
9
8
  proof_of_portfolio/min_metrics.py
10
9
  proof_of_portfolio/miner.py
10
+ proof_of_portfolio/parsing_utils.py
11
11
  proof_of_portfolio/post_install.py
12
12
  proof_of_portfolio/proof_generator.py
13
13
  proof_of_portfolio/signal_processor.py
@@ -3,6 +3,7 @@ colorama
3
3
  matplotlib
4
4
  toml
5
5
  scipy
6
+ bittensor
6
7
 
7
8
  [dev]
8
9
  black
@@ -5,12 +5,12 @@ build-backend = "setuptools.build_meta"
5
5
 
6
6
  [project]
7
7
  name = "proof-of-portfolio"
8
- version = "0.0.47"
8
+ version = "0.0.48"
9
9
  description = "Zero-Knowledge Proof framework for verifiable, private portfolio performance metrics"
10
10
  readme = "README.md"
11
11
  authors = [{ name = "Inference Labs, Inc.", email = "info@inferencelabs.com" }]
12
12
  requires-python = ">=3.10"
13
- dependencies = ["numpy", "colorama", "matplotlib", "toml", "scipy"]
13
+ dependencies = ["numpy", "colorama", "matplotlib", "toml", "scipy", "bittensor"]
14
14
 
15
15
  [project.optional-dependencies]
16
16
  dev = ["black", "flake8", "pre-commit"]
@@ -33,3 +33,6 @@ include-package-data = true
33
33
  "demo/**/*",
34
34
  "returns_generator/**/*",
35
35
  ]
36
+
37
+ [tool.ruff.lint]
38
+ ignore = ["E741"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -1,9 +0,0 @@
1
- [flake8]
2
- max-line-length = 88
3
- extend-ignore = E203,E501,E741
4
- exclude = build/,dist/,*.egg-info/
5
-
6
- [egg_info]
7
- tag_build =
8
- tag_date = 0
9
-