mirror of
https://github.com/sonr-io/sonr.git
synced 2026-08-02 17:31:39 +00:00
@@ -1 +0,0 @@
|
||||
package types
|
||||
Regular → Executable
+13
-3
@@ -9,8 +9,9 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
amino = codec.NewLegacyAmino()
|
||||
AminoCdc = codec.NewAminoCodec(amino)
|
||||
amino = codec.NewLegacyAmino()
|
||||
AminoCdc = codec.NewAminoCodec(amino)
|
||||
ModuleCdc = codec.NewProtoCodec(types.NewInterfaceRegistry())
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -22,13 +23,22 @@ func init() {
|
||||
// RegisterLegacyAminoCodec registers concrete types on the LegacyAmino codec
|
||||
func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) {
|
||||
cdc.RegisterConcrete(&MsgUpdateParams{}, ModuleName+"/MsgUpdateParams", nil)
|
||||
cdc.RegisterConcrete(&MsgRecordsWrite{}, ModuleName+"/MsgRecordsWrite", nil)
|
||||
cdc.RegisterConcrete(&MsgRecordsDelete{}, ModuleName+"/MsgRecordsDelete", nil)
|
||||
cdc.RegisterConcrete(&MsgProtocolsConfigure{}, ModuleName+"/MsgProtocolsConfigure", nil)
|
||||
cdc.RegisterConcrete(&MsgPermissionsGrant{}, ModuleName+"/MsgPermissionsGrant", nil)
|
||||
cdc.RegisterConcrete(&MsgPermissionsRevoke{}, ModuleName+"/MsgPermissionsRevoke", nil)
|
||||
}
|
||||
|
||||
func RegisterInterfaces(registry types.InterfaceRegistry) {
|
||||
|
||||
registry.RegisterImplementations(
|
||||
(*sdk.Msg)(nil),
|
||||
&MsgUpdateParams{},
|
||||
&MsgRecordsWrite{},
|
||||
&MsgRecordsDelete{},
|
||||
&MsgProtocolsConfigure{},
|
||||
&MsgPermissionsGrant{},
|
||||
&MsgPermissionsRevoke{},
|
||||
)
|
||||
|
||||
msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc)
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
apiv1 "github.com/sonr-io/sonr/api/dwn/v1"
|
||||
)
|
||||
|
||||
// ConvertAPIRecordToType converts from API DWNRecord to types.DWNRecord
|
||||
func ConvertAPIRecordToType(record *apiv1.DWNRecord) DWNRecord {
|
||||
typeRecord := DWNRecord{
|
||||
RecordId: record.RecordId,
|
||||
Target: record.Target,
|
||||
Authorization: record.Authorization,
|
||||
Data: record.Data,
|
||||
Protocol: record.Protocol,
|
||||
ProtocolPath: record.ProtocolPath,
|
||||
Schema: record.Schema,
|
||||
ParentId: record.ParentId,
|
||||
Published: record.Published,
|
||||
Attestation: record.Attestation,
|
||||
Encryption: record.Encryption,
|
||||
KeyDerivationScheme: record.KeyDerivationScheme,
|
||||
CreatedAt: record.CreatedAt,
|
||||
UpdatedAt: record.UpdatedAt,
|
||||
CreatedHeight: record.CreatedHeight,
|
||||
}
|
||||
|
||||
// Convert descriptor
|
||||
if record.Descriptor_ != nil {
|
||||
typeRecord.Descriptor_ = &DWNMessageDescriptor{
|
||||
InterfaceName: record.Descriptor_.InterfaceName,
|
||||
Method: record.Descriptor_.Method,
|
||||
MessageTimestamp: record.Descriptor_.MessageTimestamp,
|
||||
DataCid: record.Descriptor_.DataCid,
|
||||
DataSize: record.Descriptor_.DataSize,
|
||||
DataFormat: record.Descriptor_.DataFormat,
|
||||
}
|
||||
}
|
||||
|
||||
return typeRecord
|
||||
}
|
||||
|
||||
// ConvertAPIProtocolToType converts from API DWNProtocol to types.DWNProtocol
|
||||
func ConvertAPIProtocolToType(protocol *apiv1.DWNProtocol) DWNProtocol {
|
||||
return DWNProtocol{
|
||||
Target: protocol.Target,
|
||||
ProtocolUri: protocol.ProtocolUri,
|
||||
Definition: protocol.Definition,
|
||||
Published: protocol.Published,
|
||||
CreatedAt: protocol.CreatedAt,
|
||||
CreatedHeight: protocol.CreatedHeight,
|
||||
}
|
||||
}
|
||||
|
||||
// ConvertAPIPermissionToType converts from API DWNPermission to types.DWNPermission
|
||||
func ConvertAPIPermissionToType(permission *apiv1.DWNPermission) DWNPermission {
|
||||
return DWNPermission{
|
||||
PermissionId: permission.PermissionId,
|
||||
Grantor: permission.Grantor,
|
||||
Grantee: permission.Grantee,
|
||||
Target: permission.Target,
|
||||
InterfaceName: permission.InterfaceName,
|
||||
Method: permission.Method,
|
||||
Protocol: permission.Protocol,
|
||||
RecordId: permission.RecordId,
|
||||
Conditions: permission.Conditions,
|
||||
ExpiresAt: permission.ExpiresAt,
|
||||
CreatedAt: permission.CreatedAt,
|
||||
Revoked: permission.Revoked,
|
||||
CreatedHeight: permission.CreatedHeight,
|
||||
}
|
||||
}
|
||||
|
||||
// ConvertAPIVaultToType converts from API VaultState to types.VaultState
|
||||
func ConvertAPIVaultToType(vault *apiv1.VaultState) VaultState {
|
||||
typeVault := VaultState{
|
||||
VaultId: vault.VaultId,
|
||||
Owner: vault.Owner,
|
||||
PublicKey: vault.PublicKey,
|
||||
CreatedAt: vault.CreatedAt,
|
||||
LastRefreshed: vault.LastRefreshed,
|
||||
CreatedHeight: vault.CreatedHeight,
|
||||
}
|
||||
|
||||
// Convert enclave data
|
||||
if vault.EnclaveData != nil {
|
||||
typeVault.EnclaveData = &EnclaveData{
|
||||
PrivateData: vault.EnclaveData.PrivateData,
|
||||
PublicKey: vault.EnclaveData.PublicKey,
|
||||
EnclaveId: vault.EnclaveData.EnclaveId,
|
||||
Version: vault.EnclaveData.Version,
|
||||
}
|
||||
}
|
||||
|
||||
return typeVault
|
||||
}
|
||||
|
||||
// ConvertAPIEncryptionMetadataToType converts from API EncryptionMetadata to types.EncryptionMetadata
|
||||
func ConvertAPIEncryptionMetadataToType(metadata *apiv1.EncryptionMetadata) *EncryptionMetadata {
|
||||
if metadata == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &EncryptionMetadata{
|
||||
Algorithm: metadata.Algorithm,
|
||||
ConsensusInput: metadata.ConsensusInput,
|
||||
Nonce: metadata.Nonce,
|
||||
AuthTag: metadata.AuthTag,
|
||||
EncryptionHeight: metadata.EncryptionHeight,
|
||||
ValidatorSet: metadata.ValidatorSet,
|
||||
KeyVersion: metadata.KeyVersion,
|
||||
SingleNodeMode: metadata.SingleNodeMode,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
apiv1 "github.com/sonr-io/sonr/api/dwn/v1"
|
||||
)
|
||||
|
||||
// ToAPIEncryptionMetadata converts types.EncryptionMetadata to apiv1.EncryptionMetadata
|
||||
func (em *EncryptionMetadata) ToAPIEncryptionMetadata() *apiv1.EncryptionMetadata {
|
||||
if em == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &apiv1.EncryptionMetadata{
|
||||
Algorithm: em.Algorithm,
|
||||
ConsensusInput: em.ConsensusInput,
|
||||
Nonce: em.Nonce,
|
||||
AuthTag: em.AuthTag,
|
||||
EncryptionHeight: em.EncryptionHeight,
|
||||
ValidatorSet: em.ValidatorSet,
|
||||
KeyVersion: em.KeyVersion,
|
||||
SingleNodeMode: em.SingleNodeMode,
|
||||
}
|
||||
}
|
||||
|
||||
// FromAPIEncryptionMetadata converts apiv1.EncryptionMetadata to types.EncryptionMetadata
|
||||
func FromAPIEncryptionMetadata(apiMeta *apiv1.EncryptionMetadata) *EncryptionMetadata {
|
||||
if apiMeta == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &EncryptionMetadata{
|
||||
Algorithm: apiMeta.Algorithm,
|
||||
ConsensusInput: apiMeta.ConsensusInput,
|
||||
Nonce: apiMeta.Nonce,
|
||||
AuthTag: apiMeta.AuthTag,
|
||||
EncryptionHeight: apiMeta.EncryptionHeight,
|
||||
ValidatorSet: apiMeta.ValidatorSet,
|
||||
KeyVersion: apiMeta.KeyVersion,
|
||||
SingleNodeMode: apiMeta.SingleNodeMode,
|
||||
}
|
||||
}
|
||||
|
||||
// ToAPIVRFContribution converts types.VRFContribution to apiv1.VRFContribution
|
||||
func (vrf *VRFContribution) ToAPIVRFContribution() *apiv1.VRFContribution {
|
||||
if vrf == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &apiv1.VRFContribution{
|
||||
ValidatorAddress: vrf.ValidatorAddress,
|
||||
Randomness: vrf.Randomness,
|
||||
Proof: vrf.Proof,
|
||||
BlockHeight: vrf.BlockHeight,
|
||||
Timestamp: vrf.Timestamp,
|
||||
}
|
||||
}
|
||||
|
||||
// FromAPIVRFContribution converts apiv1.VRFContribution to types.VRFContribution
|
||||
func FromAPIVRFContribution(apiVrf *apiv1.VRFContribution) *VRFContribution {
|
||||
if apiVrf == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &VRFContribution{
|
||||
ValidatorAddress: apiVrf.ValidatorAddress,
|
||||
Randomness: apiVrf.Randomness,
|
||||
Proof: apiVrf.Proof,
|
||||
BlockHeight: apiVrf.BlockHeight,
|
||||
Timestamp: apiVrf.Timestamp,
|
||||
}
|
||||
}
|
||||
|
||||
// ToAPIEncryptionKeyState converts types.EncryptionKeyState to apiv1.EncryptionKeyState
|
||||
func (eks *EncryptionKeyState) ToAPIEncryptionKeyState() *apiv1.EncryptionKeyState {
|
||||
if eks == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert contribution slice to API format
|
||||
apiContributions := make([]*apiv1.VRFContribution, len(eks.Contributions))
|
||||
for i, contrib := range eks.Contributions {
|
||||
if contrib != nil {
|
||||
apiContributions[i] = contrib.ToAPIVRFContribution()
|
||||
}
|
||||
}
|
||||
|
||||
return &apiv1.EncryptionKeyState{
|
||||
CurrentKey: eks.CurrentKey,
|
||||
KeyVersion: eks.KeyVersion,
|
||||
ValidatorSet: eks.ValidatorSet,
|
||||
Contributions: apiContributions,
|
||||
LastRotation: eks.LastRotation,
|
||||
NextRotation: eks.NextRotation,
|
||||
SingleNodeMode: eks.SingleNodeMode,
|
||||
}
|
||||
}
|
||||
|
||||
// FromAPIEncryptionKeyState converts apiv1.EncryptionKeyState to types.EncryptionKeyState
|
||||
func FromAPIEncryptionKeyState(apiEks *apiv1.EncryptionKeyState) *EncryptionKeyState {
|
||||
if apiEks == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert API contribution slice to types format
|
||||
contributions := make([]*VRFContribution, len(apiEks.Contributions))
|
||||
for i, apiContrib := range apiEks.Contributions {
|
||||
if apiContrib != nil {
|
||||
contributions[i] = FromAPIVRFContribution(apiContrib)
|
||||
}
|
||||
}
|
||||
|
||||
return &EncryptionKeyState{
|
||||
CurrentKey: apiEks.CurrentKey,
|
||||
KeyVersion: apiEks.KeyVersion,
|
||||
ValidatorSet: apiEks.ValidatorSet,
|
||||
Contributions: contributions,
|
||||
LastRotation: apiEks.LastRotation,
|
||||
NextRotation: apiEks.NextRotation,
|
||||
SingleNodeMode: apiEks.SingleNodeMode,
|
||||
}
|
||||
}
|
||||
|
||||
// ToAPIVRFConsensusRound converts types.VRFConsensusRound to apiv1.VRFConsensusRound
|
||||
func (vcr *VRFConsensusRound) ToAPIVRFConsensusRound() *apiv1.VRFConsensusRound {
|
||||
if vcr == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &apiv1.VRFConsensusRound{
|
||||
RoundNumber: vcr.RoundNumber,
|
||||
KeyVersion: vcr.KeyVersion,
|
||||
RequiredContributions: vcr.RequiredContributions,
|
||||
ReceivedContributions: vcr.ReceivedContributions,
|
||||
Status: vcr.Status,
|
||||
ExpiryHeight: vcr.ExpiryHeight,
|
||||
InitiatedHeight: vcr.InitiatedHeight,
|
||||
ConsensusInput: vcr.ConsensusInput,
|
||||
Completed: vcr.Completed,
|
||||
}
|
||||
}
|
||||
|
||||
// FromAPIVRFConsensusRound converts apiv1.VRFConsensusRound to types.VRFConsensusRound
|
||||
func FromAPIVRFConsensusRound(apiVcr *apiv1.VRFConsensusRound) *VRFConsensusRound {
|
||||
if apiVcr == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &VRFConsensusRound{
|
||||
RoundNumber: apiVcr.RoundNumber,
|
||||
KeyVersion: apiVcr.KeyVersion,
|
||||
RequiredContributions: apiVcr.RequiredContributions,
|
||||
ReceivedContributions: apiVcr.ReceivedContributions,
|
||||
Status: apiVcr.Status,
|
||||
ExpiryHeight: apiVcr.ExpiryHeight,
|
||||
InitiatedHeight: apiVcr.InitiatedHeight,
|
||||
ConsensusInput: apiVcr.ConsensusInput,
|
||||
Completed: apiVcr.Completed,
|
||||
}
|
||||
}
|
||||
|
||||
// ToAPIEncryptionStats converts types.EncryptionStats to apiv1.EncryptionStats
|
||||
func (es *EncryptionStats) ToAPIEncryptionStats() *apiv1.EncryptionStats {
|
||||
if es == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &apiv1.EncryptionStats{
|
||||
TotalEncryptedRecords: es.TotalEncryptedRecords,
|
||||
TotalDecryptionErrors: es.TotalDecryptionErrors,
|
||||
LastEncryptionHeight: es.LastEncryptionHeight,
|
||||
}
|
||||
}
|
||||
|
||||
// FromAPIEncryptionStats converts apiv1.EncryptionStats to types.EncryptionStats
|
||||
func FromAPIEncryptionStats(apiEs *apiv1.EncryptionStats) *EncryptionStats {
|
||||
if apiEs == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &EncryptionStats{
|
||||
TotalEncryptedRecords: apiEs.TotalEncryptedRecords,
|
||||
TotalDecryptionErrors: apiEs.TotalDecryptionErrors,
|
||||
LastEncryptionHeight: apiEs.LastEncryptionHeight,
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package embed
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/ipfs/boxo/files"
|
||||
motr "github.com/onsonr/motr/pkg/config"
|
||||
)
|
||||
|
||||
const SchemaVersion = 1
|
||||
const (
|
||||
AppManifestFileName = "app.webmanifest"
|
||||
DWNConfigFileName = "dwn.json"
|
||||
IndexHTMLFileName = "index.html"
|
||||
MainJSFileName = "main.js"
|
||||
ServiceWorkerFileName = "sw.js"
|
||||
)
|
||||
|
||||
// spawnVaultDirectory creates a new directory with the default files
|
||||
func NewVaultFS(cfg *motr.Config) (files.Directory, error) {
|
||||
manifestBz, err := NewWebManifest()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cnfBz, err := json.Marshal(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return files.NewMapDirectory(map[string]files.Node{
|
||||
AppManifestFileName: files.NewBytesFile(manifestBz),
|
||||
DWNConfigFileName: files.NewBytesFile(cnfBz),
|
||||
IndexHTMLFileName: files.NewBytesFile(IndexHTML),
|
||||
MainJSFileName: files.NewBytesFile(MainJS),
|
||||
ServiceWorkerFileName: files.NewBytesFile(WorkerJS),
|
||||
}), nil
|
||||
}
|
||||
|
||||
// NewVaultConfig returns the default vault config
|
||||
func NewVaultConfig(addr string, ucanCID string) *motr.Config {
|
||||
return &motr.Config{
|
||||
MotrToken: ucanCID,
|
||||
MotrAddress: addr,
|
||||
IpfsGatewayUrl: "http://localhost:80",
|
||||
SonrApiUrl: "http://localhost:1317",
|
||||
SonrRpcUrl: "http://localhost:26657",
|
||||
SonrChainId: "sonr-testnet-1",
|
||||
}
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Sonr DWN</title>
|
||||
|
||||
<!-- HTMX -->
|
||||
<script src="https://unpkg.com/htmx.org@1.9.10"></script>
|
||||
|
||||
<!-- WASM Support -->
|
||||
<script src="https://cdn.jsdelivr.net/gh/golang/go@go1.22.5/misc/wasm/wasm_exec.js"></script>
|
||||
|
||||
<!-- Main JS -->
|
||||
<script src="main.js"></script>
|
||||
|
||||
<!-- Tailwind (assuming you're using it based on your classes) -->
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
|
||||
<!-- Add manifest for PWA support -->
|
||||
<link
|
||||
rel="manifest"
|
||||
href="/app.webmanifest"
|
||||
crossorigin="use-credentials"
|
||||
/>
|
||||
|
||||
<!-- Offline detection styles -->
|
||||
<style>
|
||||
.offline-indicator {
|
||||
display: none;
|
||||
}
|
||||
|
||||
body.offline .offline-indicator {
|
||||
display: block;
|
||||
background: #f44336;
|
||||
color: white;
|
||||
text-align: center;
|
||||
padding: 0.5rem;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 1000;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body
|
||||
class="flex items-center justify-center h-full bg-zinc-50 lg:p-24 md:16 p-4"
|
||||
>
|
||||
<!-- Offline indicator -->
|
||||
<div class="offline-indicator">
|
||||
You are currently offline. Some features may be limited.
|
||||
</div>
|
||||
|
||||
<!-- Loading indicator -->
|
||||
<div
|
||||
id="loading-indicator"
|
||||
class="fixed top-0 left-0 w-full h-1 bg-blue-200 transition-all duration-300"
|
||||
style="display: none"
|
||||
>
|
||||
<div class="h-full bg-blue-600 w-0 transition-all duration-300"></div>
|
||||
</div>
|
||||
|
||||
<main
|
||||
class="flex-row items-center justify-center mx-auto w-fit max-w-screen-sm gap-y-3"
|
||||
>
|
||||
<div
|
||||
id="content"
|
||||
hx-get="/#"
|
||||
hx-trigger="load"
|
||||
hx-swap="outerHTML"
|
||||
hx-indicator="#loading-indicator"
|
||||
>
|
||||
Loading...
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- WASM Ready Indicator (hidden) -->
|
||||
<div
|
||||
id="wasm-status"
|
||||
class="hidden fixed bottom-4 right-4 p-2 rounded-md bg-green-500 text-white"
|
||||
hx-swap-oob="true"
|
||||
>
|
||||
WASM Ready
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Initialize service worker
|
||||
if ("serviceWorker" in navigator) {
|
||||
window.addEventListener("load", async function () {
|
||||
try {
|
||||
const registration =
|
||||
await navigator.serviceWorker.register("/sw.js");
|
||||
console.log(
|
||||
"Service Worker registered with scope:",
|
||||
registration.scope,
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("Service Worker registration failed:", error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// HTMX loading indicator
|
||||
htmx.on("htmx:beforeRequest", function (evt) {
|
||||
document.getElementById("loading-indicator").style.display = "block";
|
||||
});
|
||||
|
||||
htmx.on("htmx:afterRequest", function (evt) {
|
||||
document.getElementById("loading-indicator").style.display = "none";
|
||||
});
|
||||
|
||||
// WASM ready event handler
|
||||
document.addEventListener("wasm-ready", function () {
|
||||
const status = document.getElementById("wasm-status");
|
||||
status.classList.remove("hidden");
|
||||
setTimeout(() => {
|
||||
status.classList.add("hidden");
|
||||
}, 3000);
|
||||
});
|
||||
|
||||
// Offline status handler
|
||||
window.addEventListener("offline", function () {
|
||||
document.body.classList.add("offline");
|
||||
});
|
||||
|
||||
window.addEventListener("online", function () {
|
||||
document.body.classList.remove("offline");
|
||||
});
|
||||
|
||||
// Initial offline check
|
||||
if (!navigator.onLine) {
|
||||
document.body.classList.add("offline");
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,158 +0,0 @@
|
||||
// MessageChannel for WASM communication
|
||||
let wasmChannel;
|
||||
let wasmPort;
|
||||
|
||||
async function initWasmChannel() {
|
||||
wasmChannel = new MessageChannel();
|
||||
wasmPort = wasmChannel.port1;
|
||||
|
||||
// Setup message handling from WASM
|
||||
wasmPort.onmessage = (event) => {
|
||||
const { type, data } = event.data;
|
||||
switch (type) {
|
||||
case "WASM_READY":
|
||||
console.log("WASM is ready");
|
||||
document.dispatchEvent(new CustomEvent("wasm-ready"));
|
||||
break;
|
||||
case "RESPONSE":
|
||||
handleWasmResponse(data);
|
||||
break;
|
||||
case "SYNC_COMPLETE":
|
||||
handleSyncComplete(data);
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Initialize WebAssembly and Service Worker
|
||||
async function init() {
|
||||
try {
|
||||
// Register service worker
|
||||
if ("serviceWorker" in navigator) {
|
||||
const registration = await navigator.serviceWorker.register("./sw.js");
|
||||
console.log("ServiceWorker registered");
|
||||
|
||||
// Wait for the service worker to be ready
|
||||
await navigator.serviceWorker.ready;
|
||||
|
||||
// Initialize MessageChannel
|
||||
await initWasmChannel();
|
||||
|
||||
// Send the MessageChannel port to the service worker
|
||||
navigator.serviceWorker.controller.postMessage(
|
||||
{
|
||||
type: "PORT_INITIALIZATION",
|
||||
port: wasmChannel.port2,
|
||||
},
|
||||
[wasmChannel.port2],
|
||||
);
|
||||
|
||||
// Register for periodic sync if available
|
||||
if ("periodicSync" in registration) {
|
||||
try {
|
||||
await registration.periodicSync.register("wasm-sync", {
|
||||
minInterval: 24 * 60 * 60 * 1000, // 24 hours
|
||||
});
|
||||
} catch (error) {
|
||||
console.log("Periodic sync could not be registered:", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize HTMX with custom config
|
||||
htmx.config.withCredentials = true;
|
||||
htmx.config.wsReconnectDelay = "full-jitter";
|
||||
|
||||
// Override HTMX's internal request handling
|
||||
htmx.config.beforeRequest = function (config) {
|
||||
// Add request ID for tracking
|
||||
const requestId = "req_" + Date.now();
|
||||
config.headers["X-Wasm-Request-ID"] = requestId;
|
||||
|
||||
// If offline, handle through service worker
|
||||
if (!navigator.onLine) {
|
||||
return false; // Let service worker handle it
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
// Handle HTMX after request
|
||||
htmx.config.afterRequest = function (config) {
|
||||
// Additional processing after request if needed
|
||||
};
|
||||
|
||||
// Handle HTMX errors
|
||||
htmx.config.errorHandler = function (error) {
|
||||
console.error("HTMX Error:", error);
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Initialization failed:", error);
|
||||
}
|
||||
}
|
||||
|
||||
function handleWasmResponse(data) {
|
||||
const { requestId, response } = data;
|
||||
// Process the WASM response
|
||||
// This might update the UI or trigger HTMX swaps
|
||||
const targetElement = document.querySelector(
|
||||
`[data-request-id="${requestId}"]`,
|
||||
);
|
||||
if (targetElement) {
|
||||
htmx.process(targetElement);
|
||||
}
|
||||
}
|
||||
|
||||
function handleSyncComplete(data) {
|
||||
const { url } = data;
|
||||
// Handle successful sync
|
||||
// Maybe refresh the relevant part of the UI
|
||||
htmx.trigger("body", "sync:complete", { url });
|
||||
}
|
||||
|
||||
// Handle offline status changes
|
||||
window.addEventListener("online", () => {
|
||||
document.body.classList.remove("offline");
|
||||
// Trigger sync when back online
|
||||
if (wasmPort) {
|
||||
wasmPort.postMessage({ type: "SYNC_REQUEST" });
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener("offline", () => {
|
||||
document.body.classList.add("offline");
|
||||
});
|
||||
|
||||
// Custom event handlers for HTMX
|
||||
document.addEventListener("htmx:beforeRequest", (event) => {
|
||||
const { elt, xhr } = event.detail;
|
||||
// Add request tracking
|
||||
const requestId = xhr.headers["X-Wasm-Request-ID"];
|
||||
elt.setAttribute("data-request-id", requestId);
|
||||
});
|
||||
|
||||
document.addEventListener("htmx:afterRequest", (event) => {
|
||||
const { elt, successful } = event.detail;
|
||||
if (successful) {
|
||||
elt.removeAttribute("data-request-id");
|
||||
}
|
||||
});
|
||||
|
||||
// Initialize everything when the page loads
|
||||
document.addEventListener("DOMContentLoaded", init);
|
||||
|
||||
// Export functions that might be needed by WASM
|
||||
window.wasmBridge = {
|
||||
triggerUIUpdate: function (selector, content) {
|
||||
const target = document.querySelector(selector);
|
||||
if (target) {
|
||||
htmx.process(
|
||||
htmx.parse(content).forEach((node) => target.appendChild(node)),
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
showNotification: function (message, type = "info") {
|
||||
// Implement notification system
|
||||
console.log(`${type}: ${message}`);
|
||||
},
|
||||
};
|
||||
@@ -1,257 +0,0 @@
|
||||
// Cache names for different types of resources
|
||||
const CACHE_NAMES = {
|
||||
wasm: "wasm-cache-v1",
|
||||
static: "static-cache-v1",
|
||||
dynamic: "dynamic-cache-v1",
|
||||
};
|
||||
|
||||
// Import required scripts
|
||||
importScripts(
|
||||
"https://cdn.jsdelivr.net/gh/golang/go@go1.22.5/misc/wasm/wasm_exec.js",
|
||||
"https://cdn.jsdelivr.net/gh/nlepage/go-wasm-http-server@v1.1.0/sw.js",
|
||||
);
|
||||
|
||||
// Initialize WASM HTTP listener
|
||||
const wasmInstance = registerWasmHTTPListener(
|
||||
"https://cdn.sonr.id/wasm/app.wasm",
|
||||
);
|
||||
|
||||
// MessageChannel port for WASM communication
|
||||
let wasmPort;
|
||||
|
||||
// Request queue for offline operations
|
||||
let requestQueue = new Map();
|
||||
|
||||
// Setup message channel handler
|
||||
self.addEventListener("message", async (event) => {
|
||||
if (event.data.type === "PORT_INITIALIZATION") {
|
||||
wasmPort = event.data.port;
|
||||
setupWasmCommunication();
|
||||
}
|
||||
});
|
||||
|
||||
function setupWasmCommunication() {
|
||||
wasmPort.onmessage = async (event) => {
|
||||
const { type, data } = event.data;
|
||||
|
||||
switch (type) {
|
||||
case "WASM_REQUEST":
|
||||
handleWasmRequest(data);
|
||||
break;
|
||||
case "SYNC_REQUEST":
|
||||
processSyncQueue();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
// Notify that WASM is ready
|
||||
wasmPort.postMessage({ type: "WASM_READY" });
|
||||
}
|
||||
|
||||
// Enhanced install event
|
||||
self.addEventListener("install", (event) => {
|
||||
event.waitUntil(
|
||||
Promise.all([
|
||||
skipWaiting(),
|
||||
// Cache WASM binary and essential resources
|
||||
caches
|
||||
.open(CACHE_NAMES.wasm)
|
||||
.then((cache) =>
|
||||
cache.addAll([
|
||||
"https://cdn.sonr.id/wasm/app.wasm",
|
||||
"https://cdn.jsdelivr.net/gh/golang/go@go1.22.5/misc/wasm/wasm_exec.js",
|
||||
]),
|
||||
),
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
// Enhanced activate event
|
||||
self.addEventListener("activate", (event) => {
|
||||
event.waitUntil(
|
||||
Promise.all([
|
||||
clients.claim(),
|
||||
// Clean up old caches
|
||||
caches.keys().then((keys) =>
|
||||
Promise.all(
|
||||
keys.map((key) => {
|
||||
if (!Object.values(CACHE_NAMES).includes(key)) {
|
||||
return caches.delete(key);
|
||||
}
|
||||
}),
|
||||
),
|
||||
),
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
// Intercept fetch events
|
||||
self.addEventListener("fetch", (event) => {
|
||||
const request = event.request;
|
||||
|
||||
// Handle API requests differently from static resources
|
||||
if (request.url.includes("/api/")) {
|
||||
event.respondWith(handleApiRequest(request));
|
||||
} else {
|
||||
event.respondWith(handleStaticRequest(request));
|
||||
}
|
||||
});
|
||||
|
||||
async function handleApiRequest(request) {
|
||||
try {
|
||||
// Try to make the request
|
||||
const response = await fetch(request.clone());
|
||||
|
||||
// If successful, pass through WASM handler
|
||||
if (response.ok) {
|
||||
return await processWasmResponse(request, response);
|
||||
}
|
||||
|
||||
// If offline or failed, queue the request
|
||||
await queueRequest(request);
|
||||
|
||||
// Return cached response if available
|
||||
const cachedResponse = await caches.match(request);
|
||||
if (cachedResponse) {
|
||||
return cachedResponse;
|
||||
}
|
||||
|
||||
// Return offline response
|
||||
return new Response(JSON.stringify({ error: "Currently offline" }), {
|
||||
status: 503,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
} catch (error) {
|
||||
await queueRequest(request);
|
||||
return new Response(JSON.stringify({ error: "Request failed" }), {
|
||||
status: 500,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function handleStaticRequest(request) {
|
||||
// Check cache first
|
||||
const cachedResponse = await caches.match(request);
|
||||
if (cachedResponse) {
|
||||
return cachedResponse;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(request);
|
||||
|
||||
// Cache successful responses
|
||||
if (response.ok) {
|
||||
const cache = await caches.open(CACHE_NAMES.static);
|
||||
cache.put(request, response.clone());
|
||||
}
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
// Return offline page for navigation requests
|
||||
if (request.mode === "navigate") {
|
||||
return caches.match("/offline.html");
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async function processWasmResponse(request, response) {
|
||||
// Clone the response before processing
|
||||
const responseClone = response.clone();
|
||||
|
||||
try {
|
||||
// Process through WASM
|
||||
const processedResponse = await wasmInstance.processResponse(responseClone);
|
||||
|
||||
// Notify client through message channel
|
||||
if (wasmPort) {
|
||||
wasmPort.postMessage({
|
||||
type: "RESPONSE",
|
||||
requestId: request.headers.get("X-Wasm-Request-ID"),
|
||||
response: processedResponse,
|
||||
});
|
||||
}
|
||||
|
||||
return processedResponse;
|
||||
} catch (error) {
|
||||
console.error("WASM processing error:", error);
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
async function queueRequest(request) {
|
||||
const serializedRequest = await serializeRequest(request);
|
||||
requestQueue.set(request.url, serializedRequest);
|
||||
|
||||
// Register for background sync
|
||||
try {
|
||||
await self.registration.sync.register("wasm-sync");
|
||||
} catch (error) {
|
||||
console.error("Sync registration failed:", error);
|
||||
}
|
||||
}
|
||||
|
||||
async function serializeRequest(request) {
|
||||
const headers = {};
|
||||
for (const [key, value] of request.headers.entries()) {
|
||||
headers[key] = value;
|
||||
}
|
||||
|
||||
return {
|
||||
url: request.url,
|
||||
method: request.method,
|
||||
headers,
|
||||
body: await request.text(),
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
}
|
||||
|
||||
// Handle background sync
|
||||
self.addEventListener("sync", (event) => {
|
||||
if (event.tag === "wasm-sync") {
|
||||
event.waitUntil(processSyncQueue());
|
||||
}
|
||||
});
|
||||
|
||||
async function processSyncQueue() {
|
||||
const requests = Array.from(requestQueue.values());
|
||||
|
||||
for (const serializedRequest of requests) {
|
||||
try {
|
||||
const response = await fetch(
|
||||
new Request(serializedRequest.url, {
|
||||
method: serializedRequest.method,
|
||||
headers: serializedRequest.headers,
|
||||
body: serializedRequest.body,
|
||||
}),
|
||||
);
|
||||
|
||||
if (response.ok) {
|
||||
requestQueue.delete(serializedRequest.url);
|
||||
|
||||
// Notify client of successful sync
|
||||
if (wasmPort) {
|
||||
wasmPort.postMessage({
|
||||
type: "SYNC_COMPLETE",
|
||||
url: serializedRequest.url,
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Sync failed for request:", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle payment requests
|
||||
self.addEventListener("canmakepayment", function (e) {
|
||||
e.respondWith(Promise.resolve(true));
|
||||
});
|
||||
|
||||
// Handle periodic sync if available
|
||||
self.addEventListener("periodicsync", (event) => {
|
||||
if (event.tag === "wasm-sync") {
|
||||
event.waitUntil(processSyncQueue());
|
||||
}
|
||||
});
|
||||
@@ -1,47 +0,0 @@
|
||||
package embed
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
//go:embed index.html
|
||||
var IndexHTML []byte
|
||||
|
||||
//go:embed main.js
|
||||
var MainJS []byte
|
||||
|
||||
//go:embed sw.js
|
||||
var WorkerJS []byte
|
||||
|
||||
func getSchema(structType interface{}) string {
|
||||
t := reflect.TypeOf(structType)
|
||||
if t.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
}
|
||||
|
||||
if t.Kind() != reflect.Struct {
|
||||
return ""
|
||||
}
|
||||
|
||||
var fields []string
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
field := t.Field(i)
|
||||
fieldName := toCamelCase(field.Name)
|
||||
fields = append(fields, fieldName)
|
||||
}
|
||||
|
||||
// Add "++" at the beginning, separated by a comma
|
||||
return "++, " + strings.Join(fields, ", ")
|
||||
}
|
||||
|
||||
func toCamelCase(s string) string {
|
||||
if s == "" {
|
||||
return s
|
||||
}
|
||||
if len(s) == 1 {
|
||||
return strings.ToLower(s)
|
||||
}
|
||||
return strings.ToLower(s[:1]) + s[1:]
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
package embed
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
func NewWebManifest() ([]byte, error) {
|
||||
return json.Marshal(baseWebManifest)
|
||||
}
|
||||
|
||||
var baseWebManifest = WebManifest{
|
||||
Name: "Sonr Vault",
|
||||
ShortName: "Sonr.ID",
|
||||
StartURL: "/index.html",
|
||||
Display: "standalone",
|
||||
DisplayOverride: []string{
|
||||
"fullscreen",
|
||||
"minimal-ui",
|
||||
},
|
||||
Icons: []IconDefinition{
|
||||
{
|
||||
Src: "/icons/icon-192x192.png",
|
||||
Sizes: "192x192",
|
||||
Type: "image/png",
|
||||
},
|
||||
},
|
||||
ServiceWorker: ServiceWorker{
|
||||
Scope: "/",
|
||||
Src: "/sw.js",
|
||||
UseCache: true,
|
||||
},
|
||||
ProtocolHandlers: []ProtocolHandler{
|
||||
{
|
||||
Scheme: "did.sonr",
|
||||
URL: "/resolve/sonr/%s",
|
||||
},
|
||||
{
|
||||
Scheme: "did.eth",
|
||||
URL: "/resolve/eth/%s",
|
||||
},
|
||||
{
|
||||
Scheme: "did.btc",
|
||||
URL: "/resolve/btc/%s",
|
||||
},
|
||||
{
|
||||
Scheme: "did.usdc",
|
||||
URL: "/resolve/usdc/%s",
|
||||
},
|
||||
{
|
||||
Scheme: "did.ipfs",
|
||||
URL: "/resolve/ipfs/%s",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
type WebManifest struct {
|
||||
// Required fields
|
||||
Name string `json:"name"` // Full name of the application
|
||||
ShortName string `json:"short_name"` // Short version of the name
|
||||
|
||||
// Display and appearance
|
||||
Description string `json:"description,omitempty"` // Purpose and features of the application
|
||||
Display string `json:"display,omitempty"` // Preferred display mode: fullscreen, standalone, minimal-ui, browser
|
||||
DisplayOverride []string `json:"display_override,omitempty"`
|
||||
ThemeColor string `json:"theme_color,omitempty"` // Default theme color for the application
|
||||
BackgroundColor string `json:"background_color,omitempty"` // Background color during launch
|
||||
Orientation string `json:"orientation,omitempty"` // Default orientation: any, natural, landscape, portrait
|
||||
|
||||
// URLs and scope
|
||||
StartURL string `json:"start_url"` // Starting URL when launching
|
||||
Scope string `json:"scope,omitempty"` // Navigation scope of the web application
|
||||
ServiceWorker ServiceWorker `json:"service_worker,omitempty"`
|
||||
|
||||
// Icons
|
||||
Icons []IconDefinition `json:"icons,omitempty"`
|
||||
|
||||
// Optional features
|
||||
RelatedApplications []RelatedApplication `json:"related_applications,omitempty"`
|
||||
PreferRelatedApplications bool `json:"prefer_related_applications,omitempty"`
|
||||
Shortcuts []Shortcut `json:"shortcuts,omitempty"`
|
||||
|
||||
// Experimental features (uncomment if needed)
|
||||
FileHandlers []FileHandler `json:"file_handlers,omitempty"`
|
||||
ProtocolHandlers []ProtocolHandler `json:"protocol_handlers,omitempty"`
|
||||
}
|
||||
|
||||
type FileHandler struct {
|
||||
Action string `json:"action"`
|
||||
Accept map[string][]string `json:"accept"`
|
||||
}
|
||||
|
||||
type LaunchHandler struct {
|
||||
Action string `json:"action"`
|
||||
}
|
||||
|
||||
type IconDefinition struct {
|
||||
Src string `json:"src"`
|
||||
Sizes string `json:"sizes"`
|
||||
Type string `json:"type,omitempty"`
|
||||
Purpose string `json:"purpose,omitempty"`
|
||||
}
|
||||
|
||||
type ProtocolHandler struct {
|
||||
Scheme string `json:"scheme"`
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
type RelatedApplication struct {
|
||||
Platform string `json:"platform"`
|
||||
URL string `json:"url,omitempty"`
|
||||
ID string `json:"id,omitempty"`
|
||||
}
|
||||
|
||||
type Shortcut struct {
|
||||
Name string `json:"name"`
|
||||
ShortName string `json:"short_name,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
URL string `json:"url"`
|
||||
Icons []IconDefinition `json:"icons,omitempty"`
|
||||
}
|
||||
|
||||
type ServiceWorker struct {
|
||||
Scope string `json:"scope"`
|
||||
Src string `json:"src"`
|
||||
UseCache bool `json:"use_cache"`
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"cosmossdk.io/errors"
|
||||
)
|
||||
|
||||
// DWN module sentinel errors
|
||||
var (
|
||||
// Request validation errors (1-15)
|
||||
ErrInvalidRequest = errors.Register(ModuleName, 1, "invalid request")
|
||||
ErrRequestCannotBeNil = errors.Register(ModuleName, 2, "request cannot be nil")
|
||||
ErrTargetDIDEmpty = errors.Register(ModuleName, 3, "target DID cannot be empty")
|
||||
ErrRecordIDEmpty = errors.Register(ModuleName, 4, "record ID cannot be empty")
|
||||
ErrProtocolURIEmpty = errors.Register(ModuleName, 5, "protocol URI cannot be empty")
|
||||
ErrVaultIDEmpty = errors.Register(ModuleName, 6, "vault ID cannot be empty")
|
||||
ErrPermissionIDEmpty = errors.Register(ModuleName, 7, "permission ID cannot be empty")
|
||||
ErrPublicKeyEmpty = errors.Register(ModuleName, 8, "public key cannot be empty")
|
||||
ErrMessageEmpty = errors.Register(ModuleName, 9, "message cannot be empty")
|
||||
ErrSignatureEmpty = errors.Register(ModuleName, 10, "signature cannot be empty")
|
||||
ErrDIDEmpty = errors.Register(ModuleName, 11, "DID cannot be empty")
|
||||
ErrSaltEmpty = errors.Register(ModuleName, 12, "salt cannot be empty")
|
||||
ErrAddressEmpty = errors.Register(ModuleName, 13, "address cannot be empty")
|
||||
ErrInvalidAddressFormat = errors.Register(ModuleName, 14, "invalid address format")
|
||||
ErrInvalidAuthorityFormat = errors.Register(ModuleName, 15, "invalid authority format")
|
||||
|
||||
// Record management errors (16-25)
|
||||
ErrRecordNotFound = errors.Register(ModuleName, 16, "record not found")
|
||||
ErrRecordSizeExceeded = errors.Register(ModuleName, 17, "record size exceeds maximum allowed")
|
||||
ErrRecordAlreadyExists = errors.Register(ModuleName, 18, "record already exists")
|
||||
ErrRecordDataInvalid = errors.Register(ModuleName, 19, "record data is invalid")
|
||||
ErrRecordSchemaInvalid = errors.Register(ModuleName, 20, "record schema is invalid")
|
||||
ErrRecordEncrypted = errors.Register(ModuleName, 21, "record is encrypted")
|
||||
ErrRecordDecryption = errors.Register(ModuleName, 22, "failed to decrypt record")
|
||||
ErrRecordEncryption = errors.Register(ModuleName, 23, "failed to encrypt record")
|
||||
ErrRecordSignature = errors.Register(ModuleName, 24, "invalid record signature")
|
||||
ErrRecordPermission = errors.Register(ModuleName, 25, "insufficient permissions for record")
|
||||
|
||||
// Protocol management errors (26-35)
|
||||
ErrProtocolNotFound = errors.Register(ModuleName, 26, "protocol not found")
|
||||
ErrProtocolAlreadyExists = errors.Register(ModuleName, 27, "protocol already exists")
|
||||
ErrProtocolLimitReached = errors.Register(ModuleName, 28, "protocol limit reached for DWN")
|
||||
ErrProtocolInvalid = errors.Register(ModuleName, 29, "protocol definition is invalid")
|
||||
ErrProtocolPermission = errors.Register(
|
||||
ModuleName,
|
||||
30,
|
||||
"insufficient permissions for protocol",
|
||||
)
|
||||
ErrProtocolVersionInvalid = errors.Register(ModuleName, 31, "protocol version is invalid")
|
||||
ErrProtocolURIInvalid = errors.Register(ModuleName, 32, "protocol URI is invalid")
|
||||
ErrProtocolConfigInvalid = errors.Register(ModuleName, 33, "protocol configuration is invalid")
|
||||
ErrProtocolRuleInvalid = errors.Register(ModuleName, 34, "protocol rule is invalid")
|
||||
ErrProtocolActionInvalid = errors.Register(ModuleName, 35, "protocol action is invalid")
|
||||
|
||||
// Permission management errors (36-46)
|
||||
ErrPermissionNotFound = errors.Register(ModuleName, 36, "permission not found")
|
||||
ErrPermissionAlreadyExists = errors.Register(ModuleName, 37, "permission already exists")
|
||||
ErrPermissionLimitReached = errors.Register(
|
||||
ModuleName,
|
||||
38,
|
||||
"permission limit reached for DWN",
|
||||
)
|
||||
ErrPermissionAlreadyRevoked = errors.Register(ModuleName, 39, "permission already revoked")
|
||||
ErrPermissionInvalid = errors.Register(ModuleName, 40, "permission is invalid")
|
||||
ErrPermissionExpired = errors.Register(ModuleName, 41, "permission has expired")
|
||||
ErrPermissionScope = errors.Register(ModuleName, 42, "permission scope is invalid")
|
||||
ErrPermissionGrantInvalid = errors.Register(ModuleName, 43, "permission grant is invalid")
|
||||
ErrPermissionDenied = errors.Register(ModuleName, 44, "permission denied")
|
||||
ErrPermissionInherited = errors.Register(
|
||||
ModuleName,
|
||||
45,
|
||||
"cannot modify inherited permission",
|
||||
)
|
||||
ErrInvalidUCANToken = errors.Register(
|
||||
ModuleName,
|
||||
46,
|
||||
"UCAN token is invalid or insufficient",
|
||||
)
|
||||
|
||||
// Vault management errors (47-56)
|
||||
ErrVaultNotFound = errors.Register(ModuleName, 47, "vault not found")
|
||||
ErrVaultAlreadyExists = errors.Register(ModuleName, 48, "vault already exists")
|
||||
ErrVaultNotInitialized = errors.Register(ModuleName, 49, "vault not initialized")
|
||||
ErrVaultInitializationFailed = errors.Register(ModuleName, 50, "vault initialization failed")
|
||||
ErrVaultOperationFailed = errors.Register(ModuleName, 51, "vault operation failed")
|
||||
ErrVaultPermission = errors.Register(ModuleName, 52, "insufficient vault permissions")
|
||||
ErrVaultKeyNotFound = errors.Register(ModuleName, 53, "vault key not found")
|
||||
ErrVaultKeyInvalid = errors.Register(ModuleName, 54, "vault key is invalid")
|
||||
ErrVaultSecretInvalid = errors.Register(ModuleName, 55, "vault secret is invalid")
|
||||
ErrVaultLocked = errors.Register(ModuleName, 56, "vault is locked")
|
||||
|
||||
// Wallet derivation errors (57-68)
|
||||
ErrWalletDerivationFailed = errors.Register(
|
||||
ModuleName,
|
||||
57,
|
||||
"failed to derive wallet addresses",
|
||||
)
|
||||
ErrWalletCreateFailed = errors.Register(ModuleName, 58, "failed to create wallet")
|
||||
ErrWalletSignFailed = errors.Register(ModuleName, 59, "failed to sign message")
|
||||
ErrWalletVerifyFailed = errors.Register(ModuleName, 60, "failed to verify signature")
|
||||
ErrWalletAddressMismatch = errors.Register(ModuleName, 61, "wallet address mismatch")
|
||||
ErrWalletKeyInvalid = errors.Register(ModuleName, 62, "wallet key is invalid")
|
||||
ErrWalletSeedInvalid = errors.Register(ModuleName, 63, "wallet seed is invalid")
|
||||
ErrWalletPathInvalid = errors.Register(
|
||||
ModuleName,
|
||||
64,
|
||||
"wallet derivation path is invalid",
|
||||
)
|
||||
ErrWalletTypeUnsupported = errors.Register(ModuleName, 65, "wallet type is unsupported")
|
||||
ErrWalletNotFound = errors.Register(ModuleName, 66, "wallet not found")
|
||||
ErrUnsupportedTransactionType = errors.Register(ModuleName, 67, "unsupported transaction type")
|
||||
ErrWalletOperationFailed = errors.Register(ModuleName, 68, "wallet operation failed")
|
||||
|
||||
// Cryptographic errors (69-78)
|
||||
ErrCryptographicOperation = errors.Register(ModuleName, 69, "cryptographic operation failed")
|
||||
ErrSignatureVerification = errors.Register(ModuleName, 70, "signature verification failed")
|
||||
ErrKeyGeneration = errors.Register(ModuleName, 71, "key generation failed")
|
||||
ErrHashGeneration = errors.Register(ModuleName, 72, "hash generation failed")
|
||||
ErrEncryptionFailed = errors.Register(ModuleName, 73, "encryption failed")
|
||||
ErrDecryptionFailed = errors.Register(ModuleName, 74, "decryption failed")
|
||||
ErrKeyExchange = errors.Register(ModuleName, 75, "key exchange failed")
|
||||
ErrKeyDerivation = errors.Register(ModuleName, 76, "key derivation failed")
|
||||
ErrCertificateInvalid = errors.Register(ModuleName, 77, "certificate is invalid")
|
||||
ErrCertificateExpired = errors.Register(ModuleName, 78, "certificate has expired")
|
||||
|
||||
// Storage and state errors (79-88)
|
||||
ErrStorageOperation = errors.Register(ModuleName, 79, "storage operation failed")
|
||||
ErrStateCorrupted = errors.Register(ModuleName, 80, "state is corrupted")
|
||||
ErrStateMismatch = errors.Register(ModuleName, 81, "state mismatch")
|
||||
ErrStateNotFound = errors.Register(ModuleName, 82, "state not found")
|
||||
ErrStateInvalid = errors.Register(ModuleName, 83, "state is invalid")
|
||||
ErrStateConflict = errors.Register(ModuleName, 84, "state conflict")
|
||||
ErrStateLocked = errors.Register(ModuleName, 85, "state is locked")
|
||||
ErrStateExpired = errors.Register(ModuleName, 86, "state has expired")
|
||||
ErrStatePermission = errors.Register(ModuleName, 87, "insufficient state permissions")
|
||||
ErrStateVersion = errors.Register(ModuleName, 88, "state version mismatch")
|
||||
|
||||
// Network and communication errors (89-98)
|
||||
ErrNetworkOperation = errors.Register(ModuleName, 89, "network operation failed")
|
||||
ErrConnectionFailed = errors.Register(ModuleName, 90, "connection failed")
|
||||
ErrTimeoutExceeded = errors.Register(ModuleName, 91, "timeout exceeded")
|
||||
ErrRateLimitExceeded = errors.Register(ModuleName, 92, "rate limit exceeded")
|
||||
ErrQuotaExceeded = errors.Register(ModuleName, 93, "quota exceeded")
|
||||
ErrResourceExhausted = errors.Register(ModuleName, 94, "resource exhausted")
|
||||
ErrServiceUnavailable = errors.Register(ModuleName, 95, "service unavailable")
|
||||
ErrServiceTimeout = errors.Register(ModuleName, 96, "service timeout")
|
||||
ErrServiceError = errors.Register(ModuleName, 97, "service error")
|
||||
ErrServiceMaintenance = errors.Register(ModuleName, 98, "service under maintenance")
|
||||
|
||||
// Generic operation errors (99-102)
|
||||
ErrNotImplemented = errors.Register(ModuleName, 99, "operation not implemented")
|
||||
ErrOperationFailed = errors.Register(ModuleName, 100, "operation failed")
|
||||
ErrInternalError = errors.Register(ModuleName, 101, "internal error")
|
||||
ErrUnknownError = errors.Register(ModuleName, 102, "unknown error")
|
||||
|
||||
// Service verification errors (103-104)
|
||||
ErrServiceNotVerified = errors.Register(ModuleName, 103, "service not verified for domain")
|
||||
ErrUnauthorized = errors.Register(ModuleName, 104, "unauthorized access")
|
||||
|
||||
// Fee grant and wallet sponsorship errors (105-115)
|
||||
ErrInvalidWalletAddress = errors.Register(ModuleName, 105, "invalid wallet address")
|
||||
ErrInvalidSpendLimit = errors.Register(ModuleName, 106, "invalid spend limit")
|
||||
ErrFeeGrantNotFound = errors.Register(ModuleName, 107, "fee grant not found")
|
||||
ErrFeeGrantAlreadyExists = errors.Register(ModuleName, 108, "fee grant already exists")
|
||||
ErrWalletNotSponsorable = errors.Register(ModuleName, 109, "wallet is not sponsorable")
|
||||
ErrSelfGrantNotAllowed = errors.Register(ModuleName, 110, "self grants are not allowed")
|
||||
ErrDailyLimitExceeded = errors.Register(ModuleName, 111, "daily limit exceeded")
|
||||
ErrMessageTypeNotAllowed = errors.Register(ModuleName, 112, "message type not allowed")
|
||||
ErrSponsorshipExpired = errors.Register(ModuleName, 113, "sponsorship has expired")
|
||||
ErrInsufficientSponsorBalance = errors.Register(ModuleName, 114, "insufficient sponsor balance")
|
||||
ErrGasEstimationFailed = errors.Register(ModuleName, 115, "gas estimation failed")
|
||||
ErrFeeGrantExhausted = errors.Register(ModuleName, 116, "fee grant has been exhausted")
|
||||
|
||||
// IPFS errors (117-126)
|
||||
ErrIPFSClientNotAvailable = errors.Register(ModuleName, 117, "IPFS client not available")
|
||||
)
|
||||
@@ -0,0 +1,391 @@
|
||||
package types_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/sonr-io/sonr/x/dwn/types"
|
||||
)
|
||||
|
||||
func TestErrorDefinitions(t *testing.T) {
|
||||
// Test that all errors are properly defined
|
||||
require.NotNil(t, types.ErrInvalidRequest)
|
||||
require.NotNil(t, types.ErrRequestCannotBeNil)
|
||||
require.NotNil(t, types.ErrTargetDIDEmpty)
|
||||
require.NotNil(t, types.ErrRecordIDEmpty)
|
||||
require.NotNil(t, types.ErrProtocolURIEmpty)
|
||||
require.NotNil(t, types.ErrVaultIDEmpty)
|
||||
require.NotNil(t, types.ErrPermissionIDEmpty)
|
||||
require.NotNil(t, types.ErrPublicKeyEmpty)
|
||||
require.NotNil(t, types.ErrMessageEmpty)
|
||||
require.NotNil(t, types.ErrSignatureEmpty)
|
||||
require.NotNil(t, types.ErrDIDEmpty)
|
||||
require.NotNil(t, types.ErrSaltEmpty)
|
||||
require.NotNil(t, types.ErrAddressEmpty)
|
||||
require.NotNil(t, types.ErrInvalidAddressFormat)
|
||||
require.NotNil(t, types.ErrInvalidAuthorityFormat)
|
||||
|
||||
// Record management errors
|
||||
require.NotNil(t, types.ErrRecordNotFound)
|
||||
require.NotNil(t, types.ErrRecordSizeExceeded)
|
||||
require.NotNil(t, types.ErrRecordAlreadyExists)
|
||||
require.NotNil(t, types.ErrRecordDataInvalid)
|
||||
require.NotNil(t, types.ErrRecordSchemaInvalid)
|
||||
require.NotNil(t, types.ErrRecordEncrypted)
|
||||
require.NotNil(t, types.ErrRecordDecryption)
|
||||
require.NotNil(t, types.ErrRecordEncryption)
|
||||
require.NotNil(t, types.ErrRecordSignature)
|
||||
require.NotNil(t, types.ErrRecordPermission)
|
||||
|
||||
// Protocol management errors
|
||||
require.NotNil(t, types.ErrProtocolNotFound)
|
||||
require.NotNil(t, types.ErrProtocolAlreadyExists)
|
||||
require.NotNil(t, types.ErrProtocolLimitReached)
|
||||
require.NotNil(t, types.ErrProtocolInvalid)
|
||||
require.NotNil(t, types.ErrProtocolPermission)
|
||||
require.NotNil(t, types.ErrProtocolVersionInvalid)
|
||||
require.NotNil(t, types.ErrProtocolURIInvalid)
|
||||
require.NotNil(t, types.ErrProtocolConfigInvalid)
|
||||
require.NotNil(t, types.ErrProtocolRuleInvalid)
|
||||
require.NotNil(t, types.ErrProtocolActionInvalid)
|
||||
|
||||
// Permission management errors
|
||||
require.NotNil(t, types.ErrPermissionNotFound)
|
||||
require.NotNil(t, types.ErrPermissionAlreadyExists)
|
||||
require.NotNil(t, types.ErrPermissionLimitReached)
|
||||
require.NotNil(t, types.ErrPermissionAlreadyRevoked)
|
||||
require.NotNil(t, types.ErrPermissionInvalid)
|
||||
require.NotNil(t, types.ErrPermissionExpired)
|
||||
require.NotNil(t, types.ErrPermissionScope)
|
||||
require.NotNil(t, types.ErrPermissionGrantInvalid)
|
||||
require.NotNil(t, types.ErrPermissionDenied)
|
||||
require.NotNil(t, types.ErrPermissionInherited)
|
||||
require.NotNil(t, types.ErrInvalidUCANToken)
|
||||
|
||||
// Vault management errors
|
||||
require.NotNil(t, types.ErrVaultNotFound)
|
||||
require.NotNil(t, types.ErrVaultAlreadyExists)
|
||||
require.NotNil(t, types.ErrVaultNotInitialized)
|
||||
require.NotNil(t, types.ErrVaultInitializationFailed)
|
||||
require.NotNil(t, types.ErrVaultOperationFailed)
|
||||
require.NotNil(t, types.ErrVaultPermission)
|
||||
require.NotNil(t, types.ErrVaultKeyNotFound)
|
||||
require.NotNil(t, types.ErrVaultKeyInvalid)
|
||||
require.NotNil(t, types.ErrVaultSecretInvalid)
|
||||
require.NotNil(t, types.ErrVaultLocked)
|
||||
|
||||
// Wallet derivation errors
|
||||
require.NotNil(t, types.ErrWalletDerivationFailed)
|
||||
require.NotNil(t, types.ErrWalletCreateFailed)
|
||||
require.NotNil(t, types.ErrWalletSignFailed)
|
||||
require.NotNil(t, types.ErrWalletVerifyFailed)
|
||||
require.NotNil(t, types.ErrWalletAddressMismatch)
|
||||
require.NotNil(t, types.ErrWalletKeyInvalid)
|
||||
require.NotNil(t, types.ErrWalletSeedInvalid)
|
||||
require.NotNil(t, types.ErrWalletPathInvalid)
|
||||
require.NotNil(t, types.ErrWalletTypeUnsupported)
|
||||
require.NotNil(t, types.ErrWalletNotFound)
|
||||
|
||||
// Cryptographic errors
|
||||
require.NotNil(t, types.ErrCryptographicOperation)
|
||||
require.NotNil(t, types.ErrSignatureVerification)
|
||||
require.NotNil(t, types.ErrKeyGeneration)
|
||||
require.NotNil(t, types.ErrHashGeneration)
|
||||
require.NotNil(t, types.ErrEncryptionFailed)
|
||||
require.NotNil(t, types.ErrDecryptionFailed)
|
||||
require.NotNil(t, types.ErrKeyExchange)
|
||||
require.NotNil(t, types.ErrKeyDerivation)
|
||||
require.NotNil(t, types.ErrCertificateInvalid)
|
||||
require.NotNil(t, types.ErrCertificateExpired)
|
||||
|
||||
// Storage and state errors
|
||||
require.NotNil(t, types.ErrStorageOperation)
|
||||
require.NotNil(t, types.ErrStateCorrupted)
|
||||
require.NotNil(t, types.ErrStateMismatch)
|
||||
require.NotNil(t, types.ErrStateNotFound)
|
||||
require.NotNil(t, types.ErrStateInvalid)
|
||||
require.NotNil(t, types.ErrStateConflict)
|
||||
require.NotNil(t, types.ErrStateLocked)
|
||||
require.NotNil(t, types.ErrStateExpired)
|
||||
require.NotNil(t, types.ErrStatePermission)
|
||||
require.NotNil(t, types.ErrStateVersion)
|
||||
|
||||
// Network and communication errors
|
||||
require.NotNil(t, types.ErrNetworkOperation)
|
||||
require.NotNil(t, types.ErrConnectionFailed)
|
||||
require.NotNil(t, types.ErrTimeoutExceeded)
|
||||
require.NotNil(t, types.ErrRateLimitExceeded)
|
||||
require.NotNil(t, types.ErrQuotaExceeded)
|
||||
require.NotNil(t, types.ErrResourceExhausted)
|
||||
require.NotNil(t, types.ErrServiceUnavailable)
|
||||
require.NotNil(t, types.ErrServiceTimeout)
|
||||
require.NotNil(t, types.ErrServiceError)
|
||||
require.NotNil(t, types.ErrServiceMaintenance)
|
||||
|
||||
// Generic operation errors
|
||||
require.NotNil(t, types.ErrNotImplemented)
|
||||
require.NotNil(t, types.ErrOperationFailed)
|
||||
require.NotNil(t, types.ErrInternalError)
|
||||
require.NotNil(t, types.ErrUnknownError)
|
||||
|
||||
// Service verification errors
|
||||
require.NotNil(t, types.ErrServiceNotVerified)
|
||||
}
|
||||
|
||||
func TestErrorMessages(t *testing.T) {
|
||||
// Test that error messages are correct
|
||||
require.Contains(t, types.ErrInvalidRequest.Error(), "invalid request")
|
||||
require.Contains(t, types.ErrRequestCannotBeNil.Error(), "request cannot be nil")
|
||||
require.Contains(t, types.ErrTargetDIDEmpty.Error(), "target DID cannot be empty")
|
||||
require.Contains(t, types.ErrRecordIDEmpty.Error(), "record ID cannot be empty")
|
||||
require.Contains(t, types.ErrProtocolURIEmpty.Error(), "protocol URI cannot be empty")
|
||||
require.Contains(t, types.ErrVaultIDEmpty.Error(), "vault ID cannot be empty")
|
||||
require.Contains(t, types.ErrPermissionIDEmpty.Error(), "permission ID cannot be empty")
|
||||
require.Contains(t, types.ErrPublicKeyEmpty.Error(), "public key cannot be empty")
|
||||
require.Contains(t, types.ErrMessageEmpty.Error(), "message cannot be empty")
|
||||
require.Contains(t, types.ErrSignatureEmpty.Error(), "signature cannot be empty")
|
||||
require.Contains(t, types.ErrDIDEmpty.Error(), "DID cannot be empty")
|
||||
require.Contains(t, types.ErrSaltEmpty.Error(), "salt cannot be empty")
|
||||
require.Contains(t, types.ErrAddressEmpty.Error(), "address cannot be empty")
|
||||
require.Contains(t, types.ErrInvalidAddressFormat.Error(), "invalid address format")
|
||||
require.Contains(t, types.ErrInvalidAuthorityFormat.Error(), "invalid authority format")
|
||||
|
||||
// Record management error messages
|
||||
require.Contains(t, types.ErrRecordNotFound.Error(), "record not found")
|
||||
require.Contains(t, types.ErrRecordSizeExceeded.Error(), "record size exceeds maximum allowed")
|
||||
require.Contains(t, types.ErrRecordAlreadyExists.Error(), "record already exists")
|
||||
require.Contains(t, types.ErrRecordDataInvalid.Error(), "record data is invalid")
|
||||
require.Contains(t, types.ErrRecordSchemaInvalid.Error(), "record schema is invalid")
|
||||
require.Contains(t, types.ErrRecordEncrypted.Error(), "record is encrypted")
|
||||
require.Contains(t, types.ErrRecordDecryption.Error(), "failed to decrypt record")
|
||||
require.Contains(t, types.ErrRecordEncryption.Error(), "failed to encrypt record")
|
||||
require.Contains(t, types.ErrRecordSignature.Error(), "invalid record signature")
|
||||
require.Contains(t, types.ErrRecordPermission.Error(), "insufficient permissions for record")
|
||||
|
||||
// Protocol management error messages
|
||||
require.Contains(t, types.ErrProtocolNotFound.Error(), "protocol not found")
|
||||
require.Contains(t, types.ErrProtocolAlreadyExists.Error(), "protocol already exists")
|
||||
require.Contains(t, types.ErrProtocolLimitReached.Error(), "protocol limit reached for DWN")
|
||||
require.Contains(t, types.ErrProtocolInvalid.Error(), "protocol definition is invalid")
|
||||
require.Contains(
|
||||
t,
|
||||
types.ErrProtocolPermission.Error(),
|
||||
"insufficient permissions for protocol",
|
||||
)
|
||||
|
||||
// Permission management error messages
|
||||
require.Contains(t, types.ErrPermissionNotFound.Error(), "permission not found")
|
||||
require.Contains(t, types.ErrPermissionAlreadyExists.Error(), "permission already exists")
|
||||
require.Contains(t, types.ErrPermissionLimitReached.Error(), "permission limit reached for DWN")
|
||||
require.Contains(t, types.ErrPermissionAlreadyRevoked.Error(), "permission already revoked")
|
||||
require.Contains(t, types.ErrPermissionInvalid.Error(), "permission is invalid")
|
||||
require.Contains(t, types.ErrPermissionExpired.Error(), "permission has expired")
|
||||
require.Contains(t, types.ErrPermissionScope.Error(), "permission scope is invalid")
|
||||
require.Contains(t, types.ErrPermissionGrantInvalid.Error(), "permission grant is invalid")
|
||||
require.Contains(t, types.ErrPermissionDenied.Error(), "permission denied")
|
||||
require.Contains(t, types.ErrPermissionInherited.Error(), "cannot modify inherited permission")
|
||||
require.Contains(t, types.ErrInvalidUCANToken.Error(), "UCAN token is invalid or insufficient")
|
||||
|
||||
// Vault management error messages
|
||||
require.Contains(t, types.ErrVaultNotFound.Error(), "vault not found")
|
||||
require.Contains(t, types.ErrVaultAlreadyExists.Error(), "vault already exists")
|
||||
require.Contains(t, types.ErrVaultNotInitialized.Error(), "vault not initialized")
|
||||
require.Contains(t, types.ErrVaultInitializationFailed.Error(), "vault initialization failed")
|
||||
require.Contains(t, types.ErrVaultOperationFailed.Error(), "vault operation failed")
|
||||
require.Contains(t, types.ErrVaultPermission.Error(), "insufficient vault permissions")
|
||||
require.Contains(t, types.ErrVaultKeyNotFound.Error(), "vault key not found")
|
||||
require.Contains(t, types.ErrVaultKeyInvalid.Error(), "vault key is invalid")
|
||||
require.Contains(t, types.ErrVaultSecretInvalid.Error(), "vault secret is invalid")
|
||||
require.Contains(t, types.ErrVaultLocked.Error(), "vault is locked")
|
||||
|
||||
// Wallet derivation error messages
|
||||
require.Contains(
|
||||
t,
|
||||
types.ErrWalletDerivationFailed.Error(),
|
||||
"failed to derive wallet addresses",
|
||||
)
|
||||
require.Contains(t, types.ErrWalletCreateFailed.Error(), "failed to create wallet")
|
||||
require.Contains(t, types.ErrWalletSignFailed.Error(), "failed to sign message")
|
||||
require.Contains(t, types.ErrWalletVerifyFailed.Error(), "failed to verify signature")
|
||||
require.Contains(t, types.ErrWalletAddressMismatch.Error(), "wallet address mismatch")
|
||||
require.Contains(t, types.ErrWalletKeyInvalid.Error(), "wallet key is invalid")
|
||||
require.Contains(t, types.ErrWalletSeedInvalid.Error(), "wallet seed is invalid")
|
||||
require.Contains(t, types.ErrWalletPathInvalid.Error(), "wallet derivation path is invalid")
|
||||
require.Contains(t, types.ErrWalletTypeUnsupported.Error(), "wallet type is unsupported")
|
||||
require.Contains(t, types.ErrWalletNotFound.Error(), "wallet not found")
|
||||
|
||||
// Cryptographic error messages
|
||||
require.Contains(t, types.ErrCryptographicOperation.Error(), "cryptographic operation failed")
|
||||
require.Contains(t, types.ErrSignatureVerification.Error(), "signature verification failed")
|
||||
require.Contains(t, types.ErrKeyGeneration.Error(), "key generation failed")
|
||||
require.Contains(t, types.ErrHashGeneration.Error(), "hash generation failed")
|
||||
require.Contains(t, types.ErrEncryptionFailed.Error(), "encryption failed")
|
||||
require.Contains(t, types.ErrDecryptionFailed.Error(), "decryption failed")
|
||||
require.Contains(t, types.ErrKeyExchange.Error(), "key exchange failed")
|
||||
require.Contains(t, types.ErrKeyDerivation.Error(), "key derivation failed")
|
||||
require.Contains(t, types.ErrCertificateInvalid.Error(), "certificate is invalid")
|
||||
require.Contains(t, types.ErrCertificateExpired.Error(), "certificate has expired")
|
||||
|
||||
// Storage and state error messages
|
||||
require.Contains(t, types.ErrStorageOperation.Error(), "storage operation failed")
|
||||
require.Contains(t, types.ErrStateCorrupted.Error(), "state is corrupted")
|
||||
require.Contains(t, types.ErrStateMismatch.Error(), "state mismatch")
|
||||
require.Contains(t, types.ErrStateNotFound.Error(), "state not found")
|
||||
require.Contains(t, types.ErrStateInvalid.Error(), "state is invalid")
|
||||
require.Contains(t, types.ErrStateConflict.Error(), "state conflict")
|
||||
require.Contains(t, types.ErrStateLocked.Error(), "state is locked")
|
||||
require.Contains(t, types.ErrStateExpired.Error(), "state has expired")
|
||||
require.Contains(t, types.ErrStatePermission.Error(), "insufficient state permissions")
|
||||
require.Contains(t, types.ErrStateVersion.Error(), "state version mismatch")
|
||||
|
||||
// Network and communication error messages
|
||||
require.Contains(t, types.ErrNetworkOperation.Error(), "network operation failed")
|
||||
require.Contains(t, types.ErrConnectionFailed.Error(), "connection failed")
|
||||
require.Contains(t, types.ErrTimeoutExceeded.Error(), "timeout exceeded")
|
||||
require.Contains(t, types.ErrRateLimitExceeded.Error(), "rate limit exceeded")
|
||||
require.Contains(t, types.ErrQuotaExceeded.Error(), "quota exceeded")
|
||||
require.Contains(t, types.ErrResourceExhausted.Error(), "resource exhausted")
|
||||
require.Contains(t, types.ErrServiceUnavailable.Error(), "service unavailable")
|
||||
require.Contains(t, types.ErrServiceTimeout.Error(), "service timeout")
|
||||
require.Contains(t, types.ErrServiceError.Error(), "service error")
|
||||
require.Contains(t, types.ErrServiceMaintenance.Error(), "service under maintenance")
|
||||
|
||||
// Generic operation error messages
|
||||
require.Contains(t, types.ErrNotImplemented.Error(), "operation not implemented")
|
||||
require.Contains(t, types.ErrOperationFailed.Error(), "operation failed")
|
||||
require.Contains(t, types.ErrInternalError.Error(), "internal error")
|
||||
require.Contains(t, types.ErrUnknownError.Error(), "unknown error")
|
||||
|
||||
// Service verification error messages
|
||||
require.Contains(t, types.ErrServiceNotVerified.Error(), "service not verified for domain")
|
||||
}
|
||||
|
||||
func TestErrorCodes(t *testing.T) {
|
||||
// Test that error codes are in the expected ranges
|
||||
|
||||
// Request validation errors (1-15)
|
||||
require.Equal(t, uint32(1), types.ErrInvalidRequest.ABCICode())
|
||||
require.Equal(t, uint32(2), types.ErrRequestCannotBeNil.ABCICode())
|
||||
require.Equal(t, uint32(3), types.ErrTargetDIDEmpty.ABCICode())
|
||||
require.Equal(t, uint32(4), types.ErrRecordIDEmpty.ABCICode())
|
||||
require.Equal(t, uint32(5), types.ErrProtocolURIEmpty.ABCICode())
|
||||
require.Equal(t, uint32(6), types.ErrVaultIDEmpty.ABCICode())
|
||||
require.Equal(t, uint32(7), types.ErrPermissionIDEmpty.ABCICode())
|
||||
require.Equal(t, uint32(8), types.ErrPublicKeyEmpty.ABCICode())
|
||||
require.Equal(t, uint32(9), types.ErrMessageEmpty.ABCICode())
|
||||
require.Equal(t, uint32(10), types.ErrSignatureEmpty.ABCICode())
|
||||
require.Equal(t, uint32(11), types.ErrDIDEmpty.ABCICode())
|
||||
require.Equal(t, uint32(12), types.ErrSaltEmpty.ABCICode())
|
||||
require.Equal(t, uint32(13), types.ErrAddressEmpty.ABCICode())
|
||||
require.Equal(t, uint32(14), types.ErrInvalidAddressFormat.ABCICode())
|
||||
require.Equal(t, uint32(15), types.ErrInvalidAuthorityFormat.ABCICode())
|
||||
|
||||
// Record management errors (16-25)
|
||||
require.Equal(t, uint32(16), types.ErrRecordNotFound.ABCICode())
|
||||
require.Equal(t, uint32(17), types.ErrRecordSizeExceeded.ABCICode())
|
||||
require.Equal(t, uint32(18), types.ErrRecordAlreadyExists.ABCICode())
|
||||
require.Equal(t, uint32(19), types.ErrRecordDataInvalid.ABCICode())
|
||||
require.Equal(t, uint32(20), types.ErrRecordSchemaInvalid.ABCICode())
|
||||
require.Equal(t, uint32(21), types.ErrRecordEncrypted.ABCICode())
|
||||
require.Equal(t, uint32(22), types.ErrRecordDecryption.ABCICode())
|
||||
require.Equal(t, uint32(23), types.ErrRecordEncryption.ABCICode())
|
||||
require.Equal(t, uint32(24), types.ErrRecordSignature.ABCICode())
|
||||
require.Equal(t, uint32(25), types.ErrRecordPermission.ABCICode())
|
||||
|
||||
// Protocol management errors (26-35)
|
||||
require.Equal(t, uint32(26), types.ErrProtocolNotFound.ABCICode())
|
||||
require.Equal(t, uint32(27), types.ErrProtocolAlreadyExists.ABCICode())
|
||||
require.Equal(t, uint32(28), types.ErrProtocolLimitReached.ABCICode())
|
||||
require.Equal(t, uint32(29), types.ErrProtocolInvalid.ABCICode())
|
||||
require.Equal(t, uint32(30), types.ErrProtocolPermission.ABCICode())
|
||||
require.Equal(t, uint32(31), types.ErrProtocolVersionInvalid.ABCICode())
|
||||
require.Equal(t, uint32(32), types.ErrProtocolURIInvalid.ABCICode())
|
||||
require.Equal(t, uint32(33), types.ErrProtocolConfigInvalid.ABCICode())
|
||||
require.Equal(t, uint32(34), types.ErrProtocolRuleInvalid.ABCICode())
|
||||
require.Equal(t, uint32(35), types.ErrProtocolActionInvalid.ABCICode())
|
||||
|
||||
// Permission management errors (36-45)
|
||||
require.Equal(t, uint32(36), types.ErrPermissionNotFound.ABCICode())
|
||||
require.Equal(t, uint32(37), types.ErrPermissionAlreadyExists.ABCICode())
|
||||
require.Equal(t, uint32(38), types.ErrPermissionLimitReached.ABCICode())
|
||||
require.Equal(t, uint32(39), types.ErrPermissionAlreadyRevoked.ABCICode())
|
||||
require.Equal(t, uint32(40), types.ErrPermissionInvalid.ABCICode())
|
||||
require.Equal(t, uint32(41), types.ErrPermissionExpired.ABCICode())
|
||||
require.Equal(t, uint32(42), types.ErrPermissionScope.ABCICode())
|
||||
require.Equal(t, uint32(43), types.ErrPermissionGrantInvalid.ABCICode())
|
||||
require.Equal(t, uint32(44), types.ErrPermissionDenied.ABCICode())
|
||||
require.Equal(t, uint32(45), types.ErrPermissionInherited.ABCICode())
|
||||
require.Equal(t, uint32(46), types.ErrInvalidUCANToken.ABCICode())
|
||||
|
||||
// Vault management errors (47-56)
|
||||
require.Equal(t, uint32(47), types.ErrVaultNotFound.ABCICode())
|
||||
require.Equal(t, uint32(48), types.ErrVaultAlreadyExists.ABCICode())
|
||||
require.Equal(t, uint32(49), types.ErrVaultNotInitialized.ABCICode())
|
||||
require.Equal(t, uint32(50), types.ErrVaultInitializationFailed.ABCICode())
|
||||
require.Equal(t, uint32(51), types.ErrVaultOperationFailed.ABCICode())
|
||||
require.Equal(t, uint32(52), types.ErrVaultPermission.ABCICode())
|
||||
require.Equal(t, uint32(53), types.ErrVaultKeyNotFound.ABCICode())
|
||||
require.Equal(t, uint32(54), types.ErrVaultKeyInvalid.ABCICode())
|
||||
require.Equal(t, uint32(55), types.ErrVaultSecretInvalid.ABCICode())
|
||||
require.Equal(t, uint32(56), types.ErrVaultLocked.ABCICode())
|
||||
|
||||
// Wallet derivation errors (57-66)
|
||||
require.Equal(t, uint32(57), types.ErrWalletDerivationFailed.ABCICode())
|
||||
require.Equal(t, uint32(58), types.ErrWalletCreateFailed.ABCICode())
|
||||
require.Equal(t, uint32(59), types.ErrWalletSignFailed.ABCICode())
|
||||
require.Equal(t, uint32(60), types.ErrWalletVerifyFailed.ABCICode())
|
||||
require.Equal(t, uint32(61), types.ErrWalletAddressMismatch.ABCICode())
|
||||
require.Equal(t, uint32(62), types.ErrWalletKeyInvalid.ABCICode())
|
||||
require.Equal(t, uint32(63), types.ErrWalletSeedInvalid.ABCICode())
|
||||
require.Equal(t, uint32(64), types.ErrWalletPathInvalid.ABCICode())
|
||||
require.Equal(t, uint32(65), types.ErrWalletTypeUnsupported.ABCICode())
|
||||
require.Equal(t, uint32(66), types.ErrWalletNotFound.ABCICode())
|
||||
require.Equal(t, uint32(67), types.ErrUnsupportedTransactionType.ABCICode())
|
||||
require.Equal(t, uint32(68), types.ErrWalletOperationFailed.ABCICode())
|
||||
|
||||
// Cryptographic errors (69-78)
|
||||
require.Equal(t, uint32(69), types.ErrCryptographicOperation.ABCICode())
|
||||
require.Equal(t, uint32(70), types.ErrSignatureVerification.ABCICode())
|
||||
require.Equal(t, uint32(71), types.ErrKeyGeneration.ABCICode())
|
||||
require.Equal(t, uint32(72), types.ErrHashGeneration.ABCICode())
|
||||
require.Equal(t, uint32(73), types.ErrEncryptionFailed.ABCICode())
|
||||
require.Equal(t, uint32(74), types.ErrDecryptionFailed.ABCICode())
|
||||
require.Equal(t, uint32(75), types.ErrKeyExchange.ABCICode())
|
||||
require.Equal(t, uint32(76), types.ErrKeyDerivation.ABCICode())
|
||||
require.Equal(t, uint32(77), types.ErrCertificateInvalid.ABCICode())
|
||||
require.Equal(t, uint32(78), types.ErrCertificateExpired.ABCICode())
|
||||
|
||||
// Storage and state errors (79-88)
|
||||
require.Equal(t, uint32(79), types.ErrStorageOperation.ABCICode())
|
||||
require.Equal(t, uint32(80), types.ErrStateCorrupted.ABCICode())
|
||||
require.Equal(t, uint32(81), types.ErrStateMismatch.ABCICode())
|
||||
require.Equal(t, uint32(82), types.ErrStateNotFound.ABCICode())
|
||||
require.Equal(t, uint32(83), types.ErrStateInvalid.ABCICode())
|
||||
require.Equal(t, uint32(84), types.ErrStateConflict.ABCICode())
|
||||
require.Equal(t, uint32(85), types.ErrStateLocked.ABCICode())
|
||||
require.Equal(t, uint32(86), types.ErrStateExpired.ABCICode())
|
||||
require.Equal(t, uint32(87), types.ErrStatePermission.ABCICode())
|
||||
require.Equal(t, uint32(88), types.ErrStateVersion.ABCICode())
|
||||
|
||||
// Network and communication errors (89-98)
|
||||
require.Equal(t, uint32(89), types.ErrNetworkOperation.ABCICode())
|
||||
require.Equal(t, uint32(90), types.ErrConnectionFailed.ABCICode())
|
||||
require.Equal(t, uint32(91), types.ErrTimeoutExceeded.ABCICode())
|
||||
require.Equal(t, uint32(92), types.ErrRateLimitExceeded.ABCICode())
|
||||
require.Equal(t, uint32(93), types.ErrQuotaExceeded.ABCICode())
|
||||
require.Equal(t, uint32(94), types.ErrResourceExhausted.ABCICode())
|
||||
require.Equal(t, uint32(95), types.ErrServiceUnavailable.ABCICode())
|
||||
require.Equal(t, uint32(96), types.ErrServiceTimeout.ABCICode())
|
||||
require.Equal(t, uint32(97), types.ErrServiceError.ABCICode())
|
||||
require.Equal(t, uint32(98), types.ErrServiceMaintenance.ABCICode())
|
||||
|
||||
// Generic operation errors (99-102)
|
||||
require.Equal(t, uint32(99), types.ErrNotImplemented.ABCICode())
|
||||
require.Equal(t, uint32(100), types.ErrOperationFailed.ABCICode())
|
||||
require.Equal(t, uint32(101), types.ErrInternalError.ABCICode())
|
||||
require.Equal(t, uint32(102), types.ErrUnknownError.ABCICode())
|
||||
|
||||
// Service verification errors (103)
|
||||
require.Equal(t, uint32(103), types.ErrServiceNotVerified.ABCICode())
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,35 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
didtypes "github.com/sonr-io/sonr/x/did/types"
|
||||
svctypes "github.com/sonr-io/sonr/x/svc/types"
|
||||
)
|
||||
|
||||
// DIDKeeper interface defines the methods needed from the DID keeper
|
||||
type DIDKeeper interface {
|
||||
// ResolveDID resolves a DID to its DID document
|
||||
ResolveDID(
|
||||
ctx context.Context,
|
||||
did string,
|
||||
) (*didtypes.DIDDocument, *didtypes.DIDDocumentMetadata, error)
|
||||
|
||||
// GetDIDDocument gets a DID document by its ID
|
||||
GetDIDDocument(ctx context.Context, did string) (*didtypes.DIDDocument, error)
|
||||
}
|
||||
|
||||
// ServiceKeeper interface defines the methods needed from the Service keeper
|
||||
type ServiceKeeper interface {
|
||||
// VerifyServiceRegistration verifies service registration and domain ownership
|
||||
VerifyServiceRegistration(ctx context.Context, serviceID string, domain string) (bool, error)
|
||||
|
||||
// GetService gets service by ID
|
||||
GetService(ctx context.Context, serviceID string) (*svctypes.Service, error)
|
||||
|
||||
// IsDomainVerified checks if domain is verified
|
||||
IsDomainVerified(ctx context.Context, domain string, owner string) (bool, error)
|
||||
|
||||
// GetServicesByDomain gets services by domain
|
||||
GetServicesByDomain(ctx context.Context, domain string) ([]svctypes.Service, error)
|
||||
}
|
||||
Regular → Executable
-77
@@ -1,25 +1,11 @@
|
||||
package types
|
||||
|
||||
// Capability hierarchy for smart account operations
|
||||
// ----------------------------------------------
|
||||
// OWNER
|
||||
//
|
||||
// └─ OPERATOR
|
||||
// ├─ EXECUTE
|
||||
// ├─ PROPOSE
|
||||
// └─ SIGN
|
||||
// └─ SET_POLICY
|
||||
// └─ SET_THRESHOLD
|
||||
// └─ RECOVER
|
||||
// └─ SOCIAL
|
||||
//
|
||||
// DefaultIndex is the default global index
|
||||
const DefaultIndex uint64 = 1
|
||||
|
||||
// DefaultGenesis returns the default genesis state
|
||||
func DefaultGenesis() *GenesisState {
|
||||
return &GenesisState{
|
||||
// this line is used by starport scaffolding # genesis/types/default
|
||||
Params: DefaultParams(),
|
||||
}
|
||||
}
|
||||
@@ -27,68 +13,5 @@ func DefaultGenesis() *GenesisState {
|
||||
// Validate performs basic genesis state validation returning an error upon any
|
||||
// failure.
|
||||
func (gs GenesisState) Validate() error {
|
||||
// this line is used by starport scaffolding # genesis/types/validate
|
||||
|
||||
return gs.Params.Validate()
|
||||
}
|
||||
|
||||
// Equal checks if two Attenuation are equal
|
||||
func (a *Attenuation) Equal(that *Attenuation) bool {
|
||||
if that == nil {
|
||||
return false
|
||||
}
|
||||
if a.Resource != nil {
|
||||
if that.Resource == nil {
|
||||
return false
|
||||
}
|
||||
if !a.Resource.Equal(that.Resource) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if len(a.Capabilities) != len(that.Capabilities) {
|
||||
return false
|
||||
}
|
||||
for i := range a.Capabilities {
|
||||
if !a.Capabilities[i].Equal(that.Capabilities[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Equal checks if two Capability are equal
|
||||
func (c *Capability) Equal(that *Capability) bool {
|
||||
if that == nil {
|
||||
return false
|
||||
}
|
||||
if c.Name != that.Name {
|
||||
return false
|
||||
}
|
||||
if c.Parent != that.Parent {
|
||||
return false
|
||||
}
|
||||
// TODO: check description
|
||||
if len(c.Resources) != len(that.Resources) {
|
||||
return false
|
||||
}
|
||||
for i := range c.Resources {
|
||||
if c.Resources[i] != that.Resources[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Equal checks if two Resource are equal
|
||||
func (r *Resource) Equal(that *Resource) bool {
|
||||
if that == nil {
|
||||
return false
|
||||
}
|
||||
if r.Kind != that.Kind {
|
||||
return false
|
||||
}
|
||||
if r.Template != that.Template {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
+791
-627
File diff suppressed because it is too large
Load Diff
@@ -1,38 +0,0 @@
|
||||
package types_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/sonr-io/snrd/x/dwn/types"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGenesisState_Validate(t *testing.T) {
|
||||
tests := []struct {
|
||||
desc string
|
||||
genState *types.GenesisState
|
||||
valid bool
|
||||
}{
|
||||
{
|
||||
desc: "default is valid",
|
||||
genState: types.DefaultGenesis(),
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
desc: "valid genesis state",
|
||||
genState: &types.GenesisState{},
|
||||
valid: true,
|
||||
},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
err := tc.genState.Validate()
|
||||
if tc.valid {
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
require.Error(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Regular → Executable
+4
-4
@@ -6,10 +6,8 @@ import (
|
||||
ormv1alpha1 "cosmossdk.io/api/cosmos/orm/v1alpha1"
|
||||
)
|
||||
|
||||
var (
|
||||
// ParamsKey saves the current module params.
|
||||
ParamsKey = collections.NewPrefix(0)
|
||||
)
|
||||
// ParamsKey saves the current module params.
|
||||
var ParamsKey = collections.NewPrefix(0)
|
||||
|
||||
const (
|
||||
ModuleName = "dwn"
|
||||
@@ -17,6 +15,8 @@ const (
|
||||
StoreKey = ModuleName
|
||||
|
||||
QuerierRoute = ModuleName
|
||||
|
||||
RouterKey = ModuleName
|
||||
)
|
||||
|
||||
var ORMModuleSchema = ormv1alpha1.ModuleSchemaDescriptor{
|
||||
|
||||
Regular → Executable
+163
-16
@@ -3,22 +3,28 @@ package types
|
||||
import (
|
||||
"cosmossdk.io/errors"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/address"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
)
|
||||
|
||||
var _ sdk.Msg = &MsgUpdateParams{}
|
||||
|
||||
// ╭───────────────────────────────────────────────────────────╮
|
||||
// │ MsgUpdateParams type definition │
|
||||
// ╰───────────────────────────────────────────────────────────╯
|
||||
var (
|
||||
_ sdk.Msg = &MsgUpdateParams{}
|
||||
_ sdk.Msg = &MsgRecordsWrite{}
|
||||
_ sdk.Msg = &MsgRecordsDelete{}
|
||||
_ sdk.Msg = &MsgProtocolsConfigure{}
|
||||
_ sdk.Msg = &MsgPermissionsGrant{}
|
||||
_ sdk.Msg = &MsgPermissionsRevoke{}
|
||||
_ sdk.Msg = &MsgRotateVaultKeys{}
|
||||
)
|
||||
|
||||
// NewMsgUpdateParams creates new instance of MsgUpdateParams
|
||||
func NewMsgUpdateParams(
|
||||
sender sdk.Address,
|
||||
someValue bool,
|
||||
params Params,
|
||||
) *MsgUpdateParams {
|
||||
return &MsgUpdateParams{
|
||||
Authority: sender.String(),
|
||||
Params: Params{},
|
||||
Params: params,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,22 +34,163 @@ func (msg MsgUpdateParams) Route() string { return ModuleName }
|
||||
// Type returns the the action
|
||||
func (msg MsgUpdateParams) Type() string { return "update_params" }
|
||||
|
||||
// GetSignBytes implements the LegacyMsg interface.
|
||||
func (msg MsgUpdateParams) GetSignBytes() []byte {
|
||||
return sdk.MustSortJSON(AminoCdc.MustMarshalJSON(&msg))
|
||||
// GetSignBytes implements the Msg interface.
|
||||
func (m MsgUpdateParams) GetSignBytes() []byte {
|
||||
return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&m))
|
||||
}
|
||||
|
||||
// GetSigners returns the expected signers for a MsgUpdateParams message.
|
||||
func (msg *MsgUpdateParams) GetSigners() []sdk.AccAddress {
|
||||
addr, _ := sdk.AccAddressFromBech32(msg.Authority)
|
||||
func (m *MsgUpdateParams) GetSigners() []sdk.AccAddress {
|
||||
addr, _ := sdk.AccAddressFromBech32(m.Authority)
|
||||
return []sdk.AccAddress{addr}
|
||||
}
|
||||
|
||||
// ValidateBasic does a sanity check on the provided data.
|
||||
func (msg *MsgUpdateParams) Validate() error {
|
||||
if _, err := sdk.AccAddressFromBech32(msg.Authority); err != nil {
|
||||
// Validate does a sanity check on the provided data.
|
||||
func (m *MsgUpdateParams) Validate() error {
|
||||
if _, err := sdk.AccAddressFromBech32(m.Authority); err != nil {
|
||||
return errors.Wrap(err, "invalid authority address")
|
||||
}
|
||||
|
||||
return msg.Params.Validate()
|
||||
return m.Params.Validate()
|
||||
}
|
||||
|
||||
// MsgRecordsWrite implementation
|
||||
func (m *MsgRecordsWrite) GetSigners() []sdk.AccAddress {
|
||||
addr, _ := sdk.AccAddressFromBech32(m.Author)
|
||||
return []sdk.AccAddress{addr}
|
||||
}
|
||||
|
||||
func (m *MsgRecordsWrite) ValidateBasic() error {
|
||||
if _, err := sdk.AccAddressFromBech32(m.Author); err != nil {
|
||||
return errors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid author address: %s", err)
|
||||
}
|
||||
if m.Target == "" {
|
||||
return errors.Wrap(sdkerrors.ErrInvalidRequest, "target DID cannot be empty")
|
||||
}
|
||||
if m.Descriptor_ == nil {
|
||||
return errors.Wrap(sdkerrors.ErrInvalidRequest, "descriptor cannot be nil")
|
||||
}
|
||||
if len(m.Data) == 0 {
|
||||
return errors.Wrap(sdkerrors.ErrInvalidRequest, "data cannot be empty")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MsgRecordsDelete implementation
|
||||
func (m *MsgRecordsDelete) GetSigners() []sdk.AccAddress {
|
||||
addr, _ := sdk.AccAddressFromBech32(m.Author)
|
||||
return []sdk.AccAddress{addr}
|
||||
}
|
||||
|
||||
func (m *MsgRecordsDelete) ValidateBasic() error {
|
||||
if _, err := sdk.AccAddressFromBech32(m.Author); err != nil {
|
||||
return errors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid author address: %s", err)
|
||||
}
|
||||
if m.Target == "" {
|
||||
return errors.Wrap(sdkerrors.ErrInvalidRequest, "target DID cannot be empty")
|
||||
}
|
||||
if m.RecordId == "" {
|
||||
return errors.Wrap(sdkerrors.ErrInvalidRequest, "record ID cannot be empty")
|
||||
}
|
||||
if m.Descriptor_ == nil {
|
||||
return errors.Wrap(sdkerrors.ErrInvalidRequest, "descriptor cannot be nil")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MsgProtocolsConfigure implementation
|
||||
func (m *MsgProtocolsConfigure) GetSigners() []sdk.AccAddress {
|
||||
addr, _ := sdk.AccAddressFromBech32(m.Author)
|
||||
return []sdk.AccAddress{addr}
|
||||
}
|
||||
|
||||
func (m *MsgProtocolsConfigure) ValidateBasic() error {
|
||||
if _, err := sdk.AccAddressFromBech32(m.Author); err != nil {
|
||||
return errors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid author address: %s", err)
|
||||
}
|
||||
if m.Target == "" {
|
||||
return errors.Wrap(sdkerrors.ErrInvalidRequest, "target DID cannot be empty")
|
||||
}
|
||||
if m.ProtocolUri == "" {
|
||||
return errors.Wrap(sdkerrors.ErrInvalidRequest, "protocol URI cannot be empty")
|
||||
}
|
||||
if m.Descriptor_ == nil {
|
||||
return errors.Wrap(sdkerrors.ErrInvalidRequest, "descriptor cannot be nil")
|
||||
}
|
||||
if len(m.Definition) == 0 {
|
||||
return errors.Wrap(sdkerrors.ErrInvalidRequest, "definition cannot be empty")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MsgPermissionsGrant implementation
|
||||
func (m *MsgPermissionsGrant) GetSigners() []sdk.AccAddress {
|
||||
addr, _ := sdk.AccAddressFromBech32(m.Grantor)
|
||||
return []sdk.AccAddress{addr}
|
||||
}
|
||||
|
||||
func (m *MsgPermissionsGrant) ValidateBasic() error {
|
||||
if _, err := sdk.AccAddressFromBech32(m.Grantor); err != nil {
|
||||
return errors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid grantor address: %s", err)
|
||||
}
|
||||
if m.Grantee == "" {
|
||||
return errors.Wrap(sdkerrors.ErrInvalidRequest, "grantee DID cannot be empty")
|
||||
}
|
||||
if m.Target == "" {
|
||||
return errors.Wrap(sdkerrors.ErrInvalidRequest, "target DID cannot be empty")
|
||||
}
|
||||
if m.InterfaceName == "" {
|
||||
return errors.Wrap(sdkerrors.ErrInvalidRequest, "interface name cannot be empty")
|
||||
}
|
||||
if m.Method == "" {
|
||||
return errors.Wrap(sdkerrors.ErrInvalidRequest, "method cannot be empty")
|
||||
}
|
||||
if m.Descriptor_ == nil {
|
||||
return errors.Wrap(sdkerrors.ErrInvalidRequest, "descriptor cannot be nil")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MsgPermissionsRevoke implementation
|
||||
|
||||
// GetSigners returns the expected signers for a MsgPermissionsRevoke message.
|
||||
func (m *MsgPermissionsRevoke) GetSigners() []sdk.AccAddress {
|
||||
addr, _ := sdk.AccAddressFromBech32(m.Grantor)
|
||||
return []sdk.AccAddress{addr}
|
||||
}
|
||||
|
||||
// ValidateBasic does a sanity check on the provided data
|
||||
func (m *MsgPermissionsRevoke) ValidateBasic() error {
|
||||
if _, err := sdk.AccAddressFromBech32(m.Grantor); err != nil {
|
||||
return errors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid grantor address: %s", err)
|
||||
}
|
||||
if m.PermissionId == "" {
|
||||
return errors.Wrap(sdkerrors.ErrInvalidRequest, "permission ID cannot be empty")
|
||||
}
|
||||
if m.Descriptor_ == nil {
|
||||
return errors.Wrap(sdkerrors.ErrInvalidRequest, "descriptor cannot be nil")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetSigners returns the expected signers for a MsgRotateVaultKeys message
|
||||
func (m *MsgRotateVaultKeys) GetSigners() []sdk.AccAddress {
|
||||
addr, _ := sdk.AccAddressFromBech32(m.Authority)
|
||||
return []sdk.AccAddress{addr}
|
||||
}
|
||||
|
||||
// ValidateBasic does a sanity check on the provided data
|
||||
func (m *MsgRotateVaultKeys) ValidateBasic() error {
|
||||
if _, err := sdk.AccAddressFromBech32(m.Authority); err != nil {
|
||||
return errors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid authority address: %s", err)
|
||||
}
|
||||
if m.Reason == "" {
|
||||
return errors.Wrap(sdkerrors.ErrInvalidRequest, "reason cannot be empty")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetModuleAddress returns the dwn module account address
|
||||
func GetModuleAddress() sdk.AccAddress {
|
||||
return address.Module(ModuleName)
|
||||
}
|
||||
|
||||
Regular → Executable
+163
-7
@@ -2,20 +2,63 @@ package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const (
|
||||
// Default max record size: 10MB
|
||||
DefaultMaxRecordSize = 10 * 1024 * 1024
|
||||
// Default max protocols per DWN: 100
|
||||
DefaultMaxProtocolsPerDWN = 100
|
||||
// Default max permissions per DWN: 1000
|
||||
DefaultMaxPermissionsPerDWN = 1000
|
||||
// Default vault creation enabled
|
||||
DefaultVaultCreationEnabled = true
|
||||
// Default min vault refresh interval: 100 blocks
|
||||
DefaultMinVaultRefreshInterval = 100
|
||||
// Default encryption enabled
|
||||
DefaultEncryptionEnabled = true
|
||||
// Default key rotation days: 30
|
||||
DefaultKeyRotationDays = 30
|
||||
// Default min validators for key generation: 67% of active set
|
||||
DefaultMinValidatorsForKeyGen = 67
|
||||
// Default single node fallback disabled
|
||||
DefaultSingleNodeFallback = false
|
||||
)
|
||||
|
||||
// DefaultEncryptedProtocols are protocols that require encryption by default
|
||||
var DefaultEncryptedProtocols = []string{
|
||||
"vault.enclave/v1", // SECURITY: Enclave data must always be encrypted
|
||||
"medical.records/v1",
|
||||
"financial.data/v1",
|
||||
"private.messages/v1",
|
||||
}
|
||||
|
||||
// DefaultEncryptedSchemas are schemas that require encryption by default
|
||||
var DefaultEncryptedSchemas = []string{
|
||||
"https://schemas.sonr.io/medical/",
|
||||
"https://schemas.sonr.io/financial/",
|
||||
"https://schemas.sonr.io/personal/",
|
||||
}
|
||||
|
||||
// DefaultParams returns default module parameters.
|
||||
func DefaultParams() Params {
|
||||
return Params{
|
||||
AllowedOperators: []string{ // TODO:
|
||||
"localhost",
|
||||
"didao.xyz",
|
||||
"sonr.id",
|
||||
},
|
||||
MaxRecordSize: DefaultMaxRecordSize,
|
||||
MaxProtocolsPerDwn: DefaultMaxProtocolsPerDWN,
|
||||
MaxPermissionsPerDwn: DefaultMaxPermissionsPerDWN,
|
||||
VaultCreationEnabled: DefaultVaultCreationEnabled,
|
||||
MinVaultRefreshInterval: DefaultMinVaultRefreshInterval,
|
||||
EncryptionEnabled: DefaultEncryptionEnabled,
|
||||
KeyRotationDays: DefaultKeyRotationDays,
|
||||
MinValidatorsForKeyGen: DefaultMinValidatorsForKeyGen,
|
||||
EncryptedProtocols: DefaultEncryptedProtocols,
|
||||
EncryptedSchemas: DefaultEncryptedSchemas,
|
||||
SingleNodeFallback: DefaultSingleNodeFallback,
|
||||
}
|
||||
}
|
||||
|
||||
// Stringer method for Params.
|
||||
// String method for Params.
|
||||
func (p Params) String() string {
|
||||
bz, err := json.Marshal(p)
|
||||
if err != nil {
|
||||
@@ -27,6 +70,119 @@ func (p Params) String() string {
|
||||
|
||||
// Validate does the sanity check on the params.
|
||||
func (p Params) Validate() error {
|
||||
// TODO:
|
||||
if err := validateMaxRecordSize(p.MaxRecordSize); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateMaxProtocolsPerDWN(p.MaxProtocolsPerDwn); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateMaxPermissionsPerDWN(p.MaxPermissionsPerDwn); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateMinVaultRefreshInterval(p.MinVaultRefreshInterval); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateKeyRotationDays(p.KeyRotationDays); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateMinValidatorsForKeyGen(p.MinValidatorsForKeyGen); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateEncryptedProtocols(p.EncryptedProtocols); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateEncryptedSchemas(p.EncryptedSchemas); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateMaxRecordSize(maxSize uint64) error {
|
||||
if maxSize == 0 {
|
||||
return fmt.Errorf("max record size must be positive")
|
||||
}
|
||||
if maxSize > 100*1024*1024 { // 100MB max
|
||||
return fmt.Errorf("max record size cannot exceed 100MB")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateMaxProtocolsPerDWN(max uint32) error {
|
||||
if max == 0 {
|
||||
return fmt.Errorf("max protocols per DWN must be positive")
|
||||
}
|
||||
if max > 10000 {
|
||||
return fmt.Errorf("max protocols per DWN cannot exceed 10000")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateMaxPermissionsPerDWN(max uint32) error {
|
||||
if max == 0 {
|
||||
return fmt.Errorf("max permissions per DWN must be positive")
|
||||
}
|
||||
if max > 100000 {
|
||||
return fmt.Errorf("max permissions per DWN cannot exceed 100000")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateMinVaultRefreshInterval(interval uint64) error {
|
||||
if interval == 0 {
|
||||
return fmt.Errorf("min vault refresh interval must be positive")
|
||||
}
|
||||
if interval > 1000000 {
|
||||
return fmt.Errorf("min vault refresh interval cannot exceed 1000000 blocks")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateKeyRotationDays(days uint32) error {
|
||||
if days == 0 {
|
||||
return fmt.Errorf("key rotation days must be positive")
|
||||
}
|
||||
if days > 365 {
|
||||
return fmt.Errorf("key rotation days cannot exceed 365")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateMinValidatorsForKeyGen(percentage uint32) error {
|
||||
if percentage == 0 {
|
||||
return fmt.Errorf("min validators percentage must be positive")
|
||||
}
|
||||
if percentage > 100 {
|
||||
return fmt.Errorf("min validators percentage cannot exceed 100")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateEncryptedProtocols(protocols []string) error {
|
||||
if len(protocols) > 1000 {
|
||||
return fmt.Errorf("cannot have more than 1000 encrypted protocols")
|
||||
}
|
||||
for _, protocol := range protocols {
|
||||
if protocol == "" {
|
||||
return fmt.Errorf("encrypted protocol cannot be empty")
|
||||
}
|
||||
if len(protocol) > 256 {
|
||||
return fmt.Errorf("encrypted protocol cannot exceed 256 characters")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateEncryptedSchemas(schemas []string) error {
|
||||
if len(schemas) > 1000 {
|
||||
return fmt.Errorf("cannot have more than 1000 encrypted schemas")
|
||||
}
|
||||
for _, schema := range schemas {
|
||||
if schema == "" {
|
||||
return fmt.Errorf("encrypted schema cannot be empty")
|
||||
}
|
||||
if len(schema) > 512 {
|
||||
return fmt.Errorf("encrypted schema cannot exceed 512 characters")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,333 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
unknownOperationString = "unknown"
|
||||
|
||||
// UCAN Action Constants for DWN operations (reference from ucan_capabilities.go)
|
||||
UCANRecordsWrite = "records-write"
|
||||
UCANRecordsDelete = "records-delete"
|
||||
UCANRecordsRead = "records-read"
|
||||
UCANRecordsQuery = "records-query"
|
||||
UCANProtocolsConfigure = "protocols-configure"
|
||||
UCANProtocolsQuery = "protocols-query"
|
||||
UCANPermissionsGrant = "permissions-grant"
|
||||
UCANPermissionsRevoke = "permissions-revoke"
|
||||
UCANPermissionsQuery = "permissions-query"
|
||||
UCANDataSync = "data-sync"
|
||||
UCANMessageSync = "message-sync"
|
||||
UCANCreate = "create"
|
||||
UCANRead = "read"
|
||||
UCANUpdate = "update"
|
||||
UCANDelete = "delete"
|
||||
UCANAdmin = "admin"
|
||||
UCANAll = "*"
|
||||
)
|
||||
|
||||
// DWNOperation represents different operations that can be performed on DWN
|
||||
type DWNOperation int
|
||||
|
||||
const (
|
||||
// Record operations
|
||||
RecordCreate DWNOperation = iota
|
||||
RecordRead
|
||||
RecordUpdate
|
||||
RecordDelete
|
||||
RecordQuery
|
||||
|
||||
// Protocol operations
|
||||
ProtocolInstall
|
||||
ProtocolQuery
|
||||
ProtocolUpdate
|
||||
|
||||
// Permission operations
|
||||
PermissionGrant
|
||||
PermissionRevoke
|
||||
PermissionQuery
|
||||
|
||||
// Sync operations
|
||||
DataSync
|
||||
MessageSync
|
||||
|
||||
// Administrative operations
|
||||
AdminConfig
|
||||
AdminReset
|
||||
)
|
||||
|
||||
// String returns the string representation of the operation
|
||||
func (op DWNOperation) String() string {
|
||||
switch op {
|
||||
case RecordCreate:
|
||||
return "record_create"
|
||||
case RecordRead:
|
||||
return "record_read"
|
||||
case RecordUpdate:
|
||||
return "record_update"
|
||||
case RecordDelete:
|
||||
return "record_delete"
|
||||
case RecordQuery:
|
||||
return "record_query"
|
||||
case ProtocolInstall:
|
||||
return "protocol_install"
|
||||
case ProtocolQuery:
|
||||
return "protocol_query"
|
||||
case ProtocolUpdate:
|
||||
return "protocol_update"
|
||||
case PermissionGrant:
|
||||
return "permission_grant"
|
||||
case PermissionRevoke:
|
||||
return "permission_revoke"
|
||||
case PermissionQuery:
|
||||
return "permission_query"
|
||||
case DataSync:
|
||||
return "data_sync"
|
||||
case MessageSync:
|
||||
return "message_sync"
|
||||
case AdminConfig:
|
||||
return "admin_config"
|
||||
case AdminReset:
|
||||
return "admin_reset"
|
||||
default:
|
||||
return unknownOperationString
|
||||
}
|
||||
}
|
||||
|
||||
// RecordOperation represents specific record operations
|
||||
type RecordOperation int
|
||||
|
||||
const (
|
||||
RecordOpCreate RecordOperation = iota
|
||||
RecordOpRead
|
||||
RecordOpUpdate
|
||||
RecordOpDelete
|
||||
RecordOpList
|
||||
)
|
||||
|
||||
// String returns the string representation
|
||||
func (op RecordOperation) String() string {
|
||||
switch op {
|
||||
case RecordOpCreate:
|
||||
return "create"
|
||||
case RecordOpRead:
|
||||
return "read"
|
||||
case RecordOpUpdate:
|
||||
return "update"
|
||||
case RecordOpDelete:
|
||||
return "delete"
|
||||
case RecordOpList:
|
||||
return "list"
|
||||
default:
|
||||
return unknownOperationString
|
||||
}
|
||||
}
|
||||
|
||||
// ProtocolOperation represents specific protocol operations
|
||||
type ProtocolOperation int
|
||||
|
||||
const (
|
||||
ProtocolOpInstall ProtocolOperation = iota
|
||||
ProtocolOpQuery
|
||||
ProtocolOpUpdate
|
||||
ProtocolOpDelete
|
||||
)
|
||||
|
||||
// String returns the string representation
|
||||
func (op ProtocolOperation) String() string {
|
||||
switch op {
|
||||
case ProtocolOpInstall:
|
||||
return "install"
|
||||
case ProtocolOpQuery:
|
||||
return "query"
|
||||
case ProtocolOpUpdate:
|
||||
return "update"
|
||||
case ProtocolOpDelete:
|
||||
return "delete"
|
||||
default:
|
||||
return unknownOperationString
|
||||
}
|
||||
}
|
||||
|
||||
// PermissionRegistry manages DWN-specific UCAN capability mappings
|
||||
type PermissionRegistry struct {
|
||||
operationCapabilities map[DWNOperation][]string
|
||||
recordCapabilities map[RecordOperation][]string
|
||||
protocolCapabilities map[ProtocolOperation][]string
|
||||
}
|
||||
|
||||
// NewDWNPermissionRegistry creates a new permission registry with UCAN-compatible capabilities
|
||||
func NewDWNPermissionRegistry() PermissionRegistry {
|
||||
registry := PermissionRegistry{
|
||||
operationCapabilities: make(map[DWNOperation][]string),
|
||||
recordCapabilities: make(map[RecordOperation][]string),
|
||||
protocolCapabilities: make(map[ProtocolOperation][]string),
|
||||
}
|
||||
|
||||
// Initialize UCAN-compatible operation capabilities
|
||||
registry.operationCapabilities[RecordCreate] = []string{UCANRecordsWrite, UCANCreate}
|
||||
registry.operationCapabilities[RecordRead] = []string{UCANRecordsRead, UCANRead}
|
||||
registry.operationCapabilities[RecordUpdate] = []string{UCANRecordsWrite, UCANUpdate}
|
||||
registry.operationCapabilities[RecordDelete] = []string{UCANRecordsDelete, UCANDelete}
|
||||
registry.operationCapabilities[RecordQuery] = []string{UCANRecordsQuery, UCANRead}
|
||||
|
||||
registry.operationCapabilities[ProtocolInstall] = []string{UCANProtocolsConfigure, UCANCreate, UCANAdmin}
|
||||
registry.operationCapabilities[ProtocolQuery] = []string{UCANProtocolsQuery, UCANRead}
|
||||
registry.operationCapabilities[ProtocolUpdate] = []string{UCANProtocolsConfigure, UCANUpdate, UCANAdmin}
|
||||
|
||||
registry.operationCapabilities[PermissionGrant] = []string{UCANPermissionsGrant, UCANAdmin}
|
||||
registry.operationCapabilities[PermissionRevoke] = []string{UCANPermissionsRevoke, UCANAdmin}
|
||||
registry.operationCapabilities[PermissionQuery] = []string{UCANPermissionsQuery, UCANRead}
|
||||
|
||||
registry.operationCapabilities[DataSync] = []string{UCANDataSync, UCANRead, UCANUpdate}
|
||||
registry.operationCapabilities[MessageSync] = []string{UCANMessageSync, UCANRead}
|
||||
|
||||
registry.operationCapabilities[AdminConfig] = []string{UCANAdmin}
|
||||
registry.operationCapabilities[AdminReset] = []string{UCANAdmin, UCANDelete}
|
||||
|
||||
// Initialize UCAN-compatible record operation capabilities
|
||||
registry.recordCapabilities[RecordOpCreate] = []string{UCANRecordsWrite, UCANCreate}
|
||||
registry.recordCapabilities[RecordOpRead] = []string{UCANRecordsRead, UCANRead}
|
||||
registry.recordCapabilities[RecordOpUpdate] = []string{UCANRecordsWrite, UCANUpdate}
|
||||
registry.recordCapabilities[RecordOpDelete] = []string{UCANRecordsDelete, UCANDelete}
|
||||
registry.recordCapabilities[RecordOpList] = []string{UCANRecordsQuery, UCANRead}
|
||||
|
||||
// Initialize UCAN-compatible protocol operation capabilities
|
||||
registry.protocolCapabilities[ProtocolOpInstall] = []string{UCANProtocolsConfigure, UCANCreate}
|
||||
registry.protocolCapabilities[ProtocolOpQuery] = []string{UCANProtocolsQuery, UCANRead}
|
||||
registry.protocolCapabilities[ProtocolOpUpdate] = []string{UCANProtocolsConfigure, UCANUpdate}
|
||||
registry.protocolCapabilities[ProtocolOpDelete] = []string{UCANProtocolsConfigure, UCANDelete}
|
||||
|
||||
return registry
|
||||
}
|
||||
|
||||
// GetRequiredCapabilities returns the required UCAN capabilities for a DWN operation
|
||||
func (pr *PermissionRegistry) GetRequiredCapabilities(operation DWNOperation) ([]string, error) {
|
||||
capabilities, exists := pr.operationCapabilities[operation]
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("no capabilities defined for operation: %s", operation.String())
|
||||
}
|
||||
return capabilities, nil
|
||||
}
|
||||
|
||||
// GetRecordCapabilities returns the required capabilities for a record operation
|
||||
func (pr *PermissionRegistry) GetRecordCapabilities(operation RecordOperation) []string {
|
||||
if capabilities, exists := pr.recordCapabilities[operation]; exists {
|
||||
return capabilities
|
||||
}
|
||||
return []string{"read"} // Default to read permission
|
||||
}
|
||||
|
||||
// GetProtocolCapabilities returns the required capabilities for a protocol operation
|
||||
func (pr *PermissionRegistry) GetProtocolCapabilities(operation ProtocolOperation) []string {
|
||||
if capabilities, exists := pr.protocolCapabilities[operation]; exists {
|
||||
return capabilities
|
||||
}
|
||||
return []string{"read"} // Default to read permission
|
||||
}
|
||||
|
||||
// AddOperationCapabilities adds or updates capabilities for an operation
|
||||
func (pr *PermissionRegistry) AddOperationCapabilities(
|
||||
operation DWNOperation,
|
||||
capabilities []string,
|
||||
) {
|
||||
pr.operationCapabilities[operation] = capabilities
|
||||
}
|
||||
|
||||
// AddRecordCapabilities adds or updates capabilities for a record operation
|
||||
func (pr *PermissionRegistry) AddRecordCapabilities(
|
||||
operation RecordOperation,
|
||||
capabilities []string,
|
||||
) {
|
||||
pr.recordCapabilities[operation] = capabilities
|
||||
}
|
||||
|
||||
// AddProtocolCapabilities adds or updates capabilities for a protocol operation
|
||||
func (pr *PermissionRegistry) AddProtocolCapabilities(
|
||||
operation ProtocolOperation,
|
||||
capabilities []string,
|
||||
) {
|
||||
pr.protocolCapabilities[operation] = capabilities
|
||||
}
|
||||
|
||||
// ParseOperationFromString parses a string into a DWNOperation
|
||||
func ParseOperationFromString(operationStr string) (DWNOperation, error) {
|
||||
switch strings.ToLower(operationStr) {
|
||||
case "record_create":
|
||||
return RecordCreate, nil
|
||||
case "record_read":
|
||||
return RecordRead, nil
|
||||
case "record_update":
|
||||
return RecordUpdate, nil
|
||||
case "record_delete":
|
||||
return RecordDelete, nil
|
||||
case "record_query":
|
||||
return RecordQuery, nil
|
||||
case "protocol_install":
|
||||
return ProtocolInstall, nil
|
||||
case "protocol_query":
|
||||
return ProtocolQuery, nil
|
||||
case "protocol_update":
|
||||
return ProtocolUpdate, nil
|
||||
case "permission_grant":
|
||||
return PermissionGrant, nil
|
||||
case "permission_revoke":
|
||||
return PermissionRevoke, nil
|
||||
case "permission_query":
|
||||
return PermissionQuery, nil
|
||||
case "data_sync":
|
||||
return DataSync, nil
|
||||
case "message_sync":
|
||||
return MessageSync, nil
|
||||
case "admin_config":
|
||||
return AdminConfig, nil
|
||||
case "admin_reset":
|
||||
return AdminReset, nil
|
||||
default:
|
||||
return 0, fmt.Errorf("unknown operation: %s", operationStr)
|
||||
}
|
||||
}
|
||||
|
||||
// IsAdminOperation checks if the operation requires admin privileges
|
||||
func IsAdminOperation(operation DWNOperation) bool {
|
||||
switch operation {
|
||||
case PermissionGrant, PermissionRevoke, AdminConfig, AdminReset:
|
||||
return true
|
||||
case ProtocolInstall, ProtocolUpdate:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// IsWriteOperation checks if the operation requires write privileges
|
||||
func IsWriteOperation(operation DWNOperation) bool {
|
||||
switch operation {
|
||||
case RecordCreate, RecordUpdate, RecordDelete:
|
||||
return true
|
||||
case ProtocolInstall, ProtocolUpdate:
|
||||
return true
|
||||
case DataSync:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// IsReadOperation checks if the operation requires read privileges
|
||||
func IsReadOperation(operation DWNOperation) bool {
|
||||
switch operation {
|
||||
case RecordRead, RecordQuery:
|
||||
return true
|
||||
case ProtocolQuery:
|
||||
return true
|
||||
case PermissionQuery:
|
||||
return true
|
||||
case MessageSync:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
+6425
-22
File diff suppressed because it is too large
Load Diff
+1243
-1
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,139 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// WalletSponsorship represents metadata for a wallet sponsorship
|
||||
// This stores additional data beyond what BasicAllowance provides
|
||||
type WalletSponsorship struct {
|
||||
// Core sponsorship info
|
||||
Granter string `json:"granter"` // Address of the sponsor
|
||||
Grantee string `json:"grantee"` // Address of the sponsored wallet
|
||||
WalletAddress string `json:"wallet_address"` // Same as grantee for simplicity
|
||||
VaultId string `json:"vault_id"` // Associated vault ID
|
||||
CreatedAt time.Time `json:"created_at"` // Creation timestamp
|
||||
|
||||
// Additional restrictions (not enforced by BasicAllowance)
|
||||
DailyLimit *sdk.Coins `json:"daily_limit,omitempty"` // Optional daily limit
|
||||
AllowedMessages []string `json:"allowed_messages,omitempty"` // Optional message restrictions
|
||||
|
||||
// Usage tracking (managed by our keeper)
|
||||
DailySpent sdk.Coins `json:"daily_spent"` // Amount spent today
|
||||
LastResetDate time.Time `json:"last_reset_date"` // Last daily reset
|
||||
LastUsedAt *time.Time `json:"last_used_at,omitempty"`
|
||||
}
|
||||
|
||||
// SponsorshipInfo represents sponsorship information for queries
|
||||
type SponsorshipInfo struct {
|
||||
Granter string `json:"granter"`
|
||||
Grantee string `json:"grantee"`
|
||||
WalletAddress string `json:"wallet_address"`
|
||||
VaultId string `json:"vault_id"`
|
||||
SpendLimit *sdk.Coins `json:"spend_limit,omitempty"`
|
||||
Expiration *time.Time `json:"expiration,omitempty"`
|
||||
DailyLimit *sdk.Coins `json:"daily_limit,omitempty"`
|
||||
AllowedMessages []string `json:"allowed_messages,omitempty"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
DailySpent sdk.Coins `json:"daily_spent"`
|
||||
LastUsedAt *time.Time `json:"last_used_at,omitempty"`
|
||||
}
|
||||
|
||||
// GaslessTransactionResponse represents the response from a gasless transaction
|
||||
type GaslessTransactionResponse struct {
|
||||
Success bool `json:"success"`
|
||||
TxHash string `json:"tx_hash"`
|
||||
WalletAddress string `json:"wallet_address"`
|
||||
GasUsed uint64 `json:"gas_used"`
|
||||
FeesDeducted sdk.Coins `json:"fees_deducted"`
|
||||
}
|
||||
|
||||
// ValidateBasic performs basic validation on WalletSponsorship
|
||||
func (ws *WalletSponsorship) ValidateBasic() error {
|
||||
if ws.Granter == "" {
|
||||
return ErrInvalidWalletAddress.Wrap("granter address is empty")
|
||||
}
|
||||
if ws.Grantee == "" {
|
||||
return ErrInvalidWalletAddress.Wrap("grantee address is empty")
|
||||
}
|
||||
if ws.VaultId == "" {
|
||||
return ErrVaultIDEmpty
|
||||
}
|
||||
|
||||
// Validate addresses
|
||||
if _, err := sdk.AccAddressFromBech32(ws.Granter); err != nil {
|
||||
return ErrInvalidWalletAddress.Wrapf("invalid granter address: %s", err)
|
||||
}
|
||||
if _, err := sdk.AccAddressFromBech32(ws.Grantee); err != nil {
|
||||
return ErrInvalidWalletAddress.Wrapf("invalid grantee address: %s", err)
|
||||
}
|
||||
|
||||
// Validate daily limit if present
|
||||
if ws.DailyLimit != nil {
|
||||
if !ws.DailyLimit.IsValid() {
|
||||
return ErrInvalidSpendLimit.Wrap("daily limit is invalid")
|
||||
}
|
||||
if !ws.DailyLimit.IsAllPositive() {
|
||||
return ErrInvalidSpendLimit.Wrap("daily limit must be positive")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsDailyLimitExceeded checks if the daily limit would be exceeded by the given amount
|
||||
func (ws *WalletSponsorship) IsDailyLimitExceeded(amount sdk.Coins) bool {
|
||||
if ws.DailyLimit == nil {
|
||||
return false // No daily limit
|
||||
}
|
||||
|
||||
// Reset daily spent if it's a new day
|
||||
now := time.Now()
|
||||
if now.Day() != ws.LastResetDate.Day() || now.Month() != ws.LastResetDate.Month() ||
|
||||
now.Year() != ws.LastResetDate.Year() {
|
||||
ws.DailySpent = sdk.NewCoins()
|
||||
ws.LastResetDate = now
|
||||
}
|
||||
|
||||
// Check if adding this amount would exceed the daily limit
|
||||
totalSpent := ws.DailySpent.Add(amount...)
|
||||
for _, limitCoin := range *ws.DailyLimit {
|
||||
spentAmount := totalSpent.AmountOf(limitCoin.Denom)
|
||||
if spentAmount.GT(limitCoin.Amount) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// AddDailySpent adds to the daily spent amount and updates the last used time
|
||||
func (ws *WalletSponsorship) AddDailySpent(amount sdk.Coins) {
|
||||
now := time.Now()
|
||||
|
||||
// Reset daily spent if it's a new day
|
||||
if now.Day() != ws.LastResetDate.Day() || now.Month() != ws.LastResetDate.Month() ||
|
||||
now.Year() != ws.LastResetDate.Year() {
|
||||
ws.DailySpent = sdk.NewCoins()
|
||||
ws.LastResetDate = now
|
||||
}
|
||||
|
||||
ws.DailySpent = ws.DailySpent.Add(amount...)
|
||||
ws.LastUsedAt = &now
|
||||
}
|
||||
|
||||
// IsMessageAllowed checks if a message type is allowed by this sponsorship
|
||||
func (ws *WalletSponsorship) IsMessageAllowed(msgTypeURL string) bool {
|
||||
if len(ws.AllowedMessages) == 0 {
|
||||
return true // No restrictions
|
||||
}
|
||||
|
||||
for _, allowed := range ws.AllowedMessages {
|
||||
if allowed == msgTypeURL {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
+6216
-286
File diff suppressed because it is too large
Load Diff
+4224
-136
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,306 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/sonr-io/sonr/crypto/ucan"
|
||||
)
|
||||
|
||||
// UCAN Action Constants are defined in permissions.go
|
||||
|
||||
// UCANCapabilityMapper provides conversion between DWN operations and UCAN capabilities
|
||||
type UCANCapabilityMapper struct{}
|
||||
|
||||
// NewUCANCapabilityMapper creates a new capability mapper
|
||||
func NewUCANCapabilityMapper() *UCANCapabilityMapper {
|
||||
return &UCANCapabilityMapper{}
|
||||
}
|
||||
|
||||
// GetUCANCapabilitiesForOperation returns UCAN-specific capabilities for a DWN operation
|
||||
func (m *UCANCapabilityMapper) GetUCANCapabilitiesForOperation(operation DWNOperation) []string {
|
||||
switch operation {
|
||||
case RecordCreate:
|
||||
return []string{UCANRecordsWrite, UCANCreate}
|
||||
case RecordRead:
|
||||
return []string{UCANRecordsRead, UCANRead}
|
||||
case RecordUpdate:
|
||||
return []string{UCANRecordsWrite, UCANUpdate}
|
||||
case RecordDelete:
|
||||
return []string{UCANRecordsDelete, UCANDelete}
|
||||
case RecordQuery:
|
||||
return []string{UCANRecordsQuery, UCANRead}
|
||||
|
||||
case ProtocolInstall:
|
||||
return []string{UCANProtocolsConfigure, UCANCreate, UCANAdmin}
|
||||
case ProtocolQuery:
|
||||
return []string{UCANProtocolsQuery, UCANRead}
|
||||
case ProtocolUpdate:
|
||||
return []string{UCANProtocolsConfigure, UCANUpdate, UCANAdmin}
|
||||
|
||||
case PermissionGrant:
|
||||
return []string{UCANPermissionsGrant, UCANAdmin}
|
||||
case PermissionRevoke:
|
||||
return []string{UCANPermissionsRevoke, UCANAdmin}
|
||||
case PermissionQuery:
|
||||
return []string{UCANPermissionsQuery, UCANRead}
|
||||
|
||||
case DataSync:
|
||||
return []string{UCANDataSync, UCANRead, UCANUpdate}
|
||||
case MessageSync:
|
||||
return []string{UCANMessageSync, UCANRead}
|
||||
|
||||
case AdminConfig:
|
||||
return []string{UCANAdmin}
|
||||
case AdminReset:
|
||||
return []string{UCANAdmin, UCANDelete}
|
||||
|
||||
default:
|
||||
return []string{UCANRead} // Default to read permission
|
||||
}
|
||||
}
|
||||
|
||||
// GetUCANCapabilitiesForRecordOperation returns UCAN capabilities for record operations
|
||||
func (m *UCANCapabilityMapper) GetUCANCapabilitiesForRecordOperation(operation RecordOperation) []string {
|
||||
switch operation {
|
||||
case RecordOpCreate:
|
||||
return []string{UCANRecordsWrite, UCANCreate}
|
||||
case RecordOpRead:
|
||||
return []string{UCANRecordsRead, UCANRead}
|
||||
case RecordOpUpdate:
|
||||
return []string{UCANRecordsWrite, UCANUpdate}
|
||||
case RecordOpDelete:
|
||||
return []string{UCANRecordsDelete, UCANDelete}
|
||||
case RecordOpList:
|
||||
return []string{UCANRecordsQuery, UCANRead}
|
||||
default:
|
||||
return []string{UCANRead}
|
||||
}
|
||||
}
|
||||
|
||||
// GetUCANCapabilitiesForProtocolOperation returns UCAN capabilities for protocol operations
|
||||
func (m *UCANCapabilityMapper) GetUCANCapabilitiesForProtocolOperation(operation ProtocolOperation) []string {
|
||||
switch operation {
|
||||
case ProtocolOpInstall:
|
||||
return []string{UCANProtocolsConfigure, UCANCreate}
|
||||
case ProtocolOpQuery:
|
||||
return []string{UCANProtocolsQuery, UCANRead}
|
||||
case ProtocolOpUpdate:
|
||||
return []string{UCANProtocolsConfigure, UCANUpdate}
|
||||
case ProtocolOpDelete:
|
||||
return []string{UCANProtocolsConfigure, UCANDelete}
|
||||
default:
|
||||
return []string{UCANRead}
|
||||
}
|
||||
}
|
||||
|
||||
// CreateDWNResourceURI builds a DWN resource URI for UCAN validation
|
||||
func (m *UCANCapabilityMapper) CreateDWNResourceURI(target, resourceType, resourceID string) string {
|
||||
if resourceID != "" {
|
||||
return fmt.Sprintf("dwn://%s/%s/%s", target, resourceType, resourceID)
|
||||
}
|
||||
return fmt.Sprintf("dwn://%s/%s", target, resourceType)
|
||||
}
|
||||
|
||||
// CreateRecordResourceURI builds a resource URI for a specific record
|
||||
func (m *UCANCapabilityMapper) CreateRecordResourceURI(target, recordID string) string {
|
||||
return m.CreateDWNResourceURI(target, "records", recordID)
|
||||
}
|
||||
|
||||
// CreateProtocolResourceURI builds a resource URI for a protocol
|
||||
func (m *UCANCapabilityMapper) CreateProtocolResourceURI(target, protocolURI string) string {
|
||||
return m.CreateDWNResourceURI(target, "protocols", protocolURI)
|
||||
}
|
||||
|
||||
// CreatePermissionResourceURI builds a resource URI for permissions
|
||||
func (m *UCANCapabilityMapper) CreatePermissionResourceURI(target string) string {
|
||||
return m.CreateDWNResourceURI(target, "permissions", "")
|
||||
}
|
||||
|
||||
// CreateDWNAttenuation creates a UCAN attenuation for DWN operations
|
||||
func (m *UCANCapabilityMapper) CreateDWNAttenuation(
|
||||
actions []string,
|
||||
target, recordType, protocol string,
|
||||
) ucan.Attenuation {
|
||||
resourceURI := m.CreateDWNResourceURI(target, "records", "")
|
||||
|
||||
resource := &ucan.DWNResource{
|
||||
SimpleResource: ucan.SimpleResource{
|
||||
Scheme: "dwn",
|
||||
Value: fmt.Sprintf("records/%s", target),
|
||||
URI: resourceURI,
|
||||
},
|
||||
RecordType: recordType,
|
||||
Protocol: protocol,
|
||||
Owner: target,
|
||||
}
|
||||
|
||||
capability := &ucan.DWNCapability{
|
||||
Actions: actions,
|
||||
}
|
||||
|
||||
return ucan.Attenuation{
|
||||
Capability: capability,
|
||||
Resource: resource,
|
||||
}
|
||||
}
|
||||
|
||||
// CreateRecordAttenuation creates a UCAN attenuation for specific record operations
|
||||
func (m *UCANCapabilityMapper) CreateRecordAttenuation(
|
||||
actions []string,
|
||||
target, recordID, recordType string,
|
||||
) ucan.Attenuation {
|
||||
resourceURI := m.CreateRecordResourceURI(target, recordID)
|
||||
|
||||
resource := &ucan.DWNResource{
|
||||
SimpleResource: ucan.SimpleResource{
|
||||
Scheme: "dwn",
|
||||
Value: fmt.Sprintf("records/%s", recordID),
|
||||
URI: resourceURI,
|
||||
},
|
||||
RecordType: recordType,
|
||||
Owner: target,
|
||||
}
|
||||
|
||||
capability := &ucan.DWNCapability{
|
||||
Actions: actions,
|
||||
}
|
||||
|
||||
return ucan.Attenuation{
|
||||
Capability: capability,
|
||||
Resource: resource,
|
||||
}
|
||||
}
|
||||
|
||||
// CreateProtocolAttenuation creates a UCAN attenuation for protocol operations
|
||||
func (m *UCANCapabilityMapper) CreateProtocolAttenuation(
|
||||
actions []string,
|
||||
target, protocolURI string,
|
||||
) ucan.Attenuation {
|
||||
resourceURI := m.CreateProtocolResourceURI(target, protocolURI)
|
||||
|
||||
resource := &ucan.DWNResource{
|
||||
SimpleResource: ucan.SimpleResource{
|
||||
Scheme: "dwn",
|
||||
Value: fmt.Sprintf("protocols/%s", protocolURI),
|
||||
URI: resourceURI,
|
||||
},
|
||||
Protocol: protocolURI,
|
||||
Owner: target,
|
||||
}
|
||||
|
||||
capability := &ucan.DWNCapability{
|
||||
Actions: actions,
|
||||
}
|
||||
|
||||
return ucan.Attenuation{
|
||||
Capability: capability,
|
||||
Resource: resource,
|
||||
}
|
||||
}
|
||||
|
||||
// ValidateUCANCapabilities validates that a UCAN capability grants the required DWN actions
|
||||
func (m *UCANCapabilityMapper) ValidateUCANCapabilities(
|
||||
capability ucan.Capability,
|
||||
requiredActions []string,
|
||||
) bool {
|
||||
return capability.Grants(requiredActions)
|
||||
}
|
||||
|
||||
// ConvertLegacyCapabilities converts old string-based capabilities to UCAN format
|
||||
func (m *UCANCapabilityMapper) ConvertLegacyCapabilities(legacyCapabilities []string) []string {
|
||||
var ucanCapabilities []string
|
||||
|
||||
for _, legacy := range legacyCapabilities {
|
||||
switch strings.ToLower(legacy) {
|
||||
case "write", "create":
|
||||
ucanCapabilities = append(ucanCapabilities, UCANCreate)
|
||||
case "read", "list", "query":
|
||||
ucanCapabilities = append(ucanCapabilities, UCANRead)
|
||||
case "update":
|
||||
ucanCapabilities = append(ucanCapabilities, UCANUpdate)
|
||||
case "delete":
|
||||
ucanCapabilities = append(ucanCapabilities, UCANDelete)
|
||||
case "admin":
|
||||
ucanCapabilities = append(ucanCapabilities, UCANAdmin)
|
||||
case "sync":
|
||||
ucanCapabilities = append(ucanCapabilities, UCANDataSync)
|
||||
case "*":
|
||||
ucanCapabilities = append(ucanCapabilities, UCANAll)
|
||||
default:
|
||||
// Pass through unknown capabilities
|
||||
ucanCapabilities = append(ucanCapabilities, legacy)
|
||||
}
|
||||
}
|
||||
|
||||
return ucanCapabilities
|
||||
}
|
||||
|
||||
// IsUCANAction checks if an action string is a valid UCAN action
|
||||
func IsUCANAction(action string) bool {
|
||||
validActions := []string{
|
||||
UCANRecordsWrite, UCANRecordsDelete, UCANRecordsRead, UCANRecordsQuery,
|
||||
UCANProtocolsConfigure, UCANProtocolsQuery,
|
||||
UCANPermissionsGrant, UCANPermissionsRevoke, UCANPermissionsQuery,
|
||||
UCANDataSync, UCANMessageSync,
|
||||
UCANCreate, UCANRead, UCANUpdate, UCANDelete,
|
||||
UCANAdmin, UCANAll,
|
||||
}
|
||||
|
||||
for _, validAction := range validActions {
|
||||
if action == validAction {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetDWNCapabilityTemplate returns a preconfigured capability template for DWN
|
||||
func GetDWNCapabilityTemplate() *ucan.CapabilityTemplate {
|
||||
return ucan.StandardDWNTemplate()
|
||||
}
|
||||
|
||||
// UCANPermissionRegistry extends the basic permission registry with UCAN capabilities
|
||||
type UCANPermissionRegistry struct {
|
||||
*PermissionRegistry
|
||||
mapper *UCANCapabilityMapper
|
||||
}
|
||||
|
||||
// NewUCANPermissionRegistry creates a new UCAN-aware permission registry
|
||||
func NewUCANPermissionRegistry() *UCANPermissionRegistry {
|
||||
return &UCANPermissionRegistry{
|
||||
PermissionRegistry: &PermissionRegistry{
|
||||
operationCapabilities: make(map[DWNOperation][]string),
|
||||
recordCapabilities: make(map[RecordOperation][]string),
|
||||
protocolCapabilities: make(map[ProtocolOperation][]string),
|
||||
},
|
||||
mapper: NewUCANCapabilityMapper(),
|
||||
}
|
||||
}
|
||||
|
||||
// GetRequiredUCANCapabilities returns UCAN-specific capabilities for a DWN operation
|
||||
func (r *UCANPermissionRegistry) GetRequiredUCANCapabilities(operation DWNOperation) ([]string, error) {
|
||||
capabilities := r.mapper.GetUCANCapabilitiesForOperation(operation)
|
||||
if len(capabilities) == 0 {
|
||||
return nil, fmt.Errorf("no UCAN capabilities defined for operation: %s", operation.String())
|
||||
}
|
||||
return capabilities, nil
|
||||
}
|
||||
|
||||
// GetRecordUCANCapabilities returns UCAN capabilities for record operations
|
||||
func (r *UCANPermissionRegistry) GetRecordUCANCapabilities(operation RecordOperation) []string {
|
||||
return r.mapper.GetUCANCapabilitiesForRecordOperation(operation)
|
||||
}
|
||||
|
||||
// GetProtocolUCANCapabilities returns UCAN capabilities for protocol operations
|
||||
func (r *UCANPermissionRegistry) GetProtocolUCANCapabilities(operation ProtocolOperation) []string {
|
||||
return r.mapper.GetUCANCapabilitiesForProtocolOperation(operation)
|
||||
}
|
||||
|
||||
// CreateDWNAttenuation creates a UCAN attenuation for DWN operations
|
||||
func (r *UCANPermissionRegistry) CreateDWNAttenuation(
|
||||
actions []string,
|
||||
target, recordType, protocol string,
|
||||
) ucan.Attenuation {
|
||||
return r.mapper.CreateDWNAttenuation(actions, target, recordType, protocol)
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package types
|
||||
|
||||
// VaultMetadata contains metadata about an encrypted vault
|
||||
type VaultMetadata struct {
|
||||
Did string `json:"did"`
|
||||
VaultId string `json:"vault_id"`
|
||||
Owner string `json:"owner"`
|
||||
KeyId string `json:"key_id"`
|
||||
Algorithm string `json:"algorithm"`
|
||||
Nonce string `json:"nonce"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
BlockHeight int64 `json:"block_height"`
|
||||
ValidatorSet []string `json:"validator_set"`
|
||||
}
|
||||
|
||||
// EncryptedVaultData represents encrypted vault data stored in IPFS
|
||||
type EncryptedVaultData struct {
|
||||
Metadata *VaultMetadata `json:"metadata"`
|
||||
EncryptedData string `json:"encrypted_data"`
|
||||
Version int `json:"version"`
|
||||
}
|
||||
|
||||
// EncryptedVaultState represents the on-chain state of an encrypted vault
|
||||
type EncryptedVaultState struct {
|
||||
VaultId string `json:"vault_id"`
|
||||
Did string `json:"did"`
|
||||
Owner string `json:"owner"`
|
||||
IpfsCid string `json:"ipfs_cid"`
|
||||
PublicKey string `json:"public_key"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
LastUpdated int64 `json:"last_updated"`
|
||||
Status string `json:"status"`
|
||||
EncryptionType string `json:"encryption_type"`
|
||||
}
|
||||
Reference in New Issue
Block a user