Skip to content

Commit

Permalink
wallet - lock ecash to a public key
Browse files Browse the repository at this point in the history
  • Loading branch information
elnosh committed Jul 9, 2024
1 parent abd55b0 commit f6dd3ca
Show file tree
Hide file tree
Showing 6 changed files with 307 additions and 37 deletions.
28 changes: 28 additions & 0 deletions cashu/cashu.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ import (
"fmt"
)

type SecretKind int

const (
Random SecretKind = iota
P2PK
)

// Cashu BlindedMessage. See https://github.com/cashubtc/nuts/blob/main/00.md#blindedmessage
type BlindedMessage struct {
Amount uint64 `json:"amount"`
Expand Down Expand Up @@ -51,6 +58,27 @@ type Proof struct {
Witness string `json:"witness,omitempty"`
}

// TODO
func (p Proof) IsSecretP2PK() bool {
return false
}

// TODO
func (p Proof) SecretType() SecretKind {
return Random
}

func (kind SecretKind) String() string {
switch kind {
case Random:
return "random"
case P2PK:
return "P2PK"
default:
return "unknown"
}
}

type Proofs []Proof

// Amount returns the total amount from
Expand Down
27 changes: 27 additions & 0 deletions cashu/nuts/nut10/nut10.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package nut10

import (
"encoding/json"
"fmt"

"github.com/elnosh/gonuts/cashu"
)

type WellKnownSecret struct {
Nonce string `json:"nonce"`
Data string `json:"data"`
Tags [][]string `json:"tags"`
}

// SerializeSecret returns the json string to be put in the secret field of a proof
func SerializeSecret(kind cashu.SecretKind, secretData WellKnownSecret) (string, error) {
jsonSecret, err := json.Marshal(secretData)
if err != nil {
return "", err
}

secretKind := kind.String()
secret := fmt.Sprintf("[\"%s\", %v]", secretKind, string(jsonSecret))

return secret, nil
}
37 changes: 37 additions & 0 deletions cashu/nuts/nut11/nut11.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package nut11

import (
"crypto/rand"
"encoding/hex"

"github.com/elnosh/gonuts/cashu"
"github.com/elnosh/gonuts/cashu/nuts/nut10"
)

type P2PKWitness struct {
Signatures []string `json:"signatures"`
}

// P2PKSecret returns a secret with a spending condition
// that will lock ecash to a public key
func P2PKSecret(pubkey string) (string, error) {
// generate random nonce
nonceBytes := make([]byte, 32)
_, err := rand.Read(nonceBytes)
if err != nil {
return "", err
}
nonce := hex.EncodeToString(nonceBytes)

secretData := nut10.WellKnownSecret{
Nonce: nonce,
Data: pubkey,
}

secret, err := nut10.SerializeSecret(cashu.P2PK, secretData)
if err != nil {
return "", err
}

return secret, nil
}
35 changes: 32 additions & 3 deletions cmd/nutw/nutw.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"bufio"
"encoding/hex"
"errors"
"fmt"
"log"
Expand All @@ -12,6 +13,7 @@ import (
"strconv"
"strings"

"github.com/btcsuite/btcd/btcec/v2"
"github.com/elnosh/gonuts/cashu"
"github.com/elnosh/gonuts/wallet"
"github.com/joho/godotenv"
Expand Down Expand Up @@ -259,9 +261,17 @@ func mintTokens(paymentRequest string) error {
return nil
}

const lockFlag = "lock"

var sendCmd = &cli.Command{
Name: "send",
Before: setupWallet,
Flags: []cli.Flag{
&cli.StringFlag{
Name: lockFlag,
Usage: "generate ecash locked to a public key",
},
},
Action: send,
}

Expand All @@ -278,9 +288,28 @@ func send(ctx *cli.Context) error {

selectedMint := promptMintSelection("send")

token, err := nutw.Send(sendAmount, selectedMint)
if err != nil {
printErr(err)
var token *cashu.Token
// if lock flag is set, get ecash locked to the pubkey
if ctx.IsSet(lockFlag) {
lockpubkey := ctx.String(lockFlag)
lockbytes, err := hex.DecodeString(lockpubkey)
if err != nil {
printErr(err)
}
pubkey, err := btcec.ParsePubKey(lockbytes)
if err != nil {
printErr(err)
}

token, err = nutw.SendToPubkey(sendAmount, selectedMint, pubkey)
if err != nil {
printErr(err)
}
} else {
token, err = nutw.Send(sendAmount, selectedMint)
if err != nil {
printErr(err)
}
}

fmt.Printf("%v\n", token.ToString())
Expand Down
40 changes: 40 additions & 0 deletions wallet/p2pk.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package wallet

import (
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcutil/hdkeychain"
)

// Derive key that wallet will use to receive locked ecash
func DeriveP2PK(key *hdkeychain.ExtendedKey) (*btcec.PrivateKey, error) {
// m/129372
purpose, err := key.Derive(hdkeychain.HardenedKeyStart + 129372)
if err != nil {
return nil, err
}

// m/129372'/0'
coinType, err := purpose.Derive(hdkeychain.HardenedKeyStart + 0)
if err != nil {
return nil, err
}

// m/129372'/0'/1'
first, err := coinType.Derive(hdkeychain.HardenedKeyStart + 1)
if err != nil {
return nil, err
}

// m/129372'/0'/1'/0
extKey, err := first.Derive(0)
if err != nil {
return nil, err
}

pk, err := extKey.ECPrivKey()
if err != nil {
return nil, err
}

return pk, nil
}
Loading

0 comments on commit f6dd3ca

Please sign in to comment.