Posting works!

Signed-off-by: William Casarin <jb55@jb55.com>
This commit is contained in:
William Casarin
2022-04-12 06:47:54 -07:00
parent 510926fa70
commit 967c463976
8 changed files with 230 additions and 13 deletions

View File

@@ -74,6 +74,11 @@ struct ContentView: View {
.onReceive(NotificationCenter.default.publisher(for: .post)) { obj in
let post = obj.object as! NostrPost
print("post \(post.content)")
let pubkey = ""
let privkey = ""
let new_ev = NostrEvent(content: post.content, pubkey: pubkey)
new_ev.sign(privkey: privkey)
self.pool?.send(.event(new_ev))
}
}
@@ -129,7 +134,7 @@ struct ContentView: View {
self.sub_id = sub_id
}
print("subscribing to \(sub_id)")
self.pool?.send(filters: filters, sub_id: sub_id)
self.pool?.send(.subscribe(.init(filters: filters, sub_id: sub_id)))
}
func handle_event(relay_id: String, conn_event: NostrConnectionEvent) {

View File

@@ -6,6 +6,8 @@
//
import Foundation
import CommonCrypto
import secp256k1
struct OtherEvent {
let event_id: String
@@ -17,15 +19,84 @@ struct KeyEvent {
let relay_url: String
}
struct NostrEvent: Decodable, Identifiable {
let id: String
class NostrEvent: Codable, Identifiable {
var id: String
var sig: String
var tags: [[String]]
// cached field for pow calc
var pow: Int?
let pubkey: String
let created_at: Int64
let kind: Int
let tags: [[String]]
let content: String
let sig: String
var pow: Int?
private enum CodingKeys: String, CodingKey {
case id, sig, tags, pubkey, created_at, kind, content
}
init(content: String, pubkey: String, kind: Int = 1, tags: [[String]] = []) {
self.id = ""
self.sig = ""
self.content = content
self.pubkey = pubkey
self.kind = kind
self.tags = tags
self.created_at = Int64(Date().timeIntervalSince1970)
self.calculate_id()
}
func calculate_id() {
self.id = calculate_event_id(ev: self)
self.pow = count_hash_leading_zero_bits(self.id)
}
// TODO: timeout
/*
func mine_id(pow: Int, done: @escaping (String) -> ()) {
let nonce_ind = self.ensure_nonce_tag()
let nonce: Int64 = 0
DispatchQueue.global(qos: .background).async {
while
}
}
*/
private func ensure_nonce_tag() -> Int {
for (i, tags) in self.tags.enumerated() {
for tag in tags {
if tags.count == 2 && tag == "nonce" {
return i
}
}
}
self.tags.append(["nonce", "0"])
return self.tags.count - 1
}
func sign(privkey: String) {
self.sig = sign_event(privkey: privkey, ev: self)
}
}
func sign_event(privkey: String, ev: NostrEvent) -> String {
let priv_key_bytes = try! privkey.byteArray()
let key = try! secp256k1.Signing.PrivateKey(rawRepresentation: priv_key_bytes)
// Extra params for custom signing
var aux_rand = random_bytes(count: 64)
var digest = try! ev.id.byteArray()
// API allows for signing variable length messages
let signature = try! key.schnorr.signature(message: &digest, auxiliaryRand: &aux_rand)
return hex_encode(signature.rawRepresentation)
}
func decode_nostr_event(txt: String) -> NostrResponse? {
@@ -42,3 +113,62 @@ func decode_data<T: Decodable>(_ data: Data) -> T? {
return nil
}
func event_commitment(ev: NostrEvent, tags: String) -> String {
return "[0,\"\(ev.pubkey)\",\(ev.created_at),\(ev.kind),\(tags),\"\(ev.content)\"]"
}
func calculate_event_id(ev: NostrEvent) -> String {
let tags_encoder = JSONEncoder()
let tags_data = try! tags_encoder.encode(ev.tags)
let tags = String(decoding: tags_data, as: UTF8.self)
let target = event_commitment(ev: ev, tags: tags)
let target_data = target.data(using: .utf8)!
let hash = sha256(target_data)
return hex_encode(hash)
}
func sha256(_ data: Data) -> Data {
var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
data.withUnsafeBytes {
_ = CC_SHA256($0.baseAddress, CC_LONG(data.count), &hash)
}
return Data(hash)
}
func hexchar(_ val: UInt8) -> UInt8 {
if val < 10 {
return 48 + val;
}
if val < 16 {
return 97 + val - 10;
}
assertionFailure("impossiburu")
return 0
}
func hex_encode(_ data: Data) -> String {
var str = ""
for c in data {
let c1 = hexchar(c >> 4)
let c2 = hexchar(c & 0xF)
str.append(Character(Unicode.Scalar(c1)))
str.append(Character(Unicode.Scalar(c2)))
}
return str
}
func random_bytes(count: Int) -> Data {
var data = Data(count: count)
_ = data.withUnsafeMutableBytes { mutableBytes in
SecRandomCopyBytes(kSecRandomDefault, count, mutableBytes)
}
return data
}

View File

@@ -0,0 +1,18 @@
//
// NostrRequest.swift
// damus
//
// Created by William Casarin on 2022-04-12.
//
import Foundation
struct NostrSubscribe {
let filters: [NostrFilter]
let sub_id: String
}
enum NostrRequest {
case subscribe(NostrSubscribe)
case event(NostrEvent)
}

View File

@@ -35,9 +35,9 @@ class RelayConnection: WebSocketDelegate {
socket.disconnect()
}
func send(_ filters: [NostrFilter], sub_id: String) {
guard let req = make_nostr_req(filters, sub_id: sub_id) else {
print("failed to encode nostr req: \(filters)")
func send(_ req: NostrRequest) {
guard let req = make_nostr_req(req) else {
print("failed to encode nostr req: \(req)")
return
}
socket.write(string: req)
@@ -71,7 +71,23 @@ class RelayConnection: WebSocketDelegate {
}
func make_nostr_req(_ filters: [NostrFilter], sub_id: String) -> String? {
func make_nostr_req(_ req: NostrRequest) -> String? {
switch req {
case .subscribe(let sub):
return make_nostr_subscription_req(sub.filters, sub_id: sub.sub_id)
case .event(let ev):
return make_nostr_push_event(ev: ev)
}
}
func make_nostr_push_event(ev: NostrEvent) -> String? {
let encoder = JSONEncoder()
let event_data = try! encoder.encode(ev)
let event = String(decoding: event_data, as: UTF8.self)
return "[\"EVENT\",\(event)]"
}
func make_nostr_subscription_req(_ filters: [NostrFilter], sub_id: String) -> String? {
let encoder = JSONEncoder()
var req = "[\"REQ\",\"\(sub_id)\""
for filter in filters {

View File

@@ -34,12 +34,12 @@ class RelayPool {
}
}
func send(filters: [NostrFilter], sub_id: String, to: [String]? = nil) {
func send(_ req: NostrRequest, to: [String]? = nil) {
let relays = to.map{ get_relays($0) } ?? self.relays
for relay in relays {
if relay.connection.isConnected {
relay.connection.send(filters, sub_id: sub_id)
relay.connection.send(req)
}
}
}

View File

@@ -63,5 +63,5 @@ struct EventView: View {
func calculate_pow_color(_ pow: Int) -> Color
{
let x = Double(pow) / 30.0;
return Color(.sRGB, red: 2.0 * (1.0 - x), green: 2.0 * x, blue: 0, opacity: 1.0)
return Color(.sRGB, red: 2.0 * (1.0 - x), green: 2.0 * x, blue: 0, opacity: 0.5)
}