-
Notifications
You must be signed in to change notification settings - Fork 227
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added cardano connector plugin #1636
base: main
Are you sure you want to change the base?
Changes from 18 commits
bf9b6c4
0b47496
7b9e934
fb574c6
b9b766a
0238a58
2e9baf2
67c8b1c
ef68f8e
89c1a24
1e5f445
a95c7af
07f4dbd
dba31c7
89dd513
8a1372f
e992174
b4e295f
1f4d2f9
4efbdd8
581623f
77b0e82
d219791
0fbba39
85486a8
5bf7193
3bd61a9
bc93388
96531e3
710b94e
1649d7c
3b39818
d22629b
976a0b1
995e2bf
0d3cc67
a6d26e7
18e6eec
17bab49
c177dcb
f424f72
34390c6
6ef9ba7
e19609d
0c170a7
725f706
05bab1b
7321ba9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
// Copyright © 2025 Kaleido, Inc. | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package cardano | ||
|
||
import ( | ||
"github.com/hyperledger/firefly-common/pkg/config" | ||
"github.com/hyperledger/firefly-common/pkg/wsclient" | ||
) | ||
|
||
const ( | ||
defaultBatchSize = 50 | ||
defaultBatchTimeout = 500 | ||
) | ||
|
||
const ( | ||
// CardanoconnectConfigKey is a sub-key in the config to contain all the cardanoconnect specific config | ||
CardanoconnectConfigKey = "cardanoconnect" | ||
// CardanoconnectConfigTopic is the websocket listen topic that the node should register on, which is important if there are multiple | ||
// nodes using a single cardanoconnect | ||
CardanoconnectConfigTopic = "topic" | ||
// CardanoconnectConfigBatchSize is the batch size to configure on event streams, when auto-defining them | ||
CardanoconnectConfigBatchSize = "batchSize" | ||
// CardanoconnectConfigBatchTimeout is the batch timeout to configure on event streams, when auto-defining them | ||
CardanoconnectConfigBatchTimeout = "batchTimeout" | ||
) | ||
|
||
func (c *Cardano) InitConfig(config config.Section) { | ||
c.cardanoconnectConf = config.SubSection(CardanoconnectConfigKey) | ||
wsclient.InitConfig(c.cardanoconnectConf) | ||
c.cardanoconnectConf.AddKnownKey(CardanoconnectConfigTopic) | ||
c.cardanoconnectConf.AddKnownKey(CardanoconnectConfigBatchSize, defaultBatchSize) | ||
c.cardanoconnectConf.AddKnownKey(CardanoconnectConfigBatchTimeout, defaultBatchTimeout) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,213 @@ | ||
// Copyright © 2025 Kaleido, Inc. | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package cardano | ||
EnriqueL8 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
import ( | ||
"context" | ||
"fmt" | ||
|
||
"github.com/go-resty/resty/v2" | ||
"github.com/hyperledger/firefly-common/pkg/ffresty" | ||
"github.com/hyperledger/firefly-common/pkg/log" | ||
"github.com/hyperledger/firefly/internal/coremsgs" | ||
) | ||
|
||
type streamManager struct { | ||
client *resty.Client | ||
batchSize uint | ||
batchTimeout uint | ||
} | ||
|
||
type eventStream struct { | ||
ID string `json:"id"` | ||
Name string `json:"name"` | ||
ErrorHandling string `json:"errorHandling"` | ||
BatchSize uint `json:"batchSize"` | ||
BatchTimeoutMS uint `json:"batchTimeoutMS"` | ||
Type string `json:"type"` | ||
Timestamps bool `json:"timestamps"` | ||
} | ||
|
||
type listener struct { | ||
ID string `json:"id"` | ||
Name string `json:"name,omitempty"` | ||
} | ||
|
||
type filter struct { | ||
Event eventfilter `json:"event"` | ||
} | ||
|
||
type eventfilter struct { | ||
Contract string `json:"contract"` | ||
EventPath string `json:"eventPath"` | ||
} | ||
|
||
func newStreamManager(client *resty.Client, batchSize, batchTimeout uint) *streamManager { | ||
return &streamManager{ | ||
client: client, | ||
batchSize: batchSize, | ||
batchTimeout: batchTimeout, | ||
} | ||
} | ||
|
||
func (s *streamManager) getEventStreams(ctx context.Context) (streams []*eventStream, err error) { | ||
res, err := s.client.R(). | ||
SetContext(ctx). | ||
SetResult(&streams). | ||
Get("/eventstreams") | ||
if err != nil || !res.IsSuccess() { | ||
return nil, ffresty.WrapRestErr(ctx, res, err, coremsgs.MsgCardanoconnectRESTErr) | ||
} | ||
return streams, nil | ||
} | ||
|
||
func buildEventStream(topic string, batchSize, batchTimeout uint) *eventStream { | ||
return &eventStream{ | ||
Name: topic, | ||
ErrorHandling: "block", | ||
BatchSize: batchSize, | ||
BatchTimeoutMS: batchTimeout, | ||
Type: "websocket", | ||
Timestamps: true, | ||
} | ||
} | ||
|
||
func (s *streamManager) createEventStream(ctx context.Context, topic string) (*eventStream, error) { | ||
stream := buildEventStream(topic, s.batchSize, s.batchTimeout) | ||
res, err := s.client.R(). | ||
SetContext(ctx). | ||
SetBody(stream). | ||
SetResult(stream). | ||
Post("/eventstreams") | ||
if err != nil || !res.IsSuccess() { | ||
return nil, ffresty.WrapRestErr(ctx, res, err, coremsgs.MsgCardanoconnectRESTErr) | ||
} | ||
return stream, nil | ||
} | ||
|
||
func (s *streamManager) updateEventStream(ctx context.Context, topic string, batchSize, batchTimeout uint, eventStreamID string) (*eventStream, error) { | ||
stream := buildEventStream(topic, batchSize, batchTimeout) | ||
res, err := s.client.R(). | ||
SetContext(ctx). | ||
SetBody(stream). | ||
SetResult(stream). | ||
Patch("/eventstreams/" + eventStreamID) | ||
if err != nil || !res.IsSuccess() { | ||
return nil, ffresty.WrapRestErr(ctx, res, err, coremsgs.MsgCardanoconnectRESTErr) | ||
} | ||
return stream, nil | ||
} | ||
|
||
func (s *streamManager) ensureEventStream(ctx context.Context, topic string) (*eventStream, error) { | ||
existingStreams, err := s.getEventStreams(ctx) | ||
if err != nil { | ||
return nil, err | ||
} | ||
for _, stream := range existingStreams { | ||
if stream.Name == topic { | ||
stream, err = s.updateEventStream(ctx, topic, s.batchSize, s.batchTimeout, stream.ID) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return stream, nil | ||
} | ||
} | ||
return s.createEventStream(ctx, topic) | ||
} | ||
|
||
func (s *streamManager) getListener(ctx context.Context, streamID string, listenerID string, okNotFound bool) (listener *listener, err error) { | ||
res, err := s.client.R(). | ||
SetContext(ctx). | ||
SetResult(&listener). | ||
Get(fmt.Sprintf("/eventstreams/%s/listeners/%s", streamID, listenerID)) | ||
if err != nil || !res.IsSuccess() { | ||
if okNotFound && res.StatusCode() == 404 { | ||
return nil, nil | ||
} | ||
return nil, ffresty.WrapRestErr(ctx, res, err, coremsgs.MsgCardanoconnectRESTErr) | ||
} | ||
return listener, nil | ||
} | ||
|
||
func (s *streamManager) getListeners(ctx context.Context, streamID string) (listeners *[]listener, err error) { | ||
res, err := s.client.R(). | ||
SetContext(ctx). | ||
SetResult(&listeners). | ||
Get(fmt.Sprintf("/eventstreams/%s/listeners", streamID)) | ||
if err != nil || !res.IsSuccess() { | ||
return nil, ffresty.WrapRestErr(ctx, res, err, coremsgs.MsgCardanoconnectRESTErr) | ||
} | ||
return listeners, nil | ||
} | ||
|
||
func (s *streamManager) createListener(ctx context.Context, streamID, name, lastEvent string, filters []filter) (listener *listener, err error) { | ||
body := map[string]interface{}{ | ||
"name": name, | ||
"type": "events", | ||
"fromBlock": lastEvent, | ||
"filters": filters, | ||
} | ||
|
||
res, err := s.client.R(). | ||
SetContext(ctx). | ||
SetBody(body). | ||
SetResult(&listener). | ||
Post(fmt.Sprintf("/eventstreams/%s/listeners", streamID)) | ||
|
||
if err != nil || !res.IsSuccess() { | ||
return nil, ffresty.WrapRestErr(ctx, res, err, coremsgs.MsgCardanoconnectRESTErr) | ||
} | ||
|
||
return listener, nil | ||
} | ||
|
||
func (s *streamManager) deleteListener(ctx context.Context, streamID, listenerID string) error { | ||
res, err := s.client.R(). | ||
SetContext(ctx). | ||
Delete(fmt.Sprintf("/eventstreams/%s/listeners/%s", streamID, listenerID)) | ||
|
||
if err != nil || !res.IsSuccess() { | ||
return ffresty.WrapRestErr(ctx, res, err, coremsgs.MsgCardanoconnectRESTErr) | ||
} | ||
return nil | ||
} | ||
|
||
func (s *streamManager) ensureFireFlyListener(ctx context.Context, namespace string, version int, address, firstEvent, streamID string) (l *listener, err error) { | ||
existingListeners, err := s.getListeners(ctx, streamID) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
name := fmt.Sprintf("%s_%d_BatchPin", namespace, version) | ||
for _, l := range *existingListeners { | ||
if l.Name == name { | ||
return &l, nil | ||
} | ||
} | ||
|
||
filters := []filter{{ | ||
eventfilter{ | ||
Contract: address, | ||
EventPath: "BatchPin", | ||
}, | ||
}} | ||
if l, err = s.createListener(ctx, streamID, name, firstEvent, filters); err != nil { | ||
return nil, err | ||
} | ||
log.L(ctx).Infof("BatchPin subscription: %s", l.ID) | ||
return l, nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
// Copyright © 2023 Kaleido, Inc. | ||
// Copyright © 2025 Kaleido, Inc. | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// | ||
|
@@ -75,6 +75,8 @@ func (nm *networkMap) generateDIDDocument(ctx context.Context, identity *core.Id | |
|
||
func (nm *networkMap) generateDIDAuthentication(ctx context.Context, identity *core.Identity, verifier *core.Verifier) *VerificationMethod { | ||
switch verifier.Type { | ||
case core.VerifierTypeCardanoAddress: | ||
return nm.generateCardanoAddressVerifier(identity, verifier) | ||
case core.VerifierTypeEthAddress: | ||
return nm.generateEthAddressVerifier(identity, verifier) | ||
case core.VerifierTypeTezosAddress: | ||
|
@@ -89,6 +91,15 @@ func (nm *networkMap) generateDIDAuthentication(ctx context.Context, identity *c | |
} | ||
} | ||
|
||
func (nm *networkMap) generateCardanoAddressVerifier(identity *core.Identity, verifier *core.Verifier) *VerificationMethod { | ||
return &VerificationMethod{ | ||
ID: verifier.Hash.String(), | ||
Type: "PaymentVerificationKeyShelley_ed25519", // hope that it's safe to assume we always use Shelley | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What happenes when it's a script witness? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can that happen? I thought DID documents were for identities attached to firefly nodes, i.e. the wallets they could spend from. |
||
Controller: identity.DID, | ||
BlockchainAccountID: verifier.Value, | ||
} | ||
} | ||
|
||
func (nm *networkMap) generateEthAddressVerifier(identity *core.Identity, verifier *core.Verifier) *VerificationMethod { | ||
return &VerificationMethod{ | ||
ID: verifier.Hash.String(), | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Update all copyrights to be the company you work for in the new files added