22 changed files with 425 additions and 63 deletions
@ -0,0 +1,180 @@ |
|||||||
|
package app |
||||||
|
|
||||||
|
import ( |
||||||
|
"fmt" |
||||||
|
|
||||||
|
database "database.orly" |
||||||
|
"database.orly/indexes/types" |
||||||
|
"encoders.orly/envelopes/eventenvelope" |
||||||
|
"encoders.orly/event" |
||||||
|
"encoders.orly/filter" |
||||||
|
"encoders.orly/hex" |
||||||
|
"encoders.orly/ints" |
||||||
|
"encoders.orly/kind" |
||||||
|
"encoders.orly/tag" |
||||||
|
"encoders.orly/tag/atag" |
||||||
|
"lol.mleku.dev/chk" |
||||||
|
"lol.mleku.dev/log" |
||||||
|
utils "utils.orly" |
||||||
|
) |
||||||
|
|
||||||
|
func (l *Listener) GetSerialsFromFilter(f *filter.F) ( |
||||||
|
sers types.Uint40s, err error, |
||||||
|
) { |
||||||
|
var idxs []database.Range |
||||||
|
if idxs, err = database.GetIndexesFromFilter(f); chk.E(err) { |
||||||
|
return |
||||||
|
} |
||||||
|
for _, idx := range idxs { |
||||||
|
var s types.Uint40s |
||||||
|
if s, err = l.GetSerialsByRange(idx); chk.E(err) { |
||||||
|
continue |
||||||
|
} |
||||||
|
sers = append(sers, s...) |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func (l *Listener) HandleDelete(env *eventenvelope.Submission) { |
||||||
|
log.T.C( |
||||||
|
func() string { |
||||||
|
return fmt.Sprintf( |
||||||
|
"delete event\n%s", env.E.Serialize(), |
||||||
|
) |
||||||
|
}, |
||||||
|
) |
||||||
|
var ownerDelete bool |
||||||
|
for _, pk := range l.Admins { |
||||||
|
if utils.FastEqual(pk, env.E.Pubkey) { |
||||||
|
ownerDelete = true |
||||||
|
break |
||||||
|
} |
||||||
|
} |
||||||
|
// process the tags in the delete event
|
||||||
|
var err error |
||||||
|
for _, t := range *env.E.Tags { |
||||||
|
// first search for a tags, as these are the simplest to process
|
||||||
|
if utils.FastEqual(t.Key(), []byte("a")) { |
||||||
|
at := new(atag.T) |
||||||
|
if _, err = at.Unmarshal(t.Value()); chk.E(err) { |
||||||
|
continue |
||||||
|
} |
||||||
|
if ownerDelete || utils.FastEqual(env.E.Pubkey, at.Pubkey) { |
||||||
|
// find the event and delete it
|
||||||
|
f := &filter.F{ |
||||||
|
Authors: tag.NewFromBytesSlice(at.Pubkey), |
||||||
|
Kinds: kind.NewS(at.Kind), |
||||||
|
} |
||||||
|
if len(at.DTag) > 0 { |
||||||
|
f.Tags = tag.NewS( |
||||||
|
tag.NewFromAny("d", at.DTag), |
||||||
|
) |
||||||
|
} |
||||||
|
var sers types.Uint40s |
||||||
|
if sers, err = l.GetSerialsFromFilter(f); chk.E(err) { |
||||||
|
continue |
||||||
|
} |
||||||
|
// if found, delete them
|
||||||
|
if len(sers) > 0 { |
||||||
|
for _, s := range sers { |
||||||
|
var ev *event.E |
||||||
|
if ev, err = l.FetchEventBySerial(s); chk.E(err) { |
||||||
|
continue |
||||||
|
} |
||||||
|
if !(kind.IsReplaceable(ev.Kind) && len(at.DTag) == 0) { |
||||||
|
// skip a tags with no dtag if the kind is not
|
||||||
|
// replaceable.
|
||||||
|
continue |
||||||
|
} |
||||||
|
if err = l.DeleteEventBySerial( |
||||||
|
l.Ctx, s, ev, |
||||||
|
); chk.E(err) { |
||||||
|
continue |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
continue |
||||||
|
} |
||||||
|
// if e tags are found, delete them if the author is signer, or one of
|
||||||
|
// the owners is signer
|
||||||
|
if utils.FastEqual(t.Key(), []byte("e")) { |
||||||
|
var dst []byte |
||||||
|
if _, err = hex.DecBytes(dst, t.Value()); chk.E(err) { |
||||||
|
continue |
||||||
|
} |
||||||
|
f := &filter.F{ |
||||||
|
Ids: tag.NewFromBytesSlice(dst), |
||||||
|
} |
||||||
|
var sers types.Uint40s |
||||||
|
if sers, err = l.GetSerialsFromFilter(f); chk.E(err) { |
||||||
|
continue |
||||||
|
} |
||||||
|
// if found, delete them
|
||||||
|
if len(sers) > 0 { |
||||||
|
// there should be only one event per serial, so we can just
|
||||||
|
// delete them all
|
||||||
|
for _, s := range sers { |
||||||
|
var ev *event.E |
||||||
|
if ev, err = l.FetchEventBySerial(s); chk.E(err) { |
||||||
|
continue |
||||||
|
} |
||||||
|
// check that the author is the same as the signer of the
|
||||||
|
// delete, for the k tag case the author is the signer of
|
||||||
|
// the event.
|
||||||
|
if !utils.FastEqual(env.E.Pubkey, ev.Pubkey) { |
||||||
|
continue |
||||||
|
} |
||||||
|
// exclude delete events
|
||||||
|
if ev.Kind == kind.EventDeletion.K { |
||||||
|
continue |
||||||
|
} |
||||||
|
if err = l.DeleteEventBySerial(l.Ctx, s, ev); chk.E(err) { |
||||||
|
continue |
||||||
|
} |
||||||
|
} |
||||||
|
continue |
||||||
|
} |
||||||
|
} |
||||||
|
// if k tags are found, check they are replaceable
|
||||||
|
if utils.FastEqual(t.Key(), []byte("k")) { |
||||||
|
ki := ints.New(0) |
||||||
|
if _, err = ki.Unmarshal(t.Value()); chk.E(err) { |
||||||
|
continue |
||||||
|
} |
||||||
|
kn := ki.Uint16() |
||||||
|
// skip events that are delete events or that are not replaceable
|
||||||
|
if !kind.IsReplaceable(kn) || kn != kind.EventDeletion.K { |
||||||
|
continue |
||||||
|
} |
||||||
|
f := &filter.F{ |
||||||
|
Authors: tag.NewFromBytesSlice(env.E.Pubkey), |
||||||
|
Kinds: kind.NewS(kind.New(kn)), |
||||||
|
} |
||||||
|
var sers types.Uint40s |
||||||
|
if sers, err = l.GetSerialsFromFilter(f); chk.E(err) { |
||||||
|
continue |
||||||
|
} |
||||||
|
// if found, delete them
|
||||||
|
if len(sers) > 0 { |
||||||
|
// there should be only one event per serial because replaces
|
||||||
|
// delete old ones, so we can just delete them all
|
||||||
|
for _, s := range sers { |
||||||
|
var ev *event.E |
||||||
|
if ev, err = l.FetchEventBySerial(s); chk.E(err) { |
||||||
|
continue |
||||||
|
} |
||||||
|
// check that the author is the same as the signer of the
|
||||||
|
// delete, for the k tag case the author is the signer of
|
||||||
|
// the event.
|
||||||
|
if !utils.FastEqual(env.E.Pubkey, ev.Pubkey) { |
||||||
|
continue |
||||||
|
} |
||||||
|
} |
||||||
|
continue |
||||||
|
} |
||||||
|
} |
||||||
|
continue |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
@ -0,0 +1,143 @@ |
|||||||
|
package main |
||||||
|
|
||||||
|
import ( |
||||||
|
"flag" |
||||||
|
"fmt" |
||||||
|
"os" |
||||||
|
"strings" |
||||||
|
|
||||||
|
"crypto.orly/ec/schnorr" |
||||||
|
"crypto.orly/ec/secp256k1" |
||||||
|
b32 "encoders.orly/bech32encoding" |
||||||
|
"encoders.orly/hex" |
||||||
|
) |
||||||
|
|
||||||
|
func usage() { |
||||||
|
fmt.Fprintf(os.Stderr, "Usage: convert [--secret] <key>\n") |
||||||
|
fmt.Fprintf( |
||||||
|
os.Stderr, " <key> can be hex (64 chars) or bech32 (npub/nsec).\n", |
||||||
|
) |
||||||
|
fmt.Fprintf( |
||||||
|
os.Stderr, |
||||||
|
" --secret: interpret input key as a secret key; print both nsec and npub in hex and bech32.\n"+ |
||||||
|
" --secret is implied if <key> starts with nsec.\n", |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
func main() { |
||||||
|
var isSecret bool |
||||||
|
flag.BoolVar( |
||||||
|
&isSecret, "secret", false, "interpret the input as a secret key", |
||||||
|
) |
||||||
|
flag.Parse() |
||||||
|
|
||||||
|
if flag.NArg() < 1 { |
||||||
|
usage() |
||||||
|
os.Exit(2) |
||||||
|
} |
||||||
|
|
||||||
|
input := strings.TrimSpace(flag.Arg(0)) |
||||||
|
|
||||||
|
// Auto-detect secret if input starts with nsec
|
||||||
|
if strings.HasPrefix(input, string(b32.SecHRP)) { |
||||||
|
isSecret = true |
||||||
|
} |
||||||
|
|
||||||
|
if isSecret { |
||||||
|
if err := handleSecret(input); err != nil { |
||||||
|
fmt.Fprintf(os.Stderr, "error: %v\n", err) |
||||||
|
os.Exit(1) |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
if err := handlePublic(input); err != nil { |
||||||
|
fmt.Fprintf(os.Stderr, "error: %v\n", err) |
||||||
|
os.Exit(1) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func handleSecret(input string) error { |
||||||
|
// Accept nsec bech32 or 64-char hex as secret key
|
||||||
|
var sk *secp256k1.SecretKey |
||||||
|
var err error |
||||||
|
|
||||||
|
if strings.HasPrefix(input, string(b32.SecHRP)) { // nsec...
|
||||||
|
if sk, err = b32.NsecToSecretKey([]byte(input)); err != nil { |
||||||
|
return fmt.Errorf("failed to decode nsec: %w", err) |
||||||
|
} |
||||||
|
} else { |
||||||
|
// Expect hex
|
||||||
|
if len(input) != b32.HexKeyLen { |
||||||
|
return fmt.Errorf("secret key hex must be %d chars", b32.HexKeyLen) |
||||||
|
} |
||||||
|
var b []byte |
||||||
|
if b, err = hex.Dec(input); err != nil { |
||||||
|
return fmt.Errorf("invalid secret hex: %w", err) |
||||||
|
} |
||||||
|
sk = secp256k1.SecKeyFromBytes(b) |
||||||
|
} |
||||||
|
|
||||||
|
// Prepare outputs for secret
|
||||||
|
nsec, err := b32.SecretKeyToNsec(sk) |
||||||
|
if err != nil { |
||||||
|
return fmt.Errorf("encode nsec: %w", err) |
||||||
|
} |
||||||
|
secHex := hex.EncAppend(nil, sk.Serialize()) |
||||||
|
|
||||||
|
// Derive public key
|
||||||
|
pk := sk.PubKey() |
||||||
|
npub, err := b32.PublicKeyToNpub(pk) |
||||||
|
if err != nil { |
||||||
|
return fmt.Errorf("encode npub: %w", err) |
||||||
|
} |
||||||
|
pkBytes := schnorr.SerializePubKey(pk) |
||||||
|
pkHex := hex.EncAppend(nil, pkBytes) |
||||||
|
|
||||||
|
// Print results
|
||||||
|
fmt.Printf("nsec (hex): %s\n", string(secHex)) |
||||||
|
fmt.Printf("nsec (bech32): %s\n", string(nsec)) |
||||||
|
fmt.Printf("npub (hex): %s\n", string(pkHex)) |
||||||
|
fmt.Printf("npub (bech32): %s\n", string(npub)) |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func handlePublic(input string) error { |
||||||
|
// Accept npub bech32, nsec bech32 (derive pub), or 64-char hex pubkey
|
||||||
|
var pubBytes []byte |
||||||
|
var err error |
||||||
|
|
||||||
|
if strings.HasPrefix(input, string(b32.PubHRP)) { // npub...
|
||||||
|
if pubBytes, err = b32.NpubToBytes([]byte(input)); err != nil { |
||||||
|
return fmt.Errorf("failed to decode npub: %w", err) |
||||||
|
} |
||||||
|
} else if strings.HasPrefix( |
||||||
|
input, string(b32.SecHRP), |
||||||
|
) { // nsec without --secret: show pub only
|
||||||
|
var sk *secp256k1.SecretKey |
||||||
|
if sk, err = b32.NsecToSecretKey([]byte(input)); err != nil { |
||||||
|
return fmt.Errorf("failed to decode nsec: %w", err) |
||||||
|
} |
||||||
|
pubBytes = schnorr.SerializePubKey(sk.PubKey()) |
||||||
|
} else { |
||||||
|
// Expect hex pubkey
|
||||||
|
if len(input) != b32.HexKeyLen { |
||||||
|
return fmt.Errorf("public key hex must be %d chars", b32.HexKeyLen) |
||||||
|
} |
||||||
|
if pubBytes, err = hex.Dec(input); err != nil { |
||||||
|
return fmt.Errorf("invalid public hex: %w", err) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Compute encodings
|
||||||
|
npub, err := b32.BinToNpub(pubBytes) |
||||||
|
if err != nil { |
||||||
|
return fmt.Errorf("encode npub: %w", err) |
||||||
|
} |
||||||
|
pubHex := hex.EncAppend(nil, pubBytes) |
||||||
|
|
||||||
|
// Print only pubkey representations
|
||||||
|
fmt.Printf("npub (hex): %s\n", string(pubHex)) |
||||||
|
fmt.Printf("npub (bech32): %s\n", string(npub)) |
||||||
|
return nil |
||||||
|
} |
||||||
@ -0,0 +1,5 @@ |
|||||||
|
package constraints |
||||||
|
|
||||||
|
type Bytes interface { |
||||||
|
~string | ~[]byte |
||||||
|
} |
||||||
Loading…
Reference in new issue