1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-12-23 23:08:14 +00:00

nem_mosaics: Initial commit

This commit is contained in:
Saleem Rashid 2017-07-20 22:58:19 +01:00
parent 4a55dd89e6
commit 8dbd6182fb
9 changed files with 297 additions and 81 deletions

View File

@ -25,6 +25,7 @@ OBJS += crypto.o
OBJS += ethereum.o
OBJS += ethereum_tokens.o
OBJS += nem2.o
OBJS += nem_mosaics.o
OBJS += debug.o

View File

@ -30,8 +30,8 @@
#include "qr_encode.h"
#include "timer.h"
#include "bignum.h"
#include "nem.h"
#include "secp256k1.h"
#include "nem2.h"
#include "gettext.h"
#define BITCOIN_DIVISIBILITY (8)
@ -468,18 +468,7 @@ void layoutU2FDialog(const char *verb, const char *appname, const BITMAP *appico
layoutDialog(appicon, NULL, verb, NULL, verb, _("U2F security key?"), NULL, appname, NULL, NULL);
}
static inline void nemFormatAmount(bignum256 *amnt, uint64_t quantity, int divisibility, const bignum256 *mul, const char *ticker, char *str_out, size_t size) {
bn_read_uint64(quantity, amnt);
if (mul) {
bn_multiply(mul, amnt, &secp256k1.prime);
bn_format(amnt, NULL, ticker, divisibility + NEM_XEM_DIVISIBILITY, str_out, size);
} else {
bn_format(amnt, NULL, ticker, divisibility, str_out, size);
}
}
void layoutNEMDialog(const BITMAP *icon, const char *btnNo, const char *btnYes, const char *desc, const char *line1, const char *address, const char *line5, const char *line6) {
void layoutNEMDialog(const BITMAP *icon, const char *btnNo, const char *btnYes, const char *desc, const char *line1, const char *address) {
static char first_third[NEM_ADDRESS_SIZE / 3 + 1];
strlcpy(first_third, address, sizeof(first_third));
@ -496,18 +485,16 @@ void layoutNEMDialog(const BITMAP *icon, const char *btnNo, const char *btnYes,
first_third,
second_third,
third_third,
line5,
line6);
NULL,
NULL);
}
void layoutNEMTransferXEM(const char *desc, uint64_t quantity, const bignum256 *mul, uint64_t fee) {
void layoutNEMTransferXEM(const char *desc, uint64_t quantity, const bignum256 *multiplier, uint64_t fee) {
char str_out[32], str_fee[32];
bignum256 amnt;
nemFormatAmount(&amnt, quantity, NEM_XEM_DIVISIBILITY, mul, " " NEM_XEM_TICKER, str_out, sizeof(str_out));
nem_mosaicFormatAmount(NEM_MOSAIC_DEFINITION_XEM, quantity, multiplier, str_out, sizeof(str_out));
nem_mosaicFormatAmount(NEM_MOSAIC_DEFINITION_XEM, fee, NULL, str_fee, sizeof(str_fee));
bn_read_uint64(fee, &amnt);
bn_format(&amnt, NULL, " " NEM_XEM_TICKER, NEM_XEM_DIVISIBILITY, str_fee, sizeof(str_fee));
layoutDialogSwipe(&bmp_icon_question,
_("Cancel"),
_("Next"),
@ -522,14 +509,11 @@ void layoutNEMTransferXEM(const char *desc, uint64_t quantity, const bignum256 *
void layoutNEMNetworkFee(const char *desc, bool confirm, const char *fee1_desc, uint64_t fee1, const char *fee2_desc, uint64_t fee2) {
char str_fee1[32], str_fee2[32];
bignum256 amnt;
bn_read_uint64(fee1, &amnt);
bn_format(&amnt, NULL, " " NEM_XEM_TICKER, NEM_XEM_DIVISIBILITY, str_fee1, sizeof(str_fee1));
nem_mosaicFormatAmount(NEM_MOSAIC_DEFINITION_XEM, fee1, NULL, str_fee1, sizeof(str_fee1));
if (fee2_desc) {
bn_read_uint64(fee2, &amnt);
bn_format(&amnt, NULL, " " NEM_XEM_TICKER, NEM_XEM_DIVISIBILITY, str_fee2, sizeof(str_fee2));
nem_mosaicFormatAmount(NEM_MOSAIC_DEFINITION_XEM, fee2, NULL, str_fee2, sizeof(str_fee2));
}
layoutDialogSwipe(&bmp_icon_question,
@ -544,15 +528,35 @@ void layoutNEMNetworkFee(const char *desc, bool confirm, const char *fee1_desc,
NULL);
}
void layoutNEMTransferMosaic(const char *namespace, const char *mosaic, uint64_t quantity, const bignum256 *mul) {
void layoutNEMTransferMosaic(const NEMMosaicDefinition *definition, uint64_t quantity, const bignum256 *multiplier) {
char str_out[32], str_levy[32];
nem_mosaicFormatAmount(definition, quantity, multiplier, str_out, sizeof(str_out));
if (definition->has_levy) {
nem_mosaicFormatLevy(definition, quantity, multiplier, str_levy, sizeof(str_levy));
}
layoutDialogSwipe(&bmp_icon_question,
_("Cancel"),
_("Next"),
definition->has_name ? definition->name : _("Mosaic"),
_("Confirm transfer of"),
str_out,
definition->has_levy ? _("and levy of") : NULL,
definition->has_levy ? str_levy : NULL,
NULL,
NULL);
}
void layoutNEMTransferUnknownMosaic(const char *namespace, const char *mosaic, uint64_t quantity, const bignum256 *multiplier) {
char mosaic_name[256];
strlcpy(mosaic_name, namespace, sizeof(mosaic_name));
strlcat(mosaic_name, ".", sizeof(mosaic_name));
strlcat(mosaic_name, mosaic, sizeof(mosaic_name));
char str_out[32];
bignum256 amnt;
nemFormatAmount(&amnt, quantity, 0, mul, NULL, str_out, sizeof(str_out));
nem_mosaicFormatAmount(NULL, quantity, multiplier, str_out, sizeof(str_out));
char *decimal = strchr(str_out, '.');
if (decimal != NULL) {
@ -587,24 +591,3 @@ void layoutNEMTransferPayload(const uint8_t *payload, size_t length, bool encryp
str[0], str[1], str[2], str[3], NULL, NULL);
}
}
void layoutNEMTransferTo(const char *desc, const char *address) {
static char first_third[NEM_ADDRESS_SIZE / 3 + 1];
strlcpy(first_third, address, sizeof(first_third));
static char second_third[NEM_ADDRESS_SIZE / 3 + 1];
strlcpy(second_third, &address[NEM_ADDRESS_SIZE / 3], sizeof(second_third));
const char *third_third = &address[NEM_ADDRESS_SIZE * 2 / 3];
layoutDialogSwipe(&bmp_icon_question,
_("Cancel"),
_("Confirm"),
desc,
_("Confirm transfer to"),
first_third,
second_third,
third_third,
NULL,
NULL);
}

View File

@ -47,10 +47,12 @@ void layoutPublicKey(const uint8_t *pubkey);
void layoutSignIdentity(const IdentityType *identity, const char *challenge);
void layoutDecryptIdentity(const IdentityType *identity);
void layoutU2FDialog(const char *verb, const char *appname, const BITMAP *appicon);
void layoutNEMDialog(const BITMAP *icon, const char *btnNo, const char *btnYes, const char *desc, const char *line1, const char *address, const char *line5, const char *line6);
void layoutNEMTransferXEM(const char *desc, uint64_t quantity, const bignum256 *mul, uint64_t fee);
void layoutNEMDialog(const BITMAP *icon, const char *btnNo, const char *btnYes, const char *desc, const char *line1, const char *address);
void layoutNEMTransferXEM(const char *desc, uint64_t quantity, const bignum256 *multiplier, uint64_t fee);
void layoutNEMNetworkFee(const char *desc, bool confirm, const char *fee1_desc, uint64_t fee1, const char *fee2_desc, uint64_t fee2);
void layoutNEMTransferMosaic(const char *namespace, const char *mosaic, uint64_t quantity, const bignum256 *mul);
void layoutNEMTransferMosaic(const NEMMosaicDefinition *definition, uint64_t quantity, const bignum256 *multiplier);
void layoutNEMTransferUnknownMosaic(const char *namespace, const char *mosaic, uint64_t quantity, const bignum256 *multiplier);
void layoutNEMTransferPayload(const uint8_t *payload, size_t length, bool encrypted);
#endif

View File

@ -25,6 +25,9 @@
#include "layout2.h"
#include "protect.h"
#include "rng.h"
#include "secp256k1.h"
static void format_amount(const NEMMosaicDefinition *definition, uint64_t quantity, const bignum256 *multiplier, const bignum256 *multiplier2, int divisor, char *str_out, size_t size);
const char *nem_validate_common(NEMTransactionCommon *common, bool inner) {
if (!common->has_network) {
@ -92,51 +95,61 @@ const char *nem_validate_provision_namespace(const NEMProvisionNamespace *provis
bool nem_askTransfer(const NEMTransactionCommon *common, const NEMTransfer *transfer, const char *desc) {
if (transfer->mosaics_count) {
bool done[transfer->mosaics_count];
memset(done, 0, sizeof(done));
struct {
bool skip;
uint64_t quantity;
const NEMMosaicDefinition *definition;
} mosaics[transfer->mosaics_count], *xem = NULL;
uint64_t quantity[transfer->mosaics_count];
uint64_t *xemQuantity = NULL;
bignum256 mul;
bn_read_uint64(transfer->amount, &mul);
memset(mosaics, 0, sizeof(mosaics));
for (size_t i = 0; i < transfer->mosaics_count; i++) {
// Skip duplicate mosaics
if (done[i]) continue;
if (mosaics[i].skip) continue;
const NEMMosaic *mosaic = &transfer->mosaics[i];
// XEM is treated specially
if (strcmp(mosaic->namespace, "nem") == 0 && strcmp(mosaic->mosaic, "xem") == 0) {
done[i] = true;
xemQuantity = &quantity[i];
// XEM is displayed separately
if ((mosaics[i].definition = nem_mosaicByName(mosaic->namespace, mosaic->mosaic))) {
if (mosaics[i].definition == NEM_MOSAIC_DEFINITION_XEM) {
// Do not display as a mosaic
mosaics[i].skip = true;
xem = &mosaics[i];
}
}
quantity[i] = mosaic->quantity;
mosaics[i].quantity = mosaic->quantity;
for (size_t j = i + 1; j < transfer->mosaics_count; j++) {
const NEMMosaic *new_mosaic = &transfer->mosaics[j];
if (strcmp(mosaic->namespace, new_mosaic->namespace) == 0 && strcmp(mosaic->mosaic, new_mosaic->mosaic) == 0) {
// Duplicate mosaics are merged into one
done[i] = true;
quantity[i] += transfer->mosaics[j].quantity;
if (nem_mosaicMatches(mosaics[i].definition, new_mosaic->namespace, new_mosaic->mosaic)) {
// Merge duplicate mosaics
mosaics[j].skip = true;
mosaics[i].quantity += new_mosaic->quantity;
}
}
}
layoutNEMTransferXEM(desc, xemQuantity == NULL ? 0 : *xemQuantity, &mul, common->fee);
bignum256 multiplier;
bn_read_uint64(transfer->amount, &multiplier);
layoutNEMTransferXEM(desc, xem ? xem->quantity : 0, &multiplier, common->fee);
if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) {
return false;
}
for (size_t i = 0; i < transfer->mosaics_count; i++) {
// Skip special or duplicate mosaics
if (done[i]) continue;
// Skip duplicate mosaics or XEM
if (mosaics[i].skip) continue;
const NEMMosaic *mosaic = &transfer->mosaics[i];
layoutNEMTransferMosaic(mosaic->namespace, mosaic->mosaic, quantity[i], &mul);
if (mosaics[i].definition) {
layoutNEMTransferMosaic(mosaics[i].definition, mosaics[i].quantity, &multiplier);
} else {
layoutNEMTransferUnknownMosaic(mosaic->namespace, mosaic->mosaic, mosaics[i].quantity, &multiplier);
}
if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) {
return false;
}
@ -160,9 +173,7 @@ bool nem_askTransfer(const NEMTransactionCommon *common, const NEMTransfer *tran
_("Confirm"),
desc,
_("Confirm transfer to"),
transfer->recipient,
NULL,
NULL);
transfer->recipient);
if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) {
return false;
}
@ -285,16 +296,12 @@ bool nem_askMultisig(const char *address, const char *desc, bool cosigning, uint
_("Next"),
desc,
cosigning ? _("Cosign transaction for") : _("Initiate transaction for"),
address,
NULL,
NULL);
address);
if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) {
return false;
}
layoutNEMNetworkFee(desc, false, _("Confirm multisig fee"), fee, NULL, 0);
if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) {
return false;
}
@ -329,3 +336,85 @@ bool nem_fsmMultisig(nem_transaction_ctx *context, const NEMTransactionCommon *c
return true;
}
const NEMMosaicDefinition *nem_mosaicByName(const char *namespace, const char *mosaic) {
for (size_t i = 0; i < NEM_MOSAIC_DEFINITIONS_COUNT; i++) {
const NEMMosaicDefinition *definition = &NEM_MOSAIC_DEFINITIONS[i];
if (nem_mosaicMatches(definition, namespace, mosaic)) {
return definition;
}
}
return NULL;
}
void format_amount(const NEMMosaicDefinition *definition, uint64_t quantity, const bignum256 *multiplier, const bignum256 *multiplier2, int divisor, char *str_out, size_t size) {
uint32_t divisibility = definition && definition->has_divisibility ? definition->divisibility : 0;
const char *ticker = definition && definition->has_ticker ? definition->ticker : NULL;
bignum256 amnt;
bn_read_uint64(quantity, &amnt);
if (multiplier2) {
bn_multiply(multiplier2, &amnt, &secp256k1.prime);
}
// Do not use prefix/suffix with bn_format, it messes with the truncation code
if (multiplier) {
bn_multiply(multiplier, &amnt, &secp256k1.prime);
divisor += NEM_MOSAIC_DEFINITION_XEM->divisibility;
}
// bn_format(amnt / (10 ^ divisor), divisibility)
bn_format(&amnt, NULL, NULL, divisibility + divisor, str_out, size);
// Truncate as if we called bn_format with (divisibility) instead of (divisibility + divisor)
char *decimal = strchr(str_out, '.');
if (decimal != NULL) {
const char *terminator = strchr(str_out, '\0');
if (divisibility == 0) {
// Truncate as an integer
*decimal = '\0';
} else {
char *end = decimal + divisibility + 1;
if (end < terminator) {
*end = '\0';
}
}
}
if (ticker) {
strlcat(str_out, " ", size);
strlcat(str_out, ticker, size);
}
}
void nem_mosaicFormatAmount(const NEMMosaicDefinition *definition, uint64_t quantity, const bignum256 *multiplier, char *str_out, size_t size) {
format_amount(definition, quantity, multiplier, NULL, 0, str_out, size);
}
bool nem_mosaicFormatLevy(const NEMMosaicDefinition *definition, uint64_t quantity, const bignum256 *multiplier, char *str_out, size_t size) {
bignum256 multiplier2;
if (!definition->has_levy || !definition->has_fee) {
return false;
}
switch (definition->levy) {
case NEMMosaicLevy_MosaicLevy_Absolute:
format_amount(definition, definition->fee, NULL, NULL, 0, str_out, size);
break;
case NEMMosaicLevy_MosaicLevy_Percentile:
bn_read_uint64(definition->fee, &multiplier2);
format_amount(definition, quantity, multiplier, &multiplier2, NEM_LEVY_PERCENTILE_DIVISOR, str_out, size);
break;
default:
return false;
}
return true;
}

View File

@ -21,6 +21,7 @@
#define __NEM2_H__
#include "nem.h"
#include "nem_mosaics.h"
#include "messages.pb.h"
#include "types.pb.h"
@ -43,4 +44,12 @@ bool nem_fsmProvisionNamespace(nem_transaction_ctx *context, const NEMTransactio
bool nem_askMultisig(const char *address, const char *desc, bool cosigning, uint64_t fee);
bool nem_fsmMultisig(nem_transaction_ctx *context, const NEMTransactionCommon *common, const nem_transaction_ctx *inner, bool cosigning);
const NEMMosaicDefinition *nem_mosaicByName(const char *namespace, const char *mosaic);
void nem_mosaicFormatAmount(const NEMMosaicDefinition *definition, uint64_t quantity, const bignum256 *multiplier, char *str_out, size_t size);
bool nem_mosaicFormatLevy(const NEMMosaicDefinition *definition, uint64_t quantity, const bignum256 *multiplier, char *str_out, size_t size);
static inline bool nem_mosaicMatches(const NEMMosaicDefinition *definition, const char *namespace, const char *mosaic) {
return strcmp(namespace, definition->namespace) == 0 && strcmp(mosaic, definition->mosaic) == 0;
}
#endif

16
firmware/nem_mosaics.c Normal file
View File

@ -0,0 +1,16 @@
// This file is automatically generated by nem_mosaics.py -- DO NOT EDIT!
#include "nem_mosaics.h"
const NEMMosaicDefinition NEM_MOSAIC_DEFINITIONS[NEM_MOSAIC_DEFINITIONS_COUNT] = {{
.has_ticker = true,
.ticker = " XEM",
.has_namespace = true,
.namespace = "nem",
.has_mosaic = true,
.mosaic = "xem",
.has_divisibility = true,
.divisibility = 6,
}};
const NEMMosaicDefinition *NEM_MOSAIC_DEFINITION_XEM = NEM_MOSAIC_DEFINITIONS;

13
firmware/nem_mosaics.h Normal file
View File

@ -0,0 +1,13 @@
// This file is automatically generated by nem_mosaics.py -- DO NOT EDIT!
#ifndef __NEM_MOSAICS_H__
#define __NEM_MOSAICS_H__
#include "types.pb.h"
#define NEM_MOSAIC_DEFINITIONS_COUNT (1)
extern const NEMMosaicDefinition NEM_MOSAIC_DEFINITIONS[NEM_MOSAIC_DEFINITIONS_COUNT];
extern const NEMMosaicDefinition *NEM_MOSAIC_DEFINITION_XEM;
#endif

View File

@ -0,0 +1,8 @@
[
{
"ticker": " XEM",
"namespace": "nem",
"mosaic": "xem",
"divisibility": 6
}
]

95
firmware/nem_mosaics.py Executable file
View File

@ -0,0 +1,95 @@
#!/usr/bin/env python3
import json, os
from google.protobuf import json_format
from itertools import chain
import protob.types_pb2 as types
HEADER_TEMPLATE = """
// This file is automatically generated by nem_mosaics.py -- DO NOT EDIT!
#ifndef __NEM_MOSAICS_H__
#define __NEM_MOSAICS_H__
#include "types.pb.h"
#define NEM_MOSAIC_DEFINITIONS_COUNT ({count})
extern const NEMMosaicDefinition NEM_MOSAIC_DEFINITIONS[NEM_MOSAIC_DEFINITIONS_COUNT];
extern const NEMMosaicDefinition *NEM_MOSAIC_DEFINITION_XEM;
#endif
""".lstrip()
CODE_TEMPLATE = """
// This file is automatically generated by nem_mosaics.py -- DO NOT EDIT!
#include "nem_mosaics.h"
const NEMMosaicDefinition NEM_MOSAIC_DEFINITIONS[NEM_MOSAIC_DEFINITIONS_COUNT] = {code};
const NEMMosaicDefinition *NEM_MOSAIC_DEFINITION_XEM = NEM_MOSAIC_DEFINITIONS;
""".lstrip()
def format_primitive(value):
if type(value) is bool:
return ("false", "true")[value]
elif type(value) in (int, float):
return str(value)
elif type(value) is str:
return json.dumps(value)
elif type(value) is list:
return "{ " + ", ".join(
format_primitive(item) for item in value
) + " }"
else:
raise TypeError
def format_struct(struct):
return "{\n" + "\n".join(
"\t.{0} = {1},".format(member, value) for member, value in struct.items()
) + "\n}"
def format_field(field, value):
if field.message_type is not None:
raise TypeError
elif field.enum_type:
return "{0}_{1}".format(field.enum_type.name, field.enum_type.values_by_number[value].name)
elif hasattr(value, "_values"):
return format_primitive(value._values)
else:
return format_primitive(value)
def field_to_meta(field, value):
if hasattr(value, "_values"):
return ("{}_count".format(field.name), format_primitive(len(value._values)))
else:
return ("has_{}".format(field.name), format_primitive(True))
def message_to_struct(_message, proto):
message = json_format.ParseDict(_message, proto())
return dict(chain.from_iterable(
(
field_to_meta(field, value),
(field.name, format_field(field, value)),
) for field, value in message.ListFields()
))
def format_message(message, proto):
return format_struct(message_to_struct(message, proto))
def format_messages(messages, proto):
return "{" + ",\n".join(
format_message(message, proto) for message in messages
) + "}"
if __name__ == "__main__":
os.chdir(os.path.abspath(os.path.dirname(__file__)))
messages = json.load(open("nem_mosaics.json"))
with open("nem_mosaics.h", "w+") as f:
f.write(HEADER_TEMPLATE.format(count=format_primitive(len(messages))))
with open("nem_mosaics.c", "w+") as f:
f.write(CODE_TEMPLATE.format(code=format_messages(messages, types.NEMMosaicDefinition)))