30 return (n < 10U) ?
static_cast<char>(
'0' + n)
31 :
static_cast<char>(
'a' + n - 10U);
36 return ((c >=
'0') && (c <=
'9')) ||
37 ((c >=
'a') && (c <=
'f')) ||
38 ((c >=
'A') && (c <=
'F'));
43 return ((c >=
'A') && (c <=
'Z')) ?
static_cast<char>(c + (
'a' -
'A')) : c;
50 for (; s[n] !=
'\0'; n++) {
63 if (raw == NULL) {
return false; }
65 return (n != 0U) && ((n % 2U) == 0U);
71 return (addr != NULL) &&
73 (addr[0] ==
'4') && (addr[1] ==
'1');
82 uint8_t x =
static_cast<uint8_t
>(v & 0x7FU);
84 if (v != 0U) { x |= 0x80U; }
87 }
while ((v != 0U) && (n <
sizeof(b)));
89 if ((out == NULL) || (out_size < ((n * 2U) + 1U))) {
return 0U; }
91 for (
size_t i = 0U; i < n; i++) {
92 out[i * 2U] =
nibble_hex((b[i] >> 4U) & 0x0FU);
126 if ((c >=
'0') && (c <=
'9')) {
return c -
'0'; }
127 if ((c >=
'a') && (c <=
'f')) {
return (c -
'a') + 10; }
133 if (p->
pos >= p->
end) {
return false; }
136 if ((hi < 0) || (lo < 0)) {
return false; }
137 *b =
static_cast<uint8_t
>((hi << 4) | lo);
145 for (
unsigned i = 0U; i < 10U; i++) {
147 if (!
pb_byte(p, &b)) {
return false; }
148 x |=
static_cast<uint64_t
>(b & 0x7FU) << (7U * i);
149 if ((b & 0x80U) == 0U) { *v = x;
return true; }
165 if (!
pb_varint(p, &key)) {
return false; }
166 f->
num =
static_cast<uint32_t
>(key >> 3U);
167 f->
wire =
static_cast<uint32_t
>(key & 7U);
168 if ((f->
num == 0U) || (f->
num > 31U)) {
return false; }
170 if (f->
wire != 2U) {
return false; }
172 if (!
pb_varint(p, &len) || (len > (p->
end - p->
pos))) {
return false; }
175 f->
sub.
end = p->
pos +
static_cast<size_t>(len);
183 const uint32_t bit = 1UL << f->
num;
184 if ((f->
wire != wire) || ((*seen & bit) != 0U)) {
return false; }
192 const size_t n = sub->
end - sub->
pos;
193 if (strlen(want) != (n * 2U)) {
return false; }
194 const char *h = sub->
hex + (sub->
pos * 2U);
195 for (
size_t i = 0U; i < (n * 2U); i++) {
204 const size_t n = sub->
end - sub->
pos;
205 if (strlen(want) != n) {
return false; }
207 for (
size_t i = 0U; i < n; i++) {
209 if (!
pb_byte(&c, &b) || (b !=
static_cast<uint8_t
>(want[i]))) {
216#define TRON_TYPE_TRANSFER 1U
217#define TRON_TYPE_TRIGGER 31U
218#define TRON_URL_TRANSFER "type.googleapis.com/protocol.TransferContract"
219#define TRON_URL_TRIGGER "type.googleapis.com/protocol.TriggerSmartContract"
222static bool any_ok(
pb_t any,
const char *url,
const char *v1,
const char *v2)
228 while (any.
pos < any.
end) {
229 if (!
pb_next(&any, &f) || !
pb_once(&f, 2U, &seen)) {
return false; }
232 }
else if (f.
num == 2U) {
239 return url_ok && val_ok;
244 const char *v1,
const char *v2)
247 bool type_ok =
false;
248 bool param_ok =
false;
251 if (!
pb_next(&c, &f)) {
return false; }
253 if (!
pb_once(&f, 0U, &seen)) {
return false; }
254 type_ok = (f.
value == type);
255 }
else if (f.
num == 2U) {
256 if (!
pb_once(&f, 2U, &seen)) {
return false; }
262 return type_ok && param_ok;
273static bool raw_ok(
const char *raw_hex, uint32_t type,
const char *url,
274 const char *v1,
const char *v2, uint64_t fee_limit,
275 uint64_t
now_ms, uint64_t *expiration_ms)
277 pb_t p = { raw_hex, 0U, strlen(raw_hex) / 2U };
279 bool have_contract =
false;
280 bool fee_ok = (fee_limit == 0U);
281 uint64_t expiration = 0U;
284 if (!
pb_next(&p, &f)) {
return false; }
287 if (!
pb_once(&f, 2U, &seen)) {
return false; }
290 if (!
pb_once(&f, 0U, &seen)) {
return false; }
293 if (!
pb_once(&f, 0U, &seen)) {
return false; }
294 expiration = f.
value;
297 if (!
pb_once(&f, 2U, &seen)) {
return false; }
301 if (!
pb_once(&f, 0U, &seen)) {
return false; }
302 fee_ok = (fee_limit != 0U) && (f.
value == fee_limit);
308 if (!have_contract || !fee_ok || (expiration == 0U)) {
return false; }
312 if (expiration_ms != NULL) { *expiration_ms = expiration; }
317 const char *to_hex, uint64_t amount_sun,
318 uint64_t
now_ms, uint64_t *expiration_ms)
320 if (!
raw_hex_ok(raw_data_hex)) {
return false; }
323 if (amount_sun == 0U) {
return false; }
333 int k = snprintf(want,
sizeof(want),
"0a15%s1215%s18%s",
334 owner_hex, to_hex, amount);
335 if ((k <= 0) || (
static_cast<size_t>(k) >=
sizeof(want))) {
return false; }
338 want, NULL, 0U,
now_ms, expiration_ms);
342 char *out,
size_t out_size)
351 for (
size_t i = 0U; i < 24U; i++, p++) { out[p] =
'0'; }
356 for (
size_t i = 0U; i < 48U; i++, p++) { out[p] =
'0'; }
357 for (
int sh = 60; sh >= 0; sh -= 4, p++) {
358 out[p] =
nibble_hex(
static_cast<unsigned>((amount >> sh) & 0x0FULL));
366 const char *contract_hex,
const char *to_hex,
367 uint64_t amount, uint64_t fee_limit_sun,
368 uint64_t
now_ms, uint64_t *expiration_ms)
370 if (!
raw_hex_ok(raw_data_hex)) {
return false; }
377 if ((amount == 0U) || (fee_limit_sun == 0U)) {
return false; }
390 int k = snprintf(want,
sizeof(want),
392 owner_hex, contract_hex, param);
393 if ((k <= 0) || (
static_cast<size_t>(k) >=
sizeof(want))) {
return false; }
394 char want_cv[
sizeof(want)];
395 k = snprintf(want_cv,
sizeof(want_cv),
397 owner_hex, contract_hex, param);
398 if ((k <= 0) || (
static_cast<size_t>(k) >=
sizeof(want_cv))) {
403 want, want_cv, fee_limit_sun,
now_ms, expiration_ms);
407 char *out,
size_t out_size)
409 if ((raw_data_hex == NULL) || (sig_hex == NULL) || (out == NULL) ||
414 const size_t raw_len =
hex_strlen(raw_data_hex);
415 if ((raw_len == 0U) || ((raw_len % 2U) != 0U)) {
return 0U; }
423 int k = snprintf(out, out_size,
"0a%s%s1241%s",
424 len_hex, raw_data_hex, sig_hex);
425 if ((k <= 0) || (
static_cast<size_t>(k) >= out_size)) {
429 return static_cast<size_t>(k);
One field: its number and either its varint or its payload range.
Cursor over a hex byte string; positions count BYTES, not nibbles.
static uint64_t now_ms(void)
Unix time in ms, 0 if the clock is unset (the checks then refuse).
bool tron_tx_contract_ok(const char *raw_data_hex, const char *owner_hex, const char *to_hex, uint64_t amount_sun, uint64_t now_ms, uint64_t *expiration_ms)
Verify that a node-serialised raw_data really transfers what we asked.
static bool raw_hex_ok(const char *raw)
true if raw is usable raw_data hex: all hex, non-empty, whole bytes.
static bool is_hex_digit(char c)
#define TRON_TYPE_TRANSFER
static bool pb_is_str(const pb_t *sub, const char *want)
true if a payload is exactly the ASCII string want.
bool tron_tx_trc20_ok(const char *raw_data_hex, const char *owner_hex, const char *contract_hex, const char *to_hex, uint64_t amount, uint64_t fee_limit_sun, uint64_t now_ms, uint64_t *expiration_ms)
Verify that a node-serialised raw_data really is the TRC-20 transfer we asked for.
static bool addr_hex_ok(const char *addr)
true if addr is a "41"-prefixed 21-byte Tron address in hex.
size_t tron_trc20_param_hex(const char *to_hex, uint64_t amount, char *out, size_t out_size)
ABI-encode the arguments of transfer(address,uint256), hex.
static bool pb_is_hex(const pb_t *sub, const char *want)
true if a payload is exactly the hex string want (any case).
static bool contract_ok(pb_t c, uint32_t type, const char *url, const char *v1, const char *v2)
Contract { 1 type, 2 parameter } and nothing else.
#define TRON_URL_TRANSFER
static char lower_ascii(char c)
static bool raw_ok(const char *raw_hex, uint32_t type, const char *url, const char *v1, const char *v2, uint64_t fee_limit, uint64_t now_ms, uint64_t *expiration_ms)
Walk raw_data. fee_limit 0 means the field must be absent.
static size_t hex_strlen(const char *s)
Length of s if every character is hex, 0 otherwise.
#define TRON_TYPE_TRIGGER
size_t tron_varint_hex(uint64_t v, char *out, size_t out_size)
Hex-encode a value as a protobuf base-128 varint.
size_t tron_tx_envelope_hex(const char *raw_data_hex, const char *sig_hex, char *out, size_t out_size)
Build the hex of a signed Transaction protobuf for broadcasthex.
static bool any_ok(pb_t any, const char *url, const char *v1, const char *v2)
Any { 1 type_url, 2 value }: exactly url and one of the values.
static bool pb_varint(pb_t *p, uint64_t *v)
static char nibble_hex(unsigned n)
static bool pb_byte(pb_t *p, uint8_t *b)
static int hex_val(char c)
static bool pb_next(pb_t *p, pb_field_t *f)
static bool pb_once(const pb_field_t *f, uint32_t wire, uint32_t *seen)
true if field f is wire wire and was not seen before.
Tron transaction hex helpers — protobuf varints, the TransferContract integrity check and the signed-...
#define TRON_TX_MAX_EXPIRY_MS
Latest raw_data.expiration accepted, relative to now (15 min). A real node answer expires ~60 s out; ...
#define TRON_ADDR_HEX_LEN
Hex length of a Tron address (21 bytes: 0x41 || 20-byte key hash).
#define TRON_SIG_HEX_LEN
Hex length of a Tron signature (r || s || v).
#define TRON_TRC20_PARAM_HEX_LEN
Hex length of the two ABI argument words that follow the selector.
#define TRON_TRC20_SELECTOR
TRC-20 transfer(address,uint256) selector, hex (same as ERC-20).