Skip to content

Commit

Permalink
include witness in response from /v1/checkstate
Browse files Browse the repository at this point in the history
  • Loading branch information
elnosh committed Nov 7, 2024
1 parent 22ad7df commit 81bcd05
Show file tree
Hide file tree
Showing 7 changed files with 129 additions and 56 deletions.
5 changes: 3 additions & 2 deletions cashu/nuts/nut07/nut07.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,9 @@ type TempProofState struct {

func (state *ProofState) MarshalJSON() ([]byte, error) {
tempProof := TempProofState{
Y: state.Y,
State: state.State.String(),
Y: state.Y,
State: state.State.String(),
Witness: state.Witness,
}
return json.Marshal(tempProof)
}
Expand Down
23 changes: 14 additions & 9 deletions mint/mint.go
Original file line number Diff line number Diff line change
Expand Up @@ -640,10 +640,11 @@ func (m *Mint) removePendingProofsForQuote(quoteId string) (cashu.Proofs, error)
Ys[i] = dbproof.Y

proof := cashu.Proof{
Amount: dbproof.Amount,
Id: dbproof.Id,
Secret: dbproof.Secret,
C: dbproof.C,
Amount: dbproof.Amount,
Id: dbproof.Id,
Secret: dbproof.Secret,
C: dbproof.C,
Witness: dbproof.Witness,
}
proofs[i] = proof
}
Expand Down Expand Up @@ -930,19 +931,23 @@ func (m *Mint) ProofsStateCheck(Ys []string) ([]nut07.ProofState, error) {
for i, y := range Ys {
state := nut07.Unspent

YSpent := slices.ContainsFunc(usedProofs, func(proof storage.DBProof) bool {
YSpentIdx := slices.IndexFunc(usedProofs, func(proof storage.DBProof) bool {
return proof.Y == y
})
YPending := slices.ContainsFunc(pendingProofs, func(proof storage.DBProof) bool {
YPendingIdx := slices.IndexFunc(pendingProofs, func(proof storage.DBProof) bool {
return proof.Y == y
})
if YSpent {

var witness string
if YSpentIdx >= 0 {
state = nut07.Spent
} else if YPending {
witness = usedProofs[YSpentIdx].Witness
} else if YPendingIdx >= 0 {
state = nut07.Pending
witness = pendingProofs[YPendingIdx].Witness
}

proofStates[i] = nut07.ProofState{Y: y, State: state}
proofStates[i] = nut07.ProofState{Y: y, State: state, Witness: witness}
}

return proofStates, nil
Expand Down
112 changes: 77 additions & 35 deletions mint/mint_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -783,55 +783,97 @@ func TestPendingProofs(t *testing.T) {
}

func TestProofsStateCheck(t *testing.T) {
validProofs, err := testutils.GetValidProofsForAmount(5000, testMint, lnd2)
proofs, err := testutils.GetValidProofsForAmount(5000, testMint, lnd2)
if err != nil {
t.Fatalf("error generating valid proofs: %v", err)
}

Ys := make([]string, len(validProofs))
for i, proof := range validProofs {
Y, _ := crypto.HashToCurve([]byte(proof.Secret))
Yhex := hex.EncodeToString(Y.SerializeCompressed())
Ys[i] = Yhex
// proofs with P2PK witness
lock, _ := btcec.NewPrivateKey()
p2pkSpendingCondition := nut10.SpendingCondition{
Kind: nut10.P2PK,
Data: hex.EncodeToString(lock.PubKey().SerializeCompressed()),
}

proofStates, err := testMint.ProofsStateCheck(Ys)
p2pkProofs, err := testutils.GetProofsWithSpendingCondition(2100, p2pkSpendingCondition, testMint, lnd2)
if err != nil {
t.Fatalf("unexpected error checking proof states: %v", err)
}

// proofs should be unspent here
for _, proofState := range proofStates {
if proofState.State != nut07.Unspent {
t.Fatalf("expected proof state '%s' but got '%s'", nut07.Unspent, proofState.State)
}
t.Fatalf("error getting locked proofs: %v", err)
}
p2pkProofs, _ = testutils.AddP2PKWitnessToInputs(p2pkProofs, []*btcec.PrivateKey{lock})

// spend proofs and check spent state in response from mint
proofsToSpend := cashu.Proofs{}
numProofs := len(validProofs) / 2
Ys = make([]string, numProofs)
for i := 0; i < numProofs; i++ {
proofsToSpend = append(proofsToSpend, validProofs[i])
Y, _ := crypto.HashToCurve([]byte(validProofs[i].Secret))
Yhex := hex.EncodeToString(Y.SerializeCompressed())
Ys[i] = Yhex
// proofs with HTLC witness
preimage := "111111"
preimageBytes, _ := hex.DecodeString(preimage)
hashBytes := sha256.Sum256(preimageBytes)
htlcSpendingCondition := nut10.SpendingCondition{
Kind: nut10.HTLC,
Data: hex.EncodeToString(hashBytes[:]),
}

blindedMessages, _, _, _ := testutils.CreateBlindedMessages(proofsToSpend.Amount(), testMint.GetActiveKeyset())
_, err = testMint.Swap(proofsToSpend, blindedMessages)
htlcProofs, err := testutils.GetProofsWithSpendingCondition(2100, htlcSpendingCondition, testMint, lnd2)
if err != nil {
t.Fatalf("unexpected error in swap: %v", err)
t.Fatalf("error getting locked proofs: %v", err)
}
htlcProofs, _ = testutils.AddHTLCWitnessToInputs(htlcProofs, preimage, nil)

proofStates, err = testMint.ProofsStateCheck(Ys)
if err != nil {
t.Fatalf("unexpected error checking proof states: %v", err)
tests := []struct {
proofs cashu.Proofs
}{
{proofs},
{p2pkProofs},
{htlcProofs},
}

for _, proofState := range proofStates {
if proofState.State != nut07.Spent {
t.Fatalf("expected proof state '%s' but got '%s'", nut07.Spent, proofState.State)
for _, test := range tests {
Ys := make([]string, len(test.proofs))
for i, proof := range test.proofs {
Y, _ := crypto.HashToCurve([]byte(proof.Secret))
Yhex := hex.EncodeToString(Y.SerializeCompressed())
Ys[i] = Yhex
}

proofStates, err := testMint.ProofsStateCheck(Ys)
if err != nil {
t.Fatalf("unexpected error checking proof states: %v", err)
}

// proofs should be unspent here
for _, proofState := range proofStates {
if proofState.State != nut07.Unspent {
t.Fatalf("expected proof state '%s' but got '%s'", nut07.Unspent, proofState.State)
}
}

// spend proofs and check spent state in response from mint
proofsToSpend := cashu.Proofs{}
numProofs := len(test.proofs) / 2
Ys = make([]string, numProofs)
for i := 0; i < numProofs; i++ {
proofsToSpend = append(proofsToSpend, test.proofs[i])
Y, _ := crypto.HashToCurve([]byte(test.proofs[i].Secret))
Yhex := hex.EncodeToString(Y.SerializeCompressed())
Ys[i] = Yhex
}

blindedMessages, _, _, _ := testutils.CreateBlindedMessages(proofsToSpend.Amount(), testMint.GetActiveKeyset())
_, err = testMint.Swap(proofsToSpend, blindedMessages)
if err != nil {
t.Fatalf("unexpected error in swap: %v", err)
}

proofStates, err = testMint.ProofsStateCheck(Ys)
if err != nil {
t.Fatalf("unexpected error checking proof states: %v", err)
}

for i, proofState := range proofStates {
if proofState.State != nut07.Spent {
t.Fatalf("expected proof state '%s' but got '%s'", nut07.Spent, proofState.State)
}

if len(proofsToSpend[i].Witness) > 0 {
if proofState.Witness != proofsToSpend[i].Witness {
t.Fatalf("expected state witness '%s' but got '%s'", proofsToSpend[i].Witness, proofState.Witness)
}
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALTER TABLE proofs DROP COLUMN witness;
ALTER TABLE pending_proofs DROP COLUMN witness;
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALTER TABLE proofs ADD COLUMN witness TEXT;
ALTER TABLE pending_proofs ADD COLUMN witness TEXT;
30 changes: 25 additions & 5 deletions mint/storage/sqlite/sqlite.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ func (sqlite *SQLiteDB) SaveProofs(proofs cashu.Proofs) error {
return err
}

stmt, err := tx.Prepare("INSERT INTO proofs (y, amount, keyset_id, secret, c) VALUES (?, ?, ?, ?, ?)")
stmt, err := tx.Prepare("INSERT INTO proofs (y, amount, keyset_id, secret, c, witness) VALUES (?, ?, ?, ?, ?, ?)")
if err != nil {
return err
}
Expand All @@ -158,7 +158,7 @@ func (sqlite *SQLiteDB) SaveProofs(proofs cashu.Proofs) error {
}
Yhex := hex.EncodeToString(Y.SerializeCompressed())

if _, err := stmt.Exec(Yhex, proof.Amount, proof.Id, proof.Secret, proof.C); err != nil {
if _, err := stmt.Exec(Yhex, proof.Amount, proof.Id, proof.Secret, proof.C, proof.Witness); err != nil {
tx.Rollback()
return err
}
Expand Down Expand Up @@ -188,16 +188,22 @@ func (sqlite *SQLiteDB) GetProofsUsed(Ys []string) ([]storage.DBProof, error) {

for rows.Next() {
var proof storage.DBProof
var witness sql.NullString

err := rows.Scan(
&proof.Y,
&proof.Amount,
&proof.Id,
&proof.Secret,
&proof.C,
&witness,
)
if err != nil {
return nil, err
}
if witness.Valid {
proof.Witness = witness.String
}

proofs = append(proofs, proof)
}
Expand All @@ -211,7 +217,7 @@ func (sqlite *SQLiteDB) AddPendingProofs(proofs cashu.Proofs, quoteId string) er
return err
}

stmt, err := tx.Prepare("INSERT INTO pending_proofs (y, amount, keyset_id, secret, c, melt_quote_id) VALUES (?, ?, ?, ?, ?, ?)")
stmt, err := tx.Prepare("INSERT INTO pending_proofs (y, amount, keyset_id, secret, c, witness, melt_quote_id) VALUES (?, ?, ?, ?, ?, ?, ?)")
if err != nil {
return err
}
Expand All @@ -224,7 +230,7 @@ func (sqlite *SQLiteDB) AddPendingProofs(proofs cashu.Proofs, quoteId string) er
}
Yhex := hex.EncodeToString(Y.SerializeCompressed())

if _, err := stmt.Exec(Yhex, proof.Amount, proof.Id, proof.Secret, proof.C, quoteId); err != nil {
if _, err := stmt.Exec(Yhex, proof.Amount, proof.Id, proof.Secret, proof.C, proof.Witness, quoteId); err != nil {
tx.Rollback()
return err
}
Expand Down Expand Up @@ -254,18 +260,25 @@ func (sqlite *SQLiteDB) GetPendingProofs(Ys []string) ([]storage.DBProof, error)

for rows.Next() {
var proof storage.DBProof
var witness sql.NullString

err := rows.Scan(
&proof.Y,
&proof.Amount,
&proof.Id,
&proof.Secret,
&proof.C,
&proof.MeltQuoteId,
&witness,
)
if err != nil {
return nil, err
}

if witness.Valid {
proof.Witness = witness.String
}

proofs = append(proofs, proof)
}

Expand All @@ -274,7 +287,7 @@ func (sqlite *SQLiteDB) GetPendingProofs(Ys []string) ([]storage.DBProof, error)

func (sqlite *SQLiteDB) GetPendingProofsByQuote(quoteId string) ([]storage.DBProof, error) {
proofs := []storage.DBProof{}
query := `SELECT y, amount, keyset_id, secret, c FROM pending_proofs WHERE melt_quote_id = ?`
query := `SELECT y, amount, keyset_id, secret, c, witness FROM pending_proofs WHERE melt_quote_id = ?`

rows, err := sqlite.db.Query(query, quoteId)
if err != nil {
Expand All @@ -284,17 +297,24 @@ func (sqlite *SQLiteDB) GetPendingProofsByQuote(quoteId string) ([]storage.DBPro

for rows.Next() {
var proof storage.DBProof
var witness sql.NullString

err := rows.Scan(
&proof.Y,
&proof.Amount,
&proof.Id,
&proof.Secret,
&proof.C,
&witness,
)
if err != nil {
return nil, err
}

if witness.Valid {
proof.Witness = witness.String
}

proofs = append(proofs, proof)
}

Expand Down
11 changes: 6 additions & 5 deletions mint/storage/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,12 @@ type DBKeyset struct {
}

type DBProof struct {
Amount uint64
Id string
Secret string
Y string
C string
Amount uint64
Id string
Secret string
Y string
C string
Witness string
// for proofs in pending table
MeltQuoteId string
}
Expand Down

0 comments on commit 81bcd05

Please sign in to comment.