-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add a wallet restore endpoint with mnemonic #43
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,6 +16,7 @@ package api | |
|
||
import ( | ||
"fmt" | ||
"net/http" | ||
"time" | ||
|
||
ginzap "github.com/gin-contrib/zap" | ||
|
@@ -31,6 +32,11 @@ import ( | |
_ "github.com/blinklabs-io/bursa/docs" // docs is generated by Swag CLI | ||
) | ||
|
||
// WalletRestoreRequest defines the request payload for wallet restoration | ||
type WalletRestoreRequest struct { | ||
Mnemonic string `json:"mnemonic" binding:"required"` | ||
} | ||
|
||
// @title bursa | ||
// @version v0 | ||
// @description Programmable Cardano Wallet API | ||
|
@@ -91,9 +97,16 @@ func Start(cfg *config.Config) error { | |
Description: "total number of wallet failures", | ||
Labels: nil, | ||
} | ||
restoreMetric := &ginmetrics.Metric{ | ||
Type: ginmetrics.Counter, | ||
Name: "bursa_wallets_restore_count", | ||
Description: "total number of wallets restored", | ||
Labels: nil, | ||
} | ||
// Add to global monitor object | ||
_ = ginmetrics.GetMonitor().AddMetric(createdMetric) | ||
_ = ginmetrics.GetMonitor().AddMetric(failureMetric) | ||
_ = ginmetrics.GetMonitor().AddMetric(restoreMetric) | ||
|
||
// Start metrics listener | ||
go func() { | ||
|
@@ -110,6 +123,7 @@ func Start(cfg *config.Config) error { | |
|
||
// Configure API routes | ||
router.GET("/api/wallet/create", handleWalletCreate) | ||
router.POST("/api/wallet/restore", handleWalletRestore) | ||
|
||
// Start API listener | ||
err := router.Run(fmt.Sprintf("%s:%d", | ||
|
@@ -155,3 +169,39 @@ func handleWalletCreate(c *gin.Context) { | |
c.JSON(200, w) | ||
_ = ginmetrics.GetMonitor().GetMetric("bursa_wallets_create_count").Inc(nil) | ||
} | ||
|
||
// handleWalletRestore handles the wallet restoration request. | ||
// | ||
// @Summary Restore a wallet using a mnemonic seed phrase | ||
// @Description Restores a wallet using the provided mnemonic seed phrase and returns wallet details. | ||
// @Accept json | ||
// @Produce json | ||
// @Param request body WalletRestoreRequest true "Wallet Restore Request" | ||
// @Success 200 {object} bursa.Wallet "Wallet successfully restored" | ||
// @Failure 400 {string} string "Invalid request" | ||
// @Failure 500 {string} string "Internal server error" | ||
// @Router /api/wallet/restore [post] | ||
func handleWalletRestore(c *gin.Context) { | ||
var request WalletRestoreRequest | ||
if err := c.BindJSON(&request); err != nil { | ||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request"}) | ||
_ = ginmetrics.GetMonitor(). | ||
GetMetric("bursa_wallets_fail_count"). | ||
Inc(nil) | ||
return | ||
} | ||
|
||
// Restore the wallet using the mnemonic | ||
wallet, err := bursa.NewDefaultWallet(request.Mnemonic) | ||
if err != nil { | ||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Internal server error"}) | ||
_ = ginmetrics.GetMonitor(). | ||
GetMetric("bursa_wallets_fail_count"). | ||
Inc(nil) | ||
return | ||
} | ||
|
||
// Return the wallet details | ||
c.JSON(http.StatusOK, wallet) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Define a success metric and increment it here before returning. |
||
_ = ginmetrics.GetMonitor().GetMetric("bursa_wallets_restore_count").Inc(nil) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
package api | ||
|
||
import ( | ||
"bytes" | ||
"encoding/json" | ||
"io" | ||
"net/http" | ||
"net/http/httptest" | ||
"reflect" | ||
"testing" | ||
) | ||
|
||
// Mock JSON data for successful wallet restoration | ||
var mockWalletResponseJSON = `{ | ||
"mnemonic": "depth kitchen crystal history rabbit brief harbor palace tent frog city charge inflict tiger negative young furnace solid august educate bounce canal someone erode", | ||
"payment_address": "addr1qxwqkfd3qz5pdwmemtv2llmetegdyku4ffxuldjcfrs05nfjtw33ktf3j6amgxsgnj9u3fa5nrle79nv2g24npnth0esk2dy7q", | ||
"stake_address": "stake1uye9hgcm95cedwa5rgyfez7g576f3lulzek9y92ese4mhucu439t0", | ||
"payment_vkey": { | ||
"type": "PaymentVerificationKeyShelley_ed25519", | ||
"description": "Payment Verification Key", | ||
"cborHex": "582040c99562052dc67d0e265bf183d2e376905972346a11eec2dbb714600bb28911" | ||
}, | ||
"payment_skey": { | ||
"type": "PaymentExtendedSigningKeyShelley_ed25519_bip32", | ||
"description": "Payment Signing Key", | ||
"cborHex": "5880d8f05d500419f363eb81d5ed832f7264b24cb529e6e2cb643a495e82c3aa6c4203089cdb6ed0f2d0db817b5a90e9f5b689a6e4da1f1c2157b463dd6690bee72840c99562052dc67d0e265bf183d2e376905972346a11eec2dbb714600bb28911c938975e7bec39ea8e57613558571b72eb4f399ab7967e985174a23c6e767840" | ||
}, | ||
"stake_vkey": { | ||
"type": "StakeVerificationKeyShelley_ed25519", | ||
"description": "Stake Verification Key", | ||
"cborHex": "58202a786a251854a5f459a856e7ae8f9289be9a3a7a1bf421e35bfaab815868e0fd" | ||
}, | ||
"stake_skey": { | ||
"type": "StakeExtendedSigningKeyShelley_ed25519_bip32", | ||
"description": "Stake Signing Key", | ||
"cborHex": "5880b0a9a8bcddc391c2cc79dbbac792e21f21fa8a3572e8591235bdc802c9aa6c4210eee620765fb6f569ab6b2916001cdd6d289067b022847d62ea19160463bcf72a786a251854a5f459a856e7ae8f9289be9a3a7a1bf421e35bfaab815868e0fd258df1a6eb6b51f6769afcdf4634594b13dba6433ec3670ea9ea742a09e8711e" | ||
} | ||
}` | ||
|
||
func TestRestoreWallet(t *testing.T) { | ||
t.Run("Successful Wallet Restoration", func(t *testing.T) { | ||
t.Parallel() | ||
|
||
// Create a mock server | ||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
if r.URL.Path == "/api/wallet/restore" && r.Method == "POST" { | ||
var request WalletRestoreRequest | ||
if err := json.NewDecoder(r.Body).Decode(&request); err != nil { | ||
http.Error(w, "Invalid request", http.StatusBadRequest) | ||
return | ||
} | ||
|
||
// Respond with the mock wallet JSON data | ||
w.WriteHeader(http.StatusOK) | ||
_, err := w.Write([]byte(mockWalletResponseJSON)) | ||
if err != nil { | ||
t.Fatalf("Failed to write response: %v", err) | ||
} | ||
|
||
} else { | ||
w.WriteHeader(http.StatusNotFound) | ||
} | ||
})) | ||
defer server.Close() | ||
|
||
// Prepare the request body | ||
requestBody, _ := json.Marshal(WalletRestoreRequest{ | ||
Mnemonic: "depth kitchen crystal history rabbit brief harbor palace tent frog city charge inflict tiger negative young furnace solid august educate bounce canal someone erode", | ||
}) | ||
|
||
resp, err := http.Post(server.URL+"/api/wallet/restore", "application/json", bytes.NewBuffer(requestBody)) | ||
if err != nil { | ||
t.Fatalf("Failed to make request: %v", err) | ||
} | ||
defer resp.Body.Close() | ||
|
||
responseBody, err := io.ReadAll(resp.Body) | ||
if err != nil { | ||
t.Fatalf("Failed to read response body: %v", err) | ||
} | ||
|
||
var expectedResponse, actualResponse map[string]interface{} | ||
|
||
if err := json.Unmarshal([]byte(mockWalletResponseJSON), &expectedResponse); err != nil { | ||
t.Fatalf("Failed to unmarshal expected response: %v", err) | ||
} | ||
if err := json.Unmarshal(responseBody, &actualResponse); err != nil { | ||
t.Fatalf("Failed to unmarshal actual response: %v", err) | ||
} | ||
|
||
if !reflect.DeepEqual(expectedResponse, actualResponse) { | ||
t.Errorf("Expected response %v, got %v", expectedResponse, actualResponse) | ||
} | ||
}) | ||
|
||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Increment our failure metric before returning