ccxt 4.3.71__py2.py3-none-any.whl → 4.3.73__py2.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.
- ccxt/__init__.py +1 -1
- ccxt/async_support/__init__.py +1 -1
- ccxt/async_support/base/exchange.py +1 -1
- ccxt/async_support/base/ws/client.py +1 -1
- ccxt/async_support/paradex.py +1 -1
- ccxt/base/exchange.py +1 -1
- ccxt/paradex.py +1 -1
- ccxt/pro/__init__.py +1 -1
- ccxt/pro/binance.py +16 -1
- ccxt/pro/kucoin.py +4 -0
- ccxt/pro/woo.py +111 -12
- ccxt/static_dependencies/starknet/models/__init__.py +0 -0
- ccxt/static_dependencies/starknet/models/typed_data.py +45 -0
- ccxt/static_dependencies/starkware/__init__.py +0 -0
- ccxt/static_dependencies/starkware/crypto/__init__.py +0 -0
- ccxt/static_dependencies/starkware/crypto/fast_pedersen_hash.py +50 -0
- ccxt/static_dependencies/starkware/crypto/math_utils.py +78 -0
- ccxt/static_dependencies/starkware/crypto/signature.py +2344 -0
- ccxt/static_dependencies/starkware/crypto/utils.py +58 -0
- ccxt/static_dependencies/sympy/core/__init__.py +0 -0
- ccxt/static_dependencies/sympy/core/intfunc.py +35 -0
- {ccxt-4.3.71.dist-info → ccxt-4.3.73.dist-info}/METADATA +5 -5
- {ccxt-4.3.71.dist-info → ccxt-4.3.73.dist-info}/RECORD +26 -16
- {ccxt-4.3.71.dist-info → ccxt-4.3.73.dist-info}/LICENSE.txt +0 -0
- {ccxt-4.3.71.dist-info → ccxt-4.3.73.dist-info}/WHEEL +0 -0
- {ccxt-4.3.71.dist-info → ccxt-4.3.73.dist-info}/top_level.txt +0 -0
ccxt/__init__.py
CHANGED
ccxt/async_support/__init__.py
CHANGED
ccxt/async_support/paradex.py
CHANGED
@@ -130,7 +130,7 @@ class paradex(Exchange, ImplicitAPI):
|
|
130
130
|
},
|
131
131
|
'hostname': 'paradex.trade',
|
132
132
|
'urls': {
|
133
|
-
'logo': 'https://github.com/user-attachments/assets/
|
133
|
+
'logo': 'https://github.com/user-attachments/assets/84628770-784e-4ec4-a759-ec2fbb2244ea',
|
134
134
|
'api': {
|
135
135
|
'v1': 'https://api.prod.{hostname}/v1',
|
136
136
|
},
|
ccxt/base/exchange.py
CHANGED
ccxt/paradex.py
CHANGED
@@ -130,7 +130,7 @@ class paradex(Exchange, ImplicitAPI):
|
|
130
130
|
},
|
131
131
|
'hostname': 'paradex.trade',
|
132
132
|
'urls': {
|
133
|
-
'logo': 'https://github.com/user-attachments/assets/
|
133
|
+
'logo': 'https://github.com/user-attachments/assets/84628770-784e-4ec4-a759-ec2fbb2244ea',
|
134
134
|
'api': {
|
135
135
|
'v1': 'https://api.prod.{hostname}/v1',
|
136
136
|
},
|
ccxt/pro/__init__.py
CHANGED
ccxt/pro/binance.py
CHANGED
@@ -1098,11 +1098,15 @@ class binance(ccxt.async_support.binance):
|
|
1098
1098
|
async def watch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
|
1099
1099
|
"""
|
1100
1100
|
watches historical candlestick data containing the open, high, low, and close price, and the volume of a market
|
1101
|
+
:see: https://binance-docs.github.io/apidocs/spot/en/#kline-candlestick-data
|
1102
|
+
:see: https://binance-docs.github.io/apidocs/futures/en/#kline-candlestick-data
|
1103
|
+
:see: https://binance-docs.github.io/apidocs/delivery/en/#kline-candlestick-data
|
1101
1104
|
:param str symbol: unified symbol of the market to fetch OHLCV data for
|
1102
1105
|
:param str timeframe: the length of time each candle represents
|
1103
1106
|
:param int [since]: timestamp in ms of the earliest candle to fetch
|
1104
1107
|
:param int [limit]: the maximum amount of candles to fetch
|
1105
1108
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
1109
|
+
:param dict [params.timezone]: if provided, kline intervals are interpreted in that timezone instead of UTC, example '+08:00'
|
1106
1110
|
:returns int[][]: A list of candles ordered, open, high, low, close, volume
|
1107
1111
|
"""
|
1108
1112
|
params['callerMethodName'] = 'watchOHLCV'
|
@@ -1112,10 +1116,14 @@ class binance(ccxt.async_support.binance):
|
|
1112
1116
|
async def watch_ohlcv_for_symbols(self, symbolsAndTimeframes: List[List[str]], since: Int = None, limit: Int = None, params={}):
|
1113
1117
|
"""
|
1114
1118
|
watches historical candlestick data containing the open, high, low, and close price, and the volume of a market
|
1119
|
+
:see: https://binance-docs.github.io/apidocs/spot/en/#kline-candlestick-data
|
1120
|
+
:see: https://binance-docs.github.io/apidocs/futures/en/#kline-candlestick-data
|
1121
|
+
:see: https://binance-docs.github.io/apidocs/delivery/en/#kline-candlestick-data
|
1115
1122
|
:param str[][] symbolsAndTimeframes: array of arrays containing unified symbols and timeframes to fetch OHLCV data for, example [['BTC/USDT', '1m'], ['LTC/USDT', '5m']]
|
1116
1123
|
:param int [since]: timestamp in ms of the earliest candle to fetch
|
1117
1124
|
:param int [limit]: the maximum amount of candles to fetch
|
1118
1125
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
1126
|
+
:param dict [params.timezone]: if provided, kline intervals are interpreted in that timezone instead of UTC, example '+08:00'
|
1119
1127
|
:returns int[][]: A list of candles ordered, open, high, low, close, volume
|
1120
1128
|
"""
|
1121
1129
|
await self.load_markets()
|
@@ -1127,6 +1135,10 @@ class binance(ccxt.async_support.binance):
|
|
1127
1135
|
type = firstMarket['type']
|
1128
1136
|
if firstMarket['contract']:
|
1129
1137
|
type = 'future' if firstMarket['linear'] else 'delivery'
|
1138
|
+
isSpot = (type == 'spot')
|
1139
|
+
timezone = None
|
1140
|
+
timezone, params = self.handle_param_string(params, 'timezone', None)
|
1141
|
+
isUtc8 = (timezone is not None) and ((timezone == '+08:00') or Precise.string_eq(timezone, '8'))
|
1130
1142
|
rawHashes = []
|
1131
1143
|
messageHashes = []
|
1132
1144
|
for i in range(0, len(symbolsAndTimeframes)):
|
@@ -1139,7 +1151,10 @@ class binance(ccxt.async_support.binance):
|
|
1139
1151
|
if klineType == 'indexPriceKline':
|
1140
1152
|
# weird behavior for index price kline we can't use the perp suffix
|
1141
1153
|
marketId = marketId.replace('_perp', '')
|
1142
|
-
|
1154
|
+
shouldUseUTC8 = (isUtc8 and isSpot)
|
1155
|
+
suffix = '@+08:00'
|
1156
|
+
utcSuffix = suffix if shouldUseUTC8 else ''
|
1157
|
+
rawHashes.append(marketId + '@' + klineType + '_' + interval + utcSuffix)
|
1143
1158
|
messageHashes.append('ohlcv::' + symbolString + '::' + timeframeString)
|
1144
1159
|
url = self.urls['api']['ws'][type] + '/' + self.stream(type, 'multipleOHLCV')
|
1145
1160
|
requestId = self.request_id(url)
|
ccxt/pro/kucoin.py
CHANGED
@@ -1024,6 +1024,10 @@ class kucoin(ccxt.async_support.kucoin):
|
|
1024
1024
|
tradeId = self.safe_string(trade, 'tradeId')
|
1025
1025
|
price = self.safe_string(trade, 'matchPrice')
|
1026
1026
|
amount = self.safe_string(trade, 'matchSize')
|
1027
|
+
if price is None:
|
1028
|
+
# /spot/tradeFills
|
1029
|
+
price = self.safe_string(trade, 'price')
|
1030
|
+
amount = self.safe_string(trade, 'size')
|
1027
1031
|
order = self.safe_string(trade, 'orderId')
|
1028
1032
|
timestamp = self.safe_integer_product_2(trade, 'ts', 'time', 0.000001)
|
1029
1033
|
feeCurrency = market['quote']
|
ccxt/pro/woo.py
CHANGED
@@ -90,32 +90,48 @@ class woo(ccxt.async_support.woo):
|
|
90
90
|
|
91
91
|
async def watch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
|
92
92
|
"""
|
93
|
+
:see: https://docs.woo.org/#orderbookupdate
|
93
94
|
:see: https://docs.woo.org/#orderbook
|
94
95
|
watches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
|
95
96
|
:param str symbol: unified symbol of the market to fetch the order book for
|
96
97
|
:param int [limit]: the maximum amount of order book entries to return.
|
97
98
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
99
|
+
:param str [params.method]: either(default) 'orderbook' or 'orderbookupdate', default is 'orderbook'
|
98
100
|
:returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
|
99
101
|
"""
|
100
102
|
await self.load_markets()
|
101
|
-
|
103
|
+
method = None
|
104
|
+
method, params = self.handle_option_and_params(params, 'watchOrderBook', 'method', 'orderbook')
|
102
105
|
market = self.market(symbol)
|
103
|
-
topic = market['id'] + '@' +
|
106
|
+
topic = market['id'] + '@' + method
|
107
|
+
urlUid = '/' + self.uid if (self.uid) else ''
|
108
|
+
url = self.urls['api']['ws']['public'] + urlUid
|
109
|
+
requestId = self.request_id(url)
|
104
110
|
request: dict = {
|
105
111
|
'event': 'subscribe',
|
106
112
|
'topic': topic,
|
113
|
+
'id': requestId,
|
107
114
|
}
|
108
|
-
|
109
|
-
|
115
|
+
subscription: dict = {
|
116
|
+
'id': str(requestId),
|
117
|
+
'name': method,
|
118
|
+
'symbol': symbol,
|
119
|
+
'limit': limit,
|
120
|
+
'params': params,
|
121
|
+
}
|
122
|
+
if method == 'orderbookupdate':
|
123
|
+
subscription['method'] = self.handle_order_book_subscription
|
124
|
+
orderbook = await self.watch(url, topic, self.extend(request, params), topic, subscription)
|
110
125
|
return orderbook.limit()
|
111
126
|
|
112
127
|
def handle_order_book(self, client: Client, message):
|
113
128
|
#
|
114
129
|
# {
|
115
|
-
# "topic": "PERP_BTC_USDT@
|
116
|
-
# "ts":
|
130
|
+
# "topic": "PERP_BTC_USDT@orderbookupdate",
|
131
|
+
# "ts": 1722500373999,
|
117
132
|
# "data": {
|
118
133
|
# "symbol": "PERP_BTC_USDT",
|
134
|
+
# "prevTs": 1722500373799,
|
119
135
|
# "bids": [
|
120
136
|
# [
|
121
137
|
# 0.30891,
|
@@ -136,13 +152,89 @@ class woo(ccxt.async_support.woo):
|
|
136
152
|
market = self.safe_market(marketId)
|
137
153
|
symbol = market['symbol']
|
138
154
|
topic = self.safe_string(message, 'topic')
|
139
|
-
|
140
|
-
|
141
|
-
|
155
|
+
method = self.safe_string(topic.split('@'), 1)
|
156
|
+
if method == 'orderbookupdate':
|
157
|
+
if not (symbol in self.orderbooks):
|
158
|
+
return
|
159
|
+
orderbook = self.orderbooks[symbol]
|
160
|
+
timestamp = self.safe_integer(orderbook, 'timestamp')
|
161
|
+
if timestamp is None:
|
162
|
+
orderbook.cache.append(message)
|
163
|
+
else:
|
164
|
+
try:
|
165
|
+
ts = self.safe_integer(message, 'ts')
|
166
|
+
if ts > timestamp:
|
167
|
+
self.handle_order_book_message(client, message, orderbook)
|
168
|
+
client.resolve(orderbook, topic)
|
169
|
+
except Exception as e:
|
170
|
+
del self.orderbooks[symbol]
|
171
|
+
del client.subscriptions[topic]
|
172
|
+
client.reject(e, topic)
|
173
|
+
else:
|
174
|
+
if not (symbol in self.orderbooks):
|
175
|
+
defaultLimit = self.safe_integer(self.options, 'watchOrderBookLimit', 1000)
|
176
|
+
subscription = client.subscriptions[topic]
|
177
|
+
limit = self.safe_integer(subscription, 'limit', defaultLimit)
|
178
|
+
self.orderbooks[symbol] = self.order_book({}, limit)
|
179
|
+
orderbook = self.orderbooks[symbol]
|
180
|
+
timestamp = self.safe_integer(message, 'ts')
|
181
|
+
snapshot = self.parse_order_book(data, symbol, timestamp, 'bids', 'asks')
|
182
|
+
orderbook.reset(snapshot)
|
183
|
+
client.resolve(orderbook, topic)
|
184
|
+
|
185
|
+
def handle_order_book_subscription(self, client: Client, message, subscription):
|
186
|
+
defaultLimit = self.safe_integer(self.options, 'watchOrderBookLimit', 1000)
|
187
|
+
limit = self.safe_integer(subscription, 'limit', defaultLimit)
|
188
|
+
symbol = self.safe_string(subscription, 'symbol') # watchOrderBook
|
189
|
+
if symbol in self.orderbooks:
|
190
|
+
del self.orderbooks[symbol]
|
191
|
+
self.orderbooks[symbol] = self.order_book({}, limit)
|
192
|
+
self.spawn(self.fetch_order_book_snapshot, client, message, subscription)
|
193
|
+
|
194
|
+
async def fetch_order_book_snapshot(self, client, message, subscription):
|
195
|
+
symbol = self.safe_string(subscription, 'symbol')
|
196
|
+
messageHash = self.safe_string(message, 'topic')
|
197
|
+
try:
|
198
|
+
defaultLimit = self.safe_integer(self.options, 'watchOrderBookLimit', 1000)
|
199
|
+
limit = self.safe_integer(subscription, 'limit', defaultLimit)
|
200
|
+
params = self.safe_value(subscription, 'params')
|
201
|
+
snapshot = await self.fetch_rest_order_book_safe(symbol, limit, params)
|
202
|
+
if self.safe_value(self.orderbooks, symbol) is None:
|
203
|
+
# if the orderbook is dropped before the snapshot is received
|
204
|
+
return
|
205
|
+
orderbook = self.orderbooks[symbol]
|
206
|
+
orderbook.reset(snapshot)
|
207
|
+
messages = orderbook.cache
|
208
|
+
for i in range(0, len(messages)):
|
209
|
+
messageItem = messages[i]
|
210
|
+
ts = self.safe_integer(messageItem, 'ts')
|
211
|
+
if ts < orderbook['timestamp']:
|
212
|
+
continue
|
213
|
+
else:
|
214
|
+
self.handle_order_book_message(client, messageItem, orderbook)
|
215
|
+
self.orderbooks[symbol] = orderbook
|
216
|
+
client.resolve(orderbook, messageHash)
|
217
|
+
except Exception as e:
|
218
|
+
del client.subscriptions[messageHash]
|
219
|
+
client.reject(e, messageHash)
|
220
|
+
|
221
|
+
def handle_order_book_message(self, client: Client, message, orderbook):
|
222
|
+
data = self.safe_dict(message, 'data')
|
223
|
+
self.handle_deltas(orderbook['asks'], self.safe_value(data, 'asks', []))
|
224
|
+
self.handle_deltas(orderbook['bids'], self.safe_value(data, 'bids', []))
|
142
225
|
timestamp = self.safe_integer(message, 'ts')
|
143
|
-
|
144
|
-
orderbook.
|
145
|
-
|
226
|
+
orderbook['timestamp'] = timestamp
|
227
|
+
orderbook['datetime'] = self.iso8601(timestamp)
|
228
|
+
return orderbook
|
229
|
+
|
230
|
+
def handle_delta(self, bookside, delta):
|
231
|
+
price = self.safe_float_2(delta, 'price', 0)
|
232
|
+
amount = self.safe_float_2(delta, 'quantity', 1)
|
233
|
+
bookside.store(price, amount)
|
234
|
+
|
235
|
+
def handle_deltas(self, bookside, deltas):
|
236
|
+
for i in range(0, len(deltas)):
|
237
|
+
self.handle_delta(bookside, deltas[i])
|
146
238
|
|
147
239
|
async def watch_ticker(self, symbol: str, params={}) -> Ticker:
|
148
240
|
"""
|
@@ -998,6 +1090,7 @@ class woo(ccxt.async_support.woo):
|
|
998
1090
|
'pong': self.handle_pong,
|
999
1091
|
'subscribe': self.handle_subscribe,
|
1000
1092
|
'orderbook': self.handle_order_book,
|
1093
|
+
'orderbookupdate': self.handle_order_book,
|
1001
1094
|
'ticker': self.handle_ticker,
|
1002
1095
|
'tickers': self.handle_tickers,
|
1003
1096
|
'kline': self.handle_ohlcv,
|
@@ -1056,6 +1149,12 @@ class woo(ccxt.async_support.woo):
|
|
1056
1149
|
# "ts": 1657117712212
|
1057
1150
|
# }
|
1058
1151
|
#
|
1152
|
+
id = self.safe_string(message, 'id')
|
1153
|
+
subscriptionsById = self.index_by(client.subscriptions, 'id')
|
1154
|
+
subscription = self.safe_value(subscriptionsById, id, {})
|
1155
|
+
method = self.safe_value(subscription, 'method')
|
1156
|
+
if method is not None:
|
1157
|
+
method(client, message, subscription)
|
1059
1158
|
return message
|
1060
1159
|
|
1061
1160
|
def handle_auth(self, client: Client, message):
|
File without changes
|
@@ -0,0 +1,45 @@
|
|
1
|
+
"""
|
2
|
+
TypedDict structures for TypedData
|
3
|
+
"""
|
4
|
+
|
5
|
+
from enum import Enum
|
6
|
+
from typing import Any, Dict, List, Optional, TypedDict
|
7
|
+
|
8
|
+
class Revision(Enum):
|
9
|
+
"""
|
10
|
+
Enum representing the revision of the specification to be used.
|
11
|
+
"""
|
12
|
+
|
13
|
+
V0 = 0
|
14
|
+
V1 = 1
|
15
|
+
|
16
|
+
|
17
|
+
class ParameterDict(TypedDict):
|
18
|
+
"""
|
19
|
+
TypedDict representing a Parameter object
|
20
|
+
"""
|
21
|
+
|
22
|
+
name: str
|
23
|
+
type: str
|
24
|
+
|
25
|
+
|
26
|
+
class StarkNetDomainDict(TypedDict):
|
27
|
+
"""
|
28
|
+
TypedDict representing a domain object (both StarkNetDomain, StarknetDomain).
|
29
|
+
"""
|
30
|
+
|
31
|
+
name: str
|
32
|
+
version: str
|
33
|
+
chainId: str
|
34
|
+
revision: Optional[Revision]
|
35
|
+
|
36
|
+
|
37
|
+
class TypedDataDict(TypedDict):
|
38
|
+
"""
|
39
|
+
TypedDict representing a TypedData object
|
40
|
+
"""
|
41
|
+
|
42
|
+
types: Dict[str, List[ParameterDict]]
|
43
|
+
primaryType: str
|
44
|
+
domain: StarkNetDomainDict
|
45
|
+
message: Dict[str, Any]
|
File without changes
|
File without changes
|
@@ -0,0 +1,50 @@
|
|
1
|
+
from ...ecdsa.curves import Curve
|
2
|
+
from ...ecdsa.ellipticcurve import CurveFp, Point
|
3
|
+
|
4
|
+
from .signature import (
|
5
|
+
ALPHA,
|
6
|
+
BETA,
|
7
|
+
CONSTANT_POINTS,
|
8
|
+
EC_ORDER,
|
9
|
+
FIELD_PRIME,
|
10
|
+
N_ELEMENT_BITS_HASH,
|
11
|
+
SHIFT_POINT,
|
12
|
+
)
|
13
|
+
from .utils import from_bytes, to_bytes
|
14
|
+
|
15
|
+
curve_stark = CurveFp(FIELD_PRIME, ALPHA, BETA)
|
16
|
+
LOW_PART_BITS = 248
|
17
|
+
LOW_PART_MASK = 2**248 - 1
|
18
|
+
HASH_SHIFT_POINT = Point(curve_stark, SHIFT_POINT[0], SHIFT_POINT[1], EC_ORDER)
|
19
|
+
P_0 = Point(curve_stark, CONSTANT_POINTS[2][0], CONSTANT_POINTS[2][1], EC_ORDER)
|
20
|
+
P_1 = Point(curve_stark, CONSTANT_POINTS[2 + LOW_PART_BITS][0], CONSTANT_POINTS[2 + LOW_PART_BITS][1], EC_ORDER)
|
21
|
+
P_2 = Point(curve_stark, CONSTANT_POINTS[2 + N_ELEMENT_BITS_HASH][0], CONSTANT_POINTS[2 + N_ELEMENT_BITS_HASH][1], EC_ORDER)
|
22
|
+
P_3 = Point(curve_stark, CONSTANT_POINTS[2 + N_ELEMENT_BITS_HASH + LOW_PART_BITS][0], CONSTANT_POINTS[2 + N_ELEMENT_BITS_HASH + LOW_PART_BITS][1], EC_ORDER)
|
23
|
+
|
24
|
+
def process_single_element(element: int, p1, p2) -> Point:
|
25
|
+
assert 0 <= element < FIELD_PRIME, "Element integer value is out of range"
|
26
|
+
|
27
|
+
high_nibble = element >> LOW_PART_BITS
|
28
|
+
low_part = element & LOW_PART_MASK
|
29
|
+
return low_part * p1 + high_nibble * p2
|
30
|
+
|
31
|
+
|
32
|
+
def pedersen_hash(x: int, y: int) -> int:
|
33
|
+
"""
|
34
|
+
Computes the Starkware version of the Pedersen hash of x and y.
|
35
|
+
The hash is defined by:
|
36
|
+
shift_point + x_low * P_0 + x_high * P1 + y_low * P2 + y_high * P3
|
37
|
+
where x_low is the 248 low bits of x, x_high is the 4 high bits of x and similarly for y.
|
38
|
+
shift_point, P_0, P_1, P_2, P_3 are constant points generated from the digits of pi.
|
39
|
+
"""
|
40
|
+
return (
|
41
|
+
HASH_SHIFT_POINT + process_single_element(x, P_0, P_1) + process_single_element(y, P_2, P_3)
|
42
|
+
).x()
|
43
|
+
|
44
|
+
|
45
|
+
def pedersen_hash_func(x: bytes, y: bytes) -> bytes:
|
46
|
+
"""
|
47
|
+
A variant of 'pedersen_hash', where the elements and their resulting hash are in bytes.
|
48
|
+
"""
|
49
|
+
assert len(x) == len(y) == 32, "Unexpected element length."
|
50
|
+
return to_bytes(pedersen_hash(*(from_bytes(element) for element in (x, y))))
|
@@ -0,0 +1,78 @@
|
|
1
|
+
###############################################################################
|
2
|
+
# Copyright 2019 StarkWare Industries Ltd. #
|
3
|
+
# #
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License"). #
|
5
|
+
# You may not use this file except in compliance with the License. #
|
6
|
+
# You may obtain a copy of the License at #
|
7
|
+
# #
|
8
|
+
# https://www.starkware.co/open-source-license/ #
|
9
|
+
# #
|
10
|
+
# Unless required by applicable law or agreed to in writing, #
|
11
|
+
# software distributed under the License is distributed on an "AS IS" BASIS, #
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
|
13
|
+
# See the License for the specific language governing permissions #
|
14
|
+
# and limitations under the License. #
|
15
|
+
###############################################################################
|
16
|
+
|
17
|
+
|
18
|
+
from typing import Tuple
|
19
|
+
|
20
|
+
from ...sympy.core.intfunc import igcdex
|
21
|
+
|
22
|
+
# A type that represents a point (x,y) on an elliptic curve.
|
23
|
+
ECPoint = Tuple[int, int]
|
24
|
+
|
25
|
+
def div_mod(n: int, m: int, p: int) -> int:
|
26
|
+
"""
|
27
|
+
Finds a nonnegative integer 0 <= x < p such that (m * x) % p == n
|
28
|
+
"""
|
29
|
+
a, b, c = igcdex(m, p)
|
30
|
+
assert c == 1
|
31
|
+
return (n * a) % p
|
32
|
+
|
33
|
+
def div_ceil(x, y):
|
34
|
+
assert isinstance(x, int) and isinstance(y, int)
|
35
|
+
return -((-x) // y)
|
36
|
+
|
37
|
+
def ec_add(point1: ECPoint, point2: ECPoint, p: int) -> ECPoint:
|
38
|
+
"""
|
39
|
+
Gets two points on an elliptic curve mod p and returns their sum.
|
40
|
+
Assumes the points are given in affine form (x, y) and have different x coordinates.
|
41
|
+
"""
|
42
|
+
assert (point1[0] - point2[0]) % p != 0
|
43
|
+
m = div_mod(point1[1] - point2[1], point1[0] - point2[0], p)
|
44
|
+
x = (m * m - point1[0] - point2[0]) % p
|
45
|
+
y = (m * (point1[0] - x) - point1[1]) % p
|
46
|
+
return x, y
|
47
|
+
|
48
|
+
|
49
|
+
def ec_neg(point: ECPoint, p: int) -> ECPoint:
|
50
|
+
"""
|
51
|
+
Given a point (x,y) return (x, -y)
|
52
|
+
"""
|
53
|
+
x, y = point
|
54
|
+
return (x, (-y) % p)
|
55
|
+
|
56
|
+
|
57
|
+
def ec_double(point: ECPoint, alpha: int, p: int) -> ECPoint:
|
58
|
+
"""
|
59
|
+
Doubles a point on an elliptic curve with the equation y^2 = x^3 + alpha*x + beta mod p.
|
60
|
+
Assumes the point is given in affine form (x, y) and has y != 0.
|
61
|
+
"""
|
62
|
+
assert point[1] % p != 0
|
63
|
+
m = div_mod(3 * point[0] * point[0] + alpha, 2 * point[1], p)
|
64
|
+
x = (m * m - 2 * point[0]) % p
|
65
|
+
y = (m * (point[0] - x) - point[1]) % p
|
66
|
+
return x, y
|
67
|
+
|
68
|
+
|
69
|
+
def ec_mult(m: int, point: ECPoint, alpha: int, p: int) -> ECPoint:
|
70
|
+
"""
|
71
|
+
Multiplies by m a point on the elliptic curve with equation y^2 = x^3 + alpha*x + beta mod p.
|
72
|
+
Assumes the point is given in affine form (x, y) and that 0 < m < order(point).
|
73
|
+
"""
|
74
|
+
if m == 1:
|
75
|
+
return point
|
76
|
+
if m % 2 == 0:
|
77
|
+
return ec_mult(m // 2, ec_double(point, alpha, p), alpha, p)
|
78
|
+
return ec_add(ec_mult(m - 1, point, alpha, p), point, p)
|