Host-customized fork of https://github.com/tecnovert/basicswap/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

266 lines
7.5 KiB

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (c) 2024 tecnovert
# Distributed under the MIT software license, see the accompanying
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
'''
syntax = "proto3";
0 VARINT int32, int64, uint32, uint64, sint32, sint64, bool, enum
1 I64 fixed64, sfixed64, double
2 LEN string, bytes, embedded messages, packed repeated fields
5 I32 fixed32, sfixed32, float
Don't encode fields of default values.
When decoding initialise all fields not set from data.
protobuf ParseFromString would reset the whole object, from_bytes won't.
'''
from basicswap.util.integer import encode_varint, decode_varint
class NonProtobufClass():
def __init__(self, init_all: bool = True, **kwargs):
for key, value in kwargs.items():
found_field: bool = False
for field_num, v in self._map.items():
field_name, wire_type, field_type = v
if field_name == key:
setattr(self, field_name, value)
found_field = True
break
if found_field is False:
raise ValueError(f'got an unexpected keyword argument \'{key}\'')
if init_all:
self.init_fields()
def init_fields(self) -> None:
# Set default values for missing fields
for field_num, v in self._map.items():
field_name, wire_type, field_type = v
if hasattr(self, field_name):
continue
if wire_type == 0:
setattr(self, field_name, 0)
elif wire_type == 2:
if field_type == 1:
setattr(self, field_name, str())
else:
setattr(self, field_name, bytes())
else:
raise ValueError(f'Unknown wire_type {wire_type}')
def to_bytes(self) -> bytes:
rv = bytes()
for field_num, v in self._map.items():
field_name, wire_type, field_type = v
if not hasattr(self, field_name):
continue
field_value = getattr(self, field_name)
tag = (field_num << 3) | wire_type
if wire_type == 0:
if field_value == 0:
continue
rv += encode_varint(tag)
rv += encode_varint(field_value)
elif wire_type == 2:
if len(field_value) == 0:
continue
rv += encode_varint(tag)
if isinstance(field_value, str):
field_value = field_value.encode('utf-8')
rv += encode_varint(len(field_value))
rv += field_value
else:
raise ValueError(f'Unknown wire_type {wire_type}')
return rv
def from_bytes(self, b: bytes, init_all: bool = True) -> None:
max_len: int = len(b)
o: int = 0
while o < max_len:
tag, lv = decode_varint(b, o)
o += lv
wire_type = tag & 7
field_num = tag >> 3
field_name, wire_type_expect, field_type = self._map[field_num]
if wire_type != wire_type_expect:
raise ValueError(f'Unexpected wire_type {wire_type} for field {field_num}')
if wire_type == 0:
field_value, lv = decode_varint(b, o)
o += lv
elif wire_type == 2:
field_len, lv = decode_varint(b, o)
o += lv
field_value = b[o: o + field_len]
o += field_len
if field_type == 1:
field_value = field_value.decode('utf-8')
else:
raise ValueError(f'Unknown wire_type {wire_type}')
setattr(self, field_name, field_value)
if init_all:
self.init_fields()
class OfferMessage(NonProtobufClass):
_map = {
1: ('protocol_version', 0, 0),
2: ('coin_from', 0, 0),
3: ('coin_to', 0, 0),
4: ('amount_from', 0, 0),
5: ('amount_to', 0, 0),
6: ('min_bid_amount', 0, 0),
7: ('time_valid', 0, 0),
8: ('lock_type', 0, 0),
9: ('lock_value', 0, 0),
10: ('swap_type', 0, 0),
11: ('proof_address', 2, 1),
12: ('proof_signature', 2, 1),
13: ('pkhash_seller', 2, 0),
14: ('secret_hash', 2, 0),
15: ('fee_rate_from', 0, 0),
16: ('fee_rate_to', 0, 0),
17: ('amount_negotiable', 0, 2),
18: ('rate_negotiable', 0, 2),
19: ('proof_utxos', 2, 0),
}
class BidMessage(NonProtobufClass):
_map = {
1: ('protocol_version', 0, 0),
2: ('offer_msg_id', 2, 0),
3: ('time_valid', 0, 0),
4: ('amount', 0, 0),
5: ('amount_to', 0, 0),
6: ('pkhash_buyer', 2, 0),
7: ('proof_address', 2, 1),
8: ('proof_signature', 2, 1),
9: ('proof_utxos', 2, 0),
10: ('pkhash_buyer_to', 2, 0),
}
class BidAcceptMessage(NonProtobufClass):
# Step 3, seller -> buyer
_map = {
1: ('bid_msg_id', 2, 0),
2: ('initiate_txid', 2, 0),
3: ('contract_script', 2, 0),
4: ('pkhash_seller', 2, 0),
}
class OfferRevokeMessage(NonProtobufClass):
_map = {
1: ('offer_msg_id', 2, 0),
2: ('signature', 2, 0),
}
class BidRejectMessage(NonProtobufClass):
_map = {
1: ('bid_msg_id', 2, 0),
2: ('reject_code', 0, 0),
}
class XmrBidMessage(NonProtobufClass):
# MSG1L, F -> L
_map = {
1: ('protocol_version', 0, 0),
2: ('offer_msg_id', 2, 0),
3: ('time_valid', 0, 0),
4: ('amount', 0, 0),
5: ('amount_to', 0, 0),
6: ('pkaf', 2, 0),
7: ('kbvf', 2, 0),
8: ('kbsf_dleag', 2, 0),
9: ('dest_af', 2, 0),
}
class XmrSplitMessage(NonProtobufClass):
_map = {
1: ('msg_id', 2, 0),
2: ('msg_type', 0, 0),
3: ('sequence', 0, 0),
4: ('dleag', 2, 0),
}
class XmrBidAcceptMessage(NonProtobufClass):
_map = {
1: ('bid_msg_id', 2, 0),
2: ('pkal', 2, 0),
3: ('kbvl', 2, 0),
4: ('kbsl_dleag', 2, 0),
# MSG2F
5: ('a_lock_tx', 2, 0),
6: ('a_lock_tx_script', 2, 0),
7: ('a_lock_refund_tx', 2, 0),
8: ('a_lock_refund_tx_script', 2, 0),
9: ('a_lock_refund_spend_tx', 2, 0),
10: ('al_lock_refund_tx_sig', 2, 0),
}
class XmrBidLockTxSigsMessage(NonProtobufClass):
# MSG3L
_map = {
1: ('bid_msg_id', 2, 0),
2: ('af_lock_refund_spend_tx_esig', 2, 0),
3: ('af_lock_refund_tx_sig', 2, 0),
}
class XmrBidLockSpendTxMessage(NonProtobufClass):
# MSG4F
_map = {
1: ('bid_msg_id', 2, 0),
2: ('a_lock_spend_tx', 2, 0),
3: ('kal_sig', 2, 0),
}
class XmrBidLockReleaseMessage(NonProtobufClass):
# MSG5F
_map = {
1: ('bid_msg_id', 2, 0),
2: ('al_lock_spend_tx_esig', 2, 0),
}
class ADSBidIntentMessage(NonProtobufClass):
# L -> F Sent from bidder, construct a reverse bid
_map = {
1: ('protocol_version', 0, 0),
2: ('offer_msg_id', 2, 0),
3: ('time_valid', 0, 0),
4: ('amount_from', 0, 0),
5: ('amount_to', 0, 0),
}
class ADSBidIntentAcceptMessage(NonProtobufClass):
# F -> L Sent from offerer, construct a reverse bid
_map = {
1: ('bid_msg_id', 2, 0),
2: ('pkaf', 2, 0),
3: ('kbvf', 2, 0),
4: ('kbsf_dleag', 2, 0),
5: ('dest_af', 2, 0),
}