This repository has been archived by the owner on Mar 6, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: linfeng-crypto <[email protected]>
- Loading branch information
1 parent
06b3988
commit a1c0cf6
Showing
11 changed files
with
572 additions
and
29 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
import base64 | ||
import hashlib | ||
import json | ||
from typing import List | ||
|
||
import ecdsa | ||
|
||
from chainlibpy.amino import StdFee, StdSignDoc, SyncMode | ||
from chainlibpy.amino.message import Msg | ||
from chainlibpy.amino.tx import Pubkey, Signature, StdTx | ||
from chainlibpy.wallet import Wallet | ||
|
||
class Transaction: | ||
"""A Cosmos transaction. | ||
After initialization, one or more token transfers can be added by | ||
calling the `add_transfer()` method. Finally, call `get_pushable()` | ||
to get a signed transaction that can be pushed to the `POST /txs` | ||
endpoint of the Cosmos REST API. | ||
""" | ||
|
||
def __init__( | ||
self, | ||
wallet: Wallet, | ||
account_num: int, | ||
sequence: int, | ||
fee: StdFee = StdFee.default(), | ||
memo: str = "", | ||
chain_id: str = "crypto-org-chain-mainnet-1", | ||
sync_mode: SyncMode = "sync", | ||
timeout_height: int = 0, | ||
multi_sign_address: str = None, | ||
) -> None: | ||
self._multi_sign_address = multi_sign_address | ||
self._wallet = wallet | ||
self._account_num = str(account_num) | ||
self._sequence = str(sequence) | ||
self._fee = fee | ||
self._memo = memo | ||
self._chain_id = chain_id | ||
self._sync_mode = sync_mode | ||
self._msgs: List[Msg] = [] | ||
self._timeout_height = str(timeout_height) | ||
|
||
def add_msg(self, msg: Msg): | ||
self._msgs.append(msg) | ||
|
||
def get_pushable(self) -> dict: | ||
"""get the request post to the /txs.""" | ||
std_tx = StdTx(self._msgs, self._fee, self._memo, self._timeout_height, [self.signature]) | ||
|
||
pushable_tx = { | ||
"tx": std_tx.to_dict(), | ||
"mode": self._sync_mode, | ||
} | ||
return pushable_tx | ||
|
||
@property | ||
def signature(self) -> Signature: | ||
pubkey = self._wallet.public_key | ||
base64_pubkey = base64.b64encode(pubkey).decode("utf-8") | ||
pubkey = Pubkey(value=base64_pubkey) | ||
signature = Signature(self._sign(), pubkey, self._account_num, self._sequence) | ||
return signature | ||
|
||
def _sign(self) -> str: | ||
sign_doc = self._get_sign_doc() | ||
message_str = json.dumps( | ||
sign_doc.to_dict(), separators=(",", ":"), sort_keys=True | ||
) | ||
message_bytes = message_str.encode("utf-8") | ||
|
||
privkey = ecdsa.SigningKey.from_string( | ||
self._wallet.private_key, curve=ecdsa.SECP256k1 | ||
) | ||
signature_compact = privkey.sign_deterministic( | ||
message_bytes, | ||
hashfunc=hashlib.sha256, | ||
sigencode=ecdsa.util.sigencode_string_canonize, | ||
) | ||
|
||
print(list(signature_compact)) | ||
signature_base64_str = base64.b64encode(signature_compact).decode("utf-8") | ||
return signature_base64_str | ||
|
||
def _get_sign_doc(self) -> StdSignDoc: | ||
sign_doc = StdSignDoc( | ||
account_number=self._account_num, | ||
sequence=self._sequence, | ||
chain_id=self._chain_id, | ||
memo=self._memo, | ||
fee=self._fee.to_dict(), | ||
msgs=self._msgs, | ||
timeout_height=self._timeout_height, | ||
) | ||
return sign_doc |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! | ||
"""Client and server classes corresponding to protobuf-defined services.""" | ||
import grpc | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import base64 | ||
from chainlibpy.amino.basic import BasicObj | ||
from chainlibpy.generated.cosmos.crypto.multisig.v1beta1.multisig_pb2 import CompactBitArray as ProtoCompactBitArray | ||
|
||
|
||
class CompactBitArray(BasicObj): | ||
MaxInt32 = 2147483647 | ||
|
||
def __init__(self, bits: int): | ||
self.extra_bits_stored = 0 | ||
self.elems = bytearray() | ||
if bits <= 0: | ||
raise Exception(f"invalid bits {bits}") | ||
n_elems = (bits + 7) // 8 | ||
if n_elems <= 0 or n_elems > self.MaxInt32: | ||
raise Exception(f"invalid bits {bits}") | ||
self.extra_bits_stored = bits % 8 | ||
self.elems = bytearray([0] * n_elems) | ||
|
||
def __repr__(self): | ||
elems = base64.b64encode(self.elems).decode('utf-8') | ||
return f"extra_bits_stored:{self.extra_bits_stored}, elems:{elems}" | ||
|
||
def bit_array(self) -> ProtoCompactBitArray: | ||
return ProtoCompactBitArray(extra_bits_storede=self.extra_bits_stored, elems=self.elems) | ||
|
||
def count(self) -> int: | ||
""" | ||
returns the number of bits in the bitarray | ||
""" | ||
if self.extra_bits_stored == 0: | ||
return len(self.elems) * 8 | ||
return (len(self.elems) - 1) * 8 + int(self.extra_bits_stored) | ||
|
||
def get_index(self, index: int) -> bool: | ||
""" | ||
returns the bit at index i within the bit array. The behavior is undefined if i >= bA.Count() | ||
""" | ||
if index > self.count() or index < 0: | ||
return False | ||
return self.elems[index >> 3] & (1 << (7 - (index % 8))) > 0 | ||
|
||
def set_index(self, i: int, v: bool) -> bool: | ||
""" | ||
set_index sets the bit at index i within the bit array. Returns true if and only if the | ||
operation succeeded. The behavior is undefined if i >= self.count() | ||
""" | ||
if i > self.count() or i < 0: | ||
return False | ||
|
||
index = i >> 3 | ||
if v: | ||
self.elems[index] |= (1 << int(7 - (i % 8))) | ||
else: | ||
self.elems[i >> 3] &= ~(1 << int(7 - (i % 8))) | ||
return True |
Oops, something went wrong.