mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-09 08:04:45 +00:00
refactor(nip59): replace wrap PoW params with a template-conversion hook
GiftWrapEvent.create no longer takes powDifficulty/powIsActive — it takes a single templateConversion hook ((template, ephemeralPubKey) -> template, default identity) that runs on the finished wrap template right before the ephemeral key signs it. The hook receives the ephemeral pubkey because the NIP-01 id a nonce commits to includes it and the key never leaves create(). NIP17Factory forwards the same hook through wrapSeal/createWraps and the create*NIP17 entry points, so quartz's NIP-59/NIP-17 code no longer imports the NIP-13 miner at all; Account builds the mining closure at the call site. Any future pre-sign wrap adjustment flows through the same seam. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ADb3dez9jPk6QqyQ1rTx4V
This commit is contained in:
@@ -189,6 +189,7 @@ import com.vitorpamplona.quartz.nip10Notes.content.findHashtags
|
||||
import com.vitorpamplona.quartz.nip10Notes.content.findNostrUris
|
||||
import com.vitorpamplona.quartz.nip10Notes.content.findURLs
|
||||
import com.vitorpamplona.quartz.nip10Notes.threadRootIdOrSelf
|
||||
import com.vitorpamplona.quartz.nip13Pow.miner.PoWMiner
|
||||
import com.vitorpamplona.quartz.nip13Pow.signer.PoWNostrSigner
|
||||
import com.vitorpamplona.quartz.nip17Dm.NIP17Factory
|
||||
import com.vitorpamplona.quartz.nip17Dm.base.BaseDMGroupEvent
|
||||
@@ -253,6 +254,7 @@ import com.vitorpamplona.quartz.nip59Giftwrap.rumors.RumorAssembler
|
||||
import com.vitorpamplona.quartz.nip59Giftwrap.seals.SealedRumorEvent
|
||||
import com.vitorpamplona.quartz.nip59Giftwrap.wraps.EphemeralGiftWrapEvent
|
||||
import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapEvent
|
||||
import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapTemplateConversion
|
||||
import com.vitorpamplona.quartz.nip62RequestToVanish.RequestToVanishEvent
|
||||
import com.vitorpamplona.quartz.nip65RelayList.AdvertisedRelayListEvent
|
||||
import com.vitorpamplona.quartz.nip65RelayList.tags.AdvertisedRelayInfo
|
||||
@@ -927,7 +929,13 @@ class Account(
|
||||
difficulty = difficulty,
|
||||
persistAs = record,
|
||||
mine = { isActive ->
|
||||
seals.map { NIP17Factory().wrapSeal(it, expirationDelta, powDifficulty = difficulty, powIsActive = isActive) }
|
||||
// the wrap's ephemeral key is generated inside the wrap build;
|
||||
// the conversion hook hands its pubkey back so the nonce can
|
||||
// commit to it.
|
||||
val mineWrap: GiftWrapTemplateConversion = { template, ephemeralPubKey ->
|
||||
PoWMiner.run(template, ephemeralPubKey, difficulty, isActive)
|
||||
}
|
||||
seals.map { NIP17Factory().wrapSeal(it, expirationDelta, templateConversion = mineWrap) }
|
||||
},
|
||||
publish = { wraps -> broadcastPrivately(wraps) },
|
||||
)
|
||||
|
||||
@@ -37,6 +37,7 @@ import com.vitorpamplona.quartz.nip40Expiration.expiration
|
||||
import com.vitorpamplona.quartz.nip46RemoteSigner.signer.NostrSignerRemote
|
||||
import com.vitorpamplona.quartz.nip59Giftwrap.seals.SealedRumorEvent
|
||||
import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapEvent
|
||||
import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapTemplateConversion
|
||||
import com.vitorpamplona.quartz.utils.mapNotNullAsync
|
||||
import kotlinx.coroutines.sync.Semaphore
|
||||
import kotlinx.coroutines.sync.withPermit
|
||||
@@ -102,24 +103,23 @@ class NIP17Factory {
|
||||
}
|
||||
|
||||
/**
|
||||
* Phase two: wraps one pre-signed seal in its ephemeral-key envelope,
|
||||
* optionally mining a NIP-13 proof of work into the wrap. Local-only —
|
||||
* no user signer involved.
|
||||
* Phase two: wraps one pre-signed seal in its ephemeral-key envelope.
|
||||
* Local-only — no user signer involved. [templateConversion] is the
|
||||
* pre-sign hook on the wrap template (e.g. NIP-13 mining); see
|
||||
* [GiftWrapTemplateConversion].
|
||||
*/
|
||||
fun wrapSeal(
|
||||
addressed: AddressedSeal,
|
||||
expirationDelta: Long?,
|
||||
recipientRelayHint: NormalizedRelayUrl? = null,
|
||||
powDifficulty: Int? = null,
|
||||
powIsActive: () -> Boolean = { true },
|
||||
templateConversion: GiftWrapTemplateConversion = { template, _ -> template },
|
||||
): GiftWrapEvent =
|
||||
GiftWrapEvent.create(
|
||||
event = addressed.seal,
|
||||
recipientPubKey = addressed.recipient,
|
||||
expirationDelta = expirationDelta,
|
||||
recipientRelayHint = recipientRelayHint,
|
||||
powDifficulty = powDifficulty,
|
||||
powIsActive = powIsActive,
|
||||
templateConversion = templateConversion,
|
||||
)
|
||||
|
||||
/**
|
||||
@@ -150,8 +150,7 @@ class NIP17Factory {
|
||||
to: Set<HexKey>,
|
||||
signer: NostrSigner,
|
||||
recipientRelayHints: (HexKey) -> NormalizedRelayUrl? = { null },
|
||||
wrapPowDifficulty: Int? = null,
|
||||
wrapPowIsActive: () -> Boolean = { true },
|
||||
wrapTemplateConversion: GiftWrapTemplateConversion = { template, _ -> template },
|
||||
): List<GiftWrapEvent> {
|
||||
val innerExpDelta =
|
||||
event.expiration()?.let {
|
||||
@@ -179,8 +178,7 @@ class NIP17Factory {
|
||||
recipientPubKey = next,
|
||||
expirationDelta = innerExpDelta,
|
||||
recipientRelayHint = recipientRelayHints(next),
|
||||
powDifficulty = wrapPowDifficulty,
|
||||
powIsActive = wrapPowIsActive,
|
||||
templateConversion = wrapTemplateConversion,
|
||||
)
|
||||
}
|
||||
bunkerLimiter?.withPermit { build() } ?: build()
|
||||
@@ -202,11 +200,10 @@ class NIP17Factory {
|
||||
template: EventTemplate<ChatMessageEvent>,
|
||||
signer: NostrSigner,
|
||||
recipientRelayHints: (HexKey) -> NormalizedRelayUrl? = { null },
|
||||
wrapPowDifficulty: Int? = null,
|
||||
wrapPowIsActive: () -> Boolean = { true },
|
||||
wrapTemplateConversion: GiftWrapTemplateConversion = { template2, _ -> template2 },
|
||||
): Result {
|
||||
val senderMessage = signer.sign(template)
|
||||
val wraps = createWraps(senderMessage, senderMessage.groupMembers(), signer, recipientRelayHints, wrapPowDifficulty, wrapPowIsActive)
|
||||
val wraps = createWraps(senderMessage, senderMessage.groupMembers(), signer, recipientRelayHints, wrapTemplateConversion)
|
||||
return Result(
|
||||
msg = senderMessage,
|
||||
wraps = wraps,
|
||||
@@ -223,8 +220,7 @@ class NIP17Factory {
|
||||
suspend fun createNoteNIP17(
|
||||
template: EventTemplate<TextNoteEvent>,
|
||||
signer: NostrSigner,
|
||||
wrapPowDifficulty: Int? = null,
|
||||
wrapPowIsActive: () -> Boolean = { true },
|
||||
wrapTemplateConversion: GiftWrapTemplateConversion = { template2, _ -> template2 },
|
||||
): Result {
|
||||
val senderNote = signer.sign(template)
|
||||
val wraps =
|
||||
@@ -232,8 +228,7 @@ class NIP17Factory {
|
||||
senderNote,
|
||||
senderNote.taggedUserIds().plus(signer.pubKey).toSet(),
|
||||
signer,
|
||||
wrapPowDifficulty = wrapPowDifficulty,
|
||||
wrapPowIsActive = wrapPowIsActive,
|
||||
wrapTemplateConversion = wrapTemplateConversion,
|
||||
)
|
||||
return Result(
|
||||
msg = senderNote,
|
||||
@@ -245,11 +240,10 @@ class NIP17Factory {
|
||||
template: EventTemplate<ChatMessageEncryptedFileHeaderEvent>,
|
||||
signer: NostrSigner,
|
||||
recipientRelayHints: (HexKey) -> NormalizedRelayUrl? = { null },
|
||||
wrapPowDifficulty: Int? = null,
|
||||
wrapPowIsActive: () -> Boolean = { true },
|
||||
wrapTemplateConversion: GiftWrapTemplateConversion = { template2, _ -> template2 },
|
||||
): Result {
|
||||
val senderMessage = signer.sign(template)
|
||||
val wraps = createWraps(senderMessage, senderMessage.groupMembers(), signer, recipientRelayHints, wrapPowDifficulty, wrapPowIsActive)
|
||||
val wraps = createWraps(senderMessage, senderMessage.groupMembers(), signer, recipientRelayHints, wrapTemplateConversion)
|
||||
|
||||
return Result(
|
||||
msg = senderMessage,
|
||||
|
||||
+16
-13
@@ -30,13 +30,21 @@ import com.vitorpamplona.quartz.nip01Core.signers.EventTemplate
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerSync
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag
|
||||
import com.vitorpamplona.quartz.nip13Pow.miner.PoWMiner
|
||||
import com.vitorpamplona.quartz.nip21UriScheme.toNostrUri
|
||||
import com.vitorpamplona.quartz.nip40Expiration.ExpirationTag
|
||||
import com.vitorpamplona.quartz.nip59Giftwrap.HasInnerEvent
|
||||
import com.vitorpamplona.quartz.utils.Log
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
|
||||
/**
|
||||
* Caller hook to adjust the finished wrap template right before the ephemeral
|
||||
* key signs it — e.g. mining a NIP-13 proof of work into it. Receives the
|
||||
* ephemeral key's pubkey because the NIP-01 id (what a nonce commits to)
|
||||
* includes it, and the key never leaves [GiftWrapEvent.create]. Must return a
|
||||
* template of the same kind; the default is the identity.
|
||||
*/
|
||||
typealias GiftWrapTemplateConversion = (template: EventTemplate<GiftWrapEvent>, ephemeralPubKey: HexKey) -> EventTemplate<GiftWrapEvent>
|
||||
|
||||
@Immutable
|
||||
open class GiftWrapEvent(
|
||||
id: HexKey,
|
||||
@@ -109,10 +117,11 @@ open class GiftWrapEvent(
|
||||
* [recipientRelayHint] — `null` (the default) preserves the
|
||||
* historical 2-element `["p", pubkey]` shape.
|
||||
*
|
||||
* [powDifficulty] mines a NIP-13 proof of work into the wrap itself
|
||||
* (the ephemeral-key envelope, never the inner seal or rumor) so DM
|
||||
* relays can PoW-filter inbox spam. [powIsActive] is the cooperative
|
||||
* cancellation hook forwarded to the miner.
|
||||
* [templateConversion] runs on the finished template right before the
|
||||
* ephemeral key signs it. This is how a caller mines a NIP-13 proof
|
||||
* of work into the wrap itself (the ephemeral-key envelope, never the
|
||||
* inner seal or rumor) so DM relays can PoW-filter inbox spam —
|
||||
* without this NIP-59 code knowing anything about mining.
|
||||
*/
|
||||
fun create(
|
||||
event: Event,
|
||||
@@ -120,8 +129,7 @@ open class GiftWrapEvent(
|
||||
expirationDelta: Long? = null,
|
||||
createdAt: Long = TimeUtils.randomWithTwoDays(),
|
||||
recipientRelayHint: NormalizedRelayUrl? = null,
|
||||
powDifficulty: Int? = null,
|
||||
powIsActive: () -> Boolean = { true },
|
||||
templateConversion: GiftWrapTemplateConversion = { template, _ -> template },
|
||||
): GiftWrapEvent {
|
||||
val signer = NostrSignerSync(KeyPair()) // GiftWrap is always a random key
|
||||
|
||||
@@ -145,12 +153,7 @@ open class GiftWrapEvent(
|
||||
content = signer.nip44Encrypt(event.toJson(), recipientPubKey),
|
||||
)
|
||||
|
||||
val readyToSign =
|
||||
if (powDifficulty != null && powDifficulty > 0) {
|
||||
PoWMiner.run(template, signer.pubKey, powDifficulty, powIsActive)
|
||||
} else {
|
||||
template
|
||||
}
|
||||
val readyToSign = templateConversion(template, signer.pubKey)
|
||||
|
||||
return signer.sign(
|
||||
createdAt = readyToSign.createdAt,
|
||||
|
||||
Reference in New Issue
Block a user