Skip to content

Commit

Permalink
mint - test swap and melt
Browse files Browse the repository at this point in the history
  • Loading branch information
elnosh committed May 22, 2024
1 parent e5ecd74 commit e958977
Show file tree
Hide file tree
Showing 3 changed files with 216 additions and 22 deletions.
10 changes: 5 additions & 5 deletions mint/mint.go
Original file line number Diff line number Diff line change
Expand Up @@ -314,18 +314,18 @@ func (m *Mint) MeltTokens(method, quoteId string, proofs cashu.Proofs) (MeltQuot
return MeltQuote{}, cashu.MeltQuoteNotExistErr
}

valid, err := m.VerifyProofs(proofs)
if err != nil || !valid {
return MeltQuote{}, cashu.BuildCashuError(err.Error(), cashu.StandardErrCode)
}

proofsAmount := proofs.Amount()

// checks if amount in proofs is enough
if proofsAmount < meltQuote.Amount+meltQuote.FeeReserve {
return MeltQuote{}, cashu.InsufficientProofsAmount
}

valid, err := m.VerifyProofs(proofs)
if err != nil || !valid {
return MeltQuote{}, err
}

// if proofs are valid, ask the lightning backend
// to make the payment
preimage, err := m.LightningClient.SendPayment(meltQuote.InvoiceRequest)
Expand Down
147 changes: 131 additions & 16 deletions mint/mint_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,6 @@ import (
"github.com/lightningnetwork/lnd/lnrpc"
)

const (
BOLT11_METHOD = "bolt11"
SAT_UNIT = "sat"
)

var (
ctx context.Context
bitcoind *btcdocker.Bitcoind
Expand Down Expand Up @@ -97,27 +92,27 @@ func testMain(m *testing.M) int {

func TestRequestMintQuote(t *testing.T) {
var mintAmount uint64 = 10000
_, err := testMint.RequestMintQuote(BOLT11_METHOD, mintAmount, SAT_UNIT)
_, err := testMint.RequestMintQuote(testutils.BOLT11_METHOD, mintAmount, testutils.SAT_UNIT)
if err != nil {
t.Fatalf("error requesting mint quote: %v", err)
}

// test invalid method
_, err = testMint.RequestMintQuote("strike", mintAmount, SAT_UNIT)
_, err = testMint.RequestMintQuote("strike", mintAmount, testutils.SAT_UNIT)
if !errors.Is(err, cashu.PaymentMethodNotSupportedErr) {
t.Fatalf("expected error '%v' but got '%v' instead", cashu.PaymentMethodNotSupportedErr, err)
}

// test invalid unit
_, err = testMint.RequestMintQuote(BOLT11_METHOD, mintAmount, "eth")
_, err = testMint.RequestMintQuote(testutils.BOLT11_METHOD, mintAmount, "eth")
if !errors.Is(err, cashu.UnitNotSupportedErr) {
t.Fatalf("expected error '%v' but got '%v' instead", cashu.UnitNotSupportedErr, err)
}
}

func TestMintTokens(t *testing.T) {
var mintAmount uint64 = 42000
mintQuoteResponse, err := testMint.RequestMintQuote(BOLT11_METHOD, mintAmount, SAT_UNIT)
mintQuoteResponse, err := testMint.RequestMintQuote(testutils.BOLT11_METHOD, mintAmount, testutils.SAT_UNIT)
if err != nil {
t.Fatalf("error requesting mint quote: %v", err)
}
Expand All @@ -131,13 +126,13 @@ func TestMintTokens(t *testing.T) {
blindedMessages, _, _, err := testutils.CreateBlindedMessages(mintAmount, keyset)

// test without paying invoice
_, err = testMint.MintTokens(BOLT11_METHOD, mintQuoteResponse.Quote, blindedMessages)
_, err = testMint.MintTokens(testutils.BOLT11_METHOD, mintQuoteResponse.Quote, blindedMessages)
if !errors.Is(err, cashu.InvoiceNotPaidErr) {
t.Fatalf("expected error '%v' but got '%v' instead", cashu.InvoiceNotPaidErr, err)
}

// test invalid quote
_, err = testMint.MintTokens(BOLT11_METHOD, "mintquote1234", blindedMessages)
_, err = testMint.MintTokens(testutils.BOLT11_METHOD, "mintquote1234", blindedMessages)
if !errors.Is(err, cashu.InvoiceNotExistErr) {
t.Fatalf("expected error '%v' but got '%v' instead", cashu.InvoiceNotExistErr, err)
}
Expand All @@ -153,27 +148,147 @@ func TestMintTokens(t *testing.T) {

// test with blinded messages over request mint amount
overBlindedMessages, _, _, err := testutils.CreateBlindedMessages(mintAmount+100, keyset)
_, err = testMint.MintTokens(BOLT11_METHOD, mintQuoteResponse.Quote, overBlindedMessages)
_, err = testMint.MintTokens(testutils.BOLT11_METHOD, mintQuoteResponse.Quote, overBlindedMessages)
if !errors.Is(err, cashu.OutputsOverInvoiceErr) {
t.Fatalf("expected error '%v' but got '%v' instead", cashu.OutputsOverInvoiceErr, err)
}

// test with invalid keyset in blinded messages
invalidKeyset := crypto.GenerateKeyset("seed", "path")
invalidKeysetMessages, _, _, err := testutils.CreateBlindedMessages(mintAmount, *invalidKeyset)
_, err = testMint.MintTokens(BOLT11_METHOD, mintQuoteResponse.Quote, invalidKeysetMessages)
_, err = testMint.MintTokens(testutils.BOLT11_METHOD, mintQuoteResponse.Quote, invalidKeysetMessages)
if !errors.Is(err, cashu.InvalidSignatureRequest) {
t.Fatalf("expected error '%v' but got '%v' instead", cashu.InvalidSignatureRequest, err)
}

_, err = testMint.MintTokens(BOLT11_METHOD, mintQuoteResponse.Quote, blindedMessages)
_, err = testMint.MintTokens(testutils.BOLT11_METHOD, mintQuoteResponse.Quote, blindedMessages)
if err != nil {
t.Fatalf("got unexpected error: %v", err)
t.Fatalf("got unexpected error minting tokens: %v", err)
}

// test already minted tokens
_, err = testMint.MintTokens(BOLT11_METHOD, mintQuoteResponse.Quote, blindedMessages)
_, err = testMint.MintTokens(testutils.BOLT11_METHOD, mintQuoteResponse.Quote, blindedMessages)
if !errors.Is(err, cashu.InvoiceTokensIssuedErr) {
t.Fatalf("expected error '%v' but got '%v' instead", cashu.InvoiceTokensIssuedErr, err)
}
}

func TestSwap(t *testing.T) {
var amount uint64 = 10000
proofs, err := testutils.GetValidProofsForAmount(amount, testMint, lnd2)
if err != nil {
t.Fatalf("error generating valid proofs: %v", err)
}

var keyset crypto.Keyset
for _, k := range testMint.ActiveKeysets {
keyset = k
break
}

newBlindedMessages, _, _, err := testutils.CreateBlindedMessages(amount, keyset)
overBlindedMessages, _, _, err := testutils.CreateBlindedMessages(amount+200, keyset)

// test blinded messages over proofs amount
_, err = testMint.Swap(proofs, overBlindedMessages)
if !errors.Is(err, cashu.InputsBelowOutputs) {
t.Fatalf("expected error '%v' but got '%v' instead", cashu.OutputsOverInvoiceErr, err)
}

_, err = testMint.Swap(proofs, newBlindedMessages)
if err != nil {
t.Fatalf("got unexpected error in swap: %v", err)
}

// test already used proofs
_, err = testMint.Swap(proofs, newBlindedMessages)
if !errors.Is(err, cashu.ProofAlreadyUsedErr) {
t.Fatalf("expected error '%v' but got '%v' instead", cashu.ProofAlreadyUsedErr, err)
}
}

func TestMeltRequest(t *testing.T) {
invoice := lnrpc.Invoice{Value: 10000}
addInvoiceResponse, err := lnd2.Client.AddInvoice(ctx, &invoice)
if err != nil {
t.Fatalf("error creating invoice: %v", err)
}

// test invalid method
_, err = testMint.MeltRequest("strike", addInvoiceResponse.PaymentRequest, testutils.SAT_UNIT)
if !errors.Is(err, cashu.PaymentMethodNotSupportedErr) {
t.Fatalf("expected error '%v' but got '%v' instead", cashu.PaymentMethodNotSupportedErr, err)
}

// test invalid unit
_, err = testMint.MeltRequest(testutils.BOLT11_METHOD, addInvoiceResponse.PaymentRequest, "eth")
if !errors.Is(err, cashu.UnitNotSupportedErr) {
t.Fatalf("expected error '%v' but got '%v' instead", cashu.UnitNotSupportedErr, err)
}

// test invalid invoice
_, err = testMint.MeltRequest(testutils.BOLT11_METHOD, "invoice1111", testutils.SAT_UNIT)
if err == nil {
t.Fatal("expected error but got nil")
}

_, err = testMint.MeltRequest(testutils.BOLT11_METHOD, addInvoiceResponse.PaymentRequest, testutils.SAT_UNIT)
if err != nil {
t.Fatalf("got unexpected error in melt request: %v", err)
}

}

func TestMelt(t *testing.T) {
var amount uint64 = 1000
underProofs, err := testutils.GetValidProofsForAmount(amount, testMint, lnd2)
if err != nil {
t.Fatalf("error generating valid proofs: %v", err)
}

invoice := lnrpc.Invoice{Value: 6000}
addInvoiceResponse, err := lnd2.Client.AddInvoice(ctx, &invoice)
if err != nil {
t.Fatalf("error creating invoice: %v", err)
}

meltQuote, err := testMint.MeltRequest(testutils.BOLT11_METHOD, addInvoiceResponse.PaymentRequest, testutils.SAT_UNIT)
if err != nil {
t.Fatalf("got unexpected error in melt request: %v", err)
}

// test proofs amount under melt amount
_, err = testMint.MeltTokens(testutils.BOLT11_METHOD, meltQuote.Id, underProofs)
if !errors.Is(err, cashu.InsufficientProofsAmount) {
t.Fatalf("expected error '%v' but got '%v' instead", cashu.PaymentMethodNotSupportedErr, err)
}

validProofs, err := testutils.GetValidProofsForAmount(6500, testMint, lnd2)
if err != nil {
t.Fatalf("error generating valid proofs: %v", err)
}
validSecret := validProofs[0].Secret

// test invalid proofs
validProofs[0].Secret = "some invalid secret"
_, err = testMint.MeltTokens(testutils.BOLT11_METHOD, meltQuote.Id, validProofs)
if !errors.Is(err, cashu.InvalidProofErr) {
t.Fatalf("expected error '%v' but got '%v' instead", cashu.InvalidProofErr, err)
}

validProofs[0].Secret = validSecret
melt, err := testMint.MeltTokens(testutils.BOLT11_METHOD, meltQuote.Id, validProofs)
if err != nil {
t.Fatalf("got unexpected error in melt: %v", err)
}
if !melt.Paid {
t.Fatal("got unexpected unpaid melt quote")
}

// test already used proofs
_, err = testMint.MeltTokens(testutils.BOLT11_METHOD, meltQuote.Id, validProofs)
if !errors.Is(err, cashu.ProofAlreadyUsedErr) {
t.Fatalf("expected error '%v' but got '%v' instead", cashu.ProofAlreadyUsedErr, err)
}

}
81 changes: 80 additions & 1 deletion testutils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@ import (
"github.com/lightningnetwork/lnd/lnrpc"
)

const NUM_BLOCKS int64 = 110
const (
NUM_BLOCKS int64 = 110
BOLT11_METHOD = "bolt11"
SAT_UNIT = "sat"
)

func MineBlocks(bitcoind *btcdocker.Bitcoind, numBlocks int64) error {
address, err := bitcoind.Client.GetNewAddress("")
Expand Down Expand Up @@ -248,3 +252,78 @@ func CreateBlindedMessages(amount uint64, keyset crypto.Keyset) (cashu.BlindedMe

return blindedMessages, secrets, rs, nil
}

func ConstructProofs(blindedSignatures cashu.BlindedSignatures,
secrets []string, rs []*secp256k1.PrivateKey, keyset *crypto.Keyset) (cashu.Proofs, error) {

if len(blindedSignatures) != len(secrets) || len(blindedSignatures) != len(rs) {
return nil, errors.New("lengths do not match")
}

proofs := make(cashu.Proofs, len(blindedSignatures))
for i, blindedSignature := range blindedSignatures {
C_bytes, err := hex.DecodeString(blindedSignature.C_)
if err != nil {
return nil, err
}
C_, err := secp256k1.ParsePubKey(C_bytes)
if err != nil {
return nil, err
}

keyp, ok := keyset.Keys[blindedSignature.Amount]
if !ok {
return nil, errors.New("key not found")
}

C := crypto.UnblindSignature(C_, rs[i], keyp.PublicKey)
Cstr := hex.EncodeToString(C.SerializeCompressed())

proof := cashu.Proof{Amount: blindedSignature.Amount,
Secret: secrets[i], C: Cstr, Id: blindedSignature.Id}

proofs[i] = proof
}

return proofs, nil
}

func GetValidProofsForAmount(amount uint64, mint *mint.Mint, payer *btcdocker.Lnd) (cashu.Proofs, error) {
mintQuoteResponse, err := mint.RequestMintQuote(BOLT11_METHOD, amount, SAT_UNIT)
if err != nil {
return nil, fmt.Errorf("error requesting mint quote: %v", err)
}

var keyset crypto.Keyset
for _, k := range mint.ActiveKeysets {
keyset = k
break
}

blindedMessages, secrets, rs, err := CreateBlindedMessages(amount, keyset)
if err != nil {
return nil, fmt.Errorf("error creating blinded message: %v", err)
}

ctx := context.Background()
//pay invoice
sendPaymentRequest := lnrpc.SendRequest{
PaymentRequest: mintQuoteResponse.Request,
}
response, _ := payer.Client.SendPaymentSync(ctx, &sendPaymentRequest)
if len(response.PaymentError) > 0 {
return nil, fmt.Errorf("error paying invoice: %v", response.PaymentError)
}

blindedSignatures, err := mint.MintTokens(BOLT11_METHOD, mintQuoteResponse.Quote, blindedMessages)
if err != nil {
return nil, fmt.Errorf("got unexpected error minting tokens: %v", err)
}

proofs, err := ConstructProofs(blindedSignatures, secrets, rs, &keyset)
if err != nil {
return nil, fmt.Errorf("error constructing proofs: %v", err)
}

return proofs, nil
}

0 comments on commit e958977

Please sign in to comment.