mirror of
https://github.com/sonr-io/sonr.git
synced 2026-08-03 09:51:39 +00:00
158 lines
4.0 KiB
Go
158 lines
4.0 KiB
Go
package cli
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"encoding/hex"
|
||
|
|
"encoding/json"
|
||
|
|
"fmt"
|
||
|
|
"os"
|
||
|
|
|
||
|
|
"github.com/spf13/cobra"
|
||
|
|
|
||
|
|
"github.com/cosmos/cosmos-sdk/client"
|
||
|
|
"github.com/cosmos/cosmos-sdk/client/flags"
|
||
|
|
|
||
|
|
"github.com/sonr-io/sonr/x/dwn/client/plugin"
|
||
|
|
)
|
||
|
|
|
||
|
|
// SignCmd returns a command to sign data or transactions using the Motor plugin
|
||
|
|
func SignCmd() *cobra.Command {
|
||
|
|
cmd := &cobra.Command{
|
||
|
|
Use: "sign [message-or-file]",
|
||
|
|
Short: "Sign a message or transaction using Motor plugin",
|
||
|
|
Long: `Sign a message or transaction using the Motor plugin's MPC-based signing.
|
||
|
|
|
||
|
|
Examples:
|
||
|
|
# Sign a text message
|
||
|
|
snrd wallet sign "Hello World" --enclave-data @enclave.json
|
||
|
|
|
||
|
|
# Sign a transaction file
|
||
|
|
snrd wallet sign @unsigned_tx.json --enclave-data @enclave.json --tx
|
||
|
|
|
||
|
|
# Sign raw bytes (hex encoded)
|
||
|
|
snrd wallet sign 0xdeadbeef --enclave-data @enclave.json`,
|
||
|
|
Args: cobra.ExactArgs(1),
|
||
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||
|
|
clientCtx, err := client.GetClientQueryContext(cmd)
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
// Get enclave data
|
||
|
|
enclaveDataStr, err := cmd.Flags().GetString("enclave-data")
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
if enclaveDataStr == "" {
|
||
|
|
return fmt.Errorf("--enclave-data flag is required")
|
||
|
|
}
|
||
|
|
|
||
|
|
enclaveData, err := parseEnclaveData(enclaveDataStr)
|
||
|
|
if err != nil {
|
||
|
|
return fmt.Errorf("failed to parse enclave data: %w", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Determine what to sign
|
||
|
|
input := args[0]
|
||
|
|
var dataToSign []byte
|
||
|
|
|
||
|
|
isTransaction, _ := cmd.Flags().GetBool("tx")
|
||
|
|
|
||
|
|
if input[0] == '@' {
|
||
|
|
// Read from file
|
||
|
|
fileData, err := os.ReadFile(input[1:])
|
||
|
|
if err != nil {
|
||
|
|
return fmt.Errorf("failed to read file: %w", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
if isTransaction {
|
||
|
|
// For transaction, just use the raw bytes
|
||
|
|
// In a real implementation, we'd extract sign bytes properly
|
||
|
|
dataToSign = fileData
|
||
|
|
} else {
|
||
|
|
dataToSign = fileData
|
||
|
|
}
|
||
|
|
} else if len(input) > 2 && input[:2] == "0x" {
|
||
|
|
// Hex encoded data
|
||
|
|
dataToSign, err = hex.DecodeString(input[2:])
|
||
|
|
if err != nil {
|
||
|
|
return fmt.Errorf("failed to decode hex: %w", err)
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
// Plain text message
|
||
|
|
dataToSign = []byte(input)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Load the plugin
|
||
|
|
chainID := clientCtx.ChainID
|
||
|
|
if chainID == "" {
|
||
|
|
chainID = DefaultTestChainID
|
||
|
|
}
|
||
|
|
config := plugin.CreateEnclaveConfig(chainID, enclaveData)
|
||
|
|
|
||
|
|
ctx := context.Background()
|
||
|
|
motorPlugin, err := plugin.LoadPluginWithManager(ctx, config)
|
||
|
|
if err != nil {
|
||
|
|
return fmt.Errorf("failed to load Motor plugin: %w", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Sign the data
|
||
|
|
signReq := &plugin.SignDataRequest{
|
||
|
|
Data: dataToSign,
|
||
|
|
}
|
||
|
|
signResp, err := motorPlugin.SignData(signReq)
|
||
|
|
if err != nil {
|
||
|
|
return fmt.Errorf("failed to sign data: %w", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
if signResp.Error != "" {
|
||
|
|
return fmt.Errorf("plugin signing error: %s", signResp.Error)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Get issuer info for display
|
||
|
|
issuerResp, _ := motorPlugin.GetIssuerDID()
|
||
|
|
|
||
|
|
// Output the signature
|
||
|
|
result := map[string]any{
|
||
|
|
"signature": hex.EncodeToString(signResp.Signature),
|
||
|
|
"signer": map[string]any{
|
||
|
|
"did": issuerResp.IssuerDID,
|
||
|
|
"address": issuerResp.Address,
|
||
|
|
},
|
||
|
|
"data_hash": hex.EncodeToString(dataToSign[:min(32, len(dataToSign))]),
|
||
|
|
}
|
||
|
|
|
||
|
|
// Save to file if requested
|
||
|
|
outputFile, _ := cmd.Flags().GetString("output-file")
|
||
|
|
if outputFile != "" {
|
||
|
|
jsonData, err := json.MarshalIndent(result, "", " ")
|
||
|
|
if err != nil {
|
||
|
|
return fmt.Errorf("failed to marshal result: %w", err)
|
||
|
|
}
|
||
|
|
if err := os.WriteFile(outputFile, jsonData, 0o644); err != nil {
|
||
|
|
return fmt.Errorf("failed to write output file: %w", err)
|
||
|
|
}
|
||
|
|
fmt.Printf("Signature saved to %s\n", outputFile)
|
||
|
|
}
|
||
|
|
|
||
|
|
return clientCtx.PrintObjectLegacy(result)
|
||
|
|
},
|
||
|
|
}
|
||
|
|
|
||
|
|
flags.AddQueryFlagsToCmd(cmd)
|
||
|
|
cmd.Flags().String("enclave-data", "", "Enclave data for signing (required)")
|
||
|
|
cmd.Flags().Bool("tx", false, "Sign as transaction (parse JSON as tx)")
|
||
|
|
cmd.Flags().String("output-file", "", "Save signature to file")
|
||
|
|
cmd.MarkFlagRequired("enclave-data")
|
||
|
|
|
||
|
|
return cmd
|
||
|
|
}
|
||
|
|
|
||
|
|
func min(a, b int) int {
|
||
|
|
if a < b {
|
||
|
|
return a
|
||
|
|
}
|
||
|
|
return b
|
||
|
|
}
|