Skip to content
This repository has been archived by the owner on Mar 6, 2023. It is now read-only.

Commit

Permalink
fix
Browse files Browse the repository at this point in the history
  • Loading branch information
linfeng-crypto committed Jan 26, 2022
1 parent 2d9b85d commit cd24dc7
Show file tree
Hide file tree
Showing 14 changed files with 275 additions and 208 deletions.
1 change: 1 addition & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ on:
push:
branches:
- master
- multi-sig2

jobs:
tox_test:
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,12 @@ CHAIN_ID = "chainmaind"
GRPC_ENDPOINT = "0.0.0.0:26653"

wallet = Wallet(MNEMONIC_PHRASE)
client = GrpcClient(wallet, CHAIN_ID, GRPC_ENDPOINT)
client = GrpcClient(CHAIN_ID, DENOM, GRPC_ENDPOINT)

from_address = wallet.address
account_number = client.query_account_data(wallet.address).account_number

msg = client.get_packed_send_msg(wallet.address, TO_ADDRESS, AMOUNT)
msg = get_packed_send_msg(wallet.address, TO_ADDRESS, AMOUNT)
tx = client.generate_tx([msg], [wallet.address], [wallet.public_key])
sign_transaction(tx, wallet.private_key, CHAIN_ID, account_number)
client.broadcast_tx(tx)
Expand Down
14 changes: 7 additions & 7 deletions chainlibpy/amino/transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from chainlibpy.amino.tx import Pubkey, Signature, StdTx
from chainlibpy.wallet import Wallet


class Transaction:
"""A Cosmos transaction.
Expand Down Expand Up @@ -60,10 +61,12 @@ 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)
raw_signature = self.sign()
sig_str = base64.b64encode(raw_signature).decode("utf-8")
signature = Signature(sig_str, pubkey, self._account_num, self._sequence)
return signature

def _sign(self) -> str:
def sign(self) -> bytes:
sign_doc = self._get_sign_doc()
message_str = json.dumps(
sign_doc.to_dict(), separators=(",", ":"), sort_keys=True
Expand All @@ -78,10 +81,7 @@ def _sign(self) -> str:
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
return signature_compact

def _get_sign_doc(self) -> StdSignDoc:
sign_doc = StdSignDoc(
Expand All @@ -93,4 +93,4 @@ def _get_sign_doc(self) -> StdSignDoc:
msgs=self._msgs,
timeout_height=self._timeout_height,
)
return sign_doc
return sign_doc
133 changes: 75 additions & 58 deletions chainlibpy/grpc_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
from google.protobuf.any_pb2 import Any as ProtoAny
from grpc import ChannelCredentials, insecure_channel, secure_channel

from chainlibpy.generated.cosmos.crypto.multisig.keys_pb2 import LegacyAminoPubKey
from chainlibpy.generated.cosmos.crypto.multisig.v1beta1.multisig_pb2 import CompactBitArray, MultiSignature
from chainlibpy.generated.cosmos.auth.v1beta1.auth_pb2 import BaseAccount
from chainlibpy.generated.cosmos.auth.v1beta1.query_pb2 import QueryAccountRequest
from chainlibpy.generated.cosmos.auth.v1beta1.query_pb2_grpc import (
Expand All @@ -23,6 +21,13 @@
)
from chainlibpy.generated.cosmos.bank.v1beta1.tx_pb2 import MsgSend
from chainlibpy.generated.cosmos.base.v1beta1.coin_pb2 import Coin
from chainlibpy.generated.cosmos.crypto.multisig.keys_pb2 import LegacyAminoPubKey
from chainlibpy.generated.cosmos.crypto.multisig.v1beta1.multisig_pb2 import (
CompactBitArray as ProtoCompactBitArray,
)
from chainlibpy.generated.cosmos.crypto.multisig.v1beta1.multisig_pb2 import (
MultiSignature,
)
from chainlibpy.generated.cosmos.crypto.secp256k1.keys_pb2 import PubKey as ProtoPubKey
from chainlibpy.generated.cosmos.tx.signing.v1beta1.signing_pb2 import SignMode
from chainlibpy.generated.cosmos.tx.v1beta1.service_pb2 import (
Expand All @@ -42,20 +47,43 @@
Tx,
TxBody,
)
from chainlibpy.transaction import sign_transaction
from chainlibpy.multisign.signature import MultiSignatureData, SingleSignatureV2
from chainlibpy.transaction import sign_transaction


def get_packed_send_msg(from_address: str, to_address: str, amount: List[Coin]) -> ProtoAny:
msg_send = MsgSend(from_address=from_address, to_address=to_address, amount=amount)
send_msg_packed = ProtoAny()
send_msg_packed.Pack(msg_send, type_url_prefix="/")

return send_msg_packed


def _get_signer_info(from_acc: BaseAccount, pub_key: ProtoPubKey) -> SignerInfo:
from_pub_key_packed = ProtoAny()
from_pub_key_pb = ProtoPubKey(key=pub_key)
from_pub_key_packed.Pack(from_pub_key_pb, type_url_prefix="/")

# Prepare auth info
single = ModeInfo.Single(mode=SignMode.SIGN_MODE_DIRECT)
mode_info = ModeInfo(single=single)
signer_info = SignerInfo(
public_key=from_pub_key_packed,
mode_info=mode_info,
sequence=from_acc.sequence,
)
return signer_info


class GrpcClient:
DEFAULT_GAS_LIMIT = 200000

def __init__(
self,
chain_id: str,
grpc_endpoint: str,
account_number: int = None,
credentials: ChannelCredentials = None,
self,
chain_id: str,
denom: str,
grpc_endpoint: str,
credentials: ChannelCredentials = None,
) -> None:
if credentials is None:
channel = insecure_channel(grpc_endpoint)
Expand All @@ -66,10 +94,10 @@ def __init__(
self.tx_client = TxGrpcClient(channel)
self.auth_client = AuthGrpcClient(channel)
self.chain_id = chain_id
self.account_number = account_number
self.denom = denom

def get_balance(self, address: str, denom: str) -> QueryBalanceResponse:
res = self.bank_client.Balance(QueryBalanceRequest(address=address, denom=denom))
def get_balance(self, address: str) -> QueryBalanceResponse:
res = self.bank_client.Balance(QueryBalanceRequest(address=address, denom=self.denom))
return res

def query_account_data(self, address: str) -> BaseAccount:
Expand All @@ -82,20 +110,20 @@ def query_account_data(self, address: str) -> BaseAccount:
return account

def generate_tx(
self,
packed_msgs: List[ProtoAny],
from_addresses: List[str],
pub_keys: List[bytes],
fee: Optional[List[Coin]] = None,
memo: str = "",
gas_limit: int = DEFAULT_GAS_LIMIT,
self,
packed_msgs: List[ProtoAny],
from_addresses: List[str],
pub_keys: List[bytes],
fee: Optional[List[Coin]] = None,
memo: str = "",
gas_limit: int = DEFAULT_GAS_LIMIT,
) -> Tx:
accounts: List[BaseAccount] = []
signer_infos: List[SignerInfo] = []
for from_address, pub_key in zip(from_addresses, pub_keys):
account = self.query_account_data(from_address)
accounts.append(account)
signer_infos.append(self._get_signer_info(account, pub_key))
signer_infos.append(_get_signer_info(account, pub_key))

auth_info = AuthInfo(
signer_infos=signer_infos,
Expand All @@ -109,15 +137,8 @@ def generate_tx(
tx = Tx(body=tx_body, auth_info=auth_info)
return tx

def sign_tx(self, private_key: bytes, tx: Tx):
sign_transaction(tx, private_key, self.chain_id, self.account_number)

def get_packed_send_msg(self, from_address: str, to_address: str, amount: List[Coin]) -> ProtoAny:
msg_send = MsgSend(from_address=from_address, to_address=to_address, amount=amount)
send_msg_packed = ProtoAny()
send_msg_packed.Pack(msg_send, type_url_prefix="/")

return send_msg_packed
def sign_tx(self, private_key: bytes, tx: Tx, account_number):
sign_transaction(tx, private_key, self.chain_id, account_number)

def broadcast_tx(self, tx: Tx, wait_time: int = 10) -> GetTxResponse:
tx_data = tx.SerializeToString()
Expand All @@ -135,38 +156,36 @@ def broadcast_tx(self, tx: Tx, wait_time: int = 10) -> GetTxResponse:

return tx_response

def bank_send(self, from_address: str, public_key: bytes, to_address: str, amount: List[Coin]) -> GetTxResponse:
import pdb; pdb.set_trace()
msg = self.get_packed_send_msg(
def bank_send(
self,
from_address: str,
private_key: bytes,
public_key: bytes,
to_address: str,
amount: List[Coin]
) -> GetTxResponse:
account_info = self.query_account_data(from_address)
msg = get_packed_send_msg(
from_address=from_address, to_address=to_address, amount=amount
)

tx = self.generate_tx([msg], [from_address], [public_key])
self.sign_tx(tx)
self.sign_tx(private_key, tx, account_info.account_number)
return self.broadcast_tx(tx)

def _get_signer_info(self, from_acc: BaseAccount, pub_key: ProtoPubKey) -> SignerInfo:
from_pub_key_packed = ProtoAny()
from_pub_key_pb = ProtoPubKey(key=pub_key)
from_pub_key_packed.Pack(from_pub_key_pb, type_url_prefix="/")

# Prepare auth info
single = ModeInfo.Single(mode=SignMode.SIGN_MODE_DIRECT)
mode_info = ModeInfo(single=single)
signer_info = SignerInfo(
public_key=from_pub_key_packed,
mode_info=mode_info,
sequence=from_acc.sequence,
)
return signer_info

def get_muli_signer_info(sequence: int, multi_pubkey: LegacyAminoPubKey, bitarray: CompactBitArray) -> SignerInfo:
def get_muli_signer_info(
sequence: int,
multi_pubkey: LegacyAminoPubKey,
bitarray: ProtoCompactBitArray
) -> SignerInfo:
multi_pubkey_packed = ProtoAny()
multi_pubkey_packed.Pack(multi_pubkey, type_url_prefix="/")

# Prepare auth info
mode_infos = [ModeInfo.Single(mode=SignMode.SIGN_MODE_LEGACY_AMINO_JSON)] * multi_pubkey.threshold
multi = ModeInfo.Multi(bitarray, mode_infos)
signal_mode_info = ModeInfo(single=ModeInfo.Single(mode=SignMode.SIGN_MODE_LEGACY_AMINO_JSON))
mode_infos = [signal_mode_info] * multi_pubkey.threshold
multi = ModeInfo.Multi(bitarray=bitarray, mode_infos=mode_infos)
mode_info = ModeInfo(multi=multi)
signer_info = SignerInfo(
public_key=multi_pubkey_packed,
Expand All @@ -175,6 +194,7 @@ def get_muli_signer_info(sequence: int, multi_pubkey: LegacyAminoPubKey, bitarra
)
return signer_info


def gen_multi_tx(
packed_msgs: List[ProtoAny],
multi_pubkey: LegacyAminoPubKey,
Expand All @@ -184,18 +204,17 @@ def gen_multi_tx(
memo: str = "",
gas_limit: int = 2000,
) -> Tx:
def packed_proto_pubkey(pub_key: ProtoPubKey):
from_pub_key_packed = ProtoAny()
return from_pub_key_packed.Pack(pub_key, type_url_prefix="/")
if multi_pubkey.threshold > len(signature_batch):
raise Exception("single signatures should >= threshold")

all_proto_pubkeys = [p for p in multi_pubkey.public_keys]
n = len(signature_batch)
multi_sign_data = MultiSignatureData(n)
for sig_v2 in signature_batch:
multi_sign_data.add_signature_v2(sig_v2, all_proto_pubkeys)
multi_sign_data.add_single_sig_v2(sig_v2, all_proto_pubkeys)

signer_info = get_muli_signer_info(sequence, multi_pubkey, multi_sign_data.bit_array)
signer_infos: List[SignerInfo] = []
signer_infos = list()
signer_infos.append(signer_info)
auth_info = AuthInfo(
signer_infos=signer_infos,
Expand All @@ -206,8 +225,6 @@ def packed_proto_pubkey(pub_key: ProtoPubKey):
tx_body.memo = memo
tx_body.messages.extend(packed_msgs)
multi_signature = MultiSignature(signatures=multi_sign_data.signatures)
signatures = list()
signatures.append(multi_signature)
packed_pubkeys = [packed_proto_pubkey(p.pub_key) for p in signature_batch]
tx = Tx(body=tx_body, auth_info=auth_info, public_keys=packed_pubkeys, signatures=signatures)

tx = Tx(body=tx_body, auth_info=auth_info, signatures=[multi_signature.SerializeToString()])
return tx
50 changes: 34 additions & 16 deletions chainlibpy/multisign/bitarray.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import base64

from chainlibpy.amino.basic import BasicObj
from chainlibpy.generated.cosmos.crypto.multisig.v1beta1.multisig_pb2 import CompactBitArray as ProtoCompactBitArray
from chainlibpy.generated.cosmos.crypto.multisig.v1beta1.multisig_pb2 import (
CompactBitArray as ProtoCompactBitArray,
)
from chainlibpy.multisign.bits import Bits


class CompactBitArray(BasicObj):
Expand All @@ -22,35 +26,49 @@ def __repr__(self):
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)
return ProtoCompactBitArray(
extra_bits_stored=self.extra_bits_stored,
elems=bytes(self.elems)
)

def count(self) -> int:
"""
returns the number of bits in the bitarray
"""
"""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 >= self.count()
"""
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:
if index < 0 or index >= self.count():
return False
return self.elems[index >> 3] & (1 << (7 - (index % 8))) > 0
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()
"""
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:
if i < 0 or i >= self.count():
return False

index = i >> 3
if v:
self.elems[index] |= (1 << int(7 - (i % 8)))
self.elems[i >> 3] |= (1 << (7 - (i % 8)))
else:
self.elems[i >> 3] &= ~(1 << int(7 - (i % 8)))
self.elems[i >> 3] &= ~(1 << (7 - (i % 8)))
return True

def num_true_bits_before(self, index: int) -> int:
ones_count = 0
max_ = self.count()
index = min(index, max_)
elem = 0
while True:
if elem*8+7 >= index:
ones_count += Bits.ones_count8(self.elems[elem]) >> (7 - (index % 8) + 1)
return ones_count
ones_count += Bits.ones_count8(self.elems[elem])
elem += 1
Loading

0 comments on commit cd24dc7

Please sign in to comment.