From 72e3f9e154974cfbdb41ba4891fa6cb1691bd5b2 Mon Sep 17 00:00:00 2001 From: Mostafa Date: Sun, 13 Jul 2025 22:02:00 +0800 Subject: [PATCH 1/4] feat: add evaluate_polynomial method --- pactus/utils/utils.py | 25 +++++++++++++++++++++ tests/test_utils.py | 51 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 tests/test_utils.py diff --git a/pactus/utils/utils.py b/pactus/utils/utils.py index 69df1b2..243be7c 100644 --- a/pactus/utils/utils.py +++ b/pactus/utils/utils.py @@ -17,3 +17,28 @@ def encode_from_base256_with_type(hrp: str, typ: str, data: bytes) -> str: converted = bech32m.convertbits(list(data), 8, 5, pad=True) converted = [typ, *converted] return bech32m.bech32_encode(hrp, converted, bech32m.Encoding.BECH32M) + + +def evaluate_polynomial(c: list[int], x: int, mod: int) -> int | None: + """ + Evaluates the polynomial f(x) = c[0] + c[1] * x + c[2] * x^2 + ... + c[n-1] * x^(n-1) + + Args: + c: List of polynomial coefficients (c[0] is the constant term) + x: The value at which to evaluate the polynomial + mod: The modulus to use for the evaluation + + Returns: + The computed value f(x) if success, None otherwise + """ + if not c: + return None + + if len(c) == 1: + return c[0] + + y = c[-1] + for i in range(len(c)-2, -1, -1): + y = (y * x + c[i]) % mod + + return y diff --git a/tests/test_utils.py b/tests/test_utils.py new file mode 100644 index 0000000..1e3b199 --- /dev/null +++ b/tests/test_utils.py @@ -0,0 +1,51 @@ +import unittest +from pactus.utils import utils + + +class TestEvaluatePolynomial(unittest.TestCase): + def test_empty_coefficients(self): + self.assertIsNone(utils.evaluate_polynomial([], 2, 7)) + + def test_single_coefficient(self): + self.assertEqual(utils.evaluate_polynomial([5], 10, 7), 5) + + def test_x_zero(self): + # f(0) = c[0] % mod + self.assertEqual(utils.evaluate_polynomial([3, 2, 1], 0, 5), 3 % 5) + + def test_x_one(self): + # f(1) = sum(c) % mod + self.assertEqual(utils.evaluate_polynomial([1, 2, 3], 1, 7), (1 + 2 + 3) % 7) + + def test_multiple_coefficients(self): + # f(2) = 1 + 2*2 + 3*2^2 = 1 + 4 + 12 = 17 % 5 = 2 + self.assertEqual(utils.evaluate_polynomial([1, 2, 3], 2, 5), 2) + + def test_negative_coefficients(self): + # f(2) = -1 + 2*2 + (-3)*2^2 = -1 + 4 - 12 = -9 % 7 = 5 + self.assertEqual(utils.evaluate_polynomial([-1, 2, -3], 2, 7), 5) + + def test_negative_x(self): + # f(-1) = 2 + 3*(-1) + 4*(-1)^2 = 2 - 3 + 4 = 3 % 6 = 3 + self.assertEqual(utils.evaluate_polynomial([2, 3, 4], -1, 6), 3) + + def test_large_modulus(self): + # f(3) = 2 + 4*3 + 5*9 = 2 + 12 + 45 = 59 % 1000 = 59 + self.assertEqual(utils.evaluate_polynomial([2, 4, 5], 3, 1000), 59) + + def test_modulus_one(self): + # Any value mod 1 is 0 + self.assertEqual(utils.evaluate_polynomial([1, 2, 3], 5, 1), 0) + + def test_wikipedia_example(self): + # https://en.wikipedia.org/wiki/Shamir%27s_secret_sharing + self.assertEqual(utils.evaluate_polynomial([1234, 166, 94], 1, 2 ** 127 - 1), 1494) + self.assertEqual(utils.evaluate_polynomial([1234, 166, 94], 2, 2 ** 127 - 1), 1942) + self.assertEqual(utils.evaluate_polynomial([1234, 166, 94], 3, 2 ** 127 - 1), 2578) + self.assertEqual(utils.evaluate_polynomial([1234, 166, 94], 4, 2 ** 127 - 1), 3402) + self.assertEqual(utils.evaluate_polynomial([1234, 166, 94], 5, 2 ** 127 - 1), 4414) + self.assertEqual(utils.evaluate_polynomial([1234, 166, 94], 6, 2 ** 127 - 1), 5614) + + +if __name__ == "__main__": + unittest.main() \ No newline at end of file From 45b0074304625e15215158d31addd9d4ea5f88c5 Mon Sep 17 00:00:00 2001 From: Mostafa Date: Sun, 13 Jul 2025 22:10:06 +0800 Subject: [PATCH 2/4] chore: fix linting issues --- pactus/utils/utils.py | 5 +++-- tests/test_utils.py | 26 +++++++++++++++++++------- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/pactus/utils/utils.py b/pactus/utils/utils.py index 243be7c..39e9ad9 100644 --- a/pactus/utils/utils.py +++ b/pactus/utils/utils.py @@ -21,7 +21,7 @@ def encode_from_base256_with_type(hrp: str, typ: str, data: bytes) -> str: def evaluate_polynomial(c: list[int], x: int, mod: int) -> int | None: """ - Evaluates the polynomial f(x) = c[0] + c[1] * x + c[2] * x^2 + ... + c[n-1] * x^(n-1) + Evaluate the polynomial f(x) = c[0] + c[1] * x + c[2] * x^2 + ... + c[n-1] * x^(n-1). Args: c: List of polynomial coefficients (c[0] is the constant term) @@ -30,6 +30,7 @@ def evaluate_polynomial(c: list[int], x: int, mod: int) -> int | None: Returns: The computed value f(x) if success, None otherwise + """ if not c: return None @@ -38,7 +39,7 @@ def evaluate_polynomial(c: list[int], x: int, mod: int) -> int | None: return c[0] y = c[-1] - for i in range(len(c)-2, -1, -1): + for i in range(len(c) - 2, -1, -1): y = (y * x + c[i]) % mod return y diff --git a/tests/test_utils.py b/tests/test_utils.py index 1e3b199..0a91a52 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -39,13 +39,25 @@ def test_modulus_one(self): def test_wikipedia_example(self): # https://en.wikipedia.org/wiki/Shamir%27s_secret_sharing - self.assertEqual(utils.evaluate_polynomial([1234, 166, 94], 1, 2 ** 127 - 1), 1494) - self.assertEqual(utils.evaluate_polynomial([1234, 166, 94], 2, 2 ** 127 - 1), 1942) - self.assertEqual(utils.evaluate_polynomial([1234, 166, 94], 3, 2 ** 127 - 1), 2578) - self.assertEqual(utils.evaluate_polynomial([1234, 166, 94], 4, 2 ** 127 - 1), 3402) - self.assertEqual(utils.evaluate_polynomial([1234, 166, 94], 5, 2 ** 127 - 1), 4414) - self.assertEqual(utils.evaluate_polynomial([1234, 166, 94], 6, 2 ** 127 - 1), 5614) + self.assertEqual( + utils.evaluate_polynomial([1234, 166, 94], 1, 2**127 - 1), 1494 + ) + self.assertEqual( + utils.evaluate_polynomial([1234, 166, 94], 2, 2**127 - 1), 1942 + ) + self.assertEqual( + utils.evaluate_polynomial([1234, 166, 94], 3, 2**127 - 1), 2578 + ) + self.assertEqual( + utils.evaluate_polynomial([1234, 166, 94], 4, 2**127 - 1), 3402 + ) + self.assertEqual( + utils.evaluate_polynomial([1234, 166, 94], 5, 2**127 - 1), 4414 + ) + self.assertEqual( + utils.evaluate_polynomial([1234, 166, 94], 6, 2**127 - 1), 5614 + ) if __name__ == "__main__": - unittest.main() \ No newline at end of file + unittest.main() From c076f58da8c32af4fe7f800ae295e6f4907f1069 Mon Sep 17 00:00:00 2001 From: Mostafa Date: Sun, 13 Jul 2025 22:13:24 +0800 Subject: [PATCH 3/4] ci: update examples action --- .github/workflows/examples.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index 459b573..79553f6 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -9,7 +9,7 @@ on: - main jobs: - test: + examples: runs-on: ubuntu-latest steps: @@ -28,5 +28,6 @@ jobs: - name: Run all Examples run: | for example in examples/*.py; do + echo "=== Running $example" python3 "$example" done From 5ed9c958b52bc68054638dffaf1fcd95444208b9 Mon Sep 17 00:00:00 2001 From: Mostafa Date: Sun, 13 Jul 2025 22:48:12 +0800 Subject: [PATCH 4/4] ci: fix jsonrpc examples --- examples/example_jsonrpc_get_blockchain_info.py | 7 ++++--- examples/example_jsonrpc_get_node_info.py | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/examples/example_jsonrpc_get_blockchain_info.py b/examples/example_jsonrpc_get_blockchain_info.py index c5680c4..8950ab9 100644 --- a/examples/example_jsonrpc_get_blockchain_info.py +++ b/examples/example_jsonrpc_get_blockchain_info.py @@ -1,11 +1,12 @@ from pactus_jsonrpc.client import PactusOpenRPCClient -import pactus_jsonrpc.client import asyncio async def main() -> None: - pactus_jsonrpc.client.CLIENT_URL = "https://testnet1.pactus.org/jsonrpc" - client = PactusOpenRPCClient([]) + client_url = "https://testnet1.pactus.org/jsonrpc" + client = PactusOpenRPCClient( + headers={}, + client_url=client_url) res = await client.pactus.blockchain.get_blockchain_info() diff --git a/examples/example_jsonrpc_get_node_info.py b/examples/example_jsonrpc_get_node_info.py index 0da46bf..39f0b1a 100644 --- a/examples/example_jsonrpc_get_node_info.py +++ b/examples/example_jsonrpc_get_node_info.py @@ -1,11 +1,12 @@ from pactus_jsonrpc.client import PactusOpenRPCClient -import pactus_jsonrpc.client import asyncio async def main() -> None: - pactus_jsonrpc.client.CLIENT_URL = "https://testnet1.pactus.org/jsonrpc" - client = PactusOpenRPCClient([]) + client_url = "https://testnet1.pactus.org/jsonrpc" + client = PactusOpenRPCClient( + headers={}, + client_url=client_url) res = await client.pactus.network.get_node_info()