Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build_pb.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ CURDIR=$(pwd)
cd "device-protocol"

echo "Building with protoc version: $(protoc --version)"
for i in messages messages-ethereum messages-eos messages-nano messages-cosmos messages-ripple messages-binance messages-tendermint messages-thorchain messages-osmosis types ; do
for i in messages messages-ethereum messages-eos messages-nano messages-cosmos messages-ripple messages-binance messages-tendermint messages-thorchain messages-mayachain messages-osmosis types ; do
protoc --python_out=$CURDIR/keepkeylib/ -I/usr/include -I. $i.proto
i=${i/-/_}
sed -i -Ee 's/^import ([^.]+_pb2)/from . import \1/' $CURDIR/keepkeylib/"$i"_pb2.py
Expand Down
54 changes: 54 additions & 0 deletions keepkeyctl
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,43 @@ class Commands(object):

return signed

def mayachain_get_address(self, args):
address_n = self.client.expand_path(args.n)
address = self.client.mayachain_get_address(address_n, args.show_display, args.testnet)
return address

def mayachain_sign_tx(self, args):
address_n = self.client.expand_path(args.n)
chain_id = args.chain_id
account_number = args.account_number
sequence = args.sequence
with open(args.file, 'r') as f:
unsigned = json.load(f)

from keepkeylib.mayachain import mayachain_parse_tx, mayachain_append_sig

parsed = mayachain_parse_tx(unsigned)

res = self.client.mayachain_sign_tx(
address_n=address_n,
account_number=account_number,
chain_id=chain_id,
fee=int(parsed['fee']),
gas=int(parsed['gas']),
msgs=parsed['msgs'],
memo=parsed['memo'],
sequence=sequence,
testnet=args.testnet
)

unsigned['tx']['chain_id'] = chain_id
unsigned['tx']['account_number'] = account_number
unsigned['tx']['sequence'] = sequence

signed = mayachain_append_sig(unsigned, res.public_key, res.signature)

return signed

def get_entropy(self, args):
return binascii.hexlify(self.client.get_entropy(args.size))

Expand Down Expand Up @@ -558,6 +595,8 @@ class Commands(object):
cosmos_sign_tx.help = 'Sign Cosmos transaction'
thorchain_get_address.help = 'Get THORchain address'
thorchain_sign_tx.help = 'Sign THORChain transaction'
mayachain_get_address.help = 'Get MAYAchain address'
mayachain_sign_tx.help = 'Sign MAYAChain transaction'
get_entropy.help = 'Get example entropy'
get_features.help = 'Retrieve device features and settings'
get_public_node.help = 'Get public node of given path'
Expand Down Expand Up @@ -676,6 +715,21 @@ class Commands(object):
(('-t', '--testnet'), {'action': 'store_true', 'default': False}),
)

mayachain_get_address.arguments = (
(('-n', '-address'), {'type': str, 'help': "BIP-32 path to source address", 'default': "m/44'/931'/0'/0/0"}),
(('-d', '--show-display'), {'action': 'store_true', 'default': False}),
(('-t', '--testnet'), {'action': 'store_true', 'default': False}),
)

mayachain_sign_tx.arguments = (
(('-n', '-address'), {'type': str, 'help': "BIP-32 path to source address", 'default': "m/44'/931'/0'/0/0"}),
(('--chain-id',), {'type': str, 'help': "Chain ID of tendermint node", 'dest': 'chain_id', 'default': 'mayachain'}),
(('--account-number',), {'type': int, 'help': 'MAYAChain account number', 'dest': 'account_number', 'required': True}),
(('--sequence', ), {'type': int, 'help': 'MAYAChain account sequence', 'dest': 'sequence', 'required': True}),
(('-f', '--file'), {'type': str, 'required': True}),
(('-t', '--testnet'), {'action': 'store_true', 'default': False}),
)

get_entropy.arguments = (
(('size',), {'type': int}),
)
Expand Down
89 changes: 89 additions & 0 deletions keepkeylib/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
from . import messages_ripple_pb2 as ripple_proto
from . import messages_tendermint_pb2 as tendermint_proto
from . import messages_thorchain_pb2 as thorchain_proto
from . import messages_mayachain_pb2 as mayachain_proto
from . import types_pb2 as types
from . import eos
from . import nano
Expand Down Expand Up @@ -1051,6 +1052,94 @@ def thorchain_sign_tx(
return resp


@field('address')
@expect(mayachain_proto.MayachainAddress)
def mayachain_get_address(self, address_n, show_display=False, testnet=False):
return self.call(
mayachain_proto.MayachainGetAddress(address_n=address_n, show_display=show_display, testnet=testnet)
)

@session
def mayachain_sign_tx(
self,
address_n,
account_number,
chain_id,
fee,
gas,
msgs,
memo,
sequence,
testnet=None
):
resp = self.call(mayachain_proto.MayachainSignTx(
address_n=address_n,
account_number=account_number,
chain_id=chain_id,
fee_amount=fee,
gas=gas,
memo=memo,
sequence=sequence,
msg_count=len(msgs),
testnet=testnet
))

for msg in msgs:
if not isinstance(resp, mayachain_proto.MayachainMsgRequest):
raise CallException(
"Mayachain.ExpectedMsgRequest",
"Message request expected but not received.",
)

if msg['type'] == "mayachain/MsgSend":
if len(msg['value']['amount']) != 1:
raise CallException("Mayachain.MsgSend", "Multiple amounts per send msg not supported")

denom = msg['value']['amount'][0]['denom']
if denom != 'rune':
raise CallException("Mayachain.MsgSend", "Unsupported denomination: " + denom)

resp = self.call(mayachain_proto.MayachainMsgAck(
send=mayachain_proto.MayachainMsgSend(
from_address=msg['value']['from_address'],
to_address=msg['value']['to_address'],
amount=int(msg['value']['amount'][0]['amount']),
address_type=types.SPEND,
)
))

elif msg['type'] == "mayachain/MsgDeposit":
if len(msg['value']['coins']) != 1:
raise CallException("Mayachain.MsgDeposit", "Multiple coins per deposit msg not supported")

asset = msg['value']['coins'][0]['asset']
if asset != 'MAYA.CACAO':
raise CallException("Mayachain.MsgDeposit", "Unsupported asset: " + asset)

resp = self.call(mayachain_proto.MayachainMsgAck(
deposit=mayachain_proto.MayachainMsgDeposit(
asset=asset,
amount=int(msg['value']['coins'][0]['amount']),
memo=msg['value']['memo'],
signer=msg['value']['signer']
)
))

else:
raise CallException(
"Mayachain.UnknownMsg",
"Mayachain message %s is not yet supported" % (msg['type'],)
)

if not isinstance(resp, mayachain_proto.MayachainSignedTx):
raise CallException(
"Mayachain.UnexpectedEndOfOperations",
"Reached end of operations without a signature.",
)

return resp


@field('address')
@expect(ripple_proto.RippleAddress)
def ripple_get_address(self, address_n, show_display=False):
Expand Down
3 changes: 3 additions & 0 deletions keepkeylib/mapping.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from . import messages_binance_pb2 as binance_proto
from . import messages_tendermint_pb2 as tendermint_proto
from . import messages_thorchain_pb2 as thorchain_proto
from . import messages_mayachain_pb2 as mayachain_proto

map_type_to_class = {}
map_class_to_type = {}
Expand All @@ -33,6 +34,8 @@ def build_map():
msg_class = getattr(tendermint_proto, msg_name)
elif msg_type.startswith('MessageType_Thorchain'):
msg_class = getattr(thorchain_proto, msg_name)
elif msg_type.startswith('MessageType_Mayachain'):
msg_class = getattr(mayachain_proto, msg_name)
else:
msg_class = getattr(proto, msg_name)

Expand Down
21 changes: 21 additions & 0 deletions tests/test_msg_mayachain_getaddress.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import unittest
import common

import keepkeylib.messages_pb2 as proto
import keepkeylib.types_pb2 as proto_types
import keepkeylib.messages_mayachain_pb2 as mayachain_proto
from keepkeylib.client import CallException
from keepkeylib.tools import parse_path

DEFAULT_BIP32_PATH = "m/44h/931h/0h/0/0"

class TestMsgMayaChainGetAddress(common.KeepKeyTest):

def test_mayachain_get_address(self):
self.requires_firmware("7.0.2")
self.setup_mnemonic_nopin_nopassphrase()
address = self.client.mayachain_get_address(parse_path(DEFAULT_BIP32_PATH), testnet=True)
self.assertEqual(address, "smaya1ls33ayg26kmltw7jjy55p32ghjna09zp2mf0av")

if __name__ == '__main__':
unittest.main()
Loading