From 98d3e22f2861c0332d57a4ab8fc57a539b1c330b Mon Sep 17 00:00:00 2001 From: Andrea Giacobino Date: Mon, 22 Nov 2021 11:28:07 +0100 Subject: [PATCH 1/9] chore: remove references to local resources --- go.mod | 2 +- pkg/wallets/ssi/aries.go | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 1fd5b95..9b73e0f 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module github.com/allinbits/cosmos-cash-agent go 1.17 -replace github.com/hyperledger/aries-framework-go => /home/ghost/git/hyperledger/aries-framework-go +// replace github.com/hyperledger/aries-framework-go => /home/ghost/git/hyperledger/aries-framework-go require ( fyne.io/fyne/v2 v2.1.1 diff --git a/pkg/wallets/ssi/aries.go b/pkg/wallets/ssi/aries.go index 4e6b6df..b0079bc 100644 --- a/pkg/wallets/ssi/aries.go +++ b/pkg/wallets/ssi/aries.go @@ -43,9 +43,11 @@ var ( reqURL string ) +// SSIWallet is the wallet type SSIWallet struct { cloudAgentURL string ControllerDidID string + cloudAgentWsURL string w *wallet.Wallet ctx *context.Provider didExchangeClient *didexchange.Client @@ -173,6 +175,7 @@ func Agent(cfg config.EdgeConfigSchema, pass string) *SSIWallet { return &SSIWallet{ cloudAgentURL: cfg.CloudAgentPublicURL, ControllerDidID: fmt.Sprintf("did:cosmos:net:%s:%s", cfg.ChainID, cfg.ControllerDidID), + cloudAgentWsURL: cfg.CloudAgentWsURL, w: w, ctx: ctx, didExchangeClient: didExchangeClient, @@ -300,7 +303,7 @@ func (cw *SSIWallet) Run(hub *config.MsgHub) { if invite.Invitation == nil { reqURL = fmt.Sprint( // TODO: fix cloud agent is properly exposed on k8s cluster - "http://localhost:8090", + cw.cloudAgentURL, "/connections/create-invitation?public=did:cosmos:net:cosmoscash-testnet:mediatortestnetws1&label=BobMediatorEdgeAgent", ) post(client, reqURL, nil, &invite) From dddf216849c765df7af1a7285fe757d681a19582 Mon Sep 17 00:00:00 2001 From: Andrea Giacobino Date: Mon, 22 Nov 2021 11:52:51 +0100 Subject: [PATCH 2/9] feat: use logger when possible --- cmd/edge-agent/main.go | 1 + pkg/wallets/ssi/aries.go | 58 ++++++++++++++++++------------------- pkg/wallets/ssi/notifier.go | 4 +-- 3 files changed, 31 insertions(+), 32 deletions(-) diff --git a/cmd/edge-agent/main.go b/cmd/edge-agent/main.go index 8a3772c..ac7857b 100644 --- a/cmd/edge-agent/main.go +++ b/cmd/edge-agent/main.go @@ -16,6 +16,7 @@ import ( func init() { // Log as JSON instead of the default ASCII formatter. log.SetFormatter(&log.JSONFormatter{}) + log.SetReportCaller(true) // You could set this to any `io.Writer` such as a file // logFile, _ := config.GetAppData("edget-agent.log") file, err := os.OpenFile("app.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) diff --git a/pkg/wallets/ssi/aries.go b/pkg/wallets/ssi/aries.go index b0079bc..c80bca8 100644 --- a/pkg/wallets/ssi/aries.go +++ b/pkg/wallets/ssi/aries.go @@ -63,14 +63,14 @@ func createDIDExchangeClient(ctx *context.Provider) *didexchange.Client { // create a new did exchange client didExchange, err := didexchange.New(ctx) if err != nil { - panic(err) + log.Fatalln(err) } actions := make(chan service.DIDCommAction, 1) err = didExchange.RegisterActionEvent(actions) if err != nil { - panic(err) + log.Fatalln(err) } // NOTE: no auto execute because it doens't work with routing @@ -85,13 +85,13 @@ func createRoutingClient(ctx *context.Provider) *mediator.Client { // create the mediator client this client handler routing between edge and cloud agents routeClient, err := mediator.New(ctx) if err != nil { - panic(err) + log.Fatalln(err) } events := make(chan service.DIDCommAction) err = routeClient.RegisterActionEvent(events) if err != nil { - panic(err) + log.Fatalln(err) } go func() { service.AutoExecuteActionEvent(events) @@ -106,7 +106,7 @@ func createMessagingClient(ctx *context.Provider) *messaging.Client { msgClient, err := messaging.New(ctx, registrar, n) if err != nil { - panic(err) + log.Fatalln(err) } //genericMsg.Type = "https://didcomm.org/generic/1.0/message" @@ -116,7 +116,7 @@ func createMessagingClient(ctx *context.Provider) *messaging.Client { err = msgClient.RegisterService(name, msgType, purpose...) if err != nil { - panic(err) + log.Fatalln(err) } services := msgClient.Services() println(services[0]) @@ -153,7 +153,7 @@ func Agent(cfg config.EdgeConfigSchema, pass string) *SSIWallet { // get the context ctx, err := framework.Context() if err != nil { - panic(err) + log.Fatalln(err) } didExchangeClient := createDIDExchangeClient(ctx) @@ -163,13 +163,13 @@ func Agent(cfg config.EdgeConfigSchema, pass string) *SSIWallet { // creating wallet profile using local KMS passphrase err = wallet.CreateProfile(cfg.ControllerName, ctx, wallet.WithPassphrase(pass)) if err != nil { - panic(err) + log.Fatalln(err) } // creating vcwallet instance for user with local KMS settings. w, err = wallet.New(cfg.ControllerName, ctx) if err != nil { - panic(err) + log.Fatalln(err) } return &SSIWallet{ @@ -189,16 +189,14 @@ func (cw *SSIWallet) HandleInvitation( ) *didexchange.Connection { connectionID, err := cw.didExchangeClient.HandleInvitation(invitation) if err != nil { - panic(err) + log.Fatalln(err) } connection, err := cw.didExchangeClient.GetConnection(connectionID) if err != nil { - panic(err) + log.Fatalln(err) } - log.Infoln("Connection created", connection) - fmt.Println(connectionID) - + log.WithFields(log.Fields{"connectionID": connectionID}).Infoln("Connection created", connection) return connection } @@ -207,7 +205,7 @@ func (cw *SSIWallet) AddMediator( ) { err := cw.routeClient.Register(connectionID) if err != nil { - panic(err) + log.Fatalln(err) } log.Infoln("Mediator created") @@ -236,7 +234,7 @@ func (cw *SSIWallet) Run(hub *config.MsgHub) { // TODO handle contacts connections, err := cw.didExchangeClient.QueryConnections(&didexchange.QueryConnectionsParams{}) if err != nil { - panic(err) + log.Fatalln(err) } log.Infoln("queried connections", connections) hub.Notification <- config.NewAppMsg(config.MsgUpdateContacts, connections) @@ -271,21 +269,21 @@ func (cw *SSIWallet) Run(hub *config.MsgHub) { didexchange.WithRouterConnectionID(m.Payload.(string)), ) if err != nil { - panic(err) + log.Fatalln(err) } jsonStr, _ := json.Marshal(inv) - fmt.Println(string(jsonStr)) + log.Debugln("create invitation reply", string(jsonStr)) } else { inv, err := cw.didExchangeClient.CreateInvitation( "bob-alice-conn-direct", ) if err != nil { - panic(err) + log.Fatalln(err) } jsonStr, _ := json.Marshal(inv) - fmt.Println(string(jsonStr)) + log.Debugln("direct create invitation", string(jsonStr)) } - fmt.Print(inv) + log.Debugln("invitation is", inv) hub.Notification <- config.NewAppMsg(config.MsgUpdateContact, string(jsonStr)) case config.MsgHandleInvitation: @@ -327,7 +325,7 @@ func (cw *SSIWallet) Run(hub *config.MsgHub) { "new-with-public-did", didexchange.WithRouterConnections(params[1])) if err != nil { - panic(err) + log.Fatalln(err) } } else { err := cw.didExchangeClient.AcceptInvitation( @@ -336,7 +334,7 @@ func (cw *SSIWallet) Run(hub *config.MsgHub) { "new-wth", ) if err != nil { - panic(err) + log.Fatalln(err) } } case config.MsgApproveRequest: @@ -352,7 +350,7 @@ func (cw *SSIWallet) Run(hub *config.MsgHub) { didexchange.WithRouterConnections(params[1]), ) if err != nil { - panic(err) + log.Fatalln(err) } case config.MsgAddMediator: @@ -375,7 +373,7 @@ func (cw *SSIWallet) Run(hub *config.MsgHub) { // TODO: validate invitation is correct connection, err := cw.didExchangeClient.GetConnection(connID) if err != nil { - panic(err) + log.Fatalln(err) } sb.WriteString("ConnectionID: " + connection.ConnectionID + "\n") sb.WriteString("Status: " + connection.State + "\n") @@ -409,9 +407,9 @@ func (cw *SSIWallet) Run(hub *config.MsgHub) { resp, err := cw.messagingClient.Send(rawBytes, messaging.SendByConnectionID(params[0])) if err != nil { - panic(err) + log.Fatalln(err) } - fmt.Println(resp) + log.Debugln("message response is", resp) } } } @@ -420,18 +418,18 @@ func (cw *SSIWallet) Run(hub *config.MsgHub) { func request(client *http.Client, method, url string, requestBody io.Reader, val interface{}) { req, err := http.NewRequest(method, url, requestBody) if err != nil { - fmt.Print(err.Error()) + log.Errorln(err) } req.Header.Add("Accept", "application/json") req.Header.Add("Content-Type", "application/json") resp, err := client.Do(req) if err != nil { - fmt.Print(err.Error()) + log.Errorln(err) } defer resp.Body.Close() bodyBytes, err := ioutil.ReadAll(resp.Body) if err != nil { - fmt.Print(err.Error()) + log.Errorln(err) } json.Unmarshal(bodyBytes, &val) } diff --git a/pkg/wallets/ssi/notifier.go b/pkg/wallets/ssi/notifier.go index de7e7de..b25880b 100644 --- a/pkg/wallets/ssi/notifier.go +++ b/pkg/wallets/ssi/notifier.go @@ -1,7 +1,7 @@ package ssi import ( - "fmt" + log "github.com/sirupsen/logrus" ) // LocalNotifier handles message events @@ -10,6 +10,6 @@ type LocalNotifier struct { // Notify handlers all incoming message events. func (n LocalNotifier) Notify(topic string, message []byte) error { - fmt.Println(topic, message) + log.Infoln("local notification:", topic, message) return nil } From 971ca15847f1adda79dc74aa74dd9768b7b588c6 Mon Sep 17 00:00:00 2001 From: Andrea Giacobino Date: Thu, 25 Nov 2021 00:46:40 +0100 Subject: [PATCH 3/9] feat: groundwork to connect to a mediator --- cmd/edge-agent/main.go | 2 +- pkg/config/config.go | 18 ++++++++++ pkg/config/messages.go | 8 ++--- pkg/model/model.go | 12 +++++++ pkg/ui/handlers.go | 6 +++- pkg/wallets/chain/chain.go | 51 ++++++----------------------- pkg/wallets/chain/identifiers.go | 42 ++++++++++++++++++++++++ pkg/wallets/chain/query.go | 15 +-------- pkg/wallets/ssi/aries.go | 56 +++++++++++++++++++++++++------- 9 files changed, 137 insertions(+), 73 deletions(-) create mode 100644 pkg/wallets/chain/identifiers.go diff --git a/cmd/edge-agent/main.go b/cmd/edge-agent/main.go index ac7857b..b6c9810 100644 --- a/cmd/edge-agent/main.go +++ b/cmd/edge-agent/main.go @@ -50,7 +50,7 @@ func main() { // cosmos-sdk keystore //https://github.com/cosmos/cosmos-sdk/blob/master/client/keys/add.go - wallet := chain.Client(cfg, pwd, agent) + wallet := chain.Client(cfg, pwd) go wallet.Run(cfg.RuntimeMsgs) // render the app diff --git a/pkg/config/config.go b/pkg/config/config.go index adb8af9..57abefe 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -1,6 +1,7 @@ package config import ( + "fmt" "github.com/allinbits/cosmos-cash-agent/pkg/helpers" "github.com/google/uuid" log "github.com/sirupsen/logrus" @@ -53,6 +54,23 @@ type EdgeConfigSchema struct { RuntimeMsgs *MsgHub `json:"-"` } +// ControllerDID returns the current user did +func (ecs EdgeConfigSchema) ControllerDID() string{ + return fmt.Sprintf("did:cosmos:net:%s:%s", ecs.ChainID, ecs.ControllerDidID) +} + +// MediatorDID returns the cloud agent mediator did +func (ecs EdgeConfigSchema) MediatorDID() string{ + return "did:cosmos:net:cosmoscash-testnet:a9a67d0e-4d34-11ec-a24d-902e16d09958" +} + +// CloudAgentAPIURL returns the cloud agent mediator did +func (ecs EdgeConfigSchema) CloudAgentAPIURL() string{ + return "https://agent.cosmos-cash.app.beta.starport.cloud" +} + + + func GetAppData(subPath ...string) (string, bool) { v := []string{"data"} v = append(v, subPath...) diff --git a/pkg/config/messages.go b/pkg/config/messages.go index 9d9b1f8..c5107c6 100644 --- a/pkg/config/messages.go +++ b/pkg/config/messages.go @@ -4,18 +4,14 @@ package config type MsgHub struct { Notification chan AppMsg AgentWalletIn chan AppMsg - AgentWalletOut chan AppMsg TokenWalletIn chan AppMsg - TokenWalletOut chan AppMsg } func NewMsgHub() *MsgHub { return &MsgHub{ Notification: make(chan AppMsg, 4096), AgentWalletIn: make(chan AppMsg, 4096), - AgentWalletOut: make(chan AppMsg, 4096), TokenWalletIn: make(chan AppMsg, 4096), - TokenWalletOut: make(chan AppMsg, 4096), } } @@ -38,6 +34,8 @@ const ( MsgVCs // MsgVCData returns the details of a verifiable credential MsgVCData + // MsgAddVC add private verifiable credential to the wallet + MsgAddVC //MsgContactAdded used when a new contact si added MsgContactAdded // MsgUpdateContacts is used when updating all contacts in the list called every 30 seconds @@ -63,6 +61,8 @@ const ( MsgAddMediator // MsgGetConnectionStatus gets the connection status of a contact MsgGetConnectionStatus + // MsgDIDAddAgentKeys from aries to token to add the key agreement to the current did + MsgDIDAddAgentKeys ) // AppMsg are messages that are exchanged within the app diff --git a/pkg/model/model.go b/pkg/model/model.go index e84ce85..3b9306a 100644 --- a/pkg/model/model.go +++ b/pkg/model/model.go @@ -50,3 +50,15 @@ func NewContact(name, didID string, connection didexchange.Connection) Contact { Texts: make([]TextMessage, 0), } } + +// X25519ECDHKWPub pub key for key agreement format +type X25519ECDHKWPub struct { + Kid string `json:"kid"` + X string `json:"x"` + Curve string `json:"curve"` + Type string `json:"type"` +} + +func GenericVerifiableCredential(data map[string]interface{}) { + tig +} diff --git a/pkg/ui/handlers.go b/pkg/ui/handlers.go index 2299ce8..bd6ab2f 100644 --- a/pkg/ui/handlers.go +++ b/pkg/ui/handlers.go @@ -212,7 +212,11 @@ func executeCmd() { appCfg.RuntimeMsgs.AgentWalletIn <- config.NewAppMsg(config.MsgApproveRequest, payload) case "handle": case "h": - appCfg.RuntimeMsgs.AgentWalletIn <- config.NewAppMsg(config.MsgHandleInvitation, s[3]) + payload := "{}" // empty json + if len(s) > 3 { + payload = s[3] + } + appCfg.RuntimeMsgs.AgentWalletIn <- config.NewAppMsg(config.MsgHandleInvitation, payload) case "approve": case "a": contact, _ := contacts.GetValue(state.SelectedContact) diff --git a/pkg/wallets/chain/chain.go b/pkg/wallets/chain/chain.go index 1e2b5ce..827dcf1 100644 --- a/pkg/wallets/chain/chain.go +++ b/pkg/wallets/chain/chain.go @@ -2,13 +2,10 @@ package chain import ( "bytes" - "encoding/base64" - "encoding/json" - "fmt" "github.com/allinbits/cosmos-cash-agent/pkg/config" "github.com/allinbits/cosmos-cash-agent/pkg/helpers" - "github.com/allinbits/cosmos-cash-agent/pkg/wallets/ssi" + "github.com/allinbits/cosmos-cash-agent/pkg/model" "google.golang.org/grpc" "net/http" "time" @@ -39,7 +36,7 @@ type KeyData struct { Armor string `json:"armor"` } -func Client(cfg config.EdgeConfigSchema, password string, ssiwallet *ssi.SSIWallet) *ChainClient { +func Client(cfg config.EdgeConfigSchema, password string) *ChainClient { log.Infoln("initializing client") chainData, _ := config.GetAppData("chain") @@ -153,7 +150,7 @@ func (cc *ChainClient) BroadcastTx(msgs ...sdk.Msg) { // 1. creates account keypair // 2. get some coins from the faucet // 3. creates the did document -func initDIDDoc(chainID, didID, agentURL string, ki keyring.Info, ssiWallet *ssi.SSIWallet) sdk.Msg { +func initDIDDoc(chainID, didID, agentURL string, ki keyring.Info) sdk.Msg { log.Println("initializing new did document", didID) did := didTypes.NewChainDID(chainID, didID) // verification method id @@ -169,40 +166,6 @@ func initDIDDoc(chainID, didID, agentURL string, ki keyring.Info, ssiWallet *ssi nil, ) - keyID, pubKeyBytes, _ := ssiWallet.GetContext().KMS().CreateAndExportPubKeyBytes("X25519ECDHKW") - ariesvmID := did.NewVerificationMethodID(keyID) - - type x25519ECDHKW struct { - Kid string `json:"kid"` - X string `json:"x"` - Curve string `json:"curve"` - Type string `json:"type"` - } - - // now get into a struct - var xPubKey x25519ECDHKW - err := json.Unmarshal(pubKeyBytes, &xPubKey) - if err != nil { - panic(err) - } - // now convert the base64 encoding - rawPubKey, err := base64.StdEncoding.DecodeString(xPubKey.X) - if err != nil { - panic(err) - } - - verificationKeyAgreement := didTypes.NewVerification( - didTypes.NewVerificationMethod( - ariesvmID, - did, - didTypes.NewPublicKeyMultibase( - rawPubKey, - didTypes.DIDVMethodTypeX25519KeyAgreementKey2019), - ), - []string{didTypes.KeyAgreement}, - nil, - ) - service := didTypes.NewService( didID+"-agent", "DIDCommMessaging", @@ -211,7 +174,7 @@ func initDIDDoc(chainID, didID, agentURL string, ki keyring.Info, ssiWallet *ssi return didTypes.NewMsgCreateDidDocument( did.String(), - didTypes.Verifications{verification, verificationKeyAgreement}, + didTypes.Verifications{verification}, didTypes.Services{service}, ki.GetAddress().String(), ) @@ -271,12 +234,14 @@ func (cc *ChainClient) Run(hub *config.MsgHub) { // now process incoming queue for { m := <-hub.TokenWalletIn + log.Debugf("token wallet received message %v", m) switch m.Typ { case config.MsgPublicVCData: vc := cc.GetPublicVC(m.Payload.(string)) log.Debugln("TokenWallet received MsgPublicVCData msg for ", m.Payload.(string)) hub.Notification <- config.NewAppMsg(m.Typ, vc) case config.MsgChainOfTrust: + log.Debugln("TokenWallet received MsgPublicVCData msg for ", m.Payload.(string)) coinStr := m.Payload.(string) c, _ := sdk.ParseCoinNormalized(coinStr) cot := cc.GetDenomChainOfTrust(c.GetDenom()) @@ -285,6 +250,10 @@ func (cc *ChainClient) Run(hub *config.MsgHub) { licenseCredentialID := m.Payload.(string) cot := cc.GetChainOfTrust(licenseCredentialID) hub.Notification <- config.NewAppMsg(m.Typ, cot) + case config.MsgDIDAddAgentKeys: + log.Debugln("adding new key agreement to the did", cc.did) + pk := m.Payload.(model.X25519ECDHKWPub) + cc.DIDAddVerification(pk, didTypes.KeyAgreement, didTypes.AssertionMethod) } } } diff --git a/pkg/wallets/chain/identifiers.go b/pkg/wallets/chain/identifiers.go new file mode 100644 index 0000000..f49c036 --- /dev/null +++ b/pkg/wallets/chain/identifiers.go @@ -0,0 +1,42 @@ +package chain + +import ( + "context" + "encoding/base64" + "github.com/allinbits/cosmos-cash-agent/pkg/model" + didTypes "github.com/allinbits/cosmos-cash/v2/x/did/types" + log "github.com/sirupsen/logrus" +) + +// DIDDoc retrieve a did document for a +func (cc *ChainClient) DIDDoc(didID string) didTypes.DidDocument { + client := didTypes.NewQueryClient(cc.ctx) + res, err := client.DidDocument(context.Background(), &didTypes.QueryDidDocumentRequest{Id: didID}) + if err != nil { + log.Fatalln("error requesting balance", err) + } + log.Infoln("did document for", didID, "is", res.GetDidDocument()) + return res.GetDidDocument() +} + +// DIDAddVerification ad a verification to a did document +func (cc *ChainClient) DIDAddVerification(xPubKey model.X25519ECDHKWPub, relationships ...string) { + vmID := cc.did.NewVerificationMethodID(xPubKey.Kid) + // now convert the base64 encoding + rawPubKey, err := base64.StdEncoding.DecodeString(xPubKey.X) + if err != nil { + log.Fatalln(err) + } + verificationKeyAgreement := didTypes.NewVerification( + didTypes.NewVerificationMethod( + vmID, + cc.did, + didTypes.NewPublicKeyMultibase( + rawPubKey, + didTypes.DIDVMethodTypeX25519KeyAgreementKey2019), + ), + []string{didTypes.KeyAgreement}, + nil, + ) + cc.BroadcastTx(didTypes.NewMsgAddVerification(cc.did.String(), verificationKeyAgreement, cc.acc.String())) +} diff --git a/pkg/wallets/chain/query.go b/pkg/wallets/chain/query.go index 6c10dff..5af9ed2 100644 --- a/pkg/wallets/chain/query.go +++ b/pkg/wallets/chain/query.go @@ -96,8 +96,6 @@ func (cc *ChainClient) GetChainOfTrust(licenseCredentialID string) (cot []vcType return } - - // GetDenomChainOfTrust retrieve the chain of trust for a token DENOM func (cc *ChainClient) GetDenomChainOfTrust(denom string) (cot []vcTypes.VerifiableCredential) { client := vcTypes.NewQueryClient(cc.ctx) @@ -154,17 +152,6 @@ func (cc *ChainClient) GetDenomChainOfTrust(denom string) (cot []vcTypes.Verifia return } -// DIDDoc retrieve a did document for a -func (cc *ChainClient) DIDDoc(didID string) didTypes.DidDocument { - client := didTypes.NewQueryClient(cc.ctx) - res, err := client.DidDocument(context.Background(), &didTypes.QueryDidDocumentRequest{Id: didID}) - if err != nil { - log.Fatalln("error requesting balance", err) - } - log.Infoln("did document for", didID, "is", res.GetDidDocument()) - return res.GetDidDocument() -} - // GetHolderPublicVCS retrieve the VCS holded by a did func (cc *ChainClient) GetHolderPublicVCS(didID string) (vcs []vcTypes.VerifiableCredential) { client := vcTypes.NewQueryClient(cc.ctx) @@ -214,4 +201,4 @@ func (cc *ChainClient) GetPublicVC(vcID string) vcTypes.VerifiableCredential { log.Fatalln("error requesting balance", err) } return res.GetVerifiableCredential() -} \ No newline at end of file +} diff --git a/pkg/wallets/ssi/aries.go b/pkg/wallets/ssi/aries.go index c80bca8..c709949 100644 --- a/pkg/wallets/ssi/aries.go +++ b/pkg/wallets/ssi/aries.go @@ -4,6 +4,9 @@ import ( "bytes" "encoding/json" "fmt" + "github.com/allinbits/cosmos-cash-agent/pkg/model" + "github.com/hyperledger/aries-framework-go/component/storage/leveldb" + "github.com/hyperledger/aries-framework-go/pkg/kms" "io" "io/ioutil" "net/http" @@ -46,13 +49,16 @@ var ( // SSIWallet is the wallet type SSIWallet struct { cloudAgentURL string - ControllerDidID string + cloudAgentAPI string cloudAgentWsURL string + ControllerDID string + MediatorDID string w *wallet.Wallet ctx *context.Provider didExchangeClient *didexchange.Client routeClient *mediator.Client messagingClient *messaging.Client + walletAuthToken string } func (s SSIWallet) GetContext() *context.Provider { @@ -126,8 +132,11 @@ func createMessagingClient(ctx *context.Provider) *messaging.Client { func Agent(cfg config.EdgeConfigSchema, pass string) *SSIWallet { // datastore - provider := mem.NewProvider() - stateProvider := mem.NewProvider() + storePath, _ := config.GetAppData("aries_store") + provider := leveldb.NewProvider(storePath) + + statePath, _ := config.GetAppConfig("aries_state") + stateProvider := leveldb.NewProvider(statePath) // ws outbound var transports []transport.OutboundTransport @@ -138,7 +147,7 @@ func Agent(cfg config.EdgeConfigSchema, pass string) *SSIWallet { httpVDR, err := httpbinding.New(cfg.CosmosDIDResolverURL, httpbinding.WithAccept(func(method string) bool { return method == "cosmos" })) if err != nil { - panic(err.Error()) + log.Fatalln(err) } // create framework @@ -147,6 +156,8 @@ func Agent(cfg config.EdgeConfigSchema, pass string) *SSIWallet { aries.WithProtocolStateStoreProvider(stateProvider), aries.WithOutboundTransports(transports...), aries.WithTransportReturnRoute("all"), + aries.WithKeyType(kms.ED25519Type), + aries.WithKeyAgreementType(kms.X25519ECDHKWType), aries.WithVDR(httpVDR), // aries.WithVDR(CosmosVDR{}), ) @@ -161,26 +172,47 @@ func Agent(cfg config.EdgeConfigSchema, pass string) *SSIWallet { messagingClient := createMessagingClient(ctx) // creating wallet profile using local KMS passphrase - err = wallet.CreateProfile(cfg.ControllerName, ctx, wallet.WithPassphrase(pass)) - if err != nil { - log.Fatalln(err) + if err := wallet.CreateProfile(cfg.ControllerName, ctx, wallet.WithPassphrase(pass)); err != nil { + log.Infoln("profile already exists for", cfg.ControllerName, err) + } else { + log.Infoln("creating new profile for", cfg.ControllerName) + _, pubKeyBytes, _ := ctx.KMS().CreateAndExportPubKeyBytes(kms.X25519ECDHKWType) + + // now get into a struct + var xPubKey model.X25519ECDHKWPub + err := json.Unmarshal(pubKeyBytes, &xPubKey) + if err != nil { + log.Fatalln(err) + } + // send data to the token wallet + cfg.RuntimeMsgs.TokenWalletIn <- config.NewAppMsg(config.MsgDIDAddAgentKeys, xPubKey) + } // creating vcwallet instance for user with local KMS settings. + log.Infoln("opening wallet for", cfg.ControllerName) w, err = wallet.New(cfg.ControllerName, ctx) if err != nil { log.Fatalln(err) } + // TODO the wallet should be closed eventually + walletAuthToken, err := w.Open(wallet.WithUnlockByPassphrase(pass)) + if err != nil { + log.Fatalln("wallet cannot be opened", err) + } return &SSIWallet{ cloudAgentURL: cfg.CloudAgentPublicURL, - ControllerDidID: fmt.Sprintf("did:cosmos:net:%s:%s", cfg.ChainID, cfg.ControllerDidID), + ControllerDID: cfg.ControllerDID(), cloudAgentWsURL: cfg.CloudAgentWsURL, + cloudAgentAPI: cfg.CloudAgentAPIURL(), + MediatorDID: cfg.MediatorDID(), w: w, ctx: ctx, didExchangeClient: didExchangeClient, routeClient: routeClient, messagingClient: messagingClient, + walletAuthToken: walletAuthToken, } } @@ -321,7 +353,7 @@ func (cw *SSIWallet) Run(hub *config.MsgHub) { if len(params) > 1 && params[1] != "" { err := cw.didExchangeClient.AcceptInvitation( params[0], - cw.ControllerDidID, + cw.ControllerDID, "new-with-public-did", didexchange.WithRouterConnections(params[1])) if err != nil { @@ -330,7 +362,7 @@ func (cw *SSIWallet) Run(hub *config.MsgHub) { } else { err := cw.didExchangeClient.AcceptInvitation( params[0], - cw.ControllerDidID, + cw.ControllerDID, "new-wth", ) if err != nil { @@ -345,7 +377,7 @@ func (cw *SSIWallet) Run(hub *config.MsgHub) { params := strings.Split(m.Payload.(string), " ") err := cw.didExchangeClient.AcceptExchangeRequest( params[0], - cw.ControllerDidID, + cw.ControllerDID, "new-wth", didexchange.WithRouterConnections(params[1]), ) @@ -401,7 +433,7 @@ func (cw *SSIWallet) Run(hub *config.MsgHub) { genericMsg.Type = "https://didcomm.org/generic/1.0/message" genericMsg.Purpose = []string{"meeting"} genericMsg.Message = params[1] - genericMsg.From = cw.ControllerDidID + genericMsg.From = cw.ControllerDID rawBytes, _ := json.Marshal(genericMsg) From 6081a56d1da4de5b9c78ee6f701b322ea1adffb0 Mon Sep 17 00:00:00 2001 From: Andrea Giacobino Date: Thu, 25 Nov 2021 00:47:35 +0100 Subject: [PATCH 4/9] feat: add ability to issue verifiable credentials specificly in this case for one's own wallet address --- go.sum | 24 +++++++++++++ pkg/config/messages.go | 20 ++++++----- pkg/helpers/io.go | 6 +++- pkg/model/model.go | 47 ++++++++++++++++++++++-- pkg/ui/handlers.go | 8 ++++- pkg/ui/ui.go | 14 ++++---- pkg/wallets/chain/chain.go | 12 ++++++- pkg/wallets/ssi/aries.go | 73 ++++++++++++++++++++++++++++---------- 8 files changed, 165 insertions(+), 39 deletions(-) diff --git a/go.sum b/go.sum index 0915f2d..45b1d24 100644 --- a/go.sum +++ b/go.sum @@ -141,6 +141,7 @@ github.com/aws/aws-sdk-go v1.23.20/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpi github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.25.48/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.35.7/go.mod h1:tlPOdRjfxPBpNIwqDj61rmsnA85v9jc0Ps9+muhnW+k= github.com/aws/aws-sdk-go v1.36.29/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/aws/aws-sdk-go v1.36.30/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= @@ -498,6 +499,7 @@ github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/tink/go v1.5.0/go.mod h1:wSm19SFGYgyFRF3jqrfcMatRxFRjQ7n0Ly7Vx4ndQXQ= github.com/google/tink/go v1.6.1-0.20210519071714-58be99b3c4d0 h1:M1kxKye//XPsRJs+DaWPeDgMWK2zuZHWx/easVWhcVc= github.com/google/tink/go v1.6.1-0.20210519071714-58be99b3c4d0/go.mod h1:IGW53kTgag+st5yPhKKwJ6u2l+SSp5/v9XF7spovjlY= github.com/google/trillian v1.3.11/go.mod h1:0tPraVHrSDkA3BO6vKX67zgLXs6SsOAbHEivX+9mPgw= @@ -612,13 +614,28 @@ github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63 github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= +github.com/hyperledger/aries-framework-go v0.1.7-0.20210421203733-b5dfd703a8fc/go.mod h1:tBgxVOKcNero3QI21iNf3oxxHkgRMDOby937cqHEvW4= +github.com/hyperledger/aries-framework-go v0.1.7-0.20210603210127-e57b8c94e3cf/go.mod h1:h6L+YoXtw90OZrH2IequxukIGwzfSpz8pUueQ9T5KqI= +github.com/hyperledger/aries-framework-go v0.1.7 h1:ZauGEVaBI4GnUDJcmbiCxbG/MHabMXMlAQee1B4nw8E= +github.com/hyperledger/aries-framework-go v0.1.7/go.mod h1:uve8/q23AUnq4EM0vBkEr1lKZRC67q5RcaHXh0ZOt0Y= +github.com/hyperledger/aries-framework-go/component/storage/edv v0.0.0-20210520055214-ae429bb89bf7/go.mod h1:7D+Y5J9cIsUrMGFAsIED+3bAPNjxp6ggXo0/kT5N6BI= github.com/hyperledger/aries-framework-go/component/storage/edv v0.0.0-20210820175050-dcc7a225178d h1:Rfp6yyS21vqaSxxudqYtxCQJawI7f0RUs5oBvKbfT4Q= github.com/hyperledger/aries-framework-go/component/storage/edv v0.0.0-20210820175050-dcc7a225178d/go.mod h1:i40JkMHCh9cHHxSc1SYznO3xDH6ly5CE0B3vPYZVeWI= github.com/hyperledger/aries-framework-go/component/storage/leveldb v0.0.0-20210923135755-9446ee81c3b4 h1:kfLHoW3Z3mrk5RtHSNU5LuBxYvMKGnBQHWO8ir5xg/4= github.com/hyperledger/aries-framework-go/component/storage/leveldb v0.0.0-20210923135755-9446ee81c3b4/go.mod h1:b8XD//PLcZDaGNLn00I332HcU9a3B7e1usrOA3+2pwc= +github.com/hyperledger/aries-framework-go/component/storageutil v0.0.0-20210409151411-eeeb8508bd87/go.mod h1:kJT7bcaKsvk1lMp2jqS8srF+ZUie2H4MoPbL2V29dgA= +github.com/hyperledger/aries-framework-go/component/storageutil v0.0.0-20210421203733-b5dfd703a8fc/go.mod h1:uGc7F3tXQIY6xjs8VEI6/oxp4ZDXDfGjPMCTgax5Zhc= +github.com/hyperledger/aries-framework-go/component/storageutil v0.0.0-20210520055214-ae429bb89bf7/go.mod h1:aP6VnxeSbmD1OcV2f8y0dRV9fkIZp/+mzmgKxxmSJG4= github.com/hyperledger/aries-framework-go/component/storageutil v0.0.0-20210807121559-b41545a4f1e8/go.mod h1:k8CjDLBLxygTEj3D077OeH4SJsVE3mK60AyeO/C9sxs= github.com/hyperledger/aries-framework-go/component/storageutil v0.0.0-20210820175050-dcc7a225178d h1:x9znF6oDcA2Q8L1Ud2TlNve//DRX4z380J3KMJ/xJ30= github.com/hyperledger/aries-framework-go/component/storageutil v0.0.0-20210820175050-dcc7a225178d/go.mod h1:wdgGPwXzih+QD2Q4nvMnGO0dm0D0rxmzQcSNLcW6fcg= +github.com/hyperledger/aries-framework-go/spi v0.0.0-20210320144851-40976de98ccf/go.mod h1:fDr9wW00GJJl1lR1SFHmJW8utIocdvjO5RNhAYS05EY= +github.com/hyperledger/aries-framework-go/spi v0.0.0-20210322152545-e6ebe2c79a2a/go.mod h1:fDr9wW00GJJl1lR1SFHmJW8utIocdvjO5RNhAYS05EY= +github.com/hyperledger/aries-framework-go/spi v0.0.0-20210409151411-eeeb8508bd87/go.mod h1:dBYKKD8U8U9o0g5BdNFFaRtjt9KTkiAYfQt+TTp+w1o= +github.com/hyperledger/aries-framework-go/spi v0.0.0-20210412201938-efffe3eafcd1/go.mod h1:dBYKKD8U8U9o0g5BdNFFaRtjt9KTkiAYfQt+TTp+w1o= +github.com/hyperledger/aries-framework-go/spi v0.0.0-20210421165342-de8f911415e3/go.mod h1:dBYKKD8U8U9o0g5BdNFFaRtjt9KTkiAYfQt+TTp+w1o= +github.com/hyperledger/aries-framework-go/spi v0.0.0-20210421203733-b5dfd703a8fc/go.mod h1:dBYKKD8U8U9o0g5BdNFFaRtjt9KTkiAYfQt+TTp+w1o= +github.com/hyperledger/aries-framework-go/spi v0.0.0-20210520055214-ae429bb89bf7/go.mod h1:dBYKKD8U8U9o0g5BdNFFaRtjt9KTkiAYfQt+TTp+w1o= github.com/hyperledger/aries-framework-go/spi v0.0.0-20210603134946-53276bbf0c28/go.mod h1:dBYKKD8U8U9o0g5BdNFFaRtjt9KTkiAYfQt+TTp+w1o= github.com/hyperledger/aries-framework-go/spi v0.0.0-20210603182844-353ecb34cf4d/go.mod h1:dBYKKD8U8U9o0g5BdNFFaRtjt9KTkiAYfQt+TTp+w1o= github.com/hyperledger/aries-framework-go/spi v0.0.0-20210806210220-65863dbe349a/go.mod h1:dBYKKD8U8U9o0g5BdNFFaRtjt9KTkiAYfQt+TTp+w1o= @@ -626,6 +643,9 @@ github.com/hyperledger/aries-framework-go/spi v0.0.0-20210807121559-b41545a4f1e8 github.com/hyperledger/aries-framework-go/spi v0.0.0-20210818133831-4e22573c126d/go.mod h1:dBYKKD8U8U9o0g5BdNFFaRtjt9KTkiAYfQt+TTp+w1o= github.com/hyperledger/aries-framework-go/spi v0.0.0-20210820175050-dcc7a225178d h1:0JfPT4ORTdFMQknng3TiA2G/YY80+AMmty/47K7z4Rw= github.com/hyperledger/aries-framework-go/spi v0.0.0-20210820175050-dcc7a225178d/go.mod h1:dBYKKD8U8U9o0g5BdNFFaRtjt9KTkiAYfQt+TTp+w1o= +github.com/hyperledger/aries-framework-go/test/component v0.0.0-20210324232048-34ff560ed041/go.mod h1:eKGEEe+PJNDQo7kVif3sUKBWwnsQDkE3gD/QlpmukcQ= +github.com/hyperledger/aries-framework-go/test/component v0.0.0-20210409151411-eeeb8508bd87/go.mod h1:JHzDtgJLd0134iLFXLxGBjJF+Z+TgiElA/5oVgMazts= +github.com/hyperledger/aries-framework-go/test/component v0.0.0-20210421203733-b5dfd703a8fc/go.mod h1:asiCVCtH/nocWKhZRMz12aFgdUh8lRHqKis0M8Ei/4I= github.com/hyperledger/aries-framework-go/test/component v0.0.0-20210603182844-353ecb34cf4d/go.mod h1:J0SlvlnETEdYojUW4om/UINH0Uobmbtw46cH4DGXv5g= github.com/hyperledger/aries-framework-go/test/component v0.0.0-20210807121559-b41545a4f1e8/go.mod h1:3idbNcBl2wdRaETayzpY95KK5SfSzwXb5uqLW/Ldh0g= github.com/hyperledger/aries-framework-go/test/component v0.0.0-20210820175050-dcc7a225178d h1:6n55F8lsCR2OGGZ+3RB2ppXkdmtVaoTV7MoTvpFRyTg= @@ -687,6 +707,7 @@ github.com/kawamuray/jsonpath v0.0.0-20201211160320-7483bafabd7e h1:Eh/0JuXDdcBH github.com/kawamuray/jsonpath v0.0.0-20201211160320-7483bafabd7e/go.mod h1:dz00yqWNWlKa9ff7RJzpnHPAPUazsid3yhVzXcsok94= github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d h1:Z+RDyXzjKE0i2sTjZ/b1uxiGtPhFy34Ou/Tk0qwN0kM= github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d/go.mod h1:JJNrCn9otv/2QP4D7SMJBgaleKpOf66PnW6F5WGNRIc= +github.com/kilic/bls12-381 v0.0.0-20201104083100-a288617c07f1/go.mod h1:gcwDl9YLyNc3H3wmPXamu+8evD8TYUa6BjTsWnvdn7A= github.com/kilic/bls12-381 v0.1.1-0.20210503002446-7b7597926c69 h1:kMJlf8z8wUcpyI+FQJIdGjAhfTww1y0AbQEv86bpVQI= github.com/kilic/bls12-381 v0.1.1-0.20210503002446-7b7597926c69/go.mod h1:tlkavyke+Ac7h8R3gZIjI5LKBcvMlSWnXNMgT3vZXo8= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= @@ -921,6 +942,7 @@ github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCr github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d/go.mod h1:3OzsM7FXDQlpCiw2j81fOmAwQLnZnLGXVKUzeKQXIAw= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/piprate/json-gold v0.4.0/go.mod h1:OK1z7UgtBZk06n2cDE2OSq1kffmjFFp5/2yhLLCz9UM= github.com/piprate/json-gold v0.4.1-0.20210813112359-33b90c4ca86c h1:F4YQvOA7UTccz06y59KLw4C0iXD28hnKUP9R9zeSe8U= github.com/piprate/json-gold v0.4.1-0.20210813112359-33b90c4ca86c/go.mod h1:OK1z7UgtBZk06n2cDE2OSq1kffmjFFp5/2yhLLCz9UM= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -1130,6 +1152,7 @@ github.com/tendermint/tm-db v0.6.2/go.mod h1:GYtQ67SUvATOcoY8/+x6ylk8Qo02BQyLrAs github.com/tendermint/tm-db v0.6.3/go.mod h1:lfA1dL9/Y/Y8wwyPp2NMLyn5P5Ptr/gvDFNWtrCWSf8= github.com/tendermint/tm-db v0.6.4 h1:3N2jlnYQkXNQclQwd/eKV/NzlqPlfK21cpRRIx80XXQ= github.com/tendermint/tm-db v0.6.4/go.mod h1:dptYhIpJ2M5kUuenLr+Yyf3zQOv1SgBZcl8/BmWlMBw= +github.com/teserakt-io/golang-ed25519 v0.0.0-20200315192543-8255be791ce4/go.mod h1:9PdLyPiZIiW3UopXyRnPYyjUXSpiQNHRLu8fOsR3o8M= github.com/teserakt-io/golang-ed25519 v0.0.0-20210104091850-3888c087a4c8 h1:RBkacARv7qY5laaXGlF4wFB/tk5rnthhPb8oIBGoagY= github.com/teserakt-io/golang-ed25519 v0.0.0-20210104091850-3888c087a4c8/go.mod h1:9PdLyPiZIiW3UopXyRnPYyjUXSpiQNHRLu8fOsR3o8M= github.com/tetafro/godot v1.4.9/go.mod h1:LR3CJpxDVGlYOWn3ZZg1PgNZdTUvzsZWu8xaEohUpn8= @@ -1426,6 +1449,7 @@ golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191025090151-53bf42e6b339/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/pkg/config/messages.go b/pkg/config/messages.go index c5107c6..d26005c 100644 --- a/pkg/config/messages.go +++ b/pkg/config/messages.go @@ -2,16 +2,16 @@ package config // MsgHub contains channels used by the components to send messages to each others type MsgHub struct { - Notification chan AppMsg - AgentWalletIn chan AppMsg - TokenWalletIn chan AppMsg + Notification chan AppMsg + AgentWalletIn chan AppMsg + TokenWalletIn chan AppMsg } func NewMsgHub() *MsgHub { return &MsgHub{ - Notification: make(chan AppMsg, 4096), - AgentWalletIn: make(chan AppMsg, 4096), - TokenWalletIn: make(chan AppMsg, 4096), + Notification: make(chan AppMsg, 4096), + AgentWalletIn: make(chan AppMsg, 4096), + TokenWalletIn: make(chan AppMsg, 4096), } } @@ -34,8 +34,8 @@ const ( MsgVCs // MsgVCData returns the details of a verifiable credential MsgVCData - // MsgAddVC add private verifiable credential to the wallet - MsgAddVC + // MsgIssueVC add private verifiable credential to the wallet + MsgIssueVC //MsgContactAdded used when a new contact si added MsgContactAdded // MsgUpdateContacts is used when updating all contacts in the list called every 30 seconds @@ -63,6 +63,10 @@ const ( MsgGetConnectionStatus // MsgDIDAddAgentKeys from aries to token to add the key agreement to the current did MsgDIDAddAgentKeys + // MsgChainAddAddress generate a new address and expose it as a verifiable credential + MsgChainAddAddress + // MsgSSIAddVC add a vc to the ssi wallet + MsgSSIAddVC ) // AppMsg are messages that are exchanged within the app diff --git a/pkg/helpers/io.go b/pkg/helpers/io.go index fc4e152..86de16d 100644 --- a/pkg/helpers/io.go +++ b/pkg/helpers/io.go @@ -11,7 +11,11 @@ func WriteJson(filePath string, v interface{}) { if err != nil { panic(err) } - err = ioutil.WriteFile(filePath, data, 0600) + WriteData(filePath, data) +} + +func WriteData(filePath string, data []byte) { + err := ioutil.WriteFile(filePath, data, 0600) if err != nil { panic(err) } diff --git a/pkg/model/model.go b/pkg/model/model.go index 3b9306a..0ed302e 100644 --- a/pkg/model/model.go +++ b/pkg/model/model.go @@ -1,7 +1,10 @@ package model import ( + "encoding/json" "fmt" + "github.com/hyperledger/aries-framework-go/pkg/doc/util" + "github.com/hyperledger/aries-framework-go/pkg/doc/verifiable" "time" "github.com/hyperledger/aries-framework-go/pkg/client/didexchange" @@ -59,6 +62,46 @@ type X25519ECDHKWPub struct { Type string `json:"type"` } -func GenericVerifiableCredential(data map[string]interface{}) { - tig +// ChargedEnvelope this is used to send messages that should be sent back to the sender +type ChargedEnvelope struct { + DataIn interface{} + Callback func(message string) } + +// CREDENTIALS + +// AccountCredentialSubject represent a subject for a blockchain account +type AccountCredentialSubject struct { + ID string + Address string + Name string +} + +func ChainAccountCredential(address, did, name string) *verifiable.Credential { + return &verifiable.Credential{ + Context: []string{ + "https://www.w3.org/2018/credentials/v1", + }, + ID: fmt.Sprint("cosmos:account:", address), + Types: []string{ + "VerifiableCredential", + "CosmosAccountAddressCredential", + }, + Subject: AccountCredentialSubject{ + ID: did, + Name: name, + Address: address, + }, + Issuer: verifiable.Issuer{ + ID: did, + }, + Issued: util.NewTime(time.Now()), + } +} + +func ChainAccountCredentialRaw(address, did, name string) json.RawMessage { + vc := ChainAccountCredential(address, did, name) + rawVC, _ := json.Marshal(vc) + return json.RawMessage(string(rawVC)) +} + diff --git a/pkg/ui/handlers.go b/pkg/ui/handlers.go index bd6ab2f..7a5c207 100644 --- a/pkg/ui/handlers.go +++ b/pkg/ui/handlers.go @@ -245,7 +245,13 @@ func executeCmd() { appCfg.RuntimeMsgs.AgentWalletIn <- config.NewAppMsg(config.MsgSendText, payload) } - case "chain": + case "chain", "c": + switch s[1] { + case "address", "a": + appCfg.RuntimeMsgs.TokenWalletIn <- config.NewAppMsg(config.MsgChainAddAddress, nil) + } + + //hub.Notification <- "chain" } diff --git a/pkg/ui/ui.go b/pkg/ui/ui.go index ba0f5c8..5ef7729 100644 --- a/pkg/ui/ui.go +++ b/pkg/ui/ui.go @@ -131,17 +131,17 @@ func getCredentialsTab() *container.TabItem { ) privateList.OnSelected = privateCredentialSelected // - leftPanel := widget.NewAccordion( - widget.NewAccordionItem("Private Credentials", privateList), - widget.NewAccordionItem("Public Credentials", pubList), - ) + leftPanel := container.NewMax(widget.NewAccordion( + widget.NewAccordionItem("Private Credentials", container.NewMax(privateList)), + widget.NewAccordionItem("Public Credentials", container.NewMax(pubList)), + )) // right panel msgPanel := widget.NewLabelWithData(credentialData) - msgScroll := container.NewScroll(msgPanel) + rightPanel := container.NewScroll(msgPanel) - body := container.NewHSplit(leftPanel, msgScroll) - main := container.New(layout.NewMaxLayout(), body) + body := container.NewHSplit(leftPanel, rightPanel) + main := container.NewMax(body) return container.NewTabItem("Credentials", main) } diff --git a/pkg/wallets/chain/chain.go b/pkg/wallets/chain/chain.go index 827dcf1..cd7b274 100644 --- a/pkg/wallets/chain/chain.go +++ b/pkg/wallets/chain/chain.go @@ -130,7 +130,6 @@ func Client(cfg config.EdgeConfigSchema, password string) *ChainClient { cfg.ControllerDidID, cfg.CloudAgentPublicURL, ki, - ssiwallet, ) cc.BroadcastTx(msg) } @@ -146,6 +145,9 @@ func (cc *ChainClient) BroadcastTx(msgs ...sdk.Msg) { } +func (cc *ChainClient) Close() { +} + // Init performs the client initialization // 1. creates account keypair // 2. get some coins from the faucet @@ -254,6 +256,14 @@ func (cc *ChainClient) Run(hub *config.MsgHub) { log.Debugln("adding new key agreement to the did", cc.did) pk := m.Payload.(model.X25519ECDHKWPub) cc.DIDAddVerification(pk, didTypes.KeyAgreement, didTypes.AssertionMethod) + case config.MsgChainAddAddress: + // TODO GENERATE A NEW ACCOUNT ADDRESS + hub.AgentWalletIn <- config.NewAppMsg(config.MsgIssueVC, model.ChargedEnvelope{ + DataIn: model.ChainAccountCredentialRaw(cc.acc.String(), cc.did.String(), fmt.Sprint("Main wallet: ", cc.acc.String()[0:10])), + Callback: func(signedVC string) { + hub.AgentWalletIn <- config.NewAppMsg(config.MsgSSIAddVC, signedVC) + }, + }) } } } diff --git a/pkg/wallets/ssi/aries.go b/pkg/wallets/ssi/aries.go index c709949..f57c1d5 100644 --- a/pkg/wallets/ssi/aries.go +++ b/pkg/wallets/ssi/aries.go @@ -15,7 +15,6 @@ import ( "github.com/allinbits/cosmos-cash-agent/pkg/config" - "github.com/hyperledger/aries-framework-go/component/storageutil/mem" "github.com/hyperledger/aries-framework-go/pkg/client/didexchange" "github.com/hyperledger/aries-framework-go/pkg/client/mediator" "github.com/hyperledger/aries-framework-go/pkg/client/messaging" @@ -243,6 +242,10 @@ func (cw *SSIWallet) AddMediator( log.Infoln("Mediator created") } +func (cw *SSIWallet) Close() { + cw.w.Close() +} + // Run should be called as a goroutine, the parameters are: // State: the local state of the app that should be stored on disk // Hub: is the messages where the 3 components (ui, wallet, agent) can exchange messages @@ -253,7 +256,14 @@ func (cw *SSIWallet) Run(hub *config.MsgHub) { go func() { for { log.Infoln("ticker! retrieving verifiable credentials") - vcs := []string{} + var vcs []string + if credentials, err := cw.w.GetAll(cw.walletAuthToken, wallet.Credential); err == nil { + for _, vc := range credentials { + vcs = append(vcs, fmt.Sprint(vc)) + } + } else { + log.Errorln("failed to read credentials from wallet", err) + } hub.Notification <- config.NewAppMsg(config.MsgVCs, vcs) <-t0.C } @@ -278,12 +288,37 @@ func (cw *SSIWallet) Run(hub *config.MsgHub) { m := <-hub.AgentWalletIn log.Debugln("received message", m) switch m.Typ { + case config.MsgIssueVC: + // https://github.com/hyperledger/aries-framework-go/blob/main/docs/vc_wallet.md#add + ce := m.Payload.(model.ChargedEnvelope) + log.WithFields(log.Fields{"credential": ce.DataIn}).Debugln("adding credential") + // issue the credential + signedVC, err := cw.w.Issue(cw.walletAuthToken, ce.DataIn.(json.RawMessage), &wallet.ProofOptions{ + Controller: cw.ControllerDID, + }) + if err != nil { + log.Errorln("error issuing credential", err) + break + } + // now convert the vc to string + rawSignedVC, _ := signedVC.MarshalJSON() + log.Infof("issued credential %s", rawSignedVC) + // now trigger the function in the envelope + ce.Callback(string(rawSignedVC)) + case config.MsgSSIAddVC: + vcStr := m.Payload.(string) + if err := cw.w.Add(cw.walletAuthToken, wallet.Credential, json.RawMessage(vcStr)); err != nil { + log.Errorln("error adding credential to the wallet", err) + break + } + log.Debugln("private credential added to the wallet") + case config.MsgVCData: vcID := m.Payload.(string) - // TODO: retrieve the verifiable credential - // vc := cc.GetPublicVC(m.Payload.(string)) - log.Debugln("AgentWallet received MsgVCData msg for ", vcID) - vc := struct{}{} // <-- fake credential + vc, err := cw.w.Get(cw.walletAuthToken, wallet.Credential, vcID) + if err != nil { + log.Errorln("cannot retrieve the credential ", vcID, err) + } // always send to the notification channel for the UI // handle the notification in the ui/handlers.go dispatcher function hub.Notification <- config.NewAppMsg(m.Typ, vc) @@ -325,20 +360,19 @@ func (cw *SSIWallet) Run(hub *config.MsgHub) { ) var invite de.CreateInvitationResponse - err := json.Unmarshal([]byte(m.Payload.(string)), &invite.Invitation) - if err != nil { - println(err) - } + if err := json.Unmarshal([]byte(m.Payload.(string)), &invite.Invitation); err != nil { + log.Errorln("error unmarshalling the invitation in HsgHandleInvitation, requesting a new one") - if invite.Invitation == nil { - reqURL = fmt.Sprint( - // TODO: fix cloud agent is properly exposed on k8s cluster - cw.cloudAgentURL, - "/connections/create-invitation?public=did:cosmos:net:cosmoscash-testnet:mediatortestnetws1&label=BobMediatorEdgeAgent", - ) - post(client, reqURL, nil, &invite) + if invite.Invitation == nil { + reqURL = fmt.Sprint( + // TODO: fix cloud agent is properly exposed on k8s cluster + cw.cloudAgentURL, + fmt.Sprintf("/connections/create-invitation?public=%s&label=TDMMediatorEdgeAgent", cw.MediatorDID), + ) + post(client, reqURL, nil, &invite) + } } - + log.Infoln("invitation is ", invite) // TODO: validate invitation is correct connection := cw.HandleInvitation(invite.Invitation) @@ -449,6 +483,7 @@ func (cw *SSIWallet) Run(hub *config.MsgHub) { // TODO remove in favor of public did exchange, here for test purposes func request(client *http.Client, method, url string, requestBody io.Reader, val interface{}) { req, err := http.NewRequest(method, url, requestBody) + log.Debugln("executing http request", req) if err != nil { log.Errorln(err) } @@ -477,7 +512,7 @@ func post(client *http.Client, url string, requestBody, val interface{}) { func bitify(in interface{}) io.Reader { v, err := json.Marshal(in) if err != nil { - panic(err.Error()) + log.Fatalln(err) } return bytes.NewBuffer(v) } From b10e77c371a3a99b1a1a7ed78864c0732e337549 Mon Sep 17 00:00:00 2001 From: Andrea Giacobino Date: Fri, 26 Nov 2021 23:21:06 +0100 Subject: [PATCH 5/9] fix: key generation and credential issuance --- pkg/config/messages.go | 4 +- pkg/model/model.go | 101 ++++++++++++++++++++++++---- pkg/ui/handlers.go | 12 +++- pkg/wallets/chain/chain.go | 10 +-- pkg/wallets/chain/identifiers.go | 23 +++---- pkg/wallets/ssi/aries.go | 110 ++++++++++++++++++------------- 6 files changed, 175 insertions(+), 85 deletions(-) diff --git a/pkg/config/messages.go b/pkg/config/messages.go index d26005c..57dc8b0 100644 --- a/pkg/config/messages.go +++ b/pkg/config/messages.go @@ -61,8 +61,8 @@ const ( MsgAddMediator // MsgGetConnectionStatus gets the connection status of a contact MsgGetConnectionStatus - // MsgDIDAddAgentKeys from aries to token to add the key agreement to the current did - MsgDIDAddAgentKeys + // MsgDIDAddVerificationMethod add a verification method to a DID + MsgDIDAddVerificationMethod // MsgChainAddAddress generate a new address and expose it as a verifiable credential MsgChainAddAddress // MsgSSIAddVC add a vc to the ssi wallet diff --git a/pkg/model/model.go b/pkg/model/model.go index 0ed302e..e5dbab9 100644 --- a/pkg/model/model.go +++ b/pkg/model/model.go @@ -1,10 +1,14 @@ package model import ( + "encoding/base64" "encoding/json" "fmt" + didTypes "github.com/allinbits/cosmos-cash/v2/x/did/types" "github.com/hyperledger/aries-framework-go/pkg/doc/util" "github.com/hyperledger/aries-framework-go/pkg/doc/verifiable" + "github.com/hyperledger/aries-framework-go/pkg/wallet" + log "github.com/sirupsen/logrus" "time" "github.com/hyperledger/aries-framework-go/pkg/client/didexchange" @@ -54,14 +58,83 @@ func NewContact(name, didID string, connection didexchange.Connection) Contact { } } -// X25519ECDHKWPub pub key for key agreement format -type X25519ECDHKWPub struct { - Kid string `json:"kid"` - X string `json:"x"` - Curve string `json:"curve"` - Type string `json:"type"` +// deal with public keys + +type AriesPubKey interface { + PubKeyBytes() []byte + KeyID() string + VerificationMaterialType() didTypes.VerificationMaterialType + DIDRelationships() []string +} + +type X25519 struct { + *wallet.KeyPair +} + +func (x X25519) VerificationMaterialType() didTypes.VerificationMaterialType { + return didTypes.DIDVMethodTypeX25519KeyAgreementKey2019 +} + +func (x X25519) DIDRelationships() []string { + return []string{didTypes.KeyAgreement} } +func (x X25519) KeyID() string { + return x.KeyPair.KeyID +} + +func (x X25519) PubKeyBytes() []byte { + // decode the pub key base64 + b, err := base64.RawURLEncoding.DecodeString(x.PublicKey) + if err != nil { + log.Fatalln("cannot decode X25519 pub key base64 string", err) + } + // parse the pub key structure + var x25519 struct { + Kid string `json:"kid"` + X string `json:"x"` + Curve string `json:"curve"` + Type string `json:"type"` + } + if err := json.Unmarshal(b, &x25519); err != nil { + log.Fatalln("cannot parse X25519 pub key data", err) + } + // export the pub key bytes + pk, err := base64.StdEncoding.DecodeString(x25519.X) + if err != nil { + log.Fatalln("cannot decode X25519 X component", err) + } + return pk + +} + +type ED25519 struct { + *wallet.KeyPair +} + +func (x ED25519) VerificationMaterialType() didTypes.VerificationMaterialType { + return didTypes.DIDVMethodTypeEd25519VerificationKey2018 +} + +func (x ED25519) DIDRelationships() []string { + return []string{didTypes.AssertionMethod} +} + +func (x ED25519) KeyID() string { + return x.KeyPair.KeyID +} + +func (x ED25519) PubKeyBytes() []byte { + // decode the pub key base64 + b, err := base64.RawURLEncoding.DecodeString(x.PublicKey) + if err != nil { + log.Fatalln("cannot decode ED25519 pub key base64 string", err) + } + return b +} + +// UTLITY MESSAGES + // ChargedEnvelope this is used to send messages that should be sent back to the sender type ChargedEnvelope struct { DataIn interface{} @@ -77,15 +150,16 @@ type AccountCredentialSubject struct { Name string } -func ChainAccountCredential(address, did, name string) *verifiable.Credential { +// ChainAccountCredential creates a verifiable credential for a blockchain account +func ChainAccountCredential(chainID, address, did, name string) *verifiable.Credential { return &verifiable.Credential{ Context: []string{ "https://www.w3.org/2018/credentials/v1", }, - ID: fmt.Sprint("cosmos:account:", address), + ID: didTypes.NewBlockchainAccountID(chainID, address).EncodeToString(), Types: []string{ "VerifiableCredential", - "CosmosAccountAddressCredential", + // "CosmosAccountAddressCredential", }, Subject: AccountCredentialSubject{ ID: did, @@ -95,13 +169,14 @@ func ChainAccountCredential(address, did, name string) *verifiable.Credential { Issuer: verifiable.Issuer{ ID: did, }, - Issued: util.NewTime(time.Now()), + Issued: util.NewTime(time.Now()), } } -func ChainAccountCredentialRaw(address, did, name string) json.RawMessage { - vc := ChainAccountCredential(address, did, name) +// ChainAccountCredentialRaw creates a verifiable credential vor a blockchain account +// and returns it as a json.RawMessage ready to be used in the aries wallet context +func ChainAccountCredentialRaw(chainID, address, did, name string) json.RawMessage { + vc := ChainAccountCredential(chainID, address, did, name) rawVC, _ := json.Marshal(vc) return json.RawMessage(string(rawVC)) } - diff --git a/pkg/ui/handlers.go b/pkg/ui/handlers.go index 7a5c207..a1c96fe 100644 --- a/pkg/ui/handlers.go +++ b/pkg/ui/handlers.go @@ -9,6 +9,7 @@ import ( "github.com/allinbits/cosmos-cash-agent/pkg/model" vcTypes "github.com/allinbits/cosmos-cash/v2/x/verifiable-credential/types" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/hyperledger/aries-framework-go/pkg/doc/verifiable" log "github.com/sirupsen/logrus" "strings" @@ -84,13 +85,19 @@ func dispatcher(in chan config.AppMsg) { credentialIDs = append(credentialIDs, c.Id) } publicCredentials.Set(credentialIDs) + case config.MsgVCs: + // populate private credentials + var credentialIDs []string + for _, c := range m.Payload.([]verifiable.Credential) { + credentialIDs = append(credentialIDs, c.ID) + } + privateCredentials.Set(credentialIDs) case config.MsgVCData, config.MsgPublicVCData: if m.Payload == nil { credentialData.Set(string("No data")) continue } - vcs := m.Payload.(vcTypes.VerifiableCredential) - data, _ := json.MarshalIndent(vcs, "", " ") + data, _ := json.MarshalIndent(m.Payload, "", " ") credentialData.Set(string(data)) case config.MsgMarketplaces: var mks []string @@ -251,7 +258,6 @@ func executeCmd() { appCfg.RuntimeMsgs.TokenWalletIn <- config.NewAppMsg(config.MsgChainAddAddress, nil) } - //hub.Notification <- "chain" } diff --git a/pkg/wallets/chain/chain.go b/pkg/wallets/chain/chain.go index cd7b274..4e7b962 100644 --- a/pkg/wallets/chain/chain.go +++ b/pkg/wallets/chain/chain.go @@ -252,14 +252,14 @@ func (cc *ChainClient) Run(hub *config.MsgHub) { licenseCredentialID := m.Payload.(string) cot := cc.GetChainOfTrust(licenseCredentialID) hub.Notification <- config.NewAppMsg(m.Typ, cot) - case config.MsgDIDAddAgentKeys: - log.Debugln("adding new key agreement to the did", cc.did) - pk := m.Payload.(model.X25519ECDHKWPub) - cc.DIDAddVerification(pk, didTypes.KeyAgreement, didTypes.AssertionMethod) + case config.MsgDIDAddVerificationMethod: + log.Debugln("adding new verification method to the did", cc.did) + apk := m.Payload.(model.AriesPubKey) + cc.DIDAddVerification(apk.KeyID(), apk.PubKeyBytes(), apk.VerificationMaterialType(), apk.DIDRelationships()...) case config.MsgChainAddAddress: // TODO GENERATE A NEW ACCOUNT ADDRESS hub.AgentWalletIn <- config.NewAppMsg(config.MsgIssueVC, model.ChargedEnvelope{ - DataIn: model.ChainAccountCredentialRaw(cc.acc.String(), cc.did.String(), fmt.Sprint("Main wallet: ", cc.acc.String()[0:10])), + DataIn: model.ChainAccountCredentialRaw(cc.ctx.ChainID, cc.acc.String(), cc.did.String(), fmt.Sprint("Main wallet: ", cc.acc.String()[0:10])), Callback: func(signedVC string) { hub.AgentWalletIn <- config.NewAppMsg(config.MsgSSIAddVC, signedVC) }, diff --git a/pkg/wallets/chain/identifiers.go b/pkg/wallets/chain/identifiers.go index f49c036..029626f 100644 --- a/pkg/wallets/chain/identifiers.go +++ b/pkg/wallets/chain/identifiers.go @@ -2,8 +2,6 @@ package chain import ( "context" - "encoding/base64" - "github.com/allinbits/cosmos-cash-agent/pkg/model" didTypes "github.com/allinbits/cosmos-cash/v2/x/did/types" log "github.com/sirupsen/logrus" ) @@ -19,24 +17,19 @@ func (cc *ChainClient) DIDDoc(didID string) didTypes.DidDocument { return res.GetDidDocument() } -// DIDAddVerification ad a verification to a did document -func (cc *ChainClient) DIDAddVerification(xPubKey model.X25519ECDHKWPub, relationships ...string) { - vmID := cc.did.NewVerificationMethodID(xPubKey.Kid) - // now convert the base64 encoding - rawPubKey, err := base64.StdEncoding.DecodeString(xPubKey.X) - if err != nil { - log.Fatalln(err) - } - verificationKeyAgreement := didTypes.NewVerification( +// DIDAddVerification add verification to a a DID document +func (cc *ChainClient) DIDAddVerification(VMIDFragment string, pubKey []byte, vmType didTypes.VerificationMaterialType, relationships ... string) { + vmID := cc.did.NewVerificationMethodID(VMIDFragment) + v := didTypes.NewVerification( didTypes.NewVerificationMethod( vmID, cc.did, didTypes.NewPublicKeyMultibase( - rawPubKey, - didTypes.DIDVMethodTypeX25519KeyAgreementKey2019), + pubKey, + vmType), ), - []string{didTypes.KeyAgreement}, + relationships, nil, ) - cc.BroadcastTx(didTypes.NewMsgAddVerification(cc.did.String(), verificationKeyAgreement, cc.acc.String())) + cc.BroadcastTx(didTypes.NewMsgAddVerification(cc.did.String(), v, cc.acc.String())) } diff --git a/pkg/wallets/ssi/aries.go b/pkg/wallets/ssi/aries.go index f57c1d5..74cd788 100644 --- a/pkg/wallets/ssi/aries.go +++ b/pkg/wallets/ssi/aries.go @@ -6,6 +6,7 @@ import ( "fmt" "github.com/allinbits/cosmos-cash-agent/pkg/model" "github.com/hyperledger/aries-framework-go/component/storage/leveldb" + "github.com/hyperledger/aries-framework-go/pkg/doc/verifiable" "github.com/hyperledger/aries-framework-go/pkg/kms" "io" "io/ioutil" @@ -132,7 +133,7 @@ func createMessagingClient(ctx *context.Provider) *messaging.Client { func Agent(cfg config.EdgeConfigSchema, pass string) *SSIWallet { // datastore storePath, _ := config.GetAppData("aries_store") - provider := leveldb.NewProvider(storePath) + storeProvider := leveldb.NewProvider(storePath) statePath, _ := config.GetAppConfig("aries_state") stateProvider := leveldb.NewProvider(statePath) @@ -151,7 +152,7 @@ func Agent(cfg config.EdgeConfigSchema, pass string) *SSIWallet { // create framework framework, err := aries.New( - aries.WithStoreProvider(provider), + aries.WithStoreProvider(storeProvider), aries.WithProtocolStateStoreProvider(stateProvider), aries.WithOutboundTransports(transports...), aries.WithTransportReturnRoute("all"), @@ -170,22 +171,12 @@ func Agent(cfg config.EdgeConfigSchema, pass string) *SSIWallet { routeClient := createRoutingClient(ctx) messagingClient := createMessagingClient(ctx) + genereateKeys := false // creating wallet profile using local KMS passphrase if err := wallet.CreateProfile(cfg.ControllerName, ctx, wallet.WithPassphrase(pass)); err != nil { log.Infoln("profile already exists for", cfg.ControllerName, err) } else { - log.Infoln("creating new profile for", cfg.ControllerName) - _, pubKeyBytes, _ := ctx.KMS().CreateAndExportPubKeyBytes(kms.X25519ECDHKWType) - - // now get into a struct - var xPubKey model.X25519ECDHKWPub - err := json.Unmarshal(pubKeyBytes, &xPubKey) - if err != nil { - log.Fatalln(err) - } - // send data to the token wallet - cfg.RuntimeMsgs.TokenWalletIn <- config.NewAppMsg(config.MsgDIDAddAgentKeys, xPubKey) - + genereateKeys = true } // creating vcwallet instance for user with local KMS settings. @@ -194,10 +185,32 @@ func Agent(cfg config.EdgeConfigSchema, pass string) *SSIWallet { if err != nil { log.Fatalln(err) } + // TODO the wallet should be closed eventually walletAuthToken, err := w.Open(wallet.WithUnlockByPassphrase(pass)) if err != nil { log.Fatalln("wallet cannot be opened", err) + } else { + log.Println("wallet auth token is", walletAuthToken) + } + + if genereateKeys { + log.Infoln("creating keys for", cfg.ControllerName) + var kp *wallet.KeyPair + // create a key to perform keyAgreement between agent + kp, err = w.CreateKeyPair(walletAuthToken, kms.X25519ECDHKWType) + if err != nil { + log.Fatalln("cannot create X25519ECDHKWType key") + } + // send data to the token wallet + cfg.RuntimeMsgs.TokenWalletIn <- config.NewAppMsg(config.MsgDIDAddVerificationMethod, model.X25519{KeyPair: kp}) + + // create a key to sign credentials + kp, err = w.CreateKeyPair(walletAuthToken, kms.ED25519Type) + if err != nil { + log.Fatalln("cannot create ED25519Type key") + } + cfg.RuntimeMsgs.TokenWalletIn <- config.NewAppMsg(config.MsgDIDAddVerificationMethod, model.ED25519{KeyPair: kp}) } return &SSIWallet{ @@ -215,15 +228,15 @@ func Agent(cfg config.EdgeConfigSchema, pass string) *SSIWallet { } } -func (cw *SSIWallet) HandleInvitation( +func (s *SSIWallet) HandleInvitation( invitation *didexchange.Invitation, ) *didexchange.Connection { - connectionID, err := cw.didExchangeClient.HandleInvitation(invitation) + connectionID, err := s.didExchangeClient.HandleInvitation(invitation) if err != nil { log.Fatalln(err) } - connection, err := cw.didExchangeClient.GetConnection(connectionID) + connection, err := s.didExchangeClient.GetConnection(connectionID) if err != nil { log.Fatalln(err) } @@ -231,10 +244,10 @@ func (cw *SSIWallet) HandleInvitation( return connection } -func (cw *SSIWallet) AddMediator( +func (s *SSIWallet) AddMediator( connectionID string, ) { - err := cw.routeClient.Register(connectionID) + err := s.routeClient.Register(connectionID) if err != nil { log.Fatalln(err) } @@ -242,24 +255,27 @@ func (cw *SSIWallet) AddMediator( log.Infoln("Mediator created") } -func (cw *SSIWallet) Close() { - cw.w.Close() +func (s *SSIWallet) Close() { + s.w.Close() } // Run should be called as a goroutine, the parameters are: // State: the local state of the app that should be stored on disk // Hub: is the messages where the 3 components (ui, wallet, agent) can exchange messages -func (cw *SSIWallet) Run(hub *config.MsgHub) { +func (s *SSIWallet) Run(hub *config.MsgHub) { // send updates about verifiable credentials t0 := time.NewTicker(30 * time.Second) go func() { for { log.Infoln("ticker! retrieving verifiable credentials") - var vcs []string - if credentials, err := cw.w.GetAll(cw.walletAuthToken, wallet.Credential); err == nil { - for _, vc := range credentials { - vcs = append(vcs, fmt.Sprint(vc)) + var vcs []verifiable.Credential + if credentials, err := s.w.GetAll(s.walletAuthToken, wallet.Credential); err == nil { + for _, vcRaw := range credentials { + b, _ := vcRaw.MarshalJSON() + var vc verifiable.Credential + json.Unmarshal(b, &vc) + vcs = append(vcs, vc) } } else { log.Errorln("failed to read credentials from wallet", err) @@ -274,7 +290,7 @@ func (cw *SSIWallet) Run(hub *config.MsgHub) { go func() { for { // TODO handle contacts - connections, err := cw.didExchangeClient.QueryConnections(&didexchange.QueryConnectionsParams{}) + connections, err := s.didExchangeClient.QueryConnections(&didexchange.QueryConnectionsParams{}) if err != nil { log.Fatalln(err) } @@ -293,8 +309,8 @@ func (cw *SSIWallet) Run(hub *config.MsgHub) { ce := m.Payload.(model.ChargedEnvelope) log.WithFields(log.Fields{"credential": ce.DataIn}).Debugln("adding credential") // issue the credential - signedVC, err := cw.w.Issue(cw.walletAuthToken, ce.DataIn.(json.RawMessage), &wallet.ProofOptions{ - Controller: cw.ControllerDID, + signedVC, err := s.w.Issue(s.walletAuthToken, ce.DataIn.(json.RawMessage), &wallet.ProofOptions{ + Controller: s.ControllerDID, }) if err != nil { log.Errorln("error issuing credential", err) @@ -307,7 +323,7 @@ func (cw *SSIWallet) Run(hub *config.MsgHub) { ce.Callback(string(rawSignedVC)) case config.MsgSSIAddVC: vcStr := m.Payload.(string) - if err := cw.w.Add(cw.walletAuthToken, wallet.Credential, json.RawMessage(vcStr)); err != nil { + if err := s.w.Add(s.walletAuthToken, wallet.Credential, json.RawMessage(vcStr)); err != nil { log.Errorln("error adding credential to the wallet", err) break } @@ -315,7 +331,7 @@ func (cw *SSIWallet) Run(hub *config.MsgHub) { case config.MsgVCData: vcID := m.Payload.(string) - vc, err := cw.w.Get(cw.walletAuthToken, wallet.Credential, vcID) + vc, err := s.w.Get(s.walletAuthToken, wallet.Credential, vcID) if err != nil { log.Errorln("cannot retrieve the credential ", vcID, err) } @@ -331,7 +347,7 @@ func (cw *SSIWallet) Run(hub *config.MsgHub) { var inv didexchange.Invitation var jsonStr string if m.Payload.(string) != "" { - inv, err := cw.didExchangeClient.CreateInvitation( + inv, err := s.didExchangeClient.CreateInvitation( "bob-alice-connection-1", didexchange.WithRouterConnectionID(m.Payload.(string)), ) @@ -341,7 +357,7 @@ func (cw *SSIWallet) Run(hub *config.MsgHub) { jsonStr, _ := json.Marshal(inv) log.Debugln("create invitation reply", string(jsonStr)) } else { - inv, err := cw.didExchangeClient.CreateInvitation( + inv, err := s.didExchangeClient.CreateInvitation( "bob-alice-conn-direct", ) if err != nil { @@ -366,15 +382,15 @@ func (cw *SSIWallet) Run(hub *config.MsgHub) { if invite.Invitation == nil { reqURL = fmt.Sprint( // TODO: fix cloud agent is properly exposed on k8s cluster - cw.cloudAgentURL, - fmt.Sprintf("/connections/create-invitation?public=%s&label=TDMMediatorEdgeAgent", cw.MediatorDID), + s.cloudAgentURL, + fmt.Sprintf("/connections/create-invitation?public=%s&label=TDMMediatorEdgeAgent", s.MediatorDID), ) post(client, reqURL, nil, &invite) } } log.Infoln("invitation is ", invite) // TODO: validate invitation is correct - connection := cw.HandleInvitation(invite.Invitation) + connection := s.HandleInvitation(invite.Invitation) hub.Notification <- config.NewAppMsg(config.MsgContactAdded, connection) case config.MsgApproveInvitation: @@ -385,18 +401,18 @@ func (cw *SSIWallet) Run(hub *config.MsgHub) { params := strings.Split(m.Payload.(string), " ") if len(params) > 1 && params[1] != "" { - err := cw.didExchangeClient.AcceptInvitation( + err := s.didExchangeClient.AcceptInvitation( params[0], - cw.ControllerDID, + s.ControllerDID, "new-with-public-did", didexchange.WithRouterConnections(params[1])) if err != nil { log.Fatalln(err) } } else { - err := cw.didExchangeClient.AcceptInvitation( + err := s.didExchangeClient.AcceptInvitation( params[0], - cw.ControllerDID, + s.ControllerDID, "new-wth", ) if err != nil { @@ -409,9 +425,9 @@ func (cw *SSIWallet) Run(hub *config.MsgHub) { m.Payload.(string), ) params := strings.Split(m.Payload.(string), " ") - err := cw.didExchangeClient.AcceptExchangeRequest( + err := s.didExchangeClient.AcceptExchangeRequest( params[0], - cw.ControllerDID, + s.ControllerDID, "new-wth", didexchange.WithRouterConnections(params[1]), ) @@ -426,7 +442,7 @@ func (cw *SSIWallet) Run(hub *config.MsgHub) { ) // TODO: validate invitation is correct - cw.AddMediator(m.Payload.(string)) + s.AddMediator(m.Payload.(string)) case config.MsgGetConnectionStatus: connID := m.Payload.(string) @@ -437,14 +453,14 @@ func (cw *SSIWallet) Run(hub *config.MsgHub) { var sb strings.Builder // TODO: validate invitation is correct - connection, err := cw.didExchangeClient.GetConnection(connID) + connection, err := s.didExchangeClient.GetConnection(connID) if err != nil { log.Fatalln(err) } sb.WriteString("ConnectionID: " + connection.ConnectionID + "\n") sb.WriteString("Status: " + connection.State + "\n") sb.WriteString("Label: " + connection.TheirLabel + "\n") - routerConfig, err := cw.routeClient.GetConfig(connID) + routerConfig, err := s.routeClient.GetConfig(connID) if routerConfig != nil { log.Info(routerConfig.Endpoint()) log.Info(routerConfig.Keys()) @@ -467,11 +483,11 @@ func (cw *SSIWallet) Run(hub *config.MsgHub) { genericMsg.Type = "https://didcomm.org/generic/1.0/message" genericMsg.Purpose = []string{"meeting"} genericMsg.Message = params[1] - genericMsg.From = cw.ControllerDID + genericMsg.From = s.ControllerDID rawBytes, _ := json.Marshal(genericMsg) - resp, err := cw.messagingClient.Send(rawBytes, messaging.SendByConnectionID(params[0])) + resp, err := s.messagingClient.Send(rawBytes, messaging.SendByConnectionID(params[0])) if err != nil { log.Fatalln(err) } From 0f71b5c926b72dd80ee7895a113d41a85f7e7a99 Mon Sep 17 00:00:00 2001 From: Andrea Giacobino Date: Fri, 26 Nov 2021 23:21:56 +0100 Subject: [PATCH 6/9] feat: handle both private and public credentials --- pkg/ui/ui.go | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/pkg/ui/ui.go b/pkg/ui/ui.go index 5ef7729..24bed89 100644 --- a/pkg/ui/ui.go +++ b/pkg/ui/ui.go @@ -108,8 +108,10 @@ func getDashboardTab() *container.TabItem { func getCredentialsTab() *container.TabItem { + var pubList, privList *widget.List + // public credentials - pubList := widget.NewListWithData( + pubList = widget.NewListWithData( publicCredentials, func() fyne.CanvasObject { return widget.NewLabel("") @@ -118,9 +120,9 @@ func getCredentialsTab() *container.TabItem { o.(*widget.Label).Bind(di.(binding.String)) }, ) - pubList.OnSelected = publicCredentialSelected + // private credentials - privateList := widget.NewListWithData( + privList = widget.NewListWithData( privateCredentials, func() fyne.CanvasObject { return widget.NewLabel("") @@ -129,15 +131,27 @@ func getCredentialsTab() *container.TabItem { o.(*widget.Label).Bind(di.(binding.String)) }, ) - privateList.OnSelected = privateCredentialSelected - // - leftPanel := container.NewMax(widget.NewAccordion( - widget.NewAccordionItem("Private Credentials", container.NewMax(privateList)), - widget.NewAccordionItem("Public Credentials", container.NewMax(pubList)), - )) + + // selection actions + pubList.OnSelected = func(id widget.ListItemID) { + privList.UnselectAll() + publicCredentialSelected(id) + } + + privList.OnSelected = func(id widget.ListItemID) { + pubList.UnselectAll() + privateCredentialSelected(id) + } + + leftPanel := container.NewVSplit( + widget.NewCard("Private Credentials", "Off-chain", privList), + widget.NewCard("Public Credentials", "On-chain", pubList), + ) // right panel - msgPanel := widget.NewLabelWithData(credentialData) + + + msgPanel := widget.NewEntryWithData(credentialData) rightPanel := container.NewScroll(msgPanel) body := container.NewHSplit(leftPanel, rightPanel) From 5b83b421165d5893e1548b57da29fb7d9cfc8fe4 Mon Sep 17 00:00:00 2001 From: Andrea Giacobino Date: Sat, 27 Nov 2021 00:27:53 +0100 Subject: [PATCH 7/9] fix: aries store path --- pkg/wallets/ssi/aries.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/wallets/ssi/aries.go b/pkg/wallets/ssi/aries.go index 74cd788..1bf6a02 100644 --- a/pkg/wallets/ssi/aries.go +++ b/pkg/wallets/ssi/aries.go @@ -135,7 +135,7 @@ func Agent(cfg config.EdgeConfigSchema, pass string) *SSIWallet { storePath, _ := config.GetAppData("aries_store") storeProvider := leveldb.NewProvider(storePath) - statePath, _ := config.GetAppConfig("aries_state") + statePath, _ := config.GetAppData("aries_state") stateProvider := leveldb.NewProvider(statePath) // ws outbound From 30d659cac25f8ee83948cc01e8481c7d84f41cfc Mon Sep 17 00:00:00 2001 From: Andrea Giacobino Date: Mon, 29 Nov 2021 00:55:54 +0100 Subject: [PATCH 8/9] feat: add sample model to retrieve user input --- pkg/model/schema.go | 56 +++++++++++++++++++++++++++++++++++++ pkg/ui/handlers.go | 9 ++++++ pkg/ui/ui.go | 67 ++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 125 insertions(+), 7 deletions(-) create mode 100644 pkg/model/schema.go diff --git a/pkg/model/schema.go b/pkg/model/schema.go new file mode 100644 index 0000000..d359f41 --- /dev/null +++ b/pkg/model/schema.go @@ -0,0 +1,56 @@ +package model + +// SchemaField is a field within the credential schema +type SchemaField struct { + Name string + Title string + Description string + Value string + ReadOnly bool +} + +func NewSchemaField(name, title, description string) SchemaField { + return SchemaField{ + Name: name, + Title: title, + Description: description, + ReadOnly: false, + } +} +func NewSchemaLabel(name, title, description, value string) SchemaField { + return SchemaField{ + Name: name, + Title: title, + Description: description, + Value: value, + ReadOnly: true, + } +} + +// CredentialSchema represent a verifiable credential schema +type CredentialSchema struct { + // Should match the Verifiable Credentials that belongs to + Name string + Fields []SchemaField +} + +// LicenseSchema returns a license schema +func LicenseSchema(licenseType, authority string) CredentialSchema { + //"license_type": "MICAEMI", + //"country": "EU", + //"authority": "Another Financial Services Body (AFFB)", + //"circulation_limit": { + // "denom": "sEUR", + // "amount": "1000000000" + //} + return CredentialSchema{ + Name: "LicenseCredential", + Fields: []SchemaField{ + NewSchemaLabel("license_type", "License Type", "license acronym", licenseType), + NewSchemaLabel("authority", "Authority", "authority issuing the license", authority), + NewSchemaField("country", "Country", "country where the license applies"), + NewSchemaField("denom", "Coin denomination", "the coin symbol"), + NewSchemaField("amount", "Amount", "amount approved by the license"), + }, + } +} diff --git a/pkg/ui/handlers.go b/pkg/ui/handlers.go index a1c96fe..ab800f4 100644 --- a/pkg/ui/handlers.go +++ b/pkg/ui/handlers.go @@ -259,6 +259,15 @@ func executeCmd() { } //hub.Notification <- "chain" + case "debug", "d": + switch s[1] { + case "show-license-credentials", "lc": + ls := model.LicenseSchema("MICAEMI", "Dredd") + RenderCredentialSchema("Please supply license information",ls, func(m map[string]string) { + // todo Create verifiable Credential + + }) + } } // FINALLY RECORD THE MESSAGE IN THE CHAT diff --git a/pkg/ui/ui.go b/pkg/ui/ui.go index 24bed89..3391291 100644 --- a/pkg/ui/ui.go +++ b/pkg/ui/ui.go @@ -10,15 +10,21 @@ import ( "fyne.io/fyne/v2/theme" "fyne.io/fyne/v2/widget" "github.com/allinbits/cosmos-cash-agent/pkg/config" + "github.com/allinbits/cosmos-cash-agent/pkg/model" log "github.com/sirupsen/logrus" ) +var ( + mainApp fyne.App + mainWindow fyne.Window +) + func Render(cfg *config.EdgeConfigSchema) { appCfg = cfg - myApp := app.New() - myWindow := myApp.NewWindow(cfg.ControllerName) + mainApp = app.New() + mainWindow = mainApp.NewWindow(cfg.ControllerName) // main content tabs := container.NewAppTabs( @@ -30,22 +36,22 @@ func Render(cfg *config.EdgeConfigSchema) { getLogTab(), ) - myWindow.SetContent( + mainWindow.SetContent( container.NewMax( tabs, //footer, ), ) - myWindow.SetOnClosed(func() { + mainWindow.SetOnClosed(func() { log.Infoln(">>>>>>> TERMINATING <<<<<<<") }) // run the dispatcher that updates the ui go dispatcher(cfg.RuntimeMsgs.Notification) // lanuch the app - myWindow.Resize(fyne.NewSize(940, 660)) - myWindow.ShowAndRun() + mainWindow.Resize(fyne.NewSize(940, 660)) + mainWindow.ShowAndRun() } func getMessagesTab() *container.TabItem { @@ -150,7 +156,6 @@ func getCredentialsTab() *container.TabItem { // right panel - msgPanel := widget.NewEntryWithData(credentialData) rightPanel := container.NewScroll(msgPanel) @@ -212,3 +217,51 @@ func getLogTab() *container.TabItem { main := container.NewScroll(msgPanel) return container.NewTabItem("Logs", main) } + +func RenderCredentialSchema(title string, schema model.CredentialSchema, onSubmit func(map[string]string)) { + answers := make(map[string]binding.String) + + var popUp *widget.PopUp + var fields []*widget.FormItem + for _, s := range schema.Fields { + // data + b := binding.NewString() + e := widget.NewEntryWithData(b) + if s.ReadOnly { + b.Set(s.Value) + e.Disable() + } + answers[s.Name] = b + // form item + w := widget.NewFormItem(s.Title, e) + w.HintText = s.Description + // fields + fields = append(fields, w) + } + form := &widget.Form{ + Items: fields, + OnSubmit: func() { + // hide the popUp + popUp.Hide() + // convert data to string + data := make(map[string]string, len(fields)) + for k, b := range answers { + s, _ := b.Get() + data[k] = s + } + // execute the callback + log.Debugln("form data", data) + onSubmit(data) + }, + OnCancel: func() { popUp.Hide() }, + SubmitText: "Submit", + CancelText: "Cancel", + } + + // create the popUp + popUp = widget.NewModalPopUp( + widget.NewCard(title, "", form), + mainWindow.Canvas(), + ) + popUp.Show() +} From cf0a999eed99b297df04f2c08fb535e1b781969a Mon Sep 17 00:00:00 2001 From: Andrea Giacobino Date: Fri, 3 Dec 2021 13:43:33 +0100 Subject: [PATCH 9/9] feat: working on the payment scenario --- pkg/config/messages.go | 2 + pkg/helpers/helpers.go | 13 ++++++ pkg/model/presentations.go | 86 ++++++++++++++++++++++++++++++++++++++ pkg/model/schema.go | 56 ------------------------- pkg/ui/handlers.go | 17 +++++++- pkg/ui/ui.go | 7 +++- 6 files changed, 122 insertions(+), 59 deletions(-) create mode 100644 pkg/helpers/helpers.go create mode 100644 pkg/model/presentations.go delete mode 100644 pkg/model/schema.go diff --git a/pkg/config/messages.go b/pkg/config/messages.go index 57dc8b0..89be6e7 100644 --- a/pkg/config/messages.go +++ b/pkg/config/messages.go @@ -67,6 +67,8 @@ const ( MsgChainAddAddress // MsgSSIAddVC add a vc to the ssi wallet MsgSSIAddVC + // MsgPaymentRequest receive a payment request + MsgPaymentRequest ) // AppMsg are messages that are exchanged within the app diff --git a/pkg/helpers/helpers.go b/pkg/helpers/helpers.go new file mode 100644 index 0000000..e7ede64 --- /dev/null +++ b/pkg/helpers/helpers.go @@ -0,0 +1,13 @@ +package helpers + +import ( + "time" +) + +func DelayExec(delay int, fn func()) { + t1 := time.NewTicker(time.Duration(delay) * time.Second) + go func() { + <-t1.C + fn() + }() +} diff --git a/pkg/model/presentations.go b/pkg/model/presentations.go new file mode 100644 index 0000000..2709844 --- /dev/null +++ b/pkg/model/presentations.go @@ -0,0 +1,86 @@ +package model + +import ( + "github.com/hyperledger/aries-framework-go/pkg/doc/verifiable" + log "github.com/sirupsen/logrus" +) + +// SchemaField is a field within the credential schema +type SchemaField struct { + Name string + Title string + Description string + Value string + ReadOnly bool +} + +func NewSchemaField(name, title, description string) SchemaField { + return SchemaField{ + Name: name, + Title: title, + Description: description, + ReadOnly: false, + } +} +func NewSchemaLabel(name, title, description, value string) SchemaField { + return SchemaField{ + Name: name, + Title: title, + Description: description, + Value: value, + ReadOnly: true, + } +} + +// PresentationRequest represent a verifiable credential schema +type PresentationRequest struct { + // The name of the presentation request + Name string + // Credential Should match the Verifiable Credentials that belongs to + Credential string + // Describe the request + Fields []SchemaField +} + +// LicenseSchema returns a license schema +func LicenseSchema(licenseType, authority string) PresentationRequest { + //"license_type": "MICAEMI", + //"country": "EU", + //"authority": "Another Financial Services Body (AFFB)", + //"circulation_limit": { + // "denom": "sEUR", + // "amount": "1000000000" + //} + return PresentationRequest{ + Name: "LicensePresentationRequest", + Credential: "LicenseCredential", + Fields: []SchemaField{ + NewSchemaLabel("license_type", "License Type", "license acronym", licenseType), + NewSchemaLabel("authority", "Authority", "authority issuing the license", authority), + NewSchemaField("country", "Country", "country where the license applies"), + NewSchemaField("denom", "Token denomination", "the token symbol"), + NewSchemaField("amount", "Amount", "amount approved by the license"), + }, + } +} + +func PaymentRequest(address, denom, reason string) PresentationRequest { + return PresentationRequest{ + Name: "PaymentPresentationRequest", + Credential: "ReceiptCredential", + Fields: []SchemaField{ + NewSchemaLabel("reason", "Payment reason", "reason for the payment request", address), + NewSchemaLabel("recipient_address", "Recipient Address", "address to send the payment to", address), + NewSchemaLabel("denom", "Token denomination", "the token symbol requested by the recipient", denom), + NewSchemaField("amount", "Amount", "payment request amount"), + }, + } +} + +func LicensePresentation(license *verifiable.Credential) { + vp, err := verifiable.NewPresentation(verifiable.WithCredentials(license)) + if err != nil { + log.Errorln(err) + } + log.Debugln(vp) +} diff --git a/pkg/model/schema.go b/pkg/model/schema.go deleted file mode 100644 index d359f41..0000000 --- a/pkg/model/schema.go +++ /dev/null @@ -1,56 +0,0 @@ -package model - -// SchemaField is a field within the credential schema -type SchemaField struct { - Name string - Title string - Description string - Value string - ReadOnly bool -} - -func NewSchemaField(name, title, description string) SchemaField { - return SchemaField{ - Name: name, - Title: title, - Description: description, - ReadOnly: false, - } -} -func NewSchemaLabel(name, title, description, value string) SchemaField { - return SchemaField{ - Name: name, - Title: title, - Description: description, - Value: value, - ReadOnly: true, - } -} - -// CredentialSchema represent a verifiable credential schema -type CredentialSchema struct { - // Should match the Verifiable Credentials that belongs to - Name string - Fields []SchemaField -} - -// LicenseSchema returns a license schema -func LicenseSchema(licenseType, authority string) CredentialSchema { - //"license_type": "MICAEMI", - //"country": "EU", - //"authority": "Another Financial Services Body (AFFB)", - //"circulation_limit": { - // "denom": "sEUR", - // "amount": "1000000000" - //} - return CredentialSchema{ - Name: "LicenseCredential", - Fields: []SchemaField{ - NewSchemaLabel("license_type", "License Type", "license acronym", licenseType), - NewSchemaLabel("authority", "Authority", "authority issuing the license", authority), - NewSchemaField("country", "Country", "country where the license applies"), - NewSchemaField("denom", "Coin denomination", "the coin symbol"), - NewSchemaField("amount", "Amount", "amount approved by the license"), - }, - } -} diff --git a/pkg/ui/handlers.go b/pkg/ui/handlers.go index ab800f4..215e8d4 100644 --- a/pkg/ui/handlers.go +++ b/pkg/ui/handlers.go @@ -133,6 +133,9 @@ func dispatcher(in chan config.AppMsg) { contact, _ := state.Contacts[tm.Channel] contact.Texts = append(contact.Texts, tm) // refresh view state.Contacts[tm.Channel] = contact + // process the incoming message to see if matches a schema + + // process the incoming message to see if matches a verifiable credential // save the state appCfg.RuntimeMsgs.Notification <- config.NewAppMsg(config.MsgSaveState, nil) @@ -263,11 +266,21 @@ func executeCmd() { switch s[1] { case "show-license-credentials", "lc": ls := model.LicenseSchema("MICAEMI", "Dredd") - RenderCredentialSchema("Please supply license information",ls, func(m map[string]string) { + RenderPresentationRequest("Please supply license information", ls, func(m map[string]string) { // todo Create verifiable Credential - }) } + case "payment-request", "pr": + // TODO: the recipientAddress should be chose by the person making the payment request + // in this case is the account used for the resolver demo did. see github.com/allinbits/cosmos-cash-misc + recipientAddress := "cosmos1lcc4s4qkv0yg7ntmws6v44z5r5py27hap7pfw3" + pr := model.PaymentRequest(recipientAddress, "cash", "Demo Payment") + RenderPresentationRequest("Please fill the payment request", pr, func(m map[string]string) { + helpers.DelayExec(10, func() { + appCfg.RuntimeMsgs.Notification <- config.NewAppMsg(config.MsgPaymentRequest, nil) + }) + }) + } // FINALLY RECORD THE MESSAGE IN THE CHAT diff --git a/pkg/ui/ui.go b/pkg/ui/ui.go index 2617100..fd2430f 100644 --- a/pkg/ui/ui.go +++ b/pkg/ui/ui.go @@ -81,6 +81,11 @@ func getMessagesTab() *container.TabItem { //msgPanel := container.NewVBox() msgScroll := container.NewScroll(msgList) + input := widget.NewEntryWithData(userCommand) + input.OnSubmitted = func(_ string) { + executeCmd() + } + // footer stuff rightPanel := container.NewBorder( nil, @@ -217,7 +222,7 @@ func getLogTab() *container.TabItem { return container.NewTabItem("Logs", main) } -func RenderCredentialSchema(title string, schema model.CredentialSchema, onSubmit func(map[string]string)) { +func RenderPresentationRequest(title string, schema model.PresentationRequest, onSubmit func(map[string]string)) { answers := make(map[string]binding.String) var popUp *widget.PopUp