Skip to content
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

[EFM] Update DKG engine to submit a valid DKG index map #6490

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion engine/execution/state/bootstrap/bootstrap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func TestBootstrapLedger(t *testing.T) {
}

func TestBootstrapLedger_ZeroTokenSupply(t *testing.T) {
expectedStateCommitmentBytes, _ := hex.DecodeString("5cb14268cf6f7aa5ad4487e47aaae8d9b4c32b30c98373d35670d0c7bc080153")
expectedStateCommitmentBytes, _ := hex.DecodeString("95b38890e552404e2cfbd95796a812cc66e4ca273d869c2b82079dda60d6a65e")
expectedStateCommitment, err := flow.ToStateCommitment(expectedStateCommitmentBytes)
require.NoError(t, err)

Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ require (
github.com/onflow/cadence v1.0.0-preview.52
github.com/onflow/crypto v0.25.2
github.com/onflow/flow v0.3.4
github.com/onflow/flow-core-contracts/lib/go/contracts v1.3.2-0.20240728154212-5aafe7f667b4
github.com/onflow/flow-core-contracts/lib/go/templates v1.3.2-0.20240728154212-5aafe7f667b4
github.com/onflow/flow-core-contracts/lib/go/contracts v1.3.2-0.20240917184822-af7e508a44af
github.com/onflow/flow-core-contracts/lib/go/templates v1.3.2-0.20240917184822-af7e508a44af
github.com/onflow/flow-go-sdk v1.0.0-preview.55
github.com/onflow/flow/protobuf/go/flow v0.4.7
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58
Expand Down
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2178,10 +2178,10 @@ github.com/onflow/crypto v0.25.2 h1:GjHunqVt+vPcdqhxxhAXiMIF3YiLX7gTuTR5O+VG2ns=
github.com/onflow/crypto v0.25.2/go.mod h1:fY7eLqUdMKV8EGOw301unP8h7PvLVy8/6gVR++/g0BY=
github.com/onflow/flow v0.3.4 h1:FXUWVdYB90f/rjNcY0Owo30gL790tiYff9Pb/sycXYE=
github.com/onflow/flow v0.3.4/go.mod h1:lzyAYmbu1HfkZ9cfnL5/sjrrsnJiUU8fRL26CqLP7+c=
github.com/onflow/flow-core-contracts/lib/go/contracts v1.3.2-0.20240728154212-5aafe7f667b4 h1:pO1N4b/v2t/yRmeSfVkjYfrDDtFwvjsHgVgN+FRDQsM=
github.com/onflow/flow-core-contracts/lib/go/contracts v1.3.2-0.20240728154212-5aafe7f667b4/go.mod h1:u/mkP/B+PbV33tEG3qfkhhBlydSvAKxfLZSfB4lsJHg=
github.com/onflow/flow-core-contracts/lib/go/templates v1.3.2-0.20240728154212-5aafe7f667b4 h1:Fr1QYriOtBDgH1BLYKp1rPONndZ5qz8LsNK98LAT+YI=
github.com/onflow/flow-core-contracts/lib/go/templates v1.3.2-0.20240728154212-5aafe7f667b4/go.mod h1:NgbMOYnMh0GN48VsNKZuiwK7uyk38Wyo8jN9+C9QE30=
github.com/onflow/flow-core-contracts/lib/go/contracts v1.3.2-0.20240917184822-af7e508a44af h1:1vg6OyFMigNoyqk4SWaMlfIr5POiFX9SpFpcCvrKrUc=
github.com/onflow/flow-core-contracts/lib/go/contracts v1.3.2-0.20240917184822-af7e508a44af/go.mod h1:u/mkP/B+PbV33tEG3qfkhhBlydSvAKxfLZSfB4lsJHg=
github.com/onflow/flow-core-contracts/lib/go/templates v1.3.2-0.20240917184822-af7e508a44af h1:Vzaw1OSMOKnS3zGVyv0kgDRX+Owsj/IF4B0vbEO7kOk=
github.com/onflow/flow-core-contracts/lib/go/templates v1.3.2-0.20240917184822-af7e508a44af/go.mod h1:l8eCazFlUva+sQzh5hBWfOtk+iLmVYJ2DuhR2jSP06o=
github.com/onflow/flow-ft/lib/go/contracts v1.0.0 h1:mToacZ5NWqtlWwk/7RgIl/jeKB/Sy/tIXdw90yKHcV0=
github.com/onflow/flow-ft/lib/go/contracts v1.0.0/go.mod h1:PwsL8fC81cjnUnTfmyL/HOIyHnyaw/JA474Wfj2tl6A=
github.com/onflow/flow-ft/lib/go/templates v1.0.0 h1:6cMS/lUJJ17HjKBfMO/eh0GGvnpElPgBXx7h5aoWJhs=
Expand Down
4 changes: 2 additions & 2 deletions insecure/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -204,8 +204,8 @@ require (
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/onflow/atree v0.8.0-rc.6 // indirect
github.com/onflow/cadence v1.0.0-preview.52 // indirect
github.com/onflow/flow-core-contracts/lib/go/contracts v1.3.2-0.20240728154212-5aafe7f667b4 // indirect
github.com/onflow/flow-core-contracts/lib/go/templates v1.3.2-0.20240728154212-5aafe7f667b4 // indirect
github.com/onflow/flow-core-contracts/lib/go/contracts v1.3.2-0.20240917184822-af7e508a44af // indirect
github.com/onflow/flow-core-contracts/lib/go/templates v1.3.2-0.20240917184822-af7e508a44af // indirect
github.com/onflow/flow-ft/lib/go/contracts v1.0.0 // indirect
github.com/onflow/flow-ft/lib/go/templates v1.0.0 // indirect
github.com/onflow/flow-go-sdk v1.0.0-preview.55 // indirect
Expand Down
8 changes: 4 additions & 4 deletions insecure/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2164,10 +2164,10 @@ github.com/onflow/cadence v1.0.0-preview.52/go.mod h1:7wvvecnAZtYOspLOS3Lh+FuAmM
github.com/onflow/crypto v0.25.0/go.mod h1:C8FbaX0x8y+FxWjbkHy0Q4EASCDR9bSPWZqlpCLYyVI=
github.com/onflow/crypto v0.25.2 h1:GjHunqVt+vPcdqhxxhAXiMIF3YiLX7gTuTR5O+VG2ns=
github.com/onflow/crypto v0.25.2/go.mod h1:fY7eLqUdMKV8EGOw301unP8h7PvLVy8/6gVR++/g0BY=
github.com/onflow/flow-core-contracts/lib/go/contracts v1.3.2-0.20240728154212-5aafe7f667b4 h1:pO1N4b/v2t/yRmeSfVkjYfrDDtFwvjsHgVgN+FRDQsM=
github.com/onflow/flow-core-contracts/lib/go/contracts v1.3.2-0.20240728154212-5aafe7f667b4/go.mod h1:u/mkP/B+PbV33tEG3qfkhhBlydSvAKxfLZSfB4lsJHg=
github.com/onflow/flow-core-contracts/lib/go/templates v1.3.2-0.20240728154212-5aafe7f667b4 h1:Fr1QYriOtBDgH1BLYKp1rPONndZ5qz8LsNK98LAT+YI=
github.com/onflow/flow-core-contracts/lib/go/templates v1.3.2-0.20240728154212-5aafe7f667b4/go.mod h1:NgbMOYnMh0GN48VsNKZuiwK7uyk38Wyo8jN9+C9QE30=
github.com/onflow/flow-core-contracts/lib/go/contracts v1.3.2-0.20240917184822-af7e508a44af h1:1vg6OyFMigNoyqk4SWaMlfIr5POiFX9SpFpcCvrKrUc=
github.com/onflow/flow-core-contracts/lib/go/contracts v1.3.2-0.20240917184822-af7e508a44af/go.mod h1:u/mkP/B+PbV33tEG3qfkhhBlydSvAKxfLZSfB4lsJHg=
github.com/onflow/flow-core-contracts/lib/go/templates v1.3.2-0.20240917184822-af7e508a44af h1:Vzaw1OSMOKnS3zGVyv0kgDRX+Owsj/IF4B0vbEO7kOk=
github.com/onflow/flow-core-contracts/lib/go/templates v1.3.2-0.20240917184822-af7e508a44af/go.mod h1:l8eCazFlUva+sQzh5hBWfOtk+iLmVYJ2DuhR2jSP06o=
github.com/onflow/flow-ft/lib/go/contracts v1.0.0 h1:mToacZ5NWqtlWwk/7RgIl/jeKB/Sy/tIXdw90yKHcV0=
github.com/onflow/flow-ft/lib/go/contracts v1.0.0/go.mod h1:PwsL8fC81cjnUnTfmyL/HOIyHnyaw/JA474Wfj2tl6A=
github.com/onflow/flow-ft/lib/go/templates v1.0.0 h1:6cMS/lUJJ17HjKBfMO/eh0GGvnpElPgBXx7h5aoWJhs=
Expand Down
18 changes: 5 additions & 13 deletions integration/dkg/dkg_client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,19 +149,9 @@ func (s *ClientSuite) TestNilDKGSubmission() {
// prepare DKG
clients := s.prepareDKG(participants)

// generate list of public keys
numberOfNodes := len(participants)
publicKeys := make([]crypto.PublicKey, 0, numberOfNodes+1)
for i := 0; i < numberOfNodes; i++ {
publicKeys = append(publicKeys, nil)
}

// create a nil group public key
var groupPublicKey crypto.PublicKey

// submit empty nil keys for each participant
for _, client := range clients {
err := client.SubmitResult(groupPublicKey, publicKeys)
err := client.SubmitEmptyResult()
require.NoError(s.T(), err)
}
}
Expand All @@ -178,14 +168,16 @@ func (s *ClientSuite) TestSubmitResult() {
// generate list of public keys
numberOfNodes := len(participants)
publicKeys := make([]crypto.PublicKey, 0, numberOfNodes)
indexMap := make(flow.DKGIndexMap, numberOfNodes)
for i := 0; i < numberOfNodes; i++ {
privateKey := unittest.KeyFixture(crypto.BLSBLS12381)
publicKeys = append(publicKeys, privateKey.PublicKey())
indexMap[participants[i]] = i
}
// create a group public key
groupPublicKey := unittest.KeyFixture(crypto.BLSBLS12381).PublicKey()

err := clients[0].SubmitResult(groupPublicKey, publicKeys)
err := clients[0].SubmitParametersAndResult(indexMap, groupPublicKey, publicKeys)
require.NoError(s.T(), err)
}

Expand Down Expand Up @@ -232,7 +224,7 @@ func (s *ClientSuite) setUpAdmin() {

// set up admin resource
setUpAdminTx := sdk.NewTransaction().
SetScript(templates.GeneratePublishDKGParticipantScript(s.env)).
SetScript(templates.GeneratePublishDKGAdminScript(s.env)).
SetComputeLimit(9999).
SetProposalKey(s.blockchain.ServiceKey().Address, s.blockchain.ServiceKey().Index,
s.blockchain.ServiceKey().SequenceNumber).
Expand Down
18 changes: 15 additions & 3 deletions integration/dkg/dkg_client_wrapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"fmt"
"time"

"github.com/onflow/flow-go/module"

"github.com/onflow/crypto"
"go.uber.org/atomic"

Expand All @@ -27,6 +29,8 @@ type DKGClientWrapper struct {
enabled *atomic.Bool
}

var _ module.DKGContractClient = (*DKGClientWrapper)(nil)

// NewDKGClientWrapper instantiates a new DKGClientWrapper
func NewDKGClientWrapper(client *dkgmod.Client) *DKGClientWrapper {
return &DKGClientWrapper{
Expand Down Expand Up @@ -76,10 +80,18 @@ func (c *DKGClientWrapper) ReadBroadcast(fromIndex uint, referenceBlock flow.Ide
return c.client.ReadBroadcast(fromIndex, referenceBlock)
}

// SubmitResult implements the DKGContractClient interface
func (c *DKGClientWrapper) SubmitResult(groupPubKey crypto.PublicKey, pubKeys []crypto.PublicKey) error {
// SubmitParametersAndResult implements the DKGContractClient interface
func (c *DKGClientWrapper) SubmitParametersAndResult(indexMap flow.DKGIndexMap, groupPubKey crypto.PublicKey, pubKeys []crypto.PublicKey) error {
if !c.enabled.Load() {
return fmt.Errorf("failed to submit DKG result: %w", errClientDisabled)
}
return c.client.SubmitResult(groupPubKey, pubKeys)
return c.client.SubmitParametersAndResult(indexMap, groupPubKey, pubKeys)
}

// SubmitEmptyResult implements the DKGContractClient interface
func (c *DKGClientWrapper) SubmitEmptyResult() error {
if !c.enabled.Load() {
return fmt.Errorf("failed to submit empty DKG result: %w", errClientDisabled)
}
return c.client.SubmitEmptyResult()
}
77 changes: 57 additions & 20 deletions integration/dkg/dkg_emulator_suite.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@ package dkg

import (
"context"
"encoding/hex"
"fmt"
"os"

"github.com/onflow/crypto"
"golang.org/x/exp/slices"

"github.com/rs/zerolog"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -66,6 +70,9 @@ func (s *EmulatorSuite) SetupTest() {
s.setupDKGAdmin()

boostrapNodesInfo := unittest.PrivateNodeInfosFixture(numberOfNodes, unittest.WithRole(flow.RoleConsensus))
slices.SortFunc(boostrapNodesInfo, func(lhs, rhs bootstrap.NodeInfo) int {
return flow.IdentifierCanonical(lhs.NodeID, rhs.NodeID)
})
for _, id := range boostrapNodesInfo {
s.nodeAccounts = append(s.nodeAccounts, s.createAndFundAccount(id))
s.netIDs = append(s.netIDs, id.Identity())
Expand Down Expand Up @@ -160,7 +167,7 @@ func (s *EmulatorSuite) deployDKGContract() {

func (s *EmulatorSuite) setupDKGAdmin() {
setUpAdminTx := sdk.NewTransaction().
SetScript(templates.GeneratePublishDKGParticipantScript(s.env)).
SetScript(templates.GeneratePublishDKGAdminScript(s.env)).
SetComputeLimit(9999).
SetProposalKey(
s.blockchain.ServiceKey().Address,
Expand Down Expand Up @@ -397,31 +404,38 @@ func (s *EmulatorSuite) isDKGCompleted() bool {
return bool(value.(cadence.Bool))
}

func (s *EmulatorSuite) getResult() []string {
script := fmt.Sprintf(`
import FlowDKG from 0x%s

access(all) fun main(): [String?]? {
return FlowDKG.dkgCompleted()
} `,
s.env.DkgAddress,
)

res := s.executeScript([]byte(script), nil)
// getParametersAndResult retrieves the DKG setup parameters (`flow.DKGIndexMap`) and the DKG result from the DKG white-board smart contract.
func (s *EmulatorSuite) getParametersAndResult() (flow.DKGIndexMap, crypto.PublicKey, []crypto.PublicKey) {
res := s.executeScript(templates.GenerateGetDKGCanonicalFinalSubmissionScript(s.env), nil)
value := res.(cadence.Optional).Value
if value == nil {
return []string{}
s.Fail("DKG result is nil")
}

dkgResult := []string{}
for _, item := range value.(cadence.Array).Values {
dkgResult = append(
dkgResult,
string(item.(cadence.Optional).Value.(cadence.String)),
)
decodePubkey := func(r string) crypto.PublicKey {
pkBytes, err := hex.DecodeString(r)
require.NoError(s.T(), err)
pk, err := crypto.DecodePublicKey(crypto.BLSBLS12381, pkBytes)
require.NoError(s.T(), err)
return pk
}

fields := value.(cadence.Struct).FieldsMappedByName()
groupKey := decodePubkey(string(UnwrapOptional[cadence.String](fields["groupPubKey"])))

dkgKeyShares := CadenceArrayTo(UnwrapOptional[cadence.Array](fields["pubKeys"]), func(value cadence.Value) crypto.PublicKey {
return decodePubkey(string(value.(cadence.String)))
})

cdcIndexMap := CDCToDKGIDMapping(UnwrapOptional[cadence.Dictionary](fields["idMapping"]))
indexMap := make(flow.DKGIndexMap, len(cdcIndexMap))
for k, v := range cdcIndexMap {
nodeID, err := flow.HexStringToIdentifier(k)
require.NoError(s.T(), err)
indexMap[nodeID] = v
}

return dkgResult
return indexMap, groupKey, dkgKeyShares
}

func (s *EmulatorSuite) initEngines(node *node, ids flow.IdentityList) {
Expand Down Expand Up @@ -529,3 +543,26 @@ func (s *EmulatorSuite) executeScript(script []byte, arguments [][]byte) cadence
require.True(s.T(), result.Succeeded())
return result.Value
}

func UnwrapOptional[T cadence.Value](optional cadence.Value) T {
return optional.(cadence.Optional).Value.(T)
}

func CadenceArrayTo[T any](arr cadence.Value, convert func(cadence.Value) T) []T {
out := make([]T, len(arr.(cadence.Array).Values))
for i := range out {
out[i] = convert(arr.(cadence.Array).Values[i])
}
return out
}

func CDCToDKGIDMapping(cdc cadence.Value) map[string]int {
idMappingCDC := cdc.(cadence.Dictionary)
idMapping := make(map[string]int, len(idMappingCDC.Pairs))
for _, pair := range idMappingCDC.Pairs {
nodeID := string(pair.Key.(cadence.String))
index := pair.Value.(cadence.Int).Int()
idMapping[nodeID] = index
}
return idMapping
}
22 changes: 3 additions & 19 deletions integration/dkg/dkg_emulator_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package dkg

import (
"encoding/hex"
"fmt"
"math/rand"
"testing"
Expand Down Expand Up @@ -128,22 +127,7 @@ func (s *EmulatorSuite) runTest(goodNodes int, emulatorProblems bool) {

// the result is an array of public keys where the first item is the group
// public key
res := s.getResult()

assert.Equal(s.T(), len(s.nodes)+1, len(res))
pubKeys := make([]crypto.PublicKey, 0, len(res))
for _, r := range res {
pkBytes, err := hex.DecodeString(r)
assert.NoError(s.T(), err)
pk, err := crypto.DecodePublicKey(crypto.BLSBLS12381, pkBytes)
assert.NoError(s.T(), err)
pubKeys = append(pubKeys, pk)
}

groupPubKeyBytes, err := hex.DecodeString(res[0])
assert.NoError(s.T(), err)
groupPubKey, err := crypto.DecodePublicKey(crypto.BLSBLS12381, groupPubKeyBytes)
assert.NoError(s.T(), err)
_, groupPubKey, pubKeys := s.getParametersAndResult()

tag := "some tag"
hasher := msig.NewBLSHasher(tag)
Expand All @@ -162,9 +146,9 @@ func (s *EmulatorSuite) runTest(goodNodes int, emulatorProblems bool) {
signatures = append(signatures, signature)
indices = append(indices, i)

ok, err := pubKeys[i+1].Verify(signature, sigData, hasher)
ok, err := pubKeys[i].Verify(signature, sigData, hasher)
require.NoError(s.T(), err)
assert.True(s.T(), ok, fmt.Sprintf("signature %d share doesn't verify under the public key share", i+1))
assert.True(s.T(), ok, fmt.Sprintf("signature %d share doesn't verify under the public key share", i))
}

// shuffle the signatures and indices before constructing the group
Expand Down
27 changes: 22 additions & 5 deletions integration/dkg/dkg_whiteboard_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package dkg
import (
"sync"

"github.com/onflow/flow-go/module"

"github.com/onflow/crypto"

"github.com/onflow/flow-go/model/flow"
Expand All @@ -17,6 +19,8 @@ type WhiteboardClient struct {
whiteboard *whiteboard
}

var _ module.DKGContractClient = (*WhiteboardClient)(nil)

// NewWhiteboardClient instantiates a new WhiteboardClient with a reference to
// an existing whiteboard object.
func NewWhiteboardClient(nodeID flow.Identifier, whiteboard *whiteboard) *WhiteboardClient {
Expand Down Expand Up @@ -44,10 +48,17 @@ func (wc *WhiteboardClient) ReadBroadcast(fromIndex uint, referenceBlock flow.Id
return msgs, nil
}

// SubmitResult implements the DKGContractClient interface. It publishes the
// SubmitParametersAndResult implements the DKGContractClient interface. It publishes the
// DKG results under the node's ID.
func (wc *WhiteboardClient) SubmitResult(groupKey crypto.PublicKey, pubKeys []crypto.PublicKey) error {
wc.whiteboard.submit(wc.nodeID, groupKey, pubKeys)
func (wc *WhiteboardClient) SubmitParametersAndResult(indexMap flow.DKGIndexMap, groupKey crypto.PublicKey, pubKeys []crypto.PublicKey) error {
wc.whiteboard.submit(wc.nodeID, groupKey, pubKeys, indexMap)
return nil
}

// SubmitEmptyResult implements the DKGContractClient interface. It publishes the
// empty DKG result under the node's ID.
func (wc *WhiteboardClient) SubmitEmptyResult() error {
wc.whiteboard.submit(wc.nodeID, nil, nil, nil)
return nil
}

Expand All @@ -68,6 +79,7 @@ type whiteboard struct {
type result struct {
groupKey crypto.PublicKey
pubKeys []crypto.PublicKey
indexMap flow.DKGIndexMap
}

// Fingerprint implements the Fingerprinter interface used by MakeID
Expand Down Expand Up @@ -99,11 +111,16 @@ func (w *whiteboard) read(fromIndex uint) []messages.BroadcastDKGMessage {
return w.messages[fromIndex:]
}

func (w *whiteboard) submit(nodeID flow.Identifier, groupKey crypto.PublicKey, pubKeys []crypto.PublicKey) {
func (w *whiteboard) submit(
nodeID flow.Identifier,
groupKey crypto.PublicKey,
pubKeys []crypto.PublicKey,
indexMap flow.DKGIndexMap,
) {
w.Lock()
defer w.Unlock()

result := result{groupKey: groupKey, pubKeys: pubKeys}
result := result{groupKey: groupKey, pubKeys: pubKeys, indexMap: indexMap}
resultHash := flow.MakeID(result)

w.results[resultHash] = result
Expand Down
5 changes: 5 additions & 0 deletions integration/dkg/dkg_whiteboard_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"testing"
"time"

"golang.org/x/exp/slices"

"github.com/rs/zerolog"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -194,6 +196,9 @@ func TestWithWhiteboard(t *testing.T) {
// we run the DKG protocol with N consensus nodes
N := 10
bootstrapNodesInfo := unittest.PrivateNodeInfosFixture(N, unittest.WithRole(flow.RoleConsensus))
slices.SortFunc(bootstrapNodesInfo, func(lhs, rhs bootstrap.NodeInfo) int {
return flow.IdentifierCanonical(lhs.NodeID, rhs.NodeID)
})
conIdentities := make(flow.IdentitySkeletonList, 0, len(bootstrapNodesInfo))
for _, identity := range bootstrapNodesInfo {
conIdentities = append(conIdentities, &identity.Identity().IdentitySkeleton)
Expand Down
Loading
Loading