Compare commits
239 Commits
event-rela
...
nprofile-q
| Author | SHA1 | Date | |
|---|---|---|---|
|
8f6ea4d8dd
|
|||
|
|
05b62c5860 | ||
|
|
fae061cec0 | ||
|
|
4570ba797c | ||
|
|
d1ea081018 | ||
|
|
682704b2cb | ||
|
|
176f1a338a | ||
|
|
fc1eb326e8 | ||
|
|
5e420187e0 | ||
|
|
4815c8a6f7 | ||
|
|
f42ae0673d | ||
|
|
474e2d8d57 | ||
|
|
95a91bed7e | ||
|
|
ff12d8bd7e | ||
|
|
f8245a7b0e | ||
|
|
4036995348 | ||
|
|
5b6534fd56 | ||
|
|
bdd10cccaa | ||
|
|
e9f4cbe881 | ||
|
|
91abd187d3 | ||
|
|
b9d8b1dbf3 | ||
|
|
12a7b483a0 | ||
|
|
caa7802bce | ||
|
|
9c47d2e0bd | ||
|
|
5cd5a249ce | ||
|
|
c86b3a999d | ||
|
|
b5afa3c0b4 | ||
|
|
8f32c81b6c | ||
|
|
f8185d0ca5 | ||
|
|
eb99584501 | ||
|
|
919f644cba | ||
|
|
690e1347e0 | ||
|
|
744bf4bb07 | ||
|
|
475940aa01 | ||
|
|
28a06af534 | ||
|
|
208b3331ca | ||
|
|
5b1f0c4714 | ||
|
|
249e765642 | ||
|
|
712624f515 | ||
|
|
6e7b3b94d7 | ||
|
|
969a2b656e | ||
|
|
d8e7b4707e | ||
|
|
a51618cfd3 | ||
|
|
82da5da4d3 | ||
|
|
37f9c93705 | ||
|
|
094cf5e8cc | ||
|
|
46541694a0 | ||
|
|
04d4ff4e99 | ||
|
|
2d02766461 | ||
|
|
1e6873c879 | ||
|
|
d3496af5cc | ||
|
|
ec798bdeb2 | ||
|
|
fa9b952295 | ||
|
|
27f55bc09f | ||
|
|
52845a52bb | ||
|
|
4e27cca12b | ||
|
|
98e9ba25da | ||
|
|
e6cb6c938b | ||
|
|
af5961ce26 | ||
|
|
58de0025aa | ||
|
|
c931108741 | ||
|
|
20255198fd | ||
|
|
289a8e262a | ||
|
|
05baba9c03 | ||
|
|
e0461d3458 | ||
|
|
62aa72c215 | ||
|
|
287b35a8fb | ||
|
|
478d7b4060 | ||
|
|
2c4728508b | ||
|
|
d24a3f0ce5 | ||
|
|
efba599779 | ||
|
|
19243d49e1 | ||
|
|
6845d0df47 | ||
|
|
8e79ad582a | ||
|
|
282c02eed4 | ||
|
|
155ac27bb5 | ||
|
|
be1d149f4b | ||
|
|
9e0dc47e98 | ||
|
|
0916b14b32 | ||
|
|
6818d001f2 | ||
|
|
4bf9160502 | ||
|
|
02df1e209b | ||
|
|
3186b0e1d3 | ||
|
|
de0935582c | ||
|
|
573de6b881 | ||
|
|
44ab702792 | ||
|
|
1fdf234c46 | ||
|
|
3018200e95 | ||
|
|
47b79fc02e | ||
|
|
0c483bb55a | ||
|
|
ddd30054e8 | ||
|
|
30c5225ed0 | ||
|
|
8c446f804c | ||
|
|
e92018aee5 | ||
|
|
cfb140472d | ||
|
|
2f5fd54297 | ||
|
|
02e970eb9b | ||
|
|
b4b84e6895 | ||
|
|
7831ede057 | ||
|
|
a8d7d971b1 | ||
|
|
201cdd7edc | ||
|
|
e3ca6ca5b4 | ||
|
|
494386d211 | ||
|
|
6c53bc75f2 | ||
|
|
6001063754 | ||
|
|
eb0a1ee807 | ||
|
|
827731b9cb | ||
|
|
56d44d0004 | ||
|
|
7742c8fb3c | ||
|
|
7f2ee78512 | ||
|
|
4d75894bc4 | ||
|
|
bbed448ccb | ||
|
|
3fb4d81d48 | ||
|
|
fc30b68c40 | ||
|
|
0ac25b7aa3 | ||
|
|
b326f007f2 | ||
|
|
a86d8416fc | ||
|
|
b5c57dc935 | ||
|
|
7d6814a481 | ||
|
|
8dd048681b | ||
|
|
2d02a17af6 | ||
|
|
3171959d85 | ||
|
|
bca3716e33 | ||
|
|
57db252783 | ||
|
|
319579f912 | ||
|
|
92e1e4b08f | ||
|
|
ffc50bb2c1 | ||
|
|
a562be009d | ||
|
|
30c9bc7db7 | ||
|
|
0ac03df841 | ||
|
|
db99b4f4d4 | ||
|
|
cc9585b6e3 | ||
|
|
bd17dcfac6 | ||
|
|
25e91b386c | ||
|
|
560e9e53cd | ||
|
|
1c1e5fa2a0 | ||
|
|
2d5f86b142 | ||
|
|
89686d758a | ||
|
|
6c26add1da | ||
|
|
3c5a83392e | ||
|
|
1c63c3b9bb | ||
|
|
0bd4717e01 | ||
|
|
bebd531b58 | ||
|
|
5788c077c4 | ||
|
|
1b77b4f0e0 | ||
|
|
62625c6ff3 | ||
|
|
c8d88058d4 | ||
|
|
b8bef86ea1 | ||
|
|
b128330b2a | ||
|
|
934ea80f85 | ||
|
|
588cebd18d | ||
|
|
ccca6e58ec | ||
|
|
c1befa5221 | ||
|
|
8b3c86c5de | ||
|
|
05c5a6dacb | ||
|
|
1a6568deca | ||
|
|
1b2f4c41df | ||
|
|
25bcf9c243 | ||
|
|
3993679cc0 | ||
|
|
e302bf37fa | ||
|
|
a45f4d3087 | ||
|
|
d598e178c1 | ||
|
|
77601e77ee | ||
|
|
206efba58a | ||
|
|
a84749cd07 | ||
|
|
099b588be2 | ||
|
|
75c7adddb8 | ||
|
|
9f1b9ab945 | ||
|
|
b2080a946e | ||
|
|
942e47a720 | ||
|
|
6dbf3416b9 | ||
|
|
2b14acd62f | ||
|
|
267a9ac54b | ||
|
|
8b03ed6175 | ||
|
|
6cd7b945ca | ||
|
|
e5e6735129 | ||
|
|
9c2f7a931c | ||
|
|
b1bbf355de | ||
|
|
d7a2064786 | ||
|
|
4d14ca8d0a | ||
|
|
81d65cd5bf | ||
|
|
f03d8a5ac9 | ||
|
|
0df18ae1a4 | ||
|
|
8c5ec32eaa | ||
|
|
bdedf8bd8c | ||
|
|
c2383060aa | ||
|
|
432cdb96d9 | ||
|
|
f580c7dd93 | ||
|
|
c677233dcb | ||
|
|
d063362bd7 | ||
|
|
088683696a | ||
|
|
f2795aa71c | ||
|
|
c831976078 | ||
|
|
c2c73c3af6 | ||
|
|
971fa3e4ef | ||
|
|
dfa145dd4a | ||
|
|
4cfe28d802 | ||
|
|
034f2cc02f | ||
|
|
6f9bd6c4f4 | ||
|
|
d73422db38 | ||
|
|
c3b06d281e | ||
|
|
1b09e9458c | ||
|
|
e0a2dcf3db | ||
|
|
9ff1f69a82 | ||
|
|
623b8603c2 | ||
|
|
d8b083010d | ||
|
|
887eb4e1e2 | ||
|
|
b5ad3ed1a5 | ||
|
|
371e9fb406 | ||
|
|
aa5809d792 | ||
|
|
30ba0d72cc | ||
|
|
373cd71f69 | ||
|
|
acaf327a07 | ||
|
|
9f0bf7dff5 | ||
|
|
88d7eb8a86 | ||
|
|
76862776b8 | ||
|
|
4c55459c1f | ||
|
|
f7cdc7bc31 | ||
|
|
1bc4971111 | ||
|
|
6ce6c79160 | ||
|
|
1ffbd80c67 | ||
|
|
1fb88a912a | ||
|
|
954f48b23d | ||
|
|
cc75a8450a | ||
|
|
389c2c9695 | ||
|
|
4a6121ba13 | ||
|
|
a469f2e127 | ||
|
|
2f8f18b846 | ||
|
|
3a7cf4d08d | ||
|
|
e3001cc240 | ||
|
|
d1ef113a8b | ||
|
|
f187f4f8f2 | ||
|
|
4e9583ef54 | ||
|
|
cc95d5df6e | ||
|
|
4ca156fd83 | ||
|
|
9f6da8eb79 | ||
|
|
65a22813a3 | ||
| fdbf271432 | |||
| b26eedc633 |
@@ -103,7 +103,7 @@ struct NotificationFormatter {
|
||||
content.title = Self.zap_notification_title(zap)
|
||||
content.body = Self.zap_notification_body(profiles: state.profiles, zap: zap)
|
||||
content.sound = UNNotificationSound.default
|
||||
content.userInfo = LossyLocalNotification(type: .zap, mention: .note(notify.event.id)).to_user_info()
|
||||
content.userInfo = LossyLocalNotification(type: .zap, mention: .init(nip19: .note(notify.event.id))).to_user_info()
|
||||
return (content, "myZapNotification")
|
||||
default:
|
||||
// The sync method should have taken care of this.
|
||||
|
||||
@@ -90,7 +90,7 @@ class NotificationService: UNNotificationServiceExtension {
|
||||
return
|
||||
}
|
||||
|
||||
guard let notification_object = generate_local_notification_object(from: nostr_event, state: state) else {
|
||||
guard let notification_object = generate_local_notification_object(ndb: state.ndb, from: nostr_event, state: state) else {
|
||||
Log.debug("generate_local_notification_object failed", for: .push_notifications)
|
||||
// We could not process this notification. Probably an unsupported nostr event kind. Suppress.
|
||||
// contentHandler(UNNotificationContent())
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
//
|
||||
// block.h
|
||||
// damus
|
||||
//
|
||||
// Created by William Casarin on 2023-04-09.
|
||||
//
|
||||
|
||||
#ifndef block_h
|
||||
#define block_h
|
||||
|
||||
#include "nostr_bech32.h"
|
||||
#include "str_block.h"
|
||||
|
||||
#define MAX_BLOCKS 1024
|
||||
|
||||
enum block_type {
|
||||
BLOCK_HASHTAG = 1,
|
||||
BLOCK_TEXT = 2,
|
||||
BLOCK_MENTION_INDEX = 3,
|
||||
BLOCK_MENTION_BECH32 = 4,
|
||||
BLOCK_URL = 5,
|
||||
BLOCK_INVOICE = 6,
|
||||
};
|
||||
|
||||
|
||||
typedef struct invoice_block {
|
||||
struct str_block invstr;
|
||||
union {
|
||||
struct bolt11 *bolt11;
|
||||
};
|
||||
} invoice_block_t;
|
||||
|
||||
typedef struct mention_bech32_block {
|
||||
struct str_block str;
|
||||
struct nostr_bech32 bech32;
|
||||
} mention_bech32_block_t;
|
||||
|
||||
typedef struct note_block {
|
||||
enum block_type type;
|
||||
union {
|
||||
struct str_block str;
|
||||
struct invoice_block invoice;
|
||||
struct mention_bech32_block mention_bech32;
|
||||
int mention_index;
|
||||
} block;
|
||||
} block_t;
|
||||
|
||||
typedef struct note_blocks {
|
||||
int words;
|
||||
int num_blocks;
|
||||
struct note_block *blocks;
|
||||
} blocks_t;
|
||||
|
||||
void blocks_init(struct note_blocks *blocks);
|
||||
void blocks_free(struct note_blocks *blocks);
|
||||
|
||||
#endif /* block_h */
|
||||
@@ -2,7 +2,6 @@
|
||||
// Use this file to import your target's public headers that you would like to expose to Swift.
|
||||
//
|
||||
|
||||
#include "damus.h"
|
||||
#include "bolt11.h"
|
||||
#include "amount.h"
|
||||
#include "nostr_bech32.h"
|
||||
|
||||
393
damus-c/damus.c
393
damus-c/damus.c
@@ -1,393 +0,0 @@
|
||||
//
|
||||
// damus.c
|
||||
// damus
|
||||
//
|
||||
// Created by William Casarin on 2022-10-17.
|
||||
//
|
||||
|
||||
#include "damus.h"
|
||||
#include "cursor.h"
|
||||
#include "bolt11.h"
|
||||
#include "bech32.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static int parse_digit(struct cursor *cur, int *digit) {
|
||||
int c;
|
||||
if ((c = peek_char(cur, 0)) == -1)
|
||||
return 0;
|
||||
|
||||
c -= '0';
|
||||
|
||||
if (c >= 0 && c <= 9) {
|
||||
*digit = c;
|
||||
cur->p++;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int parse_mention_index(struct cursor *cur, struct note_block *block) {
|
||||
int d1, d2, d3, ind;
|
||||
u8 *start = cur->p;
|
||||
|
||||
if (!parse_str(cur, "#["))
|
||||
return 0;
|
||||
|
||||
if (!parse_digit(cur, &d1)) {
|
||||
cur->p = start;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ind = d1;
|
||||
|
||||
if (parse_digit(cur, &d2))
|
||||
ind = (d1 * 10) + d2;
|
||||
|
||||
if (parse_digit(cur, &d3))
|
||||
ind = (d1 * 100) + (d2 * 10) + d3;
|
||||
|
||||
if (!parse_char(cur, ']')) {
|
||||
cur->p = start;
|
||||
return 0;
|
||||
}
|
||||
|
||||
block->type = BLOCK_MENTION_INDEX;
|
||||
block->block.mention_index = ind;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int parse_hashtag(struct cursor *cur, struct note_block *block) {
|
||||
int c;
|
||||
u8 *start = cur->p;
|
||||
|
||||
if (!parse_char(cur, '#'))
|
||||
return 0;
|
||||
|
||||
c = peek_char(cur, 0);
|
||||
if (c == -1 || is_whitespace(c) || c == '#') {
|
||||
cur->p = start;
|
||||
return 0;
|
||||
}
|
||||
|
||||
consume_until_boundary(cur);
|
||||
|
||||
block->type = BLOCK_HASHTAG;
|
||||
block->block.str.start = (const char*)(start + 1);
|
||||
block->block.str.end = (const char*)cur->p;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int add_block(struct note_blocks *blocks, struct note_block block)
|
||||
{
|
||||
if (blocks->num_blocks + 1 >= MAX_BLOCKS)
|
||||
return 0;
|
||||
|
||||
blocks->blocks[blocks->num_blocks++] = block;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int add_text_block(struct note_blocks *blocks, const u8 *start, const u8 *end)
|
||||
{
|
||||
struct note_block b;
|
||||
|
||||
if (start == end)
|
||||
return 1;
|
||||
|
||||
b.type = BLOCK_TEXT;
|
||||
b.block.str.start = (const char*)start;
|
||||
b.block.str.end = (const char*)end;
|
||||
|
||||
return add_block(blocks, b);
|
||||
}
|
||||
|
||||
static int consume_url_fragment(struct cursor *cur)
|
||||
{
|
||||
int c;
|
||||
|
||||
if ((c = peek_char(cur, 0)) < 0)
|
||||
return 1;
|
||||
|
||||
if (c != '#' && c != '?') {
|
||||
return 1;
|
||||
}
|
||||
|
||||
cur->p++;
|
||||
|
||||
return consume_until_end_url(cur, 1);
|
||||
}
|
||||
|
||||
static int consume_url_path(struct cursor *cur)
|
||||
{
|
||||
int c;
|
||||
|
||||
if ((c = peek_char(cur, 0)) < 0)
|
||||
return 1;
|
||||
|
||||
if (c != '/') {
|
||||
return 1;
|
||||
}
|
||||
|
||||
while (cur->p < cur->end) {
|
||||
c = *cur->p;
|
||||
|
||||
if (c == '?' || c == '#' || is_final_url_char(cur->p, cur->end)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
cur->p++;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int consume_url_host(struct cursor *cur)
|
||||
{
|
||||
char c;
|
||||
int count = 0;
|
||||
|
||||
while (cur->p < cur->end) {
|
||||
c = *cur->p;
|
||||
// TODO: handle IDNs
|
||||
if ((is_alphanumeric(c) || c == '.' || c == '-') && !is_final_url_char(cur->p, cur->end))
|
||||
{
|
||||
count++;
|
||||
cur->p++;
|
||||
continue;
|
||||
}
|
||||
|
||||
return count != 0;
|
||||
}
|
||||
|
||||
|
||||
// this means the end of the URL hostname is the end of the buffer and we finished
|
||||
return count != 0;
|
||||
}
|
||||
|
||||
static int parse_url(struct cursor *cur, struct note_block *block) {
|
||||
u8 *start = cur->p;
|
||||
u8 *host;
|
||||
int host_len;
|
||||
struct cursor path_cur;
|
||||
|
||||
if (!parse_str(cur, "http"))
|
||||
return 0;
|
||||
|
||||
if (parse_char(cur, 's') || parse_char(cur, 'S')) {
|
||||
if (!parse_str(cur, "://")) {
|
||||
cur->p = start;
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
if (!parse_str(cur, "://")) {
|
||||
cur->p = start;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// make sure to save the hostname. We will use this to detect damus.io links
|
||||
host = cur->p;
|
||||
|
||||
if (!consume_url_host(cur)) {
|
||||
cur->p = start;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// get the length of the host string
|
||||
host_len = (int)(cur->p - host);
|
||||
|
||||
// save the current parse state so that we can continue from here when
|
||||
// parsing the bech32 in the damus.io link if we have it
|
||||
copy_cursor(cur, &path_cur);
|
||||
|
||||
// skip leading /
|
||||
cursor_skip(&path_cur, 1);
|
||||
|
||||
if (!consume_url_path(cur)) {
|
||||
cur->p = start;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!consume_url_fragment(cur)) {
|
||||
cur->p = start;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// smart parens
|
||||
if (start - 1 >= 0 &&
|
||||
start < cur->end &&
|
||||
*(start - 1) == '(' &&
|
||||
(cur->p - 1) < cur->end &&
|
||||
*(cur->p - 1) == ')')
|
||||
{
|
||||
cur->p--;
|
||||
}
|
||||
|
||||
// save the bech32 string pos in case we hit a damus.io link
|
||||
block->block.str.start = (const char *)path_cur.p;
|
||||
|
||||
// if we have a damus link, make it a mention
|
||||
if (host_len == 8
|
||||
&& !strncmp((const char *)host, "damus.io", 8)
|
||||
&& parse_nostr_bech32(&path_cur, &block->block.mention_bech32.bech32))
|
||||
{
|
||||
block->block.str.end = (const char *)path_cur.p;
|
||||
block->type = BLOCK_MENTION_BECH32;
|
||||
return 1;
|
||||
}
|
||||
|
||||
block->type = BLOCK_URL;
|
||||
block->block.str.start = (const char *)start;
|
||||
block->block.str.end = (const char *)cur->p;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int parse_invoice(struct cursor *cur, struct note_block *block) {
|
||||
u8 *start, *end;
|
||||
char *fail;
|
||||
struct bolt11 *bolt11;
|
||||
// optional
|
||||
parse_str(cur, "lightning:");
|
||||
|
||||
start = cur->p;
|
||||
|
||||
if (!parse_str(cur, "lnbc"))
|
||||
return 0;
|
||||
|
||||
if (!consume_until_whitespace(cur, 1)) {
|
||||
cur->p = start;
|
||||
return 0;
|
||||
}
|
||||
|
||||
end = cur->p;
|
||||
|
||||
char str[end - start + 1];
|
||||
str[end - start] = 0;
|
||||
memcpy(str, start, end - start);
|
||||
|
||||
if (!(bolt11 = bolt11_decode(NULL, str, &fail))) {
|
||||
cur->p = start;
|
||||
return 0;
|
||||
}
|
||||
|
||||
block->type = BLOCK_INVOICE;
|
||||
|
||||
block->block.invoice.invstr.start = (const char*)start;
|
||||
block->block.invoice.invstr.end = (const char*)end;
|
||||
block->block.invoice.bolt11 = bolt11;
|
||||
|
||||
cur->p = end;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int parse_mention_bech32(struct cursor *cur, struct note_block *block) {
|
||||
u8 *start = cur->p;
|
||||
|
||||
parse_char(cur, '@');
|
||||
parse_str(cur, "nostr:");
|
||||
|
||||
block->block.str.start = (const char *)cur->p;
|
||||
|
||||
if (!parse_nostr_bech32(cur, &block->block.mention_bech32.bech32)) {
|
||||
cur->p = start;
|
||||
return 0;
|
||||
}
|
||||
|
||||
block->block.str.end = (const char *)cur->p;
|
||||
|
||||
block->type = BLOCK_MENTION_BECH32;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int add_text_then_block(struct cursor *cur, struct note_blocks *blocks, struct note_block block, u8 **start, const u8 *pre_mention)
|
||||
{
|
||||
if (!add_text_block(blocks, *start, pre_mention))
|
||||
return 0;
|
||||
|
||||
*start = (u8*)cur->p;
|
||||
|
||||
if (!add_block(blocks, block))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int damus_parse_content(struct note_blocks *blocks, const char *content) {
|
||||
int cp, c;
|
||||
struct cursor cur;
|
||||
struct note_block block;
|
||||
u8 *start, *pre_mention;
|
||||
|
||||
blocks->words = 0;
|
||||
blocks->num_blocks = 0;
|
||||
make_cursor((u8*)content, (u8*)content + strlen(content), &cur);
|
||||
|
||||
start = cur.p;
|
||||
while (cur.p < cur.end && blocks->num_blocks < MAX_BLOCKS) {
|
||||
cp = peek_char(&cur, -1);
|
||||
c = peek_char(&cur, 0);
|
||||
|
||||
// new word
|
||||
if (is_whitespace(cp) && !is_whitespace(c)) {
|
||||
blocks->words++;
|
||||
}
|
||||
|
||||
pre_mention = cur.p;
|
||||
if (cp == -1 || is_left_boundary(cp) || c == '#') {
|
||||
if (c == '#' && (parse_mention_index(&cur, &block) || parse_hashtag(&cur, &block))) {
|
||||
if (!add_text_then_block(&cur, blocks, block, &start, pre_mention))
|
||||
return 0;
|
||||
continue;
|
||||
} else if ((c == 'h' || c == 'H') && parse_url(&cur, &block)) {
|
||||
if (!add_text_then_block(&cur, blocks, block, &start, pre_mention))
|
||||
return 0;
|
||||
continue;
|
||||
} else if ((c == 'l' || c == 'L') && parse_invoice(&cur, &block)) {
|
||||
if (!add_text_then_block(&cur, blocks, block, &start, pre_mention))
|
||||
return 0;
|
||||
continue;
|
||||
} else if ((c == 'n' || c == '@') && parse_mention_bech32(&cur, &block)) {
|
||||
if (!add_text_then_block(&cur, blocks, block, &start, pre_mention))
|
||||
return 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
cur.p++;
|
||||
}
|
||||
|
||||
if (cur.p - start > 0) {
|
||||
if (!add_text_block(blocks, start, cur.p))
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void blocks_init(struct note_blocks *blocks) {
|
||||
blocks->blocks = malloc(sizeof(struct note_block) * MAX_BLOCKS);
|
||||
blocks->num_blocks = 0;
|
||||
}
|
||||
|
||||
void blocks_free(struct note_blocks *blocks) {
|
||||
if (!blocks->blocks) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < blocks->num_blocks; ++i) {
|
||||
if (blocks->blocks[i].type == BLOCK_MENTION_BECH32) {
|
||||
free(blocks->blocks[i].block.mention_bech32.bech32.buffer);
|
||||
blocks->blocks[i].block.mention_bech32.bech32.buffer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
free(blocks->blocks);
|
||||
blocks->num_blocks = 0;
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
//
|
||||
// damus.h
|
||||
// damus
|
||||
//
|
||||
// Created by William Casarin on 2022-10-17.
|
||||
//
|
||||
|
||||
#ifndef damus_h
|
||||
#define damus_h
|
||||
|
||||
#include <stdio.h>
|
||||
#include "block.h"
|
||||
|
||||
typedef unsigned char u8;
|
||||
|
||||
int damus_parse_content(struct note_blocks *blocks, const char *content);
|
||||
|
||||
#endif /* damus_h */
|
||||
@@ -1,84 +0,0 @@
|
||||
/* CC0 (Public domain) - see LICENSE file for details */
|
||||
#ifndef CCAN_HEX_H
|
||||
#define CCAN_HEX_H
|
||||
#include "config.h"
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/**
|
||||
* hex_decode - Unpack a hex string.
|
||||
* @str: the hexadecimal string
|
||||
* @slen: the length of @str
|
||||
* @buf: the buffer to write the data into
|
||||
* @bufsize: the length of
|
||||
*
|
||||
* Returns false if there are any characters which aren't 0-9, a-f or A-F,
|
||||
* of the string wasn't the right length for @bufsize.
|
||||
*
|
||||
* Example:
|
||||
* unsigned char data[20];
|
||||
*
|
||||
* if (!hex_decode(argv[1], strlen(argv[1]), data, 20))
|
||||
* printf("String is malformed!\n");
|
||||
*/
|
||||
bool hex_decode(const char *str, size_t slen, void *buf, size_t bufsize);
|
||||
|
||||
/**
|
||||
* hex_encode - Create a nul-terminated hex string
|
||||
* @buf: the buffer to read the data from
|
||||
* @bufsize: the length of buf
|
||||
* @dest: the string to fill
|
||||
* @destsize: the max size of the string
|
||||
*
|
||||
* Returns true if the string, including terminator, fit in @destsize;
|
||||
*
|
||||
* Example:
|
||||
* unsigned char buf[] = { 0x1F, 0x2F };
|
||||
* char str[5];
|
||||
*
|
||||
* if (!hex_encode(buf, sizeof(buf), str, sizeof(str)))
|
||||
* abort();
|
||||
*/
|
||||
bool hex_encode(const void *buf, size_t bufsize, char *dest, size_t destsize);
|
||||
|
||||
/**
|
||||
* hex_str_size - Calculate how big a nul-terminated hex string is
|
||||
* @bytes: bytes of data to represent
|
||||
*
|
||||
* Example:
|
||||
* unsigned char buf[] = { 0x1F, 0x2F };
|
||||
* char str[hex_str_size(sizeof(buf))];
|
||||
*
|
||||
* hex_encode(buf, sizeof(buf), str, sizeof(str));
|
||||
*/
|
||||
static inline size_t hex_str_size(size_t bytes)
|
||||
{
|
||||
return 2 * bytes + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* hex_data_size - Calculate how many bytes of data in a hex string
|
||||
* @strlen: the length of the string (with or without NUL)
|
||||
*
|
||||
* Example:
|
||||
* const char str[] = "1F2F";
|
||||
* unsigned char buf[hex_data_size(sizeof(str))];
|
||||
*
|
||||
* hex_decode(str, strlen(str), buf, sizeof(buf));
|
||||
*/
|
||||
static inline size_t hex_data_size(size_t strlen)
|
||||
{
|
||||
return strlen / 2;
|
||||
}
|
||||
|
||||
static inline char hexchar(unsigned int val)
|
||||
{
|
||||
if (val < 10)
|
||||
return '0' + val;
|
||||
if (val < 16)
|
||||
return 'a' + val - 10;
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
#endif /* CCAN_HEX_H */
|
||||
@@ -1,325 +0,0 @@
|
||||
//
|
||||
// nostr_bech32.c
|
||||
// damus
|
||||
//
|
||||
// Created by William Casarin on 2023-04-09.
|
||||
//
|
||||
|
||||
#include "nostr_bech32.h"
|
||||
#include <stdlib.h>
|
||||
#include "endian.h"
|
||||
#include "cursor.h"
|
||||
#include "bech32.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
#define MAX_TLVS 16
|
||||
|
||||
#define TLV_SPECIAL 0
|
||||
#define TLV_RELAY 1
|
||||
#define TLV_AUTHOR 2
|
||||
#define TLV_KIND 3
|
||||
#define TLV_KNOWN_TLVS 4
|
||||
|
||||
struct nostr_tlv {
|
||||
u8 type;
|
||||
u8 len;
|
||||
const u8 *value;
|
||||
};
|
||||
|
||||
struct nostr_tlvs {
|
||||
struct nostr_tlv tlvs[MAX_TLVS];
|
||||
int num_tlvs;
|
||||
};
|
||||
|
||||
static int parse_nostr_tlv(struct cursor *cur, struct nostr_tlv *tlv) {
|
||||
// get the tlv tag
|
||||
if (!pull_byte(cur, &tlv->type))
|
||||
return 0;
|
||||
|
||||
// unknown, fail!
|
||||
if (tlv->type >= TLV_KNOWN_TLVS)
|
||||
return 0;
|
||||
|
||||
// get the length
|
||||
if (!pull_byte(cur, &tlv->len))
|
||||
return 0;
|
||||
|
||||
// is the reported length greater then our buffer? if so fail
|
||||
if (cur->p + tlv->len > cur->end)
|
||||
return 0;
|
||||
|
||||
tlv->value = cur->p;
|
||||
cur->p += tlv->len;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int parse_nostr_tlvs(struct cursor *cur, struct nostr_tlvs *tlvs) {
|
||||
int i;
|
||||
tlvs->num_tlvs = 0;
|
||||
|
||||
for (i = 0; i < MAX_TLVS; i++) {
|
||||
if (parse_nostr_tlv(cur, &tlvs->tlvs[i])) {
|
||||
tlvs->num_tlvs++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (tlvs->num_tlvs == 0)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int find_tlv(struct nostr_tlvs *tlvs, u8 type, struct nostr_tlv **tlv) {
|
||||
*tlv = NULL;
|
||||
|
||||
for (int i = 0; i < tlvs->num_tlvs; i++) {
|
||||
if (tlvs->tlvs[i].type == type) {
|
||||
*tlv = &tlvs->tlvs[i];
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_nostr_bech32_type(const char *prefix, enum nostr_bech32_type *type) {
|
||||
// Parse type
|
||||
if (strcmp(prefix, "note") == 0) {
|
||||
*type = NOSTR_BECH32_NOTE;
|
||||
return 1;
|
||||
} else if (strcmp(prefix, "npub") == 0) {
|
||||
*type = NOSTR_BECH32_NPUB;
|
||||
return 1;
|
||||
} else if (strcmp(prefix, "nsec") == 0) {
|
||||
*type = NOSTR_BECH32_NSEC;
|
||||
return 1;
|
||||
} else if (strcmp(prefix, "nprofile") == 0) {
|
||||
*type = NOSTR_BECH32_NPROFILE;
|
||||
return 1;
|
||||
} else if (strcmp(prefix, "nevent") == 0) {
|
||||
*type = NOSTR_BECH32_NEVENT;
|
||||
return 1;
|
||||
} else if (strcmp(prefix, "nrelay") == 0) {
|
||||
*type = NOSTR_BECH32_NRELAY;
|
||||
return 1;
|
||||
} else if (strcmp(prefix, "naddr") == 0) {
|
||||
*type = NOSTR_BECH32_NADDR;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_nostr_bech32_note(struct cursor *cur, struct bech32_note *note) {
|
||||
return pull_bytes(cur, 32, ¬e->event_id);
|
||||
}
|
||||
|
||||
static int parse_nostr_bech32_npub(struct cursor *cur, struct bech32_npub *npub) {
|
||||
return pull_bytes(cur, 32, &npub->pubkey);
|
||||
}
|
||||
|
||||
static int parse_nostr_bech32_nsec(struct cursor *cur, struct bech32_nsec *nsec) {
|
||||
return pull_bytes(cur, 32, &nsec->nsec);
|
||||
}
|
||||
|
||||
static int tlvs_to_relays(struct nostr_tlvs *tlvs, struct relays *relays) {
|
||||
struct nostr_tlv *tlv;
|
||||
struct str_block *str;
|
||||
|
||||
relays->num_relays = 0;
|
||||
|
||||
for (int i = 0; i < tlvs->num_tlvs; i++) {
|
||||
tlv = &tlvs->tlvs[i];
|
||||
if (tlv->type != TLV_RELAY)
|
||||
continue;
|
||||
|
||||
if (relays->num_relays + 1 > MAX_RELAYS)
|
||||
break;
|
||||
|
||||
str = &relays->relays[relays->num_relays++];
|
||||
str->start = (const char*)tlv->value;
|
||||
str->end = (const char*)(tlv->value + tlv->len);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static uint32_t decode_tlv_u32(const uint8_t *bytes) {
|
||||
beint32_t *be32_bytes = (beint32_t*)bytes;
|
||||
return be32_to_cpu(*be32_bytes);
|
||||
}
|
||||
|
||||
static int parse_nostr_bech32_nevent(struct cursor *cur, struct bech32_nevent *nevent) {
|
||||
struct nostr_tlvs tlvs;
|
||||
struct nostr_tlv *tlv;
|
||||
|
||||
if (!parse_nostr_tlvs(cur, &tlvs))
|
||||
return 0;
|
||||
|
||||
if (!find_tlv(&tlvs, TLV_SPECIAL, &tlv))
|
||||
return 0;
|
||||
|
||||
if (tlv->len != 32)
|
||||
return 0;
|
||||
|
||||
nevent->event_id = tlv->value;
|
||||
|
||||
if (find_tlv(&tlvs, TLV_AUTHOR, &tlv)) {
|
||||
nevent->pubkey = tlv->value;
|
||||
} else {
|
||||
nevent->pubkey = NULL;
|
||||
}
|
||||
|
||||
if(find_tlv(&tlvs, TLV_KIND, &tlv)) {
|
||||
nevent->kind = decode_tlv_u32(tlv->value);
|
||||
nevent->has_kind = true;
|
||||
} else {
|
||||
nevent->has_kind = false;
|
||||
}
|
||||
|
||||
return tlvs_to_relays(&tlvs, &nevent->relays);
|
||||
}
|
||||
|
||||
static int parse_nostr_bech32_naddr(struct cursor *cur, struct bech32_naddr *naddr) {
|
||||
struct nostr_tlvs tlvs;
|
||||
struct nostr_tlv *tlv;
|
||||
|
||||
if (!parse_nostr_tlvs(cur, &tlvs))
|
||||
return 0;
|
||||
|
||||
if (!find_tlv(&tlvs, TLV_SPECIAL, &tlv))
|
||||
return 0;
|
||||
|
||||
naddr->identifier.start = (const char*)tlv->value;
|
||||
naddr->identifier.end = (const char*)tlv->value + tlv->len;
|
||||
|
||||
if (!find_tlv(&tlvs, TLV_AUTHOR, &tlv))
|
||||
return 0;
|
||||
|
||||
naddr->pubkey = tlv->value;
|
||||
|
||||
if(!find_tlv(&tlvs, TLV_KIND, &tlv)) {
|
||||
return 0;
|
||||
}
|
||||
naddr->kind = decode_tlv_u32(tlv->value);
|
||||
|
||||
return tlvs_to_relays(&tlvs, &naddr->relays);
|
||||
}
|
||||
|
||||
static int parse_nostr_bech32_nprofile(struct cursor *cur, struct bech32_nprofile *nprofile) {
|
||||
struct nostr_tlvs tlvs;
|
||||
struct nostr_tlv *tlv;
|
||||
|
||||
if (!parse_nostr_tlvs(cur, &tlvs))
|
||||
return 0;
|
||||
|
||||
if (!find_tlv(&tlvs, TLV_SPECIAL, &tlv))
|
||||
return 0;
|
||||
|
||||
if (tlv->len != 32)
|
||||
return 0;
|
||||
|
||||
nprofile->pubkey = tlv->value;
|
||||
|
||||
return tlvs_to_relays(&tlvs, &nprofile->relays);
|
||||
}
|
||||
|
||||
static int parse_nostr_bech32_nrelay(struct cursor *cur, struct bech32_nrelay *nrelay) {
|
||||
struct nostr_tlvs tlvs;
|
||||
struct nostr_tlv *tlv;
|
||||
|
||||
if (!parse_nostr_tlvs(cur, &tlvs))
|
||||
return 0;
|
||||
|
||||
if (!find_tlv(&tlvs, TLV_SPECIAL, &tlv))
|
||||
return 0;
|
||||
|
||||
nrelay->relay.start = (const char*)tlv->value;
|
||||
nrelay->relay.end = (const char*)tlv->value + tlv->len;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int parse_nostr_bech32(struct cursor *cur, struct nostr_bech32 *obj) {
|
||||
u8 *start, *end;
|
||||
|
||||
start = cur->p;
|
||||
|
||||
if (!consume_until_non_alphanumeric(cur, 1)) {
|
||||
cur->p = start;
|
||||
return 0;
|
||||
}
|
||||
|
||||
end = cur->p;
|
||||
|
||||
size_t data_len;
|
||||
size_t input_len = end - start;
|
||||
if (input_len < 10 || input_len > 10000) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
obj->buffer = malloc(input_len * 2);
|
||||
if (!obj->buffer)
|
||||
return 0;
|
||||
|
||||
u8 data[input_len];
|
||||
char prefix[input_len];
|
||||
|
||||
if (bech32_decode_len(prefix, data, &data_len, (const char*)start, input_len) == BECH32_ENCODING_NONE) {
|
||||
cur->p = start;
|
||||
return 0;
|
||||
}
|
||||
|
||||
obj->buflen = 0;
|
||||
if (!bech32_convert_bits(obj->buffer, &obj->buflen, 8, data, data_len, 5, 0)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!parse_nostr_bech32_type(prefix, &obj->type)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
struct cursor bcur;
|
||||
make_cursor(obj->buffer, obj->buffer + obj->buflen, &bcur);
|
||||
|
||||
switch (obj->type) {
|
||||
case NOSTR_BECH32_NOTE:
|
||||
if (!parse_nostr_bech32_note(&bcur, &obj->data.note))
|
||||
goto fail;
|
||||
break;
|
||||
case NOSTR_BECH32_NPUB:
|
||||
if (!parse_nostr_bech32_npub(&bcur, &obj->data.npub))
|
||||
goto fail;
|
||||
break;
|
||||
case NOSTR_BECH32_NSEC:
|
||||
if (!parse_nostr_bech32_nsec(&bcur, &obj->data.nsec))
|
||||
goto fail;
|
||||
break;
|
||||
case NOSTR_BECH32_NEVENT:
|
||||
if (!parse_nostr_bech32_nevent(&bcur, &obj->data.nevent))
|
||||
goto fail;
|
||||
break;
|
||||
case NOSTR_BECH32_NADDR:
|
||||
if (!parse_nostr_bech32_naddr(&bcur, &obj->data.naddr))
|
||||
goto fail;
|
||||
break;
|
||||
case NOSTR_BECH32_NPROFILE:
|
||||
if (!parse_nostr_bech32_nprofile(&bcur, &obj->data.nprofile))
|
||||
goto fail;
|
||||
break;
|
||||
case NOSTR_BECH32_NRELAY:
|
||||
if (!parse_nostr_bech32_nrelay(&bcur, &obj->data.nrelay))
|
||||
goto fail;
|
||||
break;
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
free(obj->buffer);
|
||||
cur->p = start;
|
||||
return 0;
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
//
|
||||
// nostr_bech32.h
|
||||
// damus
|
||||
//
|
||||
// Created by William Casarin on 2023-04-09.
|
||||
//
|
||||
|
||||
#ifndef nostr_bech32_h
|
||||
#define nostr_bech32_h
|
||||
|
||||
#include <stdio.h>
|
||||
#include "str_block.h"
|
||||
#include "cursor.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef unsigned char u8;
|
||||
#define MAX_RELAYS 10
|
||||
|
||||
struct relays {
|
||||
struct str_block relays[MAX_RELAYS];
|
||||
int num_relays;
|
||||
};
|
||||
|
||||
enum nostr_bech32_type {
|
||||
NOSTR_BECH32_NOTE = 1,
|
||||
NOSTR_BECH32_NPUB = 2,
|
||||
NOSTR_BECH32_NPROFILE = 3,
|
||||
NOSTR_BECH32_NEVENT = 4,
|
||||
NOSTR_BECH32_NRELAY = 5,
|
||||
NOSTR_BECH32_NADDR = 6,
|
||||
NOSTR_BECH32_NSEC = 7,
|
||||
};
|
||||
|
||||
struct bech32_note {
|
||||
const u8 *event_id;
|
||||
};
|
||||
|
||||
struct bech32_npub {
|
||||
const u8 *pubkey;
|
||||
};
|
||||
|
||||
struct bech32_nsec {
|
||||
const u8 *nsec;
|
||||
};
|
||||
|
||||
struct bech32_nevent {
|
||||
struct relays relays;
|
||||
const u8 *event_id;
|
||||
const u8 *pubkey; // optional
|
||||
uint32_t kind;
|
||||
bool has_kind;
|
||||
};
|
||||
|
||||
struct bech32_nprofile {
|
||||
struct relays relays;
|
||||
const u8 *pubkey;
|
||||
};
|
||||
|
||||
struct bech32_naddr {
|
||||
struct relays relays;
|
||||
struct str_block identifier;
|
||||
const u8 *pubkey;
|
||||
uint32_t kind;
|
||||
};
|
||||
|
||||
struct bech32_nrelay {
|
||||
struct str_block relay;
|
||||
};
|
||||
|
||||
typedef struct nostr_bech32 {
|
||||
enum nostr_bech32_type type;
|
||||
u8 *buffer; // holds strings and tlv stuff
|
||||
size_t buflen;
|
||||
|
||||
union {
|
||||
struct bech32_note note;
|
||||
struct bech32_npub npub;
|
||||
struct bech32_nsec nsec;
|
||||
struct bech32_nevent nevent;
|
||||
struct bech32_nprofile nprofile;
|
||||
struct bech32_naddr naddr;
|
||||
struct bech32_nrelay nrelay;
|
||||
} data;
|
||||
} nostr_bech32_t;
|
||||
|
||||
|
||||
int parse_nostr_bech32(struct cursor *cur, struct nostr_bech32 *obj);
|
||||
|
||||
#endif /* nostr_bech32_h */
|
||||
308
damus-c/sha256.c
308
damus-c/sha256.c
@@ -1,308 +0,0 @@
|
||||
/* MIT (BSD) license - see LICENSE file for details */
|
||||
/* SHA256 core code translated from the Bitcoin project's C++:
|
||||
*
|
||||
* src/crypto/sha256.cpp commit 417532c8acb93c36c2b6fd052b7c11b6a2906aa2
|
||||
* Copyright (c) 2014 The Bitcoin Core developers
|
||||
* Distributed under the MIT software license, see the accompanying
|
||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
*/
|
||||
#include "sha256.h"
|
||||
#include "compiler.h"
|
||||
#include "endian.h"
|
||||
#include <stdbool.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
static void invalidate_sha256(struct sha256_ctx *ctx)
|
||||
{
|
||||
#ifdef CCAN_CRYPTO_SHA256_USE_OPENSSL
|
||||
ctx->c.md_len = 0;
|
||||
#else
|
||||
ctx->bytes = (size_t)-1;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void check_sha256(struct sha256_ctx *ctx UNUSED)
|
||||
{
|
||||
#ifdef CCAN_CRYPTO_SHA256_USE_OPENSSL
|
||||
assert(ctx->c.md_len != 0);
|
||||
#else
|
||||
assert(ctx->bytes != (size_t)-1);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef CCAN_CRYPTO_SHA256_USE_OPENSSL
|
||||
void sha256_init(struct sha256_ctx *ctx)
|
||||
{
|
||||
SHA256_Init(&ctx->c);
|
||||
}
|
||||
|
||||
void sha256_update(struct sha256_ctx *ctx, const void *p, size_t size)
|
||||
{
|
||||
check_sha256(ctx);
|
||||
SHA256_Update(&ctx->c, p, size);
|
||||
}
|
||||
|
||||
void sha256_done(struct sha256_ctx *ctx, struct sha256 *res)
|
||||
{
|
||||
SHA256_Final(res->u.u8, &ctx->c);
|
||||
invalidate_sha256(ctx);
|
||||
}
|
||||
#else
|
||||
static uint32_t Ch(uint32_t x, uint32_t y, uint32_t z)
|
||||
{
|
||||
return z ^ (x & (y ^ z));
|
||||
}
|
||||
static uint32_t Maj(uint32_t x, uint32_t y, uint32_t z)
|
||||
{
|
||||
return (x & y) | (z & (x | y));
|
||||
}
|
||||
static uint32_t Sigma0(uint32_t x)
|
||||
{
|
||||
return (x >> 2 | x << 30) ^ (x >> 13 | x << 19) ^ (x >> 22 | x << 10);
|
||||
}
|
||||
static uint32_t Sigma1(uint32_t x)
|
||||
{
|
||||
return (x >> 6 | x << 26) ^ (x >> 11 | x << 21) ^ (x >> 25 | x << 7);
|
||||
}
|
||||
static uint32_t sigma0(uint32_t x)
|
||||
{
|
||||
return (x >> 7 | x << 25) ^ (x >> 18 | x << 14) ^ (x >> 3);
|
||||
}
|
||||
static uint32_t sigma1(uint32_t x)
|
||||
{
|
||||
return (x >> 17 | x << 15) ^ (x >> 19 | x << 13) ^ (x >> 10);
|
||||
}
|
||||
|
||||
/** One round of SHA-256. */
|
||||
static void Round(uint32_t a, uint32_t b, uint32_t c, uint32_t *d, uint32_t e, uint32_t f, uint32_t g, uint32_t *h, uint32_t k, uint32_t w)
|
||||
{
|
||||
uint32_t t1 = *h + Sigma1(e) + Ch(e, f, g) + k + w;
|
||||
uint32_t t2 = Sigma0(a) + Maj(a, b, c);
|
||||
*d += t1;
|
||||
*h = t1 + t2;
|
||||
}
|
||||
|
||||
/** Perform one SHA-256 transformation, processing a 64-byte chunk. */
|
||||
static void Transform(uint32_t *s, const uint32_t *chunk)
|
||||
{
|
||||
uint32_t a = s[0], b = s[1], c = s[2], d = s[3], e = s[4], f = s[5], g = s[6], h = s[7];
|
||||
uint32_t w0, w1, w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14, w15;
|
||||
|
||||
Round(a, b, c, &d, e, f, g, &h, 0x428a2f98, w0 = be32_to_cpu(chunk[0]));
|
||||
Round(h, a, b, &c, d, e, f, &g, 0x71374491, w1 = be32_to_cpu(chunk[1]));
|
||||
Round(g, h, a, &b, c, d, e, &f, 0xb5c0fbcf, w2 = be32_to_cpu(chunk[2]));
|
||||
Round(f, g, h, &a, b, c, d, &e, 0xe9b5dba5, w3 = be32_to_cpu(chunk[3]));
|
||||
Round(e, f, g, &h, a, b, c, &d, 0x3956c25b, w4 = be32_to_cpu(chunk[4]));
|
||||
Round(d, e, f, &g, h, a, b, &c, 0x59f111f1, w5 = be32_to_cpu(chunk[5]));
|
||||
Round(c, d, e, &f, g, h, a, &b, 0x923f82a4, w6 = be32_to_cpu(chunk[6]));
|
||||
Round(b, c, d, &e, f, g, h, &a, 0xab1c5ed5, w7 = be32_to_cpu(chunk[7]));
|
||||
Round(a, b, c, &d, e, f, g, &h, 0xd807aa98, w8 = be32_to_cpu(chunk[8]));
|
||||
Round(h, a, b, &c, d, e, f, &g, 0x12835b01, w9 = be32_to_cpu(chunk[9]));
|
||||
Round(g, h, a, &b, c, d, e, &f, 0x243185be, w10 = be32_to_cpu(chunk[10]));
|
||||
Round(f, g, h, &a, b, c, d, &e, 0x550c7dc3, w11 = be32_to_cpu(chunk[11]));
|
||||
Round(e, f, g, &h, a, b, c, &d, 0x72be5d74, w12 = be32_to_cpu(chunk[12]));
|
||||
Round(d, e, f, &g, h, a, b, &c, 0x80deb1fe, w13 = be32_to_cpu(chunk[13]));
|
||||
Round(c, d, e, &f, g, h, a, &b, 0x9bdc06a7, w14 = be32_to_cpu(chunk[14]));
|
||||
Round(b, c, d, &e, f, g, h, &a, 0xc19bf174, w15 = be32_to_cpu(chunk[15]));
|
||||
|
||||
Round(a, b, c, &d, e, f, g, &h, 0xe49b69c1, w0 += sigma1(w14) + w9 + sigma0(w1));
|
||||
Round(h, a, b, &c, d, e, f, &g, 0xefbe4786, w1 += sigma1(w15) + w10 + sigma0(w2));
|
||||
Round(g, h, a, &b, c, d, e, &f, 0x0fc19dc6, w2 += sigma1(w0) + w11 + sigma0(w3));
|
||||
Round(f, g, h, &a, b, c, d, &e, 0x240ca1cc, w3 += sigma1(w1) + w12 + sigma0(w4));
|
||||
Round(e, f, g, &h, a, b, c, &d, 0x2de92c6f, w4 += sigma1(w2) + w13 + sigma0(w5));
|
||||
Round(d, e, f, &g, h, a, b, &c, 0x4a7484aa, w5 += sigma1(w3) + w14 + sigma0(w6));
|
||||
Round(c, d, e, &f, g, h, a, &b, 0x5cb0a9dc, w6 += sigma1(w4) + w15 + sigma0(w7));
|
||||
Round(b, c, d, &e, f, g, h, &a, 0x76f988da, w7 += sigma1(w5) + w0 + sigma0(w8));
|
||||
Round(a, b, c, &d, e, f, g, &h, 0x983e5152, w8 += sigma1(w6) + w1 + sigma0(w9));
|
||||
Round(h, a, b, &c, d, e, f, &g, 0xa831c66d, w9 += sigma1(w7) + w2 + sigma0(w10));
|
||||
Round(g, h, a, &b, c, d, e, &f, 0xb00327c8, w10 += sigma1(w8) + w3 + sigma0(w11));
|
||||
Round(f, g, h, &a, b, c, d, &e, 0xbf597fc7, w11 += sigma1(w9) + w4 + sigma0(w12));
|
||||
Round(e, f, g, &h, a, b, c, &d, 0xc6e00bf3, w12 += sigma1(w10) + w5 + sigma0(w13));
|
||||
Round(d, e, f, &g, h, a, b, &c, 0xd5a79147, w13 += sigma1(w11) + w6 + sigma0(w14));
|
||||
Round(c, d, e, &f, g, h, a, &b, 0x06ca6351, w14 += sigma1(w12) + w7 + sigma0(w15));
|
||||
Round(b, c, d, &e, f, g, h, &a, 0x14292967, w15 += sigma1(w13) + w8 + sigma0(w0));
|
||||
|
||||
Round(a, b, c, &d, e, f, g, &h, 0x27b70a85, w0 += sigma1(w14) + w9 + sigma0(w1));
|
||||
Round(h, a, b, &c, d, e, f, &g, 0x2e1b2138, w1 += sigma1(w15) + w10 + sigma0(w2));
|
||||
Round(g, h, a, &b, c, d, e, &f, 0x4d2c6dfc, w2 += sigma1(w0) + w11 + sigma0(w3));
|
||||
Round(f, g, h, &a, b, c, d, &e, 0x53380d13, w3 += sigma1(w1) + w12 + sigma0(w4));
|
||||
Round(e, f, g, &h, a, b, c, &d, 0x650a7354, w4 += sigma1(w2) + w13 + sigma0(w5));
|
||||
Round(d, e, f, &g, h, a, b, &c, 0x766a0abb, w5 += sigma1(w3) + w14 + sigma0(w6));
|
||||
Round(c, d, e, &f, g, h, a, &b, 0x81c2c92e, w6 += sigma1(w4) + w15 + sigma0(w7));
|
||||
Round(b, c, d, &e, f, g, h, &a, 0x92722c85, w7 += sigma1(w5) + w0 + sigma0(w8));
|
||||
Round(a, b, c, &d, e, f, g, &h, 0xa2bfe8a1, w8 += sigma1(w6) + w1 + sigma0(w9));
|
||||
Round(h, a, b, &c, d, e, f, &g, 0xa81a664b, w9 += sigma1(w7) + w2 + sigma0(w10));
|
||||
Round(g, h, a, &b, c, d, e, &f, 0xc24b8b70, w10 += sigma1(w8) + w3 + sigma0(w11));
|
||||
Round(f, g, h, &a, b, c, d, &e, 0xc76c51a3, w11 += sigma1(w9) + w4 + sigma0(w12));
|
||||
Round(e, f, g, &h, a, b, c, &d, 0xd192e819, w12 += sigma1(w10) + w5 + sigma0(w13));
|
||||
Round(d, e, f, &g, h, a, b, &c, 0xd6990624, w13 += sigma1(w11) + w6 + sigma0(w14));
|
||||
Round(c, d, e, &f, g, h, a, &b, 0xf40e3585, w14 += sigma1(w12) + w7 + sigma0(w15));
|
||||
Round(b, c, d, &e, f, g, h, &a, 0x106aa070, w15 += sigma1(w13) + w8 + sigma0(w0));
|
||||
|
||||
Round(a, b, c, &d, e, f, g, &h, 0x19a4c116, w0 += sigma1(w14) + w9 + sigma0(w1));
|
||||
Round(h, a, b, &c, d, e, f, &g, 0x1e376c08, w1 += sigma1(w15) + w10 + sigma0(w2));
|
||||
Round(g, h, a, &b, c, d, e, &f, 0x2748774c, w2 += sigma1(w0) + w11 + sigma0(w3));
|
||||
Round(f, g, h, &a, b, c, d, &e, 0x34b0bcb5, w3 += sigma1(w1) + w12 + sigma0(w4));
|
||||
Round(e, f, g, &h, a, b, c, &d, 0x391c0cb3, w4 += sigma1(w2) + w13 + sigma0(w5));
|
||||
Round(d, e, f, &g, h, a, b, &c, 0x4ed8aa4a, w5 += sigma1(w3) + w14 + sigma0(w6));
|
||||
Round(c, d, e, &f, g, h, a, &b, 0x5b9cca4f, w6 += sigma1(w4) + w15 + sigma0(w7));
|
||||
Round(b, c, d, &e, f, g, h, &a, 0x682e6ff3, w7 += sigma1(w5) + w0 + sigma0(w8));
|
||||
Round(a, b, c, &d, e, f, g, &h, 0x748f82ee, w8 += sigma1(w6) + w1 + sigma0(w9));
|
||||
Round(h, a, b, &c, d, e, f, &g, 0x78a5636f, w9 += sigma1(w7) + w2 + sigma0(w10));
|
||||
Round(g, h, a, &b, c, d, e, &f, 0x84c87814, w10 += sigma1(w8) + w3 + sigma0(w11));
|
||||
Round(f, g, h, &a, b, c, d, &e, 0x8cc70208, w11 += sigma1(w9) + w4 + sigma0(w12));
|
||||
Round(e, f, g, &h, a, b, c, &d, 0x90befffa, w12 += sigma1(w10) + w5 + sigma0(w13));
|
||||
Round(d, e, f, &g, h, a, b, &c, 0xa4506ceb, w13 += sigma1(w11) + w6 + sigma0(w14));
|
||||
Round(c, d, e, &f, g, h, a, &b, 0xbef9a3f7, w14 + sigma1(w12) + w7 + sigma0(w15));
|
||||
Round(b, c, d, &e, f, g, h, &a, 0xc67178f2, w15 + sigma1(w13) + w8 + sigma0(w0));
|
||||
|
||||
s[0] += a;
|
||||
s[1] += b;
|
||||
s[2] += c;
|
||||
s[3] += d;
|
||||
s[4] += e;
|
||||
s[5] += f;
|
||||
s[6] += g;
|
||||
s[7] += h;
|
||||
}
|
||||
|
||||
static bool alignment_ok(const void *p UNUSED, size_t n UNUSED)
|
||||
{
|
||||
#if HAVE_UNALIGNED_ACCESS
|
||||
return true;
|
||||
#else
|
||||
return ((size_t)p % n == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void add(struct sha256_ctx *ctx, const void *p, size_t len)
|
||||
{
|
||||
const unsigned char *data = p;
|
||||
size_t bufsize = ctx->bytes % 64;
|
||||
|
||||
if (bufsize + len >= 64) {
|
||||
/* Fill the buffer, and process it. */
|
||||
memcpy(ctx->buf.u8 + bufsize, data, 64 - bufsize);
|
||||
ctx->bytes += 64 - bufsize;
|
||||
data += 64 - bufsize;
|
||||
len -= 64 - bufsize;
|
||||
Transform(ctx->s, ctx->buf.u32);
|
||||
bufsize = 0;
|
||||
}
|
||||
|
||||
while (len >= 64) {
|
||||
/* Process full chunks directly from the source. */
|
||||
if (alignment_ok(data, sizeof(uint32_t)))
|
||||
Transform(ctx->s, (const uint32_t *)data);
|
||||
else {
|
||||
memcpy(ctx->buf.u8, data, sizeof(ctx->buf));
|
||||
Transform(ctx->s, ctx->buf.u32);
|
||||
}
|
||||
ctx->bytes += 64;
|
||||
data += 64;
|
||||
len -= 64;
|
||||
}
|
||||
|
||||
if (len) {
|
||||
/* Fill the buffer with what remains. */
|
||||
memcpy(ctx->buf.u8 + bufsize, data, len);
|
||||
ctx->bytes += len;
|
||||
}
|
||||
}
|
||||
|
||||
void sha256_init(struct sha256_ctx *ctx)
|
||||
{
|
||||
struct sha256_ctx init = SHA256_INIT;
|
||||
*ctx = init;
|
||||
}
|
||||
|
||||
void sha256_update(struct sha256_ctx *ctx, const void *p, size_t size)
|
||||
{
|
||||
check_sha256(ctx);
|
||||
add(ctx, p, size);
|
||||
}
|
||||
|
||||
void sha256_done(struct sha256_ctx *ctx, struct sha256 *res)
|
||||
{
|
||||
static const unsigned char pad[64] = {0x80};
|
||||
uint64_t sizedesc;
|
||||
size_t i;
|
||||
|
||||
sizedesc = cpu_to_be64((uint64_t)ctx->bytes << 3);
|
||||
/* Add '1' bit to terminate, then all 0 bits, up to next block - 8. */
|
||||
add(ctx, pad, 1 + ((128 - 8 - (ctx->bytes % 64) - 1) % 64));
|
||||
/* Add number of bits of data (big endian) */
|
||||
add(ctx, &sizedesc, 8);
|
||||
for (i = 0; i < sizeof(ctx->s) / sizeof(ctx->s[0]); i++)
|
||||
res->u.u32[i] = cpu_to_be32(ctx->s[i]);
|
||||
invalidate_sha256(ctx);
|
||||
}
|
||||
#endif
|
||||
|
||||
void sha256(struct sha256 *sha, const void *p, size_t size)
|
||||
{
|
||||
struct sha256_ctx ctx;
|
||||
|
||||
sha256_init(&ctx);
|
||||
sha256_update(&ctx, p, size);
|
||||
sha256_done(&ctx, sha);
|
||||
}
|
||||
|
||||
void sha256_u8(struct sha256_ctx *ctx, uint8_t v)
|
||||
{
|
||||
sha256_update(ctx, &v, sizeof(v));
|
||||
}
|
||||
|
||||
void sha256_u16(struct sha256_ctx *ctx, uint16_t v)
|
||||
{
|
||||
sha256_update(ctx, &v, sizeof(v));
|
||||
}
|
||||
|
||||
void sha256_u32(struct sha256_ctx *ctx, uint32_t v)
|
||||
{
|
||||
sha256_update(ctx, &v, sizeof(v));
|
||||
}
|
||||
|
||||
void sha256_u64(struct sha256_ctx *ctx, uint64_t v)
|
||||
{
|
||||
sha256_update(ctx, &v, sizeof(v));
|
||||
}
|
||||
|
||||
/* Add as little-endian */
|
||||
void sha256_le16(struct sha256_ctx *ctx, uint16_t v)
|
||||
{
|
||||
leint16_t lev = cpu_to_le16(v);
|
||||
sha256_update(ctx, &lev, sizeof(lev));
|
||||
}
|
||||
|
||||
void sha256_le32(struct sha256_ctx *ctx, uint32_t v)
|
||||
{
|
||||
leint32_t lev = cpu_to_le32(v);
|
||||
sha256_update(ctx, &lev, sizeof(lev));
|
||||
}
|
||||
|
||||
void sha256_le64(struct sha256_ctx *ctx, uint64_t v)
|
||||
{
|
||||
leint64_t lev = cpu_to_le64(v);
|
||||
sha256_update(ctx, &lev, sizeof(lev));
|
||||
}
|
||||
|
||||
/* Add as big-endian */
|
||||
void sha256_be16(struct sha256_ctx *ctx, uint16_t v)
|
||||
{
|
||||
beint16_t bev = cpu_to_be16(v);
|
||||
sha256_update(ctx, &bev, sizeof(bev));
|
||||
}
|
||||
|
||||
void sha256_be32(struct sha256_ctx *ctx, uint32_t v)
|
||||
{
|
||||
beint32_t bev = cpu_to_be32(v);
|
||||
sha256_update(ctx, &bev, sizeof(bev));
|
||||
}
|
||||
|
||||
void sha256_be64(struct sha256_ctx *ctx, uint64_t v)
|
||||
{
|
||||
beint64_t bev = cpu_to_be64(v);
|
||||
sha256_update(ctx, &bev, sizeof(bev));
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
|
||||
#ifndef PROTOVERSE_TYPEDEFS_H
|
||||
#define PROTOVERSE_TYPEDEFS_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned int u32;
|
||||
typedef unsigned short u16;
|
||||
typedef uint64_t u64;
|
||||
typedef int64_t s64;
|
||||
|
||||
|
||||
#endif /* PROTOVERSE_TYPEDEFS_H */
|
||||
@@ -1179,7 +1179,7 @@ static INLINE int parse_i64(struct cursor *read, uint64_t *val)
|
||||
shift = 0;
|
||||
|
||||
do {
|
||||
if (!pull_byte(read, &byte))
|
||||
if (!cursor_pull_byte(read, &byte))
|
||||
return 0;
|
||||
*val |= (byte & 0x7FULL) << shift;
|
||||
shift += 7;
|
||||
@@ -1199,7 +1199,7 @@ static INLINE int uleb128_read(struct cursor *read, unsigned int *val)
|
||||
*val = 0;
|
||||
|
||||
for (;;) {
|
||||
if (!pull_byte(read, &byte))
|
||||
if (!cursor_pull_byte(read, &byte))
|
||||
return 0;
|
||||
|
||||
*val |= (0x7F & byte) << shift;
|
||||
@@ -1222,7 +1222,7 @@ static INLINE int sleb128_read(struct cursor *read, signed int *val)
|
||||
shift = 0;
|
||||
|
||||
do {
|
||||
if (!pull_byte(read, &byte))
|
||||
if (!cursor_pull_byte(read, &byte))
|
||||
return 0;
|
||||
*val |= ((byte & 0x7F) << shift);
|
||||
shift += 7;
|
||||
@@ -1241,21 +1241,21 @@ static INLINE int uleb128_read(struct cursor *read, unsigned int *val)
|
||||
unsigned char p[6] = {0};
|
||||
*val = 0;
|
||||
|
||||
if (pull_byte(read, &p[0]) && (p[0] & 0x80) == 0) {
|
||||
if (cursor_pull_byte(read, &p[0]) && (p[0] & 0x80) == 0) {
|
||||
*val = LEB128_1(unsigned int);
|
||||
if (p[0] == 0x7F)
|
||||
assert((int)*val == -1);
|
||||
return 1;
|
||||
} else if (pull_byte(read, &p[1]) && (p[1] & 0x80) == 0) {
|
||||
} else if (cursor_pull_byte(read, &p[1]) && (p[1] & 0x80) == 0) {
|
||||
*val = LEB128_2(unsigned int);
|
||||
return 2;
|
||||
} else if (pull_byte(read, &p[2]) && (p[2] & 0x80) == 0) {
|
||||
} else if (cursor_pull_byte(read, &p[2]) && (p[2] & 0x80) == 0) {
|
||||
*val = LEB128_3(unsigned int);
|
||||
return 3;
|
||||
} else if (pull_byte(read, &p[3]) && (p[3] & 0x80) == 0) {
|
||||
} else if (cursor_pull_byte(read, &p[3]) && (p[3] & 0x80) == 0) {
|
||||
*val = LEB128_4(unsigned int);
|
||||
return 4;
|
||||
} else if (pull_byte(read, &p[4]) && (p[4] & 0x80) == 0) {
|
||||
} else if (cursor_pull_byte(read, &p[4]) && (p[4] & 0x80) == 0) {
|
||||
if (!(p[4] & 0xF0)) {
|
||||
*val = LEB128_5(unsigned int);
|
||||
return 5;
|
||||
@@ -1296,7 +1296,7 @@ static int parse_section_tag(struct cursor *cur, enum section_tag *section)
|
||||
|
||||
start = cur->p;
|
||||
|
||||
if (!pull_byte(cur, &byte)) {
|
||||
if (!cursor_pull_byte(cur, &byte)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1315,7 +1315,7 @@ static int parse_valtype(struct wasm_parser *p, enum valtype *valtype)
|
||||
|
||||
start = p->cur.p;
|
||||
|
||||
if (unlikely(!pull_byte(&p->cur, (unsigned char*)valtype))) {
|
||||
if (unlikely(!cursor_pull_byte(&p->cur, (unsigned char*)valtype))) {
|
||||
return parse_err(p, "valtype tag oob");
|
||||
}
|
||||
|
||||
@@ -1416,7 +1416,7 @@ static int parse_export_desc(struct wasm_parser *p, enum exportdesc *desc)
|
||||
{
|
||||
unsigned char byte;
|
||||
|
||||
if (!pull_byte(&p->cur, &byte)) {
|
||||
if (!cursor_pull_byte(&p->cur, &byte)) {
|
||||
parse_err(p, "export desc byte eof");
|
||||
return 0;
|
||||
}
|
||||
@@ -1523,7 +1523,7 @@ static int parse_name_subsection(struct wasm_parser *p, struct namesec *sec, u32
|
||||
u8 tag;
|
||||
u8 *start = p->cur.p;
|
||||
|
||||
if (!pull_byte(&p->cur, &tag))
|
||||
if (!cursor_pull_byte(&p->cur, &tag))
|
||||
return parse_err(p, "name subsection tag oob?");
|
||||
|
||||
if (!is_valid_name_subsection(tag))
|
||||
@@ -1676,7 +1676,7 @@ static int parse_reftype(struct wasm_parser *p, enum reftype *reftype)
|
||||
{
|
||||
u8 tag;
|
||||
|
||||
if (!pull_byte(&p->cur, &tag)) {
|
||||
if (!cursor_pull_byte(&p->cur, &tag)) {
|
||||
parse_err(p, "reftype");
|
||||
return 0;
|
||||
}
|
||||
@@ -1720,7 +1720,7 @@ static int parse_export_section(struct wasm_parser *p,
|
||||
static int parse_limits(struct wasm_parser *p, struct limits *limits)
|
||||
{
|
||||
unsigned char tag;
|
||||
if (!pull_byte(&p->cur, &tag)) {
|
||||
if (!cursor_pull_byte(&p->cur, &tag)) {
|
||||
return parse_err(p, "oob");
|
||||
}
|
||||
|
||||
@@ -1803,7 +1803,7 @@ static void print_code(u8 *code, int code_len)
|
||||
make_cursor(code, code + code_len, &c);
|
||||
|
||||
for (;;) {
|
||||
if (!pull_byte(&c, &tag)) {
|
||||
if (!cursor_pull_byte(&c, &tag)) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -2169,7 +2169,7 @@ static int parse_const_expr(struct expr_parser *p, struct expr *expr)
|
||||
expr->code = p->code->p;
|
||||
|
||||
while (1) {
|
||||
if (unlikely(!pull_byte(p->code, &tag))) {
|
||||
if (unlikely(!cursor_pull_byte(p->code, &tag))) {
|
||||
return note_error(p->errs, p->code, "oob");
|
||||
}
|
||||
|
||||
@@ -2332,7 +2332,7 @@ static int parse_instrs_until_at(struct expr_parser *p, u8 stop_instr,
|
||||
p->code->p - p->code->start,
|
||||
dbg_inst, instr_name(stop_instr));
|
||||
for (;;) {
|
||||
if (!pull_byte(p->code, &tag))
|
||||
if (!cursor_pull_byte(p->code, &tag))
|
||||
return note_error(p->errs, p->code, "oob");
|
||||
|
||||
if ((tag != i_if && tag == stop_instr) ||
|
||||
@@ -2413,7 +2413,7 @@ static int parse_element(struct wasm_parser *p, struct elem *elem)
|
||||
|
||||
make_expr_parser(&p->errs, &p->cur, &expr_parser);
|
||||
|
||||
if (!pull_byte(&p->cur, &tag))
|
||||
if (!cursor_pull_byte(&p->cur, &tag))
|
||||
return parse_err(p, "tag");
|
||||
|
||||
if (tag > 7)
|
||||
@@ -2545,7 +2545,7 @@ static int parse_wdata(struct wasm_parser *p, struct wdata *data)
|
||||
struct expr_parser parser;
|
||||
u8 tag;
|
||||
|
||||
if (!pull_byte(&p->cur, &tag)) {
|
||||
if (!cursor_pull_byte(&p->cur, &tag)) {
|
||||
return parse_err(p, "tag");
|
||||
}
|
||||
|
||||
@@ -2700,7 +2700,7 @@ static int parse_importdesc(struct wasm_parser *p, struct importdesc *desc)
|
||||
{
|
||||
u8 tag;
|
||||
|
||||
if (!pull_byte(&p->cur, &tag)) {
|
||||
if (!cursor_pull_byte(&p->cur, &tag)) {
|
||||
parse_err(p, "oom");
|
||||
return 0;
|
||||
}
|
||||
@@ -4134,7 +4134,7 @@ static int parse_blocktype(struct cursor *cur, struct errors *errs, struct block
|
||||
{
|
||||
unsigned char byte;
|
||||
|
||||
if (unlikely(!pull_byte(cur, &byte))) {
|
||||
if (unlikely(!cursor_pull_byte(cur, &byte))) {
|
||||
return note_error(errs, cur, "parse_blocktype: oob\n");
|
||||
}
|
||||
|
||||
@@ -4656,7 +4656,7 @@ static int parse_bulk_op(struct cursor *code, struct errors *errs,
|
||||
{
|
||||
u8 tag;
|
||||
|
||||
if (unlikely(!pull_byte(code, &tag)))
|
||||
if (unlikely(!cursor_pull_byte(code, &tag)))
|
||||
return note_error(errs, code, "oob");
|
||||
|
||||
if (unlikely(tag < 10 || tag > 17))
|
||||
@@ -6552,7 +6552,7 @@ static INLINE int interp_parse_instr(struct wasm_interp *interp,
|
||||
{
|
||||
u8 tag;
|
||||
|
||||
if (unlikely(!pull_byte(code, &tag))) {
|
||||
if (unlikely(!cursor_pull_byte(code, &tag))) {
|
||||
return interp_error(interp, "no more instrs to pull");
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,8 @@ static const unsigned char WASM_MAGIC[] = {0,'a','s','m'};
|
||||
#define interp_error(p, fmt, ...) note_error(&((p)->errors), interp_codeptr(p), fmt, ##__VA_ARGS__)
|
||||
#define parse_err(p, fmt, ...) note_error(&((p)->errs), &(p)->cur, fmt, ##__VA_ARGS__)
|
||||
|
||||
#include "short_types.h"
|
||||
|
||||
enum valtype {
|
||||
val_i32 = 0x7F,
|
||||
val_i64 = 0x7E,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -55,6 +55,7 @@
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
enableAddressSanitizer = "YES"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
//
|
||||
// AlbyGradient.swift
|
||||
// damus
|
||||
//
|
||||
// Created by William Casarin on 2023-05-09.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
fileprivate let alby_grad_c1 = hex_col(r: 226, g: 168, b: 122)
|
||||
fileprivate let alby_grad_c2 = hex_col(r: 249, g: 223, b: 127)
|
||||
fileprivate let alby_grad = [alby_grad_c2, alby_grad_c1]
|
||||
|
||||
let AlbyGradient: LinearGradient =
|
||||
LinearGradient(colors: alby_grad, startPoint: .bottomLeading, endPoint: .topTrailing)
|
||||
@@ -1220,8 +1220,8 @@ extension LossyLocalNotification {
|
||||
/// Computes a view open action from a mention reference.
|
||||
/// Use this when opening a user-presentable interface to a specific mention reference.
|
||||
func toViewOpenAction() -> ContentView.ViewOpenAction {
|
||||
switch self.mention {
|
||||
case .pubkey(let pubkey):
|
||||
switch self.mention.nip19 {
|
||||
case .npub(let pubkey):
|
||||
return .route(.ProfileByKey(pubkey: pubkey))
|
||||
case .note(let noteId):
|
||||
return .route(.LoadableNostrEvent(note_reference: .note_id(noteId)))
|
||||
@@ -1241,6 +1241,15 @@ extension LossyLocalNotification {
|
||||
)))
|
||||
case .naddr(let nAddr):
|
||||
return .route(.LoadableNostrEvent(note_reference: .naddr(nAddr)))
|
||||
case .nsec(_):
|
||||
// `nsec` urls are a terrible idea security-wise, so we should intentionally not support those — in order to discourage their use.
|
||||
return .sheet(.error(ErrorView.UserPresentableError(
|
||||
user_visible_description: NSLocalizedString("You opened an invalid link. The link you tried to open refers to \"nsec\", which is not supported.", comment: "User-visible error description for a user who tries to open an unsupported \"nsec\" link."),
|
||||
tip: NSLocalizedString("Please contact the person who provided the link, and ask for another link. Also, this link may have sensitive information, please use caution before sharing it.", comment: "User-visible tip on what to do if a link contains an unsupported \"nsec\" reference."),
|
||||
technical_info: "`MentionRef.toViewOpenAction` detected unsupported `nsec` contents"
|
||||
)))
|
||||
case .nscript(let script):
|
||||
return .route(.Script(script: ScriptModel(data: script, state: .not_loaded)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,45 +20,6 @@ enum NoteContent {
|
||||
}
|
||||
}
|
||||
|
||||
func parsed_blocks_finish(bs: inout note_blocks, tags: TagsSequence?) -> Blocks {
|
||||
var out: [Block] = []
|
||||
|
||||
var i = 0
|
||||
while (i < bs.num_blocks) {
|
||||
let block = bs.blocks[i]
|
||||
|
||||
if let converted = Block(block, tags: tags) {
|
||||
out.append(converted)
|
||||
}
|
||||
|
||||
i += 1
|
||||
}
|
||||
|
||||
let words = Int(bs.words)
|
||||
blocks_free(&bs)
|
||||
|
||||
return Blocks(words: words, blocks: out)
|
||||
|
||||
}
|
||||
|
||||
func parse_note_content(content: NoteContent) -> Blocks {
|
||||
var bs = note_blocks()
|
||||
bs.num_blocks = 0;
|
||||
|
||||
blocks_init(&bs)
|
||||
|
||||
switch content {
|
||||
case .content(let s, let tags):
|
||||
return s.withCString { cptr in
|
||||
damus_parse_content(&bs, cptr)
|
||||
return parsed_blocks_finish(bs: &bs, tags: tags)
|
||||
}
|
||||
case .note(let note):
|
||||
damus_parse_content(&bs, note.content_raw)
|
||||
return parsed_blocks_finish(bs: &bs, tags: note.tags)
|
||||
}
|
||||
}
|
||||
|
||||
func interpret_event_refs(tags: TagsSequence) -> ThreadReply? {
|
||||
// migration is long over, lets just do this to fix tests
|
||||
return interpret_event_refs_ndb(tags: tags)
|
||||
@@ -18,22 +18,42 @@ enum MentionType: AsciiCharacter, TagKey {
|
||||
}
|
||||
}
|
||||
|
||||
enum MentionRef: TagKeys, TagConvertible, Equatable, Hashable {
|
||||
case pubkey(Pubkey)
|
||||
case note(NoteId)
|
||||
case nevent(NEvent)
|
||||
case nprofile(NProfile)
|
||||
case nrelay(String)
|
||||
case naddr(NAddr)
|
||||
extension UnsafePointer<UInt8> {
|
||||
func as_data(size: Int) -> Data {
|
||||
return Data(bytes: self, count: size)
|
||||
}
|
||||
}
|
||||
|
||||
struct MentionRef: TagKeys, TagConvertible, Equatable, Hashable {
|
||||
let nip19: Bech32Object
|
||||
|
||||
static func pubkey(_ pubkey: Pubkey) -> MentionRef {
|
||||
self.init(nip19: .npub(pubkey))
|
||||
}
|
||||
|
||||
static func note(_ note_id: NoteId) -> MentionRef {
|
||||
return self.init(nip19: .note(note_id))
|
||||
}
|
||||
|
||||
init?(block: ndb_mention_bech32_block) {
|
||||
guard let bech32_obj = Bech32Object.init(block: block) else {
|
||||
return nil
|
||||
}
|
||||
self.nip19 = bech32_obj
|
||||
}
|
||||
|
||||
init(nip19: Bech32Object) {
|
||||
self.nip19 = nip19
|
||||
}
|
||||
|
||||
var key: MentionType {
|
||||
switch self {
|
||||
case .pubkey: return .p
|
||||
case .note: return .e
|
||||
case .nevent: return .e
|
||||
case .nprofile: return .p
|
||||
switch self.nip19 {
|
||||
case .note, .nevent: return .e
|
||||
case .nprofile, .npub: return .p
|
||||
case .nrelay: return .r
|
||||
case .naddr: return .a
|
||||
case .nscript: return .a
|
||||
case .nsec: return .p
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,38 +61,40 @@ enum MentionRef: TagKeys, TagConvertible, Equatable, Hashable {
|
||||
return Bech32Object.encode(toBech32Object())
|
||||
}
|
||||
|
||||
static func from_bech32(str: String) -> MentionRef? {
|
||||
switch Bech32Object.parse(str) {
|
||||
case .note(let noteid): return .note(noteid)
|
||||
case .npub(let pubkey): return .pubkey(pubkey)
|
||||
default: return nil
|
||||
init?(bech32_str: String) {
|
||||
guard let obj = Bech32Object.parse(bech32_str) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
self.nip19 = obj
|
||||
}
|
||||
|
||||
var pubkey: Pubkey? {
|
||||
switch self {
|
||||
case .pubkey(let pubkey): return pubkey
|
||||
switch self.nip19 {
|
||||
case .npub(let pubkey): return pubkey
|
||||
case .note: return nil
|
||||
case .nevent(let nevent): return nevent.author
|
||||
case .nprofile(let nprofile): return nprofile.author
|
||||
case .nrelay: return nil
|
||||
case .naddr: return nil
|
||||
case .nsec(let prv): return privkey_to_pubkey(privkey: prv)
|
||||
case .nscript(_): return nil
|
||||
}
|
||||
}
|
||||
|
||||
var tag: [String] {
|
||||
switch self {
|
||||
case .pubkey(let pubkey): return ["p", pubkey.hex()]
|
||||
switch self.nip19 {
|
||||
case .npub(let pubkey): return ["p", pubkey.hex()]
|
||||
case .note(let noteId): return ["e", noteId.hex()]
|
||||
case .nevent(let nevent):
|
||||
var tagBuilder = ["e", nevent.noteid.hex()]
|
||||
|
||||
let relay = nevent.relays.first
|
||||
if let author = nevent.author?.hex() {
|
||||
tagBuilder.append(relay ?? "")
|
||||
tagBuilder.append(relay?.absoluteString ?? "")
|
||||
tagBuilder.append(author)
|
||||
} else if let relay {
|
||||
tagBuilder.append(relay)
|
||||
tagBuilder.append(relay.absoluteString)
|
||||
}
|
||||
|
||||
return tagBuilder
|
||||
@@ -80,7 +102,7 @@ enum MentionRef: TagKeys, TagConvertible, Equatable, Hashable {
|
||||
var tagBuilder = ["p", nprofile.author.hex()]
|
||||
|
||||
if let relay = nprofile.relays.first {
|
||||
tagBuilder.append(relay)
|
||||
tagBuilder.append(relay.absoluteString)
|
||||
}
|
||||
|
||||
return tagBuilder
|
||||
@@ -89,10 +111,14 @@ enum MentionRef: TagKeys, TagConvertible, Equatable, Hashable {
|
||||
var tagBuilder = ["a", "\(naddr.kind.description):\(naddr.author.hex()):\(naddr.identifier.string())"]
|
||||
|
||||
if let relay = naddr.relays.first {
|
||||
tagBuilder.append(relay)
|
||||
tagBuilder.append(relay.absoluteString)
|
||||
}
|
||||
|
||||
return tagBuilder
|
||||
case .nsec(_):
|
||||
return []
|
||||
case .nscript(_):
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,10 +138,10 @@ enum MentionRef: TagKeys, TagConvertible, Equatable, Hashable {
|
||||
switch mention_type {
|
||||
case .p:
|
||||
guard let data = element.id() else { return nil }
|
||||
return .pubkey(Pubkey(data))
|
||||
return .init(nip19: .npub(Pubkey(data)))
|
||||
case .e:
|
||||
guard let data = element.id() else { return nil }
|
||||
return .note(NoteId(data))
|
||||
return .init(nip19: .note(NoteId(data)))
|
||||
case .a:
|
||||
let str = element.string()
|
||||
let data = str.split(separator: ":")
|
||||
@@ -124,26 +150,13 @@ enum MentionRef: TagKeys, TagConvertible, Equatable, Hashable {
|
||||
guard let pubkey = Pubkey(hex: String(data[1])) else { return nil }
|
||||
guard let kind = UInt32(data[0]) else { return nil }
|
||||
|
||||
return .naddr(NAddr(identifier: String(data[2]), author: pubkey, relays: [], kind: kind))
|
||||
case .r: return .nrelay(element.string())
|
||||
return .init(nip19: .naddr(NAddr(identifier: String(data[2]), author: pubkey, relays: [], kind: kind)))
|
||||
case .r: return .init(nip19: .nrelay(element.string()))
|
||||
}
|
||||
}
|
||||
|
||||
func toBech32Object() -> Bech32Object {
|
||||
switch self {
|
||||
case .pubkey(let pk):
|
||||
return .npub(pk)
|
||||
case .note(let noteid):
|
||||
return .note(noteid)
|
||||
case .naddr(let naddr):
|
||||
return .naddr(naddr)
|
||||
case .nevent(let nevent):
|
||||
return .nevent(nevent)
|
||||
case .nprofile(let nprofile):
|
||||
return .nprofile(nprofile)
|
||||
case .nrelay(let url):
|
||||
return .nrelay(url)
|
||||
}
|
||||
self.nip19
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,7 +198,6 @@ struct LightningInvoice<T> {
|
||||
let amount: T
|
||||
let string: String
|
||||
let expiry: UInt64
|
||||
let payment_hash: Data
|
||||
let created_at: UInt64
|
||||
|
||||
var abbreviated: String {
|
||||
@@ -207,14 +219,14 @@ struct LightningInvoice<T> {
|
||||
// avoiding code duplication and utilizing the guarantees acquired from age and testing.
|
||||
// We could also use the C function `parse_invoice`, but it requires extra C bridging logic.
|
||||
// NDBTODO: This may need updating on the nostrdb upgrade.
|
||||
let parsedBlocks = parse_note_content(content: .content(string,nil)).blocks
|
||||
guard let parsedBlocks = parse_note_content(content: .content(string,nil))?.blocks else { return nil }
|
||||
guard parsedBlocks.count == 1 else { return nil }
|
||||
return parsedBlocks[0].asInvoice
|
||||
}
|
||||
}
|
||||
|
||||
func maybe_pointee<T>(_ p: UnsafeMutablePointer<T>!) -> T? {
|
||||
guard p != nil else {
|
||||
func maybe_pointee<T>(_ p: UnsafeMutablePointer<T>?) -> T? {
|
||||
guard let p else {
|
||||
return nil
|
||||
}
|
||||
return p.pointee
|
||||
@@ -282,7 +294,7 @@ func format_msats(_ msat: Int64, locale: Locale = Locale.current) -> String {
|
||||
return String(format: format, locale: locale, sats.decimalValue as NSDecimalNumber, formattedSats)
|
||||
}
|
||||
|
||||
func convert_invoice_description(b11: bolt11) -> InvoiceDescription? {
|
||||
func convert_invoice_description(b11: ndb_invoice) -> InvoiceDescription? {
|
||||
if let desc = b11.description {
|
||||
return .description(String(cString: desc))
|
||||
}
|
||||
@@ -307,3 +319,38 @@ func find_tag_ref(type: String, id: String, tags: [[String]]) -> Int? {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
struct PostTags {
|
||||
let blocks: [Block]
|
||||
let tags: [[String]]
|
||||
}
|
||||
|
||||
/// Convert
|
||||
func make_post_tags(post_blocks: [Block], tags: [[String]]) -> PostTags {
|
||||
var new_tags = tags
|
||||
|
||||
for post_block in post_blocks {
|
||||
switch post_block {
|
||||
case .mention(let mention):
|
||||
switch(mention.ref.nip19) {
|
||||
case .note, .nevent:
|
||||
continue
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
new_tags.append(mention.ref.tag)
|
||||
case .hashtag(let hashtag):
|
||||
new_tags.append(["t", hashtag.lowercased()])
|
||||
case .text: break
|
||||
case .invoice: break
|
||||
case .relay: break
|
||||
case .url(let url):
|
||||
new_tags.append(["r", url.absoluteString])
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return PostTags(blocks: post_blocks, tags: new_tags)
|
||||
}
|
||||
|
||||
@@ -334,6 +334,27 @@ func decode_nostr_event(txt: String) -> NostrResponse? {
|
||||
return NostrResponse.owned_from_json(json: txt)
|
||||
}
|
||||
|
||||
func decode_and_verify_nostr_response(txt: String) -> NostrResponse? {
|
||||
guard let response = NostrResponse.owned_from_json(json: txt) else { return nil }
|
||||
guard verify_nostr_response(response: response) == true else { return nil }
|
||||
return response
|
||||
}
|
||||
|
||||
func verify_nostr_response(response: borrowing NostrResponse) -> Bool {
|
||||
switch response {
|
||||
case .event(_, let event):
|
||||
return event.verify()
|
||||
case .notice(_):
|
||||
return true
|
||||
case .eose(_):
|
||||
return true
|
||||
case .ok(_):
|
||||
return true
|
||||
case .auth(_):
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func encode_json<T: Encodable>(_ val: T) -> String? {
|
||||
let encoder = JSONEncoder()
|
||||
encoder.outputFormatting = .withoutEscapingSlashes
|
||||
@@ -518,6 +539,15 @@ func uniq<T: Hashable>(_ xs: [T]) -> [T] {
|
||||
return ys
|
||||
}
|
||||
|
||||
func gather_quote_ids(our_pubkey: Pubkey, from: NostrEvent) -> [RefId] {
|
||||
var ids: [RefId] = [.quote(from.id.quote_id)]
|
||||
if from.pubkey != our_pubkey {
|
||||
ids.append(.pubkey(from.pubkey))
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
|
||||
func gather_reply_ids(our_pubkey: Pubkey, from: NostrEvent) -> [RefId] {
|
||||
var ids: [RefId] = from.referenced_ids.first.map({ ref in [ .event(ref) ] }) ?? []
|
||||
|
||||
@@ -538,14 +568,6 @@ func gather_reply_ids(our_pubkey: Pubkey, from: NostrEvent) -> [RefId] {
|
||||
return ids
|
||||
}
|
||||
|
||||
func gather_quote_ids(our_pubkey: Pubkey, from: NostrEvent) -> [RefId] {
|
||||
var ids: [RefId] = [.quote(from.id.quote_id)]
|
||||
if from.pubkey != our_pubkey {
|
||||
ids.append(.pubkey(from.pubkey))
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
func event_from_json(dat: String) -> NostrEvent? {
|
||||
return NostrEvent.owned_from_json(json: dat)
|
||||
}
|
||||
@@ -778,42 +800,42 @@ func validate_event(ev: NostrEvent) -> ValidationResult {
|
||||
return ok ? .ok : .bad_sig
|
||||
}
|
||||
|
||||
func first_eref_mention(ev: NostrEvent, keypair: Keypair) -> Mention<NoteId>? {
|
||||
let blocks = ev.blocks(keypair).blocks.filter { block in
|
||||
guard case .mention(let mention) = block else {
|
||||
return false
|
||||
}
|
||||
|
||||
switch mention.ref {
|
||||
case .note, .nevent:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
func first_eref_mention(ndb: Ndb, ev: NostrEvent, keypair: Keypair) -> Mention<NoteId>? {
|
||||
guard let blockGroup = try? NdbBlockGroup.from(event: ev, using: ndb, and: keypair) else { return nil }
|
||||
|
||||
/// MARK: - Preview
|
||||
if let firstBlock = blocks.first,
|
||||
case .mention(let mention) = firstBlock {
|
||||
switch mention.ref {
|
||||
case .note(let note_id):
|
||||
return .note(note_id)
|
||||
case .nevent(let nevent):
|
||||
return .note(nevent.noteid)
|
||||
return blockGroup.forEachBlock({ index, block in
|
||||
switch block {
|
||||
case .mention(let mention):
|
||||
guard let mention = MentionRef(block: mention) else { return .loopContinue }
|
||||
switch mention.nip19 {
|
||||
case .note(let noteId):
|
||||
return .loopReturn(Mention<NoteId>.note(noteId, index: index))
|
||||
case .nevent(let nEvent):
|
||||
return .loopReturn(Mention<NoteId>.note(nEvent.noteid, index: index))
|
||||
default:
|
||||
return .loopContinue
|
||||
}
|
||||
default:
|
||||
return nil
|
||||
return .loopContinue
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func separate_invoices(ev: NostrEvent, keypair: Keypair) -> [Invoice]? {
|
||||
let invoiceBlocks: [Invoice] = ev.blocks(keypair).blocks.reduce(into: []) { invoices, block in
|
||||
guard case .invoice(let invoice) = block else {
|
||||
return
|
||||
}
|
||||
invoices.append(invoice)
|
||||
func separate_invoices(ndb: Ndb, ev: NostrEvent, keypair: Keypair) -> [Invoice]? {
|
||||
guard let blockGroup = try? NdbBlockGroup.from(event: ev, using: ndb, and: keypair) else {
|
||||
return nil
|
||||
}
|
||||
let invoiceBlocks: [Invoice] = (try? blockGroup.reduce(initialResult: [Invoice](), { index, invoices, block in
|
||||
switch block {
|
||||
case .invoice(let invoice):
|
||||
if let invoice = invoice.as_invoice() {
|
||||
return .loopReturn(invoices + [invoice])
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
return .loopContinue
|
||||
})) ?? []
|
||||
return invoiceBlocks.isEmpty ? nil : invoiceBlocks
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ enum NostrResponse {
|
||||
free(data)
|
||||
return nil
|
||||
}
|
||||
let new_note = note_data.assumingMemoryBound(to: ndb_note.self)
|
||||
let new_note = ndb_note_ptr(ptr: OpaquePointer(note_data))
|
||||
let note = NdbNote(note: new_note, size: Int(len), owned: true, key: nil)
|
||||
|
||||
guard let subid = sized_cstr(cstr: tce.subid, len: tce.subid_len) else {
|
||||
@@ -9,8 +9,32 @@ import Combine
|
||||
import Foundation
|
||||
|
||||
enum NostrConnectionEvent {
|
||||
case ws_event(WebSocketEvent)
|
||||
/// Other non-message websocket events
|
||||
case ws_connection_event(WSConnectionEvent)
|
||||
/// A nostr response
|
||||
case nostr_event(NostrResponse)
|
||||
|
||||
/// Models non-messaging websocket events
|
||||
///
|
||||
/// Implementation note: Messaging events should use `.nostr_event` in `NostrConnectionEvent`
|
||||
enum WSConnectionEvent {
|
||||
case connected
|
||||
case disconnected(URLSessionWebSocketTask.CloseCode, String?)
|
||||
case error(Error)
|
||||
|
||||
static func from(full_ws_event: WebSocketEvent) -> Self? {
|
||||
switch full_ws_event {
|
||||
case .connected:
|
||||
return .connected
|
||||
case .message(_):
|
||||
return nil
|
||||
case .disconnected(let closeCode, let string):
|
||||
return .disconnected(closeCode, string)
|
||||
case .error(let error):
|
||||
return .error(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final class RelayConnection: ObservableObject {
|
||||
@@ -31,11 +55,11 @@ final class RelayConnection: ObservableObject {
|
||||
|
||||
init(url: RelayURL,
|
||||
handleEvent: @escaping (NostrConnectionEvent) -> (),
|
||||
processEvent: @escaping (WebSocketEvent) -> ())
|
||||
processUnverifiedWSEvent: @escaping (WebSocketEvent) -> ())
|
||||
{
|
||||
self.relay_url = url
|
||||
self.handleEvent = handleEvent
|
||||
self.processEvent = processEvent
|
||||
self.processEvent = processUnverifiedWSEvent
|
||||
}
|
||||
|
||||
func ping() {
|
||||
@@ -115,6 +139,7 @@ final class RelayConnection: ObservableObject {
|
||||
}
|
||||
|
||||
private func receive(event: WebSocketEvent) {
|
||||
assert(!Thread.isMainThread, "This code must not be executed on the main thread")
|
||||
processEvent(event)
|
||||
switch event {
|
||||
case .connected:
|
||||
@@ -152,7 +177,8 @@ final class RelayConnection: ObservableObject {
|
||||
}
|
||||
}
|
||||
DispatchQueue.main.async {
|
||||
self.handleEvent(.ws_event(event))
|
||||
guard let ws_connection_event = NostrConnectionEvent.WSConnectionEvent.from(full_ws_event: event) else { return }
|
||||
self.handleEvent(.ws_connection_event(ws_connection_event))
|
||||
}
|
||||
|
||||
if let description = event.description {
|
||||
@@ -190,7 +216,9 @@ final class RelayConnection: ObservableObject {
|
||||
private func receive(message: URLSessionWebSocketTask.Message) {
|
||||
switch message {
|
||||
case .string(let messageString):
|
||||
if let ev = decode_nostr_event(txt: messageString) {
|
||||
// NOTE: Once we switch to the local relay model,
|
||||
// we will not need to verify nostr events at this point.
|
||||
if let ev = decode_and_verify_nostr_response(txt: messageString) {
|
||||
DispatchQueue.main.async {
|
||||
self.handleEvent(.nostr_event(ev))
|
||||
}
|
||||
@@ -126,7 +126,7 @@ class RelayPool {
|
||||
}
|
||||
let conn = RelayConnection(url: desc.url, handleEvent: { event in
|
||||
self.handle_event(relay_id: relay_id, event: event)
|
||||
}, processEvent: { wsev in
|
||||
}, processUnverifiedWSEvent: { wsev in
|
||||
guard case .message(let msg) = wsev,
|
||||
case .string(let str) = msg
|
||||
else { return }
|
||||
@@ -214,9 +214,9 @@ class RelayPool {
|
||||
var eoseSent = false
|
||||
self.subscribe(sub_id: sub_id, filters: filters, handler: { (relayUrl, connectionEvent) in
|
||||
switch connectionEvent {
|
||||
case .ws_event(let ev):
|
||||
case .ws_connection_event(let ev):
|
||||
// Websocket events such as connect/disconnect/error are already handled in `RelayConnection`. Do not perform any handling here.
|
||||
// For the future, perhaps we should abstract away `.ws_event` in `RelayPool`? Seems like something to be handled on the `RelayConnection` layer.
|
||||
// For the future, perhaps we should abstract away `.ws_connection_event` in `RelayPool`? Seems like something to be handled on the `RelayConnection` layer.
|
||||
break
|
||||
case .nostr_event(let nostrResponse):
|
||||
guard nostrResponse.subid == sub_id else { return } // Do not stream items that do not belong in this subscription
|
||||
@@ -357,6 +357,7 @@ class RelayPool {
|
||||
}
|
||||
seen[nev.id, default: Set()].insert(relay_id)
|
||||
counts[relay_id, default: 0] += 1
|
||||
notify(.update_stats(note_id: nev.id))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -365,7 +366,7 @@ class RelayPool {
|
||||
record_seen(relay_id: relay_id, event: event)
|
||||
|
||||
// run req queue when we reconnect
|
||||
if case .ws_event(let ws) = event {
|
||||
if case .ws_connection_event(let ws) = event {
|
||||
if case .connected = ws {
|
||||
run_queue(relay_id)
|
||||
}
|
||||
@@ -2,22 +2,11 @@
|
||||
// Block.swift
|
||||
// damus
|
||||
//
|
||||
// Created by Kyle Roucis on 2023-08-21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
fileprivate extension String {
|
||||
/// Failable initializer to build a Swift.String from a C-backed `str_block_t`.
|
||||
init?(_ s: str_block_t) {
|
||||
let len = s.end - s.start
|
||||
let bytes = Data(bytes: s.start, count: len)
|
||||
self.init(bytes: bytes, encoding: .utf8)
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a block of data stored by the NOSTR protocol. This can be
|
||||
/// Represents a block of data stored in nostrdb. This can be
|
||||
/// simple text, a hashtag, a url, a relay reference, a mention ref and
|
||||
/// potentially more in the future.
|
||||
enum Block: Equatable {
|
||||
@@ -38,22 +27,6 @@ enum Block: Equatable {
|
||||
}
|
||||
}
|
||||
|
||||
var is_previewable: Bool {
|
||||
switch self {
|
||||
case .mention(let m):
|
||||
switch m.ref {
|
||||
case .note, .nevent: return true
|
||||
default: return false
|
||||
}
|
||||
case .invoice:
|
||||
return true
|
||||
case .url:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
case text(String)
|
||||
case mention(Mention<MentionRef>)
|
||||
case hashtag(String)
|
||||
@@ -67,61 +40,56 @@ struct Blocks: Equatable {
|
||||
let blocks: [Block]
|
||||
}
|
||||
|
||||
extension ndb_str_block {
|
||||
func as_str() -> String {
|
||||
let buf = UnsafeBufferPointer(start: self.str, count: Int(self.len))
|
||||
let uint8Buf = buf.map { UInt8(bitPattern: $0) }
|
||||
return String(decoding: uint8Buf, as: UTF8.self)
|
||||
}
|
||||
}
|
||||
|
||||
extension ndb_block_ptr {
|
||||
func as_str() -> String {
|
||||
guard let str_block = ndb_block_str(self.ptr) else {
|
||||
return ""
|
||||
}
|
||||
return str_block.pointee.as_str()
|
||||
}
|
||||
|
||||
var block: ndb_block.__Unnamed_union_block {
|
||||
self.ptr.pointee.block
|
||||
}
|
||||
}
|
||||
|
||||
extension Block {
|
||||
/// Failable initializer for the C-backed type `block_t`. This initializer will inspect
|
||||
/// the underlying block type and build the appropriate enum value as needed.
|
||||
init?(_ block: block_t, tags: TagsSequence? = nil) {
|
||||
switch block.type {
|
||||
init?(block: ndb_block_ptr, tags: TagsSequence?) {
|
||||
switch ndb_get_block_type(block.ptr) {
|
||||
case BLOCK_HASHTAG:
|
||||
guard let str = String(block.block.str) else {
|
||||
return nil
|
||||
}
|
||||
self = .hashtag(str)
|
||||
self = .hashtag(block.as_str())
|
||||
case BLOCK_TEXT:
|
||||
guard let str = String(block.block.str) else {
|
||||
return nil
|
||||
}
|
||||
self = .text(str)
|
||||
self = .text(block.as_str())
|
||||
case BLOCK_MENTION_INDEX:
|
||||
guard let b = Block(index: Int(block.block.mention_index), tags: tags) else {
|
||||
return nil
|
||||
}
|
||||
self = b
|
||||
case BLOCK_URL:
|
||||
guard let b = Block(block.block.str) else {
|
||||
return nil
|
||||
}
|
||||
self = b
|
||||
guard let url = URL(string: block.as_str()) else { return nil }
|
||||
self = .url(url)
|
||||
case BLOCK_INVOICE:
|
||||
guard let b = Block(invoice: block.block.invoice) else {
|
||||
return nil
|
||||
}
|
||||
guard let b = Block(invoice: block.block.invoice) else { return nil }
|
||||
self = b
|
||||
case BLOCK_MENTION_BECH32:
|
||||
guard let b = Block(bech32: block.block.mention_bech32) else {
|
||||
return nil
|
||||
}
|
||||
guard let b = Block(bech32: block.block.mention_bech32) else { return nil }
|
||||
self = b
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
fileprivate extension Block {
|
||||
/// Failable initializer for the C-backed type `str_block_t`.
|
||||
init?(_ b: str_block_t) {
|
||||
guard let str = String(b) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
if let url = URL(string: str) {
|
||||
self = .url(url)
|
||||
}
|
||||
else {
|
||||
self = .text(str)
|
||||
}
|
||||
}
|
||||
}
|
||||
fileprivate extension Block {
|
||||
/// Failable initializer for a block index and a tag sequence.
|
||||
init?(index: Int, tags: TagsSequence? = nil) {
|
||||
@@ -143,34 +111,34 @@ fileprivate extension Block {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate extension Block {
|
||||
/// Failable initializer for the C-backed type `invoice_block_t`.
|
||||
init?(invoice: invoice_block_t) {
|
||||
guard let invstr = String(invoice.invstr) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
guard var b11 = maybe_pointee(invoice.bolt11) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
guard let description = convert_invoice_description(b11: b11) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let amount: Amount = maybe_pointee(b11.msat).map { .specific(Int64($0.millisatoshis)) } ?? .any
|
||||
let payment_hash = Data(bytes: &b11.payment_hash, count: 32)
|
||||
let created_at = b11.timestamp
|
||||
|
||||
tal_free(invoice.bolt11)
|
||||
self = .invoice(Invoice(description: description, amount: amount, string: invstr, expiry: b11.expiry, payment_hash: payment_hash, created_at: created_at))
|
||||
init?(invoice: ndb_invoice_block) {
|
||||
|
||||
guard let invoice = invoice_block_as_invoice(invoice) else { return nil }
|
||||
self = .invoice(invoice)
|
||||
}
|
||||
}
|
||||
|
||||
func invoice_block_as_invoice(_ invoice: ndb_invoice_block) -> Invoice? {
|
||||
let invstr = invoice.invstr.as_str()
|
||||
let b11 = invoice.invoice
|
||||
|
||||
guard let description = convert_invoice_description(b11: b11) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let amount: Amount = b11.amount == 0 ? .any : .specific(Int64(b11.amount))
|
||||
|
||||
return Invoice(description: description, amount: amount, string: invstr, expiry: b11.expiry, created_at: b11.timestamp)
|
||||
|
||||
}
|
||||
|
||||
fileprivate extension Block {
|
||||
/// Failable initializer for the C-backed type `mention_bech32_block_t`. This initializer will inspect the
|
||||
/// bech32 type code and build the appropriate enum type.
|
||||
init?(bech32 b: mention_bech32_block_t) {
|
||||
init?(bech32 b: ndb_mention_bech32_block) {
|
||||
guard let decoded = decodeCBech32(b.bech32) else {
|
||||
return nil
|
||||
}
|
||||
@@ -180,6 +148,7 @@ fileprivate extension Block {
|
||||
self = .mention(.any(ref))
|
||||
}
|
||||
}
|
||||
|
||||
extension Block {
|
||||
var asString: String {
|
||||
switch self {
|
||||
@@ -51,4 +51,15 @@ struct NoteId: IdType, TagKey, TagConvertible {
|
||||
|
||||
return note_id
|
||||
}
|
||||
|
||||
func withUnsafePointer<T>(_ body: (UnsafePointer<UInt8>) throws -> T) rethrows -> T {
|
||||
return try self.id.withUnsafeBytes { (bytes: UnsafeRawBufferPointer) in
|
||||
guard let baseAddress = bytes.baseAddress else {
|
||||
fatalError("Cannot get base address")
|
||||
}
|
||||
return try baseAddress.withMemoryRebound(to: UInt8.self, capacity: bytes.count) { ptr in
|
||||
return try body(ptr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,4 +44,14 @@ struct Pubkey: IdType, TagKey, TagConvertible, Identifiable {
|
||||
return pubkey
|
||||
}
|
||||
|
||||
func withUnsafePointer<T>(_ body: (UnsafePointer<UInt8>) throws -> T) rethrows -> T {
|
||||
return try self.id.withUnsafeBytes { (bytes: UnsafeRawBufferPointer) in
|
||||
guard let baseAddress = bytes.baseAddress else {
|
||||
fatalError("Cannot get base address")
|
||||
}
|
||||
return try baseAddress.withMemoryRebound(to: UInt8.self, capacity: bytes.count) { ptr in
|
||||
return try body(ptr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,12 +25,13 @@ class ActionBarModel: ObservableObject {
|
||||
@Published private(set) var zaps: Int
|
||||
@Published var zap_total: Int64
|
||||
@Published var replies: Int
|
||||
|
||||
@Published var relays: Int
|
||||
|
||||
static func empty() -> ActionBarModel {
|
||||
return ActionBarModel(likes: 0, boosts: 0, zaps: 0, zap_total: 0, replies: 0, our_like: nil, our_boost: nil, our_zap: nil, our_reply: nil)
|
||||
}
|
||||
|
||||
init(likes: Int = 0, boosts: Int = 0, zaps: Int = 0, zap_total: Int64 = 0, replies: Int = 0, our_like: NostrEvent? = nil, our_boost: NostrEvent? = nil, our_zap: Zapping? = nil, our_reply: NostrEvent? = nil, our_quote_repost: NostrEvent? = nil, quote_reposts: Int = 0) {
|
||||
init(likes: Int = 0, boosts: Int = 0, zaps: Int = 0, zap_total: Int64 = 0, replies: Int = 0, our_like: NostrEvent? = nil, our_boost: NostrEvent? = nil, our_zap: Zapping? = nil, our_reply: NostrEvent? = nil, our_quote_repost: NostrEvent? = nil, quote_reposts: Int = 0, relays: Int = 0) {
|
||||
self.likes = likes
|
||||
self.boosts = boosts
|
||||
self.zaps = zaps
|
||||
@@ -42,6 +43,7 @@ class ActionBarModel: ObservableObject {
|
||||
self.our_reply = our_reply
|
||||
self.our_quote_repost = our_quote_repost
|
||||
self.quote_reposts = quote_reposts
|
||||
self.relays = relays
|
||||
}
|
||||
|
||||
func update(damus: DamusState, evid: NoteId) {
|
||||
@@ -56,11 +58,12 @@ class ActionBarModel: ObservableObject {
|
||||
self.our_zap = damus.zaps.our_zaps[evid]?.first
|
||||
self.our_reply = damus.replies.our_reply(evid)
|
||||
self.our_quote_repost = damus.quote_reposts.our_events[evid]
|
||||
self.relays = (damus.nostrNetwork.pool.seen[evid] ?? []).count
|
||||
self.objectWillChange.send()
|
||||
}
|
||||
|
||||
var is_empty: Bool {
|
||||
return likes == 0 && boosts == 0 && zaps == 0
|
||||
return likes == 0 && boosts == 0 && zaps == 0 && quote_reposts == 0 && relays == 0
|
||||
}
|
||||
|
||||
var liked: Bool {
|
||||
@@ -218,13 +218,13 @@ struct EventActionBar: View {
|
||||
}
|
||||
}
|
||||
|
||||
var event_relay_url_strings: [String] {
|
||||
var event_relay_url_strings: [RelayURL] {
|
||||
let relays = damus_state.nostrNetwork.relaysForEvent(event: event)
|
||||
if !relays.isEmpty {
|
||||
return relays.prefix(Constants.MAX_SHARE_RELAYS).map { $0.absoluteString }
|
||||
return relays.prefix(Constants.MAX_SHARE_RELAYS).map { $0 }
|
||||
}
|
||||
|
||||
return userProfile.getCappedRelayStrings()
|
||||
return userProfile.getCappedRelays()
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
@@ -59,6 +59,16 @@ struct EventDetailBar: View {
|
||||
}
|
||||
.buttonStyle(PlainButtonStyle())
|
||||
}
|
||||
|
||||
if bar.relays > 0 {
|
||||
let relays = Array(state.nostrNetwork.pool.seen[target] ?? [])
|
||||
NavigationLink(value: Route.UserRelays(relays: relays)) {
|
||||
let nounString = pluralizedString(key: "relays_count", count: bar.relays)
|
||||
let noun = Text(nounString).foregroundColor(.gray)
|
||||
Text("\(Text(verbatim: bar.relays.formatted()).font(.body.bold())) \(noun)", comment: "Sentence composed of 2 variables to describe how many relays a note was found on. In source English, the first variable is the number of relays, and the second variable is 'Relay' or 'Relays'.")
|
||||
}
|
||||
.buttonStyle(PlainButtonStyle())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,13 +27,13 @@ struct ShareAction: View {
|
||||
self._show_share = show_share
|
||||
}
|
||||
|
||||
var event_relay_url_strings: [String] {
|
||||
var event_relay_url_strings: [RelayURL] {
|
||||
let relays = userProfile.damus.nostrNetwork.relaysForEvent(event: event)
|
||||
if !relays.isEmpty {
|
||||
return relays.prefix(Constants.MAX_SHARE_RELAYS).map { $0.absoluteString }
|
||||
return relays.prefix(Constants.MAX_SHARE_RELAYS).map { $0 }
|
||||
}
|
||||
|
||||
return userProfile.getCappedRelayStrings()
|
||||
return userProfile.getCappedRelays()
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
@@ -49,7 +49,7 @@ struct ShareAction: View {
|
||||
|
||||
ShareActionButton(img: "link", text: NSLocalizedString("Copy Link", comment: "Button to copy link to note")) {
|
||||
dismiss()
|
||||
UIPasteboard.general.string = "https://damus.io/" + Bech32Object.encode(.nevent(NEvent(event: event, relays: event_relay_url_strings)))
|
||||
UIPasteboard.general.string = "https://damus.io/" + Bech32Object.encode(.nevent(NEvent(noteid: event.id, relays: userProfile.getCappedRelays())))
|
||||
}
|
||||
|
||||
let bookmarkImg = isBookmarked ? "bookmark.fill" : "bookmark"
|
||||
@@ -144,7 +144,7 @@ struct ChatEventView: View {
|
||||
let blur_images = should_blur_images(settings: damus_state.settings, contacts: damus_state.contacts, ev: event, our_pubkey: damus_state.pubkey)
|
||||
NoteContentView(damus_state: damus_state, event: event, blur_images: blur_images, size: .normal, options: [.truncate_content])
|
||||
.padding(2)
|
||||
if let mention = first_eref_mention(ev: event, keypair: damus_state.keypair) {
|
||||
if let mention = first_eref_mention(ndb: damus_state.ndb, ev: event, keypair: damus_state.keypair) {
|
||||
MentionView(damus_state: damus_state, mention: mention)
|
||||
.background(DamusColors.adaptableWhite)
|
||||
.clipShape(RoundedRectangle(cornerSize: CGSize(width: 10, height: 10)))
|
||||
@@ -126,10 +126,10 @@ struct DMChatView: View, KeyboardReadable {
|
||||
|
||||
func send_message() {
|
||||
let tags = [["p", pubkey.hex()]]
|
||||
let post_blocks = parse_post_blocks(content: dms.draft)
|
||||
let content = post_blocks
|
||||
.map(\.asString)
|
||||
.joined(separator: "")
|
||||
guard let post_blocks = parse_post_blocks(content: dms.draft)?.blocks else {
|
||||
return
|
||||
}
|
||||
let content = post_blocks.map({ pb in pb.asString }).joined(separator: "")
|
||||
|
||||
guard let dm = NIP04.create_dm(content, to_pk: pubkey, tags: tags, keypair: damus_state.keypair) else {
|
||||
print("error creating dm")
|
||||
@@ -17,7 +17,7 @@ struct DMView: View {
|
||||
|
||||
var Mention: some View {
|
||||
Group {
|
||||
if let mention = first_eref_mention(ev: event, keypair: damus_state.keypair) {
|
||||
if let mention = first_eref_mention(ndb: damus_state.ndb, ev: event, keypair: damus_state.keypair) {
|
||||
BuilderEventView(damus: damus_state, event_id: mention.ref)
|
||||
} else {
|
||||
EmptyView()
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user