Skip to content

Commit 0e58ab6

Browse files
authored
Merge pull request #228 from Zondax/feat/remove_protobuf
Feat/remove protobuf
2 parents 42f8b35 + 30a0424 commit 0e58ab6

File tree

551 files changed

+262
-8601
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

551 files changed

+262
-8601
lines changed

.github/workflows/guidelines_enforcer.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,3 @@ jobs:
2121
guidelines_enforcer:
2222
name: Call Ledger guidelines_enforcer
2323
uses: LedgerHQ/ledger-app-workflows/.github/workflows/reusable_guidelines_enforcer.yml@v1
24-
with:
25-
relative_app_directory: app

.github/workflows/sonarcloud.yml

Lines changed: 0 additions & 60 deletions
This file was deleted.

CMakeLists.txt

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -85,13 +85,6 @@ file(GLOB_RECURSE TINYCBOR_SRC
8585
${CMAKE_CURRENT_SOURCE_DIR}/deps/tinycbor/src/cborvalidation.c
8686
)
8787

88-
# static libs
89-
file(GLOB_RECURSE TINYPB_SRC
90-
${CMAKE_CURRENT_SOURCE_DIR}/deps/nanopb_tiny/pb_common.c
91-
${CMAKE_CURRENT_SOURCE_DIR}/deps/nanopb_tiny/pb_decode.c
92-
${CMAKE_CURRENT_SOURCE_DIR}/deps/nanopb_tiny/pb_encode.c
93-
)
94-
9588
file(GLOB_RECURSE LIB_SRC
9689
${CMAKE_CURRENT_SOURCE_DIR}/deps/ledger-zxlib/src/hexutils.c
9790
${CMAKE_CURRENT_SOURCE_DIR}/deps/ledger-zxlib/src/app_mode.c
@@ -108,22 +101,19 @@ file(GLOB_RECURSE LIB_SRC
108101
${CMAKE_CURRENT_SOURCE_DIR}/app/src/parser_impl.c
109102
${CMAKE_CURRENT_SOURCE_DIR}/app/src/crypto.c
110103
${CMAKE_CURRENT_SOURCE_DIR}/app/src/base32.c
111-
${CMAKE_CURRENT_SOURCE_DIR}/app/src/protobuf/*.c
112104
${CMAKE_CURRENT_SOURCE_DIR}/app/src/parser_print_*.c
113105
)
114106

115107
add_library(app_lib STATIC
116108
${LIB_SRC}
117109
${TINYCBOR_SRC}
118-
${TINYPB_SRC}
119110
)
120111

121112
target_include_directories(app_lib PUBLIC
122113
${CMAKE_CURRENT_SOURCE_DIR}/deps/BLAKE2/ref
123114
${CMAKE_CURRENT_SOURCE_DIR}/deps/ledger-zxlib/include
124115
${CMAKE_CURRENT_SOURCE_DIR}/deps/tinycbor/src
125116
${CMAKE_CURRENT_SOURCE_DIR}/deps/picohash/
126-
${CMAKE_CURRENT_SOURCE_DIR}/deps/nanopb_tiny/
127117
${CMAKE_CURRENT_SOURCE_DIR}/app/src
128118
${CMAKE_CURRENT_SOURCE_DIR}/app/src/candid
129119
${CMAKE_CURRENT_SOURCE_DIR}/app/src/lib
@@ -146,7 +136,6 @@ target_include_directories(unittests PRIVATE
146136
${CMAKE_CURRENT_SOURCE_DIR}/app/src/lib
147137
${CMAKE_CURRENT_SOURCE_DIR}/deps/tinycbor/src
148138
${CMAKE_CURRENT_SOURCE_DIR}/deps/picohash/
149-
${CMAKE_CURRENT_SOURCE_DIR}/deps/nanopb_tiny/
150139
)
151140

152141

@@ -166,7 +155,6 @@ set_tests_properties(unittests PROPERTIES WORKING_DIRECTORY ${CMAKE_CURRENT_SOUR
166155
if (ENABLE_FUZZING)
167156
set(FUZZ_TARGETS
168157
parser_parse
169-
parser_protobuf
170158
)
171159

172160
foreach (target ${FUZZ_TARGETS})

Makefile

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,6 @@ ifeq ($(BOLOS_SDK),)
2626
ZXLIB_COMPILE_STAX ?= 1
2727
include $(CURDIR)/deps/ledger-zxlib/dockerized_build.mk
2828

29-
proto:
30-
cd $(CURDIR)/app/src/protobuf && $(CURDIR)/deps/nanopb/generator/protoc ./base_types.proto ./types.proto ./governance.proto ./dfinity.proto --nanopb_out=.
31-
3229
else
3330
default:
3431
$(MAKE) -C app

app/Makefile

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,7 @@ endif
5656

5757
APP_LOAD_PARAMS = --curve secp256k1 $(COMMON_LOAD_PARAMS) --path $(APPPATH)
5858

59-
NANOS_STACK_SIZE := 2050
60-
59+
APP_STACK_MIN_SIZE := 3000
6160
include $(CURDIR)/../deps/ledger-zxlib/makefiles/Makefile.devices
6261

6362
$(info TARGET_NAME = [$(TARGET_NAME)])
@@ -72,9 +71,6 @@ CFLAGS += -Wvla -Wno-implicit-fallthrough
7271
CFLAGS += -I$(MY_DIR)/../deps/tinycbor/src
7372
APP_SOURCE_PATH += $(MY_DIR)/../deps/tinycbor-ledger
7473

75-
CFLAGS += -I$(MY_DIR)/../deps/nanopb/
76-
APP_SOURCE_PATH += $(MY_DIR)/../deps/nanopb_tiny/
77-
7874
.PHONY: rust
7975
rust:
8076
@echo "No rust code"

app/Makefile.version

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# This is the major version of this release
2-
APPVERSION_M=2
2+
APPVERSION_M=3
33
# This is the minor version of this release
4-
APPVERSION_N=4
4+
APPVERSION_N=0
55
# This is the patch version of this release
6-
APPVERSION_P=9
6+
APPVERSION_P=0

app/src/apdu_handler.c

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ __Z_INLINE bool process_chunk(volatile uint32_t *tx, uint32_t rx) {
7373
THROW(APDU_CODE_DATA_INVALID);
7474
}
7575

76-
bool is_stake_tx = parser_tx_obj.special_transfer_type == neuron_stake_transaction;
76+
const bool is_stake_tx = parser_tx_obj.special_transfer_type == neuron_stake_transaction;
7777

7878
uint32_t added;
7979
switch (payloadType) {
@@ -128,9 +128,9 @@ __Z_INLINE bool process_chunk(volatile uint32_t *tx, uint32_t rx) {
128128
__Z_INLINE void handleGetAddr(volatile uint32_t *flags, volatile uint32_t *tx, uint32_t rx) {
129129
extractHDPath(rx, OFFSET_DATA);
130130

131-
uint8_t requireConfirmation = G_io_apdu_buffer[OFFSET_P1];
131+
const uint8_t requireConfirmation = G_io_apdu_buffer[OFFSET_P1];
132132

133-
zxerr_t zxerr = app_fill_address();
133+
const zxerr_t zxerr = app_fill_address();
134134
if (zxerr != zxerr_ok) {
135135
*tx = 0;
136136
THROW(APDU_CODE_DATA_INVALID);
@@ -158,7 +158,7 @@ __Z_INLINE void handleSign(volatile uint32_t *flags, volatile uint32_t *tx, uint
158158
CHECK_APP_CANARY()
159159

160160
if (error_msg != NULL) {
161-
int error_msg_length = strlen(error_msg);
161+
const uint32_t error_msg_length = strnlen(error_msg, sizeof(G_io_apdu_buffer));
162162
MEMCPY(G_io_apdu_buffer, error_msg, error_msg_length);
163163
*tx += (error_msg_length);
164164
THROW(APDU_CODE_DATA_INVALID);
@@ -181,7 +181,7 @@ __Z_INLINE void handleSignCombined(volatile uint32_t *flags, volatile uint32_t *
181181
CHECK_APP_CANARY()
182182

183183
if (error_msg != NULL) {
184-
int error_msg_length = strlen(error_msg);
184+
const uint32_t error_msg_length = strnlen(error_msg, sizeof(G_io_apdu_buffer));
185185
MEMCPY(G_io_apdu_buffer, error_msg, error_msg_length);
186186
*tx += (error_msg_length);
187187
THROW(APDU_CODE_DATA_INVALID);
@@ -202,7 +202,9 @@ __Z_INLINE void handle_getversion(__Z_UNUSED volatile uint32_t *flags, volatile
202202
G_io_apdu_buffer[1] = LEDGER_MAJOR_VERSION;
203203
G_io_apdu_buffer[2] = LEDGER_MINOR_VERSION;
204204
G_io_apdu_buffer[3] = LEDGER_PATCH_VERSION;
205-
G_io_apdu_buffer[4] = !IS_UX_ALLOWED;
205+
// sdk won't pass the apdu message if device is locked
206+
// keeping it for backwards compatibility
207+
G_io_apdu_buffer[4] = 0;
206208

207209
G_io_apdu_buffer[5] = (TARGET_ID >> 24) & 0xFF;
208210
G_io_apdu_buffer[6] = (TARGET_ID >> 16) & 0xFF;
@@ -214,7 +216,7 @@ __Z_INLINE void handle_getversion(__Z_UNUSED volatile uint32_t *flags, volatile
214216
}
215217

216218
void handleApdu(volatile uint32_t *flags, volatile uint32_t *tx, uint32_t rx) {
217-
uint16_t sw = 0;
219+
volatile uint16_t sw = 0;
218220

219221
BEGIN_TRY
220222
{
@@ -278,7 +280,7 @@ void handleApdu(volatile uint32_t *flags, volatile uint32_t *tx, uint32_t rx) {
278280
break;
279281
}
280282
G_io_apdu_buffer[*tx] = sw >> 8;
281-
G_io_apdu_buffer[*tx + 1] = sw;
283+
G_io_apdu_buffer[*tx + 1] = sw & 0xFF;
282284
*tx += 2;
283285
}
284286
FINALLY

app/src/crypto.c

Lines changed: 24 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,6 @@
2222

2323
uint32_t hdPath[HDPATH_LEN_DEFAULT];
2424

25-
bool isTestnet() {
26-
return hdPath[0] == HDPATH_0_TESTNET &&
27-
hdPath[1] == HDPATH_1_TESTNET;
28-
}
29-
3025
uint8_t const DER_PREFIX[] = {0x30, 0x56, 0x30, 0x10, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01,
3126
0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x0a, 0x03, 0x42, 0x00};
3227

@@ -53,7 +48,7 @@ zxerr_t hash_sha224(uint8_t *input, uint16_t inputLen, uint8_t *output, uint16_t
5348
}
5449
cx_sha256_t ctx;
5550
cx_sha224_init(&ctx);
56-
cx_hash_no_throw(&ctx.header, CX_LAST, input, inputLen, output, 224);
51+
CHECK_CX_OK(cx_hash_no_throw(&ctx.header, CX_LAST, input, inputLen, output, 224));
5752
return zxerr_ok;
5853
}
5954

@@ -69,11 +64,11 @@ zxerr_t crypto_extractPublicKey(uint8_t *pubKey, uint16_t pubKeyLen) {
6964
zxerr_t err = zxerr_ledger_api_error;
7065
CATCH_CXERROR(os_derive_bip32_no_throw(CX_CURVE_256K1, hdPath,
7166
HDPATH_LEN_DEFAULT,
72-
privateKeyData, NULL))
67+
privateKeyData, NULL));
7368

74-
CATCH_CXERROR(cx_ecfp_init_private_key_no_throw(CX_CURVE_256K1, privateKeyData, 32, &cx_privateKey))
75-
CATCH_CXERROR(cx_ecfp_init_public_key_no_throw(CX_CURVE_256K1, NULL, 0, &cx_publicKey))
76-
CATCH_CXERROR(cx_ecfp_generate_pair_no_throw(CX_CURVE_256K1, &cx_publicKey, &cx_privateKey, 1))
69+
CATCH_CXERROR(cx_ecfp_init_private_key_no_throw(CX_CURVE_256K1, privateKeyData, 32, &cx_privateKey));
70+
CATCH_CXERROR(cx_ecfp_init_public_key_no_throw(CX_CURVE_256K1, NULL, 0, &cx_publicKey));
71+
CATCH_CXERROR(cx_ecfp_generate_pair_no_throw(CX_CURVE_256K1, &cx_publicKey, &cx_privateKey, 1));
7772
memcpy(pubKey, cx_publicKey.W, SECP256K1_PK_LEN);
7873
err = zxerr_ok;
7974

@@ -118,36 +113,36 @@ typedef struct {
118113
#define HASH_U64(FIELDNAME, FIELDVALUE, TMPDIGEST) { \
119114
MEMZERO(TMPDIGEST,sizeof(TMPDIGEST)); \
120115
cx_hash_sha256((uint8_t *)FIELDNAME, sizeof(FIELDNAME) - 1, TMPDIGEST, CX_SHA256_SIZE); \
121-
cx_hash_no_throw(&ctx.header, 0, TMPDIGEST, CX_SHA256_SIZE, NULL, 0); \
116+
CHECK_CX_OK(cx_hash_no_throw(&ctx.header, 0, TMPDIGEST, CX_SHA256_SIZE, NULL, 0)); \
122117
uint8_t ingressbuf[10]; \
123118
uint16_t enc_size = 0; \
124119
CHECK_ZXERR(compressLEB128(FIELDVALUE, sizeof(ingressbuf), ingressbuf, &enc_size)); \
125120
cx_hash_sha256((uint8_t *)ingressbuf, enc_size, tmpdigest, CX_SHA256_SIZE); \
126-
cx_hash_no_throw(&ctx.header, 0, tmpdigest, CX_SHA256_SIZE, NULL, 0); \
121+
CHECK_CX_OK(cx_hash_no_throw(&ctx.header, 0, tmpdigest, CX_SHA256_SIZE, NULL, 0)); \
127122
}
128123

129124
#define HASH_BYTES_INTERMEDIATE(FIELDNAME, FIELDVALUE, TMPDIGEST) { \
130125
MEMZERO(TMPDIGEST,sizeof(TMPDIGEST)); \
131126
cx_hash_sha256((uint8_t *)FIELDNAME, sizeof(FIELDNAME) - 1, TMPDIGEST, CX_SHA256_SIZE); \
132-
cx_hash_no_throw(&ctx.header, 0, TMPDIGEST, CX_SHA256_SIZE, NULL, 0); \
127+
CHECK_CX_OK(cx_hash_no_throw(&ctx.header, 0, TMPDIGEST, CX_SHA256_SIZE, NULL, 0)); \
133128
cx_hash_sha256((uint8_t *)(FIELDVALUE).data, (FIELDVALUE).len, TMPDIGEST, CX_SHA256_SIZE); \
134-
cx_hash_no_throw(&ctx.header, 0, TMPDIGEST, CX_SHA256_SIZE, NULL, 0); \
129+
CHECK_CX_OK(cx_hash_no_throw(&ctx.header, 0, TMPDIGEST, CX_SHA256_SIZE, NULL, 0)); \
135130
}
136131

137132
#define HASH_BYTES_END(FIELDNAME, FIELDVALUE, TMPDIGEST, ENDDIGEST) { \
138133
MEMZERO(TMPDIGEST,sizeof(TMPDIGEST)); \
139134
cx_hash_sha256((uint8_t *)FIELDNAME, sizeof(FIELDNAME) - 1, TMPDIGEST, CX_SHA256_SIZE); \
140-
cx_hash_no_throw(&ctx.header, 0, TMPDIGEST, CX_SHA256_SIZE, NULL, 0); \
135+
CHECK_CX_OK(cx_hash_no_throw(&ctx.header, 0, TMPDIGEST, CX_SHA256_SIZE, NULL, 0)); \
141136
cx_hash_sha256((uint8_t *)(FIELDVALUE).data, (FIELDVALUE).len, TMPDIGEST, CX_SHA256_SIZE); \
142-
cx_hash_no_throw(&ctx.header, CX_LAST, TMPDIGEST, CX_SHA256_SIZE, ENDDIGEST, CX_SHA256_SIZE); \
137+
CHECK_CX_OK(cx_hash_no_throw(&ctx.header, CX_LAST, TMPDIGEST, CX_SHA256_SIZE, ENDDIGEST, CX_SHA256_SIZE)); \
143138
}
144139

145140
#define HASH_BYTES_PTR_END(FIELDNAME, FIELDVALUE, TMPDIGEST, ENDDIGEST) { \
146141
MEMZERO(TMPDIGEST,sizeof(TMPDIGEST)); \
147142
cx_hash_sha256((uint8_t *)FIELDNAME, sizeof(FIELDNAME) - 1, TMPDIGEST, CX_SHA256_SIZE); \
148-
cx_hash_no_throw(&ctx.header, 0, TMPDIGEST, CX_SHA256_SIZE, NULL, 0); \
143+
CHECK_CX_OK(cx_hash_no_throw(&ctx.header, 0, TMPDIGEST, CX_SHA256_SIZE, NULL, 0)); \
149144
cx_hash_sha256((uint8_t *)(FIELDVALUE).dataPtr, (FIELDVALUE).len, TMPDIGEST, CX_SHA256_SIZE); \
150-
cx_hash_no_throw(&ctx.header, CX_LAST, TMPDIGEST, CX_SHA256_SIZE, ENDDIGEST, CX_SHA256_SIZE); \
145+
CHECK_CX_OK(cx_hash_no_throw(&ctx.header, CX_LAST, TMPDIGEST, CX_SHA256_SIZE, ENDDIGEST, CX_SHA256_SIZE)); \
151146
}
152147

153148
zxerr_t crypto_getDigest(uint8_t *digest, txtype_e txtype){
@@ -178,15 +173,15 @@ zxerr_t crypto_getDigest(uint8_t *digest, txtype_e txtype){
178173
HASH_U64("ingress_expiry",fields->ingress_expiry, tmpdigest);
179174

180175
cx_hash_sha256((uint8_t *)"paths", 5, tmpdigest, CX_SHA256_SIZE);
181-
cx_hash_no_throw(&ctx.header, 0, tmpdigest, CX_SHA256_SIZE, NULL, 0);
176+
CHECK_CX_OK(cx_hash_no_throw(&ctx.header, 0, tmpdigest, CX_SHA256_SIZE, NULL, 0));
182177

183178
uint8_t arrayBuffer[PATH_MAX_ARRAY * CX_SHA256_SIZE];
184179
for (size_t index = 0; index < fields->paths.arrayLen ; index++){
185180
cx_hash_sha256((uint8_t *)fields->paths.paths[index].data, fields->paths.paths[index].len, arrayBuffer + index * CX_SHA256_SIZE, CX_SHA256_SIZE);
186181
}
187182
cx_hash_sha256(arrayBuffer, fields->paths.arrayLen*CX_SHA256_SIZE, tmpdigest, CX_SHA256_SIZE);
188183
cx_hash_sha256(tmpdigest, CX_SHA256_SIZE, tmpdigest, CX_SHA256_SIZE);
189-
cx_hash_no_throw(&ctx.header, 0, tmpdigest, CX_SHA256_SIZE, NULL, 0);
184+
CHECK_CX_OK(cx_hash_no_throw(&ctx.header, 0, tmpdigest, CX_SHA256_SIZE, NULL, 0));
190185

191186
HASH_BYTES_END("request_type", parser_tx_obj.request_type, tmpdigest, digest);
192187
return zxerr_ok;
@@ -205,8 +200,7 @@ zxerr_t crypto_sign(uint8_t *signatureBuffer,
205200
return zxerr_buffer_too_small;
206201
}
207202

208-
uint8_t message_digest[CX_SHA256_SIZE];
209-
MEMZERO(message_digest,sizeof(message_digest));
203+
uint8_t message_digest[CX_SHA256_SIZE] = {0};
210204

211205
signatureBuffer[0] = 0x0a;
212206
MEMCPY(&signatureBuffer[1], (uint8_t *)"ic-request",SIGN_PREFIX_SIZE - 1);
@@ -228,9 +222,9 @@ zxerr_t crypto_sign(uint8_t *signatureBuffer,
228222
CATCH_CXERROR(os_derive_bip32_no_throw(CX_CURVE_SECP256K1,
229223
hdPath,
230224
HDPATH_LEN_DEFAULT,
231-
privateKeyData, NULL))
225+
privateKeyData, NULL));
232226

233-
CATCH_CXERROR(cx_ecfp_init_private_key_no_throw(CX_CURVE_SECP256K1, privateKeyData, 32, &cx_privateKey))
227+
CATCH_CXERROR(cx_ecfp_init_private_key_no_throw(CX_CURVE_SECP256K1, privateKeyData, 32, &cx_privateKey));
234228

235229
// Sign
236230
CATCH_CXERROR(cx_ecdsa_sign_no_throw(&cx_privateKey,
@@ -240,7 +234,7 @@ zxerr_t crypto_sign(uint8_t *signatureBuffer,
240234
CX_SHA256_SIZE,
241235
signature->der_signature,
242236
&signatureLength,
243-
&info))
237+
&info));
244238

245239
err_convert_e err_c = convertDERtoRSV(signature->der_signature, info, signature->r, signature->s, &signature->v);
246240
if (err_c != no_error) {
@@ -309,9 +303,9 @@ zxerr_t crypto_sign_combined(uint8_t *signatureBuffer,
309303
CATCH_CXERROR(os_derive_bip32_no_throw(CX_CURVE_SECP256K1,
310304
hdPath,
311305
HDPATH_LEN_DEFAULT,
312-
privateKeyData, NULL))
306+
privateKeyData, NULL));
313307

314-
CATCH_CXERROR(cx_ecfp_init_private_key_no_throw(CX_CURVE_SECP256K1, privateKeyData, 32, &cx_privateKey))
308+
CATCH_CXERROR(cx_ecfp_init_private_key_no_throw(CX_CURVE_SECP256K1, privateKeyData, 32, &cx_privateKey));
315309

316310
// Sign request
317311
CATCH_CXERROR(cx_ecdsa_sign_no_throw(&cx_privateKey,
@@ -321,7 +315,7 @@ zxerr_t crypto_sign_combined(uint8_t *signatureBuffer,
321315
CX_SHA256_SIZE,
322316
sigma.der_signature,
323317
&sigLen,
324-
&info))
318+
&info));
325319

326320
err_convert_e err_c = convertDERtoRSV(sigma.der_signature, info, sigma.r, sigma.s, &sigma.v);
327321
if (err_c != no_error) {
@@ -343,7 +337,7 @@ zxerr_t crypto_sign_combined(uint8_t *signatureBuffer,
343337
CX_SHA256_SIZE,
344338
sigma.der_signature,
345339
&sigLen,
346-
&info))
340+
&info));
347341

348342
err_c = convertDERtoRSV(sigma.der_signature, info, sigma.r, sigma.s, &sigma.v);
349343
if (err_c != no_error) {
@@ -665,9 +659,7 @@ zxerr_t crypto_fillAddress(uint8_t *buffer, uint16_t buffer_len, uint16_t *addrL
665659
CHECK_ZXERR(crypto_computePrincipal(answer->publicKey, answer->principalBytes));
666660

667661
//For now only defeault subaccount, maybe later grab 32 bytes from the apdu buffer.
668-
uint8_t zero_subaccount[DFINITY_SUBACCOUNT_LEN];
669-
MEMZERO(zero_subaccount, DFINITY_SUBACCOUNT_LEN);
670-
662+
uint8_t zero_subaccount[DFINITY_SUBACCOUNT_LEN] = {0};
671663
CHECK_ZXERR(crypto_principalToSubaccount(answer->principalBytes, sizeof_field(answer_t, principalBytes),
672664
zero_subaccount, DFINITY_SUBACCOUNT_LEN, answer->subAccountBytes,
673665
sizeof_field(answer_t, subAccountBytes)));

0 commit comments

Comments
 (0)