mirror of
https://github.com/logos-messaging/logos-messaging-go.git
synced 2026-01-03 22:43:09 +00:00
refactor: credentials
This commit is contained in:
parent
04c90657cd
commit
42c0e123d9
@ -125,14 +125,6 @@ func execute(options Options) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.RLNRelay.Enable && options.RLNRelay.Dynamic {
|
|
||||||
err := node.WriteRLNMembershipCredentialsToFile(wakuNode.RLNRelay().MembershipKeyPair(), wakuNode.RLNRelay().MembershipIndex(), wakuNode.RLNRelay().MembershipContractAddress(), options.RLNRelay.CredentialsPath, []byte(options.RLNRelay.CredentialsPassword))
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
chat := NewChat(ctx, wakuNode, options)
|
chat := NewChat(ctx, wakuNode, options)
|
||||||
p := tea.NewProgram(chat.ui)
|
p := tea.NewProgram(chat.ui)
|
||||||
if err := p.Start(); err != nil {
|
if err := p.Start(); err != nil {
|
||||||
|
|||||||
@ -331,8 +331,6 @@ func Execute(options Options) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onStartRLN(wakuNode, options)
|
|
||||||
|
|
||||||
var rpcServer *rpc.WakuRpc
|
var rpcServer *rpc.WakuRpc
|
||||||
if options.RPCServer.Enable {
|
if options.RPCServer.Enable {
|
||||||
rpcServer = rpc.NewWakuRpc(wakuNode, options.RPCServer.Address, options.RPCServer.Port, options.RPCServer.Admin, options.RPCServer.Private, options.PProf, options.RPCServer.RelayCacheCapacity, logger)
|
rpcServer = rpc.NewWakuRpc(wakuNode, options.RPCServer.Address, options.RPCServer.Port, options.RPCServer.Admin, options.RPCServer.Private, options.PProf, options.RPCServer.RelayCacheCapacity, logger)
|
||||||
|
|||||||
@ -11,7 +11,3 @@ import (
|
|||||||
func checkForRLN(logger *zap.Logger, options Options, nodeOpts *[]node.WakuNodeOption) {
|
func checkForRLN(logger *zap.Logger, options Options, nodeOpts *[]node.WakuNodeOption) {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
func onStartRLN(wakuNode *node.WakuNode, options Options) {
|
|
||||||
// Do nothing
|
|
||||||
}
|
|
||||||
|
|||||||
@ -25,19 +25,13 @@ func checkForRLN(logger *zap.Logger, options Options, nodeOpts *[]node.WakuNodeO
|
|||||||
if options.RLNRelay.ETHPrivateKey != nil {
|
if options.RLNRelay.ETHPrivateKey != nil {
|
||||||
ethPrivKey = options.RLNRelay.ETHPrivateKey
|
ethPrivKey = options.RLNRelay.ETHPrivateKey
|
||||||
}
|
}
|
||||||
membershipCredentials, err := node.GetMembershipCredentials(
|
|
||||||
logger,
|
|
||||||
options.RLNRelay.CredentialsPath,
|
|
||||||
options.RLNRelay.CredentialsPassword,
|
|
||||||
options.RLNRelay.MembershipContractAddress,
|
|
||||||
uint(options.RLNRelay.MembershipIndex),
|
|
||||||
)
|
|
||||||
failOnErr(err, "Invalid membership credentials")
|
|
||||||
|
|
||||||
*nodeOpts = append(*nodeOpts, node.WithDynamicRLNRelay(
|
*nodeOpts = append(*nodeOpts, node.WithDynamicRLNRelay(
|
||||||
options.RLNRelay.PubsubTopic,
|
options.RLNRelay.PubsubTopic,
|
||||||
options.RLNRelay.ContentTopic,
|
options.RLNRelay.ContentTopic,
|
||||||
membershipCredentials,
|
options.RLNRelay.CredentialsPath,
|
||||||
|
options.RLNRelay.CredentialsPassword,
|
||||||
|
options.RLNRelay.MembershipContractAddress,
|
||||||
nil,
|
nil,
|
||||||
options.RLNRelay.ETHClientAddress,
|
options.RLNRelay.ETHClientAddress,
|
||||||
ethPrivKey,
|
ethPrivKey,
|
||||||
@ -46,10 +40,3 @@ func checkForRLN(logger *zap.Logger, options Options, nodeOpts *[]node.WakuNodeO
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func onStartRLN(wakuNode *node.WakuNode, options Options) {
|
|
||||||
if options.RLNRelay.Enable && options.RLNRelay.Dynamic && options.RLNRelay.CredentialsPath != "" {
|
|
||||||
err := node.WriteRLNMembershipCredentialsToFile(wakuNode.RLNRelay().MembershipKeyPair(), wakuNode.RLNRelay().MembershipIndex(), wakuNode.RLNRelay().MembershipContractAddress(), options.RLNRelay.CredentialsPath, []byte(options.RLNRelay.CredentialsPassword))
|
|
||||||
failOnErr(err, "Could not write membership credentials file")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,113 +0,0 @@
|
|||||||
//go:build gowaku_rln
|
|
||||||
// +build gowaku_rln
|
|
||||||
|
|
||||||
package node
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/waku-org/go-zerokit-rln/rln"
|
|
||||||
"go.uber.org/zap"
|
|
||||||
)
|
|
||||||
|
|
||||||
const RLN_CREDENTIALS_FILENAME = "rlnCredentials.txt"
|
|
||||||
|
|
||||||
func WriteRLNMembershipCredentialsToFile(keyPair *rln.MembershipKeyPair, idx rln.MembershipIndex, contractAddress common.Address, path string, passwd []byte) error {
|
|
||||||
if path == "" {
|
|
||||||
return nil // we dont want to use a credentials file
|
|
||||||
}
|
|
||||||
|
|
||||||
if keyPair == nil {
|
|
||||||
return nil // no credentials to store
|
|
||||||
}
|
|
||||||
|
|
||||||
credentialsJSON, err := json.Marshal(MembershipCredentials{
|
|
||||||
Keypair: keyPair,
|
|
||||||
Index: idx,
|
|
||||||
Contract: contractAddress,
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
encryptedCredentials, err := keystore.EncryptDataV3(credentialsJSON, passwd, keystore.StandardScryptN, keystore.StandardScryptP)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
output, err := json.Marshal(encryptedCredentials)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
path = filepath.Join(path, RLN_CREDENTIALS_FILENAME)
|
|
||||||
|
|
||||||
return ioutil.WriteFile(path, output, 0600)
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadMembershipCredentialsFromFile(credentialsFilePath string, passwd string) (MembershipCredentials, error) {
|
|
||||||
src, err := ioutil.ReadFile(credentialsFilePath)
|
|
||||||
if err != nil {
|
|
||||||
return MembershipCredentials{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var encryptedK keystore.CryptoJSON
|
|
||||||
err = json.Unmarshal(src, &encryptedK)
|
|
||||||
if err != nil {
|
|
||||||
return MembershipCredentials{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
credentialsBytes, err := keystore.DecryptDataV3(encryptedK, passwd)
|
|
||||||
if err != nil {
|
|
||||||
return MembershipCredentials{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var credentials MembershipCredentials
|
|
||||||
err = json.Unmarshal(credentialsBytes, &credentials)
|
|
||||||
|
|
||||||
return credentials, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetMembershipCredentials(logger *zap.Logger, credentialsPath string, password string, membershipContract common.Address, membershipIndex uint) (credentials MembershipCredentials, err error) {
|
|
||||||
if credentialsPath == "" { // Not using a file
|
|
||||||
return MembershipCredentials{
|
|
||||||
Contract: membershipContract,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
credentialsFilePath := filepath.Join(credentialsPath, RLN_CREDENTIALS_FILENAME)
|
|
||||||
if _, err = os.Stat(credentialsFilePath); err == nil {
|
|
||||||
if credentials, err := loadMembershipCredentialsFromFile(credentialsFilePath, password); err != nil {
|
|
||||||
return MembershipCredentials{}, fmt.Errorf("could not read membership credentials file: %w", err)
|
|
||||||
} else {
|
|
||||||
logger.Info("loaded rln credentials", zap.String("filepath", credentialsFilePath))
|
|
||||||
if (bytes.Equal(credentials.Contract.Bytes(), common.Address{}.Bytes())) {
|
|
||||||
credentials.Contract = membershipContract
|
|
||||||
}
|
|
||||||
if (bytes.Equal(membershipContract.Bytes(), common.Address{}.Bytes())) {
|
|
||||||
return MembershipCredentials{}, errors.New("no contract address specified")
|
|
||||||
}
|
|
||||||
return credentials, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return MembershipCredentials{
|
|
||||||
Keypair: nil,
|
|
||||||
Index: membershipIndex,
|
|
||||||
Contract: membershipContract,
|
|
||||||
}, nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return MembershipCredentials{}, fmt.Errorf("could not read membership credentials file: %w", err)
|
|
||||||
}
|
|
||||||
@ -58,21 +58,13 @@ func (w *WakuNode) mountRlnRelay(ctx context.Context) error {
|
|||||||
} else {
|
} else {
|
||||||
w.log.Info("setting up waku-rln-relay in on-chain mode")
|
w.log.Info("setting up waku-rln-relay in on-chain mode")
|
||||||
|
|
||||||
// check if the peer has provided its rln credentials
|
|
||||||
var memKeyPair *r.IdentityCredential
|
|
||||||
if w.opts.rlnRelayIDCommitment != nil && w.opts.rlnRelayIDKey != nil {
|
|
||||||
memKeyPair = &r.IdentityCredential{
|
|
||||||
IDCommitment: *w.opts.rlnRelayIDCommitment,
|
|
||||||
IDSecretHash: *w.opts.rlnRelayIDKey,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
groupManager, err = dynamic.NewDynamicGroupManager(
|
groupManager, err = dynamic.NewDynamicGroupManager(
|
||||||
w.opts.rlnETHClientAddress,
|
w.opts.rlnETHClientAddress,
|
||||||
w.opts.rlnETHPrivateKey,
|
w.opts.rlnETHPrivateKey,
|
||||||
w.opts.rlnMembershipContractAddress,
|
w.opts.rlnMembershipContractAddress,
|
||||||
memKeyPair,
|
w.opts.keystorePath,
|
||||||
w.opts.rlnRelayMemIndex,
|
w.opts.keystorePassword,
|
||||||
|
true,
|
||||||
w.opts.rlnRegistrationHandler,
|
w.opts.rlnRegistrationHandler,
|
||||||
w.log,
|
w.log,
|
||||||
)
|
)
|
||||||
|
|||||||
@ -100,10 +100,10 @@ type WakuNodeParameters struct {
|
|||||||
rlnRelayContentTopic string
|
rlnRelayContentTopic string
|
||||||
rlnRelayDynamic bool
|
rlnRelayDynamic bool
|
||||||
rlnSpamHandler func(message *pb.WakuMessage) error
|
rlnSpamHandler func(message *pb.WakuMessage) error
|
||||||
rlnRelayIDKey *[32]byte
|
|
||||||
rlnRelayIDCommitment *[32]byte
|
|
||||||
rlnETHPrivateKey *ecdsa.PrivateKey
|
rlnETHPrivateKey *ecdsa.PrivateKey
|
||||||
rlnETHClientAddress string
|
rlnETHClientAddress string
|
||||||
|
keystorePath string
|
||||||
|
keystorePassword string
|
||||||
rlnMembershipContractAddress common.Address
|
rlnMembershipContractAddress common.Address
|
||||||
rlnRegistrationHandler func(tx *types.Transaction)
|
rlnRegistrationHandler func(tx *types.Transaction)
|
||||||
|
|
||||||
|
|||||||
@ -25,29 +25,20 @@ func WithStaticRLNRelay(pubsubTopic string, contentTopic string, memberIndex r.M
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type MembershipCredentials struct {
|
// WithDynamicRLNRelay enables the Waku V2 RLN protocol in onchain mode.
|
||||||
Contract common.Address `json:"contract"`
|
|
||||||
Keypair *r.MembershipKeyPair `json:"membershipKeyPair"`
|
|
||||||
Index r.MembershipIndex `json:"rlnIndex"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithStaticRLNRelay enables the Waku V2 RLN protocol in onchain mode.
|
|
||||||
// Requires the `gowaku_rln` build constrain (or the env variable RLN=true if building go-waku)
|
// Requires the `gowaku_rln` build constrain (or the env variable RLN=true if building go-waku)
|
||||||
func WithDynamicRLNRelay(pubsubTopic string, contentTopic string, membershipCredentials MembershipCredentials, spamHandler rln.SpamHandler, ethClientAddress string, ethPrivateKey *ecdsa.PrivateKey, registrationHandler rln.RegistrationHandler) WakuNodeOption {
|
func WithDynamicRLNRelay(pubsubTopic string, contentTopic string, keystorePath string, keystorePassword string, membershipContract common.Address, spamHandler rln.SpamHandler, ethClientAddress string, ethPrivateKey *ecdsa.PrivateKey, registrationHandler rln.RegistrationHandler) WakuNodeOption {
|
||||||
return func(params *WakuNodeParameters) error {
|
return func(params *WakuNodeParameters) error {
|
||||||
params.enableRLN = true
|
params.enableRLN = true
|
||||||
params.rlnRelayDynamic = true
|
params.rlnRelayDynamic = true
|
||||||
params.rlnRelayMemIndex = membershipCredentials.Index
|
params.keystorePassword = keystorePassword
|
||||||
if membershipCredentials.Keypair != nil {
|
params.keystorePath = keystorePath
|
||||||
params.rlnRelayIDKey = &membershipCredentials.Keypair.IDKey
|
|
||||||
params.rlnRelayIDCommitment = &membershipCredentials.Keypair.IDCommitment
|
|
||||||
}
|
|
||||||
params.rlnRelayPubsubTopic = pubsubTopic
|
params.rlnRelayPubsubTopic = pubsubTopic
|
||||||
params.rlnRelayContentTopic = contentTopic
|
params.rlnRelayContentTopic = contentTopic
|
||||||
params.rlnSpamHandler = spamHandler
|
params.rlnSpamHandler = spamHandler
|
||||||
params.rlnETHClientAddress = ethClientAddress
|
params.rlnETHClientAddress = ethClientAddress
|
||||||
params.rlnETHPrivateKey = ethPrivateKey
|
params.rlnETHPrivateKey = ethPrivateKey
|
||||||
params.rlnMembershipContractAddress = membershipCredentials.Contract
|
params.rlnMembershipContractAddress = membershipContract
|
||||||
params.rlnRegistrationHandler = registrationHandler
|
params.rlnRegistrationHandler = registrationHandler
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,12 +24,6 @@ const MAX_EPOCH_GAP = int64(MAX_CLOCK_GAP_SECONDS / rln.EPOCH_UNIT_SECONDS)
|
|||||||
// Acceptable roots for merkle root validation of incoming messages
|
// Acceptable roots for merkle root validation of incoming messages
|
||||||
const AcceptableRootWindowSize = 5
|
const AcceptableRootWindowSize = 5
|
||||||
|
|
||||||
type AppInfo struct {
|
|
||||||
Application string
|
|
||||||
AppIdentifier string
|
|
||||||
Version string
|
|
||||||
}
|
|
||||||
|
|
||||||
type RegistrationHandler = func(tx *types.Transaction)
|
type RegistrationHandler = func(tx *types.Transaction)
|
||||||
|
|
||||||
type SpamHandler = func(message *pb.WakuMessage) error
|
type SpamHandler = func(message *pb.WakuMessage) error
|
||||||
|
|||||||
@ -4,17 +4,27 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"errors"
|
"errors"
|
||||||
|
"math/big"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/ethclient"
|
"github.com/ethereum/go-ethereum/ethclient"
|
||||||
|
"github.com/waku-org/go-waku/waku/v2/protocol/rln/contracts"
|
||||||
"github.com/waku-org/go-waku/waku/v2/protocol/rln/group_manager"
|
"github.com/waku-org/go-waku/waku/v2/protocol/rln/group_manager"
|
||||||
|
"github.com/waku-org/go-waku/waku/v2/protocol/rln/keystore"
|
||||||
"github.com/waku-org/go-zerokit-rln/rln"
|
"github.com/waku-org/go-zerokit-rln/rln"
|
||||||
r "github.com/waku-org/go-zerokit-rln/rln"
|
r "github.com/waku-org/go-zerokit-rln/rln"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var RLNAppInfo = keystore.AppInfo{
|
||||||
|
Application: "go-waku-rln-relay",
|
||||||
|
AppIdentifier: "01234567890abcdef",
|
||||||
|
Version: "0.1",
|
||||||
|
}
|
||||||
|
|
||||||
type DynamicGroupManager struct {
|
type DynamicGroupManager struct {
|
||||||
rln *rln.RLN
|
rln *rln.RLN
|
||||||
log *zap.Logger
|
log *zap.Logger
|
||||||
@ -22,8 +32,9 @@ type DynamicGroupManager struct {
|
|||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
|
|
||||||
identityCredential *rln.IdentityCredential
|
identityCredential *rln.IdentityCredential
|
||||||
membershipIndex *rln.MembershipIndex
|
membershipIndex *rln.MembershipIndex
|
||||||
|
|
||||||
membershipContractAddress common.Address
|
membershipContractAddress common.Address
|
||||||
ethClientAddress string
|
ethClientAddress string
|
||||||
ethClient *ethclient.Client
|
ethClient *ethclient.Client
|
||||||
@ -34,8 +45,15 @@ type DynamicGroupManager struct {
|
|||||||
ethAccountPrivateKey *ecdsa.PrivateKey
|
ethAccountPrivateKey *ecdsa.PrivateKey
|
||||||
|
|
||||||
registrationHandler RegistrationHandler
|
registrationHandler RegistrationHandler
|
||||||
|
chainId *big.Int
|
||||||
|
rlnContract *contracts.RLN
|
||||||
|
membershipFee *big.Int
|
||||||
lastIndexLoaded int64
|
lastIndexLoaded int64
|
||||||
|
|
||||||
|
saveKeystore bool
|
||||||
|
keystorePath string
|
||||||
|
keystorePassword string
|
||||||
|
|
||||||
rootTracker *group_manager.MerkleRootTracker
|
rootTracker *group_manager.MerkleRootTracker
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,22 +63,34 @@ func NewDynamicGroupManager(
|
|||||||
ethClientAddr string,
|
ethClientAddr string,
|
||||||
ethAccountPrivateKey *ecdsa.PrivateKey,
|
ethAccountPrivateKey *ecdsa.PrivateKey,
|
||||||
memContractAddr common.Address,
|
memContractAddr common.Address,
|
||||||
identityCredential *rln.IdentityCredential,
|
keystorePath string,
|
||||||
index rln.MembershipIndex,
|
keystorePassword string,
|
||||||
|
saveKeystore bool,
|
||||||
registrationHandler RegistrationHandler,
|
registrationHandler RegistrationHandler,
|
||||||
log *zap.Logger,
|
log *zap.Logger,
|
||||||
) (*DynamicGroupManager, error) {
|
) (*DynamicGroupManager, error) {
|
||||||
return &DynamicGroupManager{
|
return &DynamicGroupManager{
|
||||||
identityCredential: identityCredential,
|
|
||||||
membershipIndex: &index,
|
|
||||||
membershipContractAddress: memContractAddr,
|
membershipContractAddress: memContractAddr,
|
||||||
ethClientAddress: ethClientAddr,
|
ethClientAddress: ethClientAddr,
|
||||||
ethAccountPrivateKey: ethAccountPrivateKey,
|
ethAccountPrivateKey: ethAccountPrivateKey,
|
||||||
registrationHandler: registrationHandler,
|
registrationHandler: registrationHandler,
|
||||||
lastIndexLoaded: -1,
|
lastIndexLoaded: -1,
|
||||||
|
saveKeystore: saveKeystore,
|
||||||
|
keystorePath: keystorePath,
|
||||||
|
keystorePassword: keystorePassword,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (gm *DynamicGroupManager) getMembershipFee(ctx context.Context) (*big.Int, error) {
|
||||||
|
auth, err := bind.NewKeyedTransactorWithChainID(gm.ethAccountPrivateKey, gm.chainId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
auth.Context = ctx
|
||||||
|
|
||||||
|
return gm.rlnContract.MEMBERSHIPDEPOSIT(&bind.CallOpts{Context: ctx})
|
||||||
|
}
|
||||||
|
|
||||||
func (gm *DynamicGroupManager) Start(ctx context.Context, rlnInstance *rln.RLN, rootTracker *group_manager.MerkleRootTracker) error {
|
func (gm *DynamicGroupManager) Start(ctx context.Context, rlnInstance *rln.RLN, rootTracker *group_manager.MerkleRootTracker) error {
|
||||||
if gm.cancel != nil {
|
if gm.cancel != nil {
|
||||||
return errors.New("already started")
|
return errors.New("already started")
|
||||||
@ -71,14 +101,55 @@ func (gm *DynamicGroupManager) Start(ctx context.Context, rlnInstance *rln.RLN,
|
|||||||
|
|
||||||
gm.log.Info("mounting rln-relay in on-chain/dynamic mode")
|
gm.log.Info("mounting rln-relay in on-chain/dynamic mode")
|
||||||
|
|
||||||
|
backend, err := ethclient.Dial(gm.ethClientAddress)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
gm.ethClient = backend
|
||||||
|
|
||||||
gm.rln = rlnInstance
|
gm.rln = rlnInstance
|
||||||
gm.rootTracker = rootTracker
|
gm.rootTracker = rootTracker
|
||||||
|
|
||||||
err := rootTracker.Sync()
|
gm.chainId, err = backend.ChainID(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gm.rlnContract, err = contracts.NewRLN(gm.membershipContractAddress, backend)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the contract exists by calling a static function
|
||||||
|
gm.membershipFee, err = gm.getMembershipFee(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = rootTracker.Sync()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if gm.keystorePassword != "" && gm.keystorePath != "" {
|
||||||
|
credentials, err := keystore.GetMembershipCredentials(gm.log,
|
||||||
|
gm.keystorePath,
|
||||||
|
gm.keystorePassword,
|
||||||
|
RLNAppInfo,
|
||||||
|
nil,
|
||||||
|
[]keystore.MembershipContract{{
|
||||||
|
ChainId: gm.chainId.String(),
|
||||||
|
Address: gm.membershipContractAddress.Hex(),
|
||||||
|
}})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: accept an index from the config
|
||||||
|
gm.identityCredential = &credentials[0].IdentityCredential
|
||||||
|
gm.membershipIndex = &credentials[0].MembershipGroups[0].TreeIndex
|
||||||
|
}
|
||||||
|
|
||||||
// prepare rln membership key pair
|
// prepare rln membership key pair
|
||||||
if gm.identityCredential == nil && gm.ethAccountPrivateKey != nil {
|
if gm.identityCredential == nil && gm.ethAccountPrivateKey != nil {
|
||||||
gm.log.Debug("no rln-relay key is provided, generating one")
|
gm.log.Debug("no rln-relay key is provided, generating one")
|
||||||
@ -90,16 +161,23 @@ func (gm *DynamicGroupManager) Start(ctx context.Context, rlnInstance *rln.RLN,
|
|||||||
gm.identityCredential = identityCredential
|
gm.identityCredential = identityCredential
|
||||||
|
|
||||||
// register the rln-relay peer to the membership contract
|
// register the rln-relay peer to the membership contract
|
||||||
membershipIndex, err := gm.Register(ctx)
|
gm.membershipIndex, err = gm.Register(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
gm.membershipIndex = membershipIndex
|
err = gm.persistCredentials()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
gm.log.Info("registered peer into the membership contract")
|
gm.log.Info("registered peer into the membership contract")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if gm.identityCredential == nil || gm.membershipIndex == nil {
|
||||||
|
return errors.New("no credentials available")
|
||||||
|
}
|
||||||
|
|
||||||
handler := func(pubkey r.IDCommitment, index r.MembershipIndex) error {
|
handler := func(pubkey r.IDCommitment, index r.MembershipIndex) error {
|
||||||
return gm.InsertMember(pubkey)
|
return gm.InsertMember(pubkey)
|
||||||
}
|
}
|
||||||
@ -115,6 +193,46 @@ func (gm *DynamicGroupManager) Start(ctx context.Context, rlnInstance *rln.RLN,
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (gm *DynamicGroupManager) persistCredentials() error {
|
||||||
|
if !gm.saveKeystore {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if gm.identityCredential == nil || gm.membershipIndex == nil {
|
||||||
|
return errors.New("no credentials to persist")
|
||||||
|
}
|
||||||
|
|
||||||
|
path := gm.keystorePath
|
||||||
|
if path == "" {
|
||||||
|
gm.log.Warn("keystore: no credentials path set, using default path", zap.String("path", keystore.RLN_CREDENTIALS_FILENAME))
|
||||||
|
path = keystore.RLN_CREDENTIALS_FILENAME
|
||||||
|
}
|
||||||
|
|
||||||
|
password := gm.keystorePassword
|
||||||
|
if password == "" {
|
||||||
|
gm.log.Warn("keystore: no credentials password set, using default password", zap.String("password", keystore.RLN_CREDENTIALS_PASSWORD))
|
||||||
|
password = keystore.RLN_CREDENTIALS_PASSWORD
|
||||||
|
}
|
||||||
|
|
||||||
|
keystoreCred := keystore.MembershipCredentials{
|
||||||
|
IdentityCredential: *gm.identityCredential,
|
||||||
|
MembershipGroups: []keystore.MembershipGroup{{
|
||||||
|
TreeIndex: *gm.membershipIndex,
|
||||||
|
MembershipContract: keystore.MembershipContract{
|
||||||
|
ChainId: gm.chainId.String(),
|
||||||
|
Address: gm.membershipContractAddress.String(),
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := keystore.AddMembershipCredentials(path, []keystore.MembershipCredentials{keystoreCred}, password, RLNAppInfo, keystore.DefaultSeparator)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("failed to persist credentials")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (gm *DynamicGroupManager) InsertMember(pubkey rln.IDCommitment) error {
|
func (gm *DynamicGroupManager) InsertMember(pubkey rln.IDCommitment) error {
|
||||||
gm.log.Debug("a new key is added", zap.Binary("pubkey", pubkey[:]))
|
gm.log.Debug("a new key is added", zap.Binary("pubkey", pubkey[:]))
|
||||||
// assuming all the members arrive in order
|
// assuming all the members arrive in order
|
||||||
|
|||||||
@ -8,7 +8,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/ethclient"
|
"github.com/ethereum/go-ethereum/ethclient"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
@ -26,18 +25,7 @@ func toBigInt(i []byte) *big.Int {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func register(ctx context.Context, idComm r.IDCommitment, ethAccountPrivateKey *ecdsa.PrivateKey, ethClientAddress string, membershipContractAddress common.Address, registrationHandler RegistrationHandler, log *zap.Logger) (*r.MembershipIndex, error) {
|
func register(ctx context.Context, backend *ethclient.Client, idComm r.IDCommitment, ethAccountPrivateKey *ecdsa.PrivateKey, rlnContract *contracts.RLN, chainID *big.Int, registrationHandler RegistrationHandler, log *zap.Logger) (*r.MembershipIndex, error) {
|
||||||
backend, err := ethclient.Dial(ethClientAddress)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer backend.Close()
|
|
||||||
|
|
||||||
chainID, err := backend.ChainID(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
auth, err := bind.NewKeyedTransactorWithChainID(ethAccountPrivateKey, chainID)
|
auth, err := bind.NewKeyedTransactorWithChainID(ethAccountPrivateKey, chainID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -45,11 +33,6 @@ func register(ctx context.Context, idComm r.IDCommitment, ethAccountPrivateKey *
|
|||||||
auth.Value = MEMBERSHIP_FEE
|
auth.Value = MEMBERSHIP_FEE
|
||||||
auth.Context = ctx
|
auth.Context = ctx
|
||||||
|
|
||||||
rlnContract, err := contracts.NewRLN(membershipContractAddress, backend)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Debug("registering an id commitment", zap.Binary("idComm", idComm[:]))
|
log.Debug("registering an id commitment", zap.Binary("idComm", idComm[:]))
|
||||||
|
|
||||||
// registers the idComm into the membership contract whose address is in rlnPeer.membershipContractAddress
|
// registers the idComm into the membership contract whose address is in rlnPeer.membershipContractAddress
|
||||||
@ -100,8 +83,14 @@ func register(ctx context.Context, idComm r.IDCommitment, ethAccountPrivateKey *
|
|||||||
// Register registers the public key of the rlnPeer which is rlnPeer.membershipKeyPair.publicKey
|
// Register registers the public key of the rlnPeer which is rlnPeer.membershipKeyPair.publicKey
|
||||||
// into the membership contract whose address is in rlnPeer.membershipContractAddress
|
// into the membership contract whose address is in rlnPeer.membershipContractAddress
|
||||||
func (gm *DynamicGroupManager) Register(ctx context.Context) (*r.MembershipIndex, error) {
|
func (gm *DynamicGroupManager) Register(ctx context.Context) (*r.MembershipIndex, error) {
|
||||||
pk := gm.identityCredential.IDCommitment
|
return register(ctx,
|
||||||
return register(ctx, pk, gm.ethAccountPrivateKey, gm.ethClientAddress, gm.membershipContractAddress, gm.registrationHandler, gm.log)
|
gm.ethClient,
|
||||||
|
gm.identityCredential.IDCommitment,
|
||||||
|
gm.ethAccountPrivateKey,
|
||||||
|
gm.rlnContract,
|
||||||
|
gm.chainId,
|
||||||
|
gm.registrationHandler,
|
||||||
|
gm.log)
|
||||||
}
|
}
|
||||||
|
|
||||||
// the types of inputs to this handler matches the MemberRegistered event/proc defined in the MembershipContract interface
|
// the types of inputs to this handler matches the MemberRegistered event/proc defined in the MembershipContract interface
|
||||||
@ -129,24 +118,13 @@ func (gm *DynamicGroupManager) processLogs(evt *contracts.RLNMemberRegistered, h
|
|||||||
func (gm *DynamicGroupManager) HandleGroupUpdates(ctx context.Context, handler RegistrationEventHandler) error {
|
func (gm *DynamicGroupManager) HandleGroupUpdates(ctx context.Context, handler RegistrationEventHandler) error {
|
||||||
defer gm.wg.Done()
|
defer gm.wg.Done()
|
||||||
|
|
||||||
backend, err := ethclient.Dial(gm.ethClientAddress)
|
err := gm.loadOldEvents(ctx, gm.rlnContract, handler)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
gm.ethClient = backend
|
|
||||||
|
|
||||||
rlnContract, err := contracts.NewRLN(gm.membershipContractAddress, backend)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = gm.loadOldEvents(ctx, rlnContract, handler)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
errCh := make(chan error)
|
errCh := make(chan error)
|
||||||
go gm.watchNewEvents(ctx, rlnContract, handler, gm.log, errCh)
|
go gm.watchNewEvents(ctx, gm.rlnContract, handler, gm.log, errCh)
|
||||||
return <-errCh
|
return <-errCh
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
336
waku/v2/protocol/rln/keystore/keystore.go
Normal file
336
waku/v2/protocol/rln/keystore/keystore.go
Normal file
@ -0,0 +1,336 @@
|
|||||||
|
package keystore
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||||
|
"github.com/waku-org/go-zerokit-rln/rln"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
const RLN_CREDENTIALS_FILENAME = "rlnCredentials.json"
|
||||||
|
const RLN_CREDENTIALS_PASSWORD = "password"
|
||||||
|
|
||||||
|
type MembershipContract struct {
|
||||||
|
ChainId string `json:"chainId"`
|
||||||
|
Address string `json:"address"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MembershipGroup struct {
|
||||||
|
MembershipContract MembershipContract `json:"membershipContract"`
|
||||||
|
TreeIndex rln.MembershipIndex `json:"treeIndex"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MembershipCredentials struct {
|
||||||
|
IdentityCredential rln.IdentityCredential `json:"identityCredential"`
|
||||||
|
MembershipGroups []MembershipGroup `json:"membershipGroups"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AppInfo struct {
|
||||||
|
Application string `json:"application"`
|
||||||
|
AppIdentifier string `json:"appIdentifier"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AppKeystore struct {
|
||||||
|
Application string `json:"application"`
|
||||||
|
AppIdentifier string `json:"appIdentifier"`
|
||||||
|
Credentials []keystore.CryptoJSON `json:"credentials"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
}
|
||||||
|
|
||||||
|
const DefaultSeparator = "\n"
|
||||||
|
|
||||||
|
func (m MembershipCredentials) Equals(other MembershipCredentials) bool {
|
||||||
|
if !rln.IdentityCredentialEquals(m.IdentityCredential, other.IdentityCredential) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, x := range m.MembershipGroups {
|
||||||
|
found := false
|
||||||
|
for _, y := range other.MembershipGroups {
|
||||||
|
if x.Equals(y) {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MembershipGroup) Equals(other MembershipGroup) bool {
|
||||||
|
return m.MembershipContract.Equals(other.MembershipContract) && m.TreeIndex == other.TreeIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MembershipContract) Equals(other MembershipContract) bool {
|
||||||
|
return m.Address == other.Address && m.ChainId == other.ChainId
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateAppKeystore(path string, appInfo AppInfo, separator string) error {
|
||||||
|
if separator == "" {
|
||||||
|
separator = DefaultSeparator
|
||||||
|
}
|
||||||
|
|
||||||
|
keystore := AppKeystore{
|
||||||
|
AppIdentifier: appInfo.AppIdentifier,
|
||||||
|
Version: appInfo.Version,
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := json.Marshal(keystore)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
b = append(b, []byte(separator)...)
|
||||||
|
|
||||||
|
buffer := new(bytes.Buffer)
|
||||||
|
|
||||||
|
err = json.Compact(buffer, b)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ioutil.WriteFile(path, buffer.Bytes(), 0600)
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadAppKeystore(path string, appInfo AppInfo, separator string) (AppKeystore, error) {
|
||||||
|
if separator == "" {
|
||||||
|
separator = DefaultSeparator
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := os.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
// If no keystore exists at path we create a new empty one with passed keystore parameters
|
||||||
|
err = CreateAppKeystore(path, appInfo, separator)
|
||||||
|
if err != nil {
|
||||||
|
return AppKeystore{}, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return AppKeystore{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
src, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return AppKeystore{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, keystoreBytes := range bytes.Split(src, []byte(separator)) {
|
||||||
|
if len(keystoreBytes) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
keystore := AppKeystore{}
|
||||||
|
err := json.Unmarshal(keystoreBytes, &keystore)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if keystore.AppIdentifier == appInfo.AppIdentifier && keystore.Application == appInfo.Application && keystore.Version == appInfo.Version {
|
||||||
|
return keystore, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return AppKeystore{}, errors.New("no keystore found")
|
||||||
|
}
|
||||||
|
|
||||||
|
func filterCredential(credential MembershipCredentials, filterIdentityCredentials []MembershipCredentials, filterMembershipContracts []MembershipContract) *MembershipCredentials {
|
||||||
|
if len(filterIdentityCredentials) != 0 {
|
||||||
|
found := false
|
||||||
|
for _, filterCreds := range filterIdentityCredentials {
|
||||||
|
if filterCreds.Equals(credential) {
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(filterMembershipContracts) != 0 {
|
||||||
|
var membershipGroupsIntersection []MembershipGroup
|
||||||
|
for _, filterContract := range filterMembershipContracts {
|
||||||
|
for _, credentialGroups := range credential.MembershipGroups {
|
||||||
|
if filterContract.Equals(credentialGroups.MembershipContract) {
|
||||||
|
membershipGroupsIntersection = append(membershipGroupsIntersection, credentialGroups)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(membershipGroupsIntersection) != 0 {
|
||||||
|
// If we have a match on some groups, we return the credential with filtered groups
|
||||||
|
return &MembershipCredentials{
|
||||||
|
IdentityCredential: credential.IdentityCredential,
|
||||||
|
MembershipGroups: membershipGroupsIntersection,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We hit this return only if
|
||||||
|
// - filterIdentityCredentials.len() == 0 and filterMembershipContracts.len() == 0 (no filter)
|
||||||
|
// - filterIdentityCredentials.len() != 0 and filterMembershipContracts.len() == 0 (filter only on identity credential)
|
||||||
|
// Indeed, filterMembershipContracts.len() != 0 will have its exclusive return based on all values of membershipGroupsIntersection.len()
|
||||||
|
return &credential
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetMembershipCredentials(logger *zap.Logger, credentialsPath string, password string, appInfo AppInfo, filterIdentityCredentials []MembershipCredentials, filterMembershipContracts []MembershipContract) ([]MembershipCredentials, error) {
|
||||||
|
k, err := LoadAppKeystore(credentialsPath, appInfo, DefaultSeparator)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var result []MembershipCredentials
|
||||||
|
|
||||||
|
for _, credential := range k.Credentials {
|
||||||
|
credentialsBytes, err := keystore.DecryptDataV3(credential, password)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var credentials MembershipCredentials
|
||||||
|
err = json.Unmarshal(credentialsBytes, &credentials)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
filteredCredential := filterCredential(credentials, filterIdentityCredentials, filterMembershipContracts)
|
||||||
|
if filterIdentityCredentials != nil {
|
||||||
|
result = append(result, *filteredCredential)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds a sequence of membership credential to the keystore matching the application, appIdentifier and version filters.
|
||||||
|
func AddMembershipCredentials(path string, credentials []MembershipCredentials, password string, appInfo AppInfo, separator string) error {
|
||||||
|
k, err := LoadAppKeystore(path, appInfo, DefaultSeparator)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var credentialsToAdd []MembershipCredentials
|
||||||
|
for _, newCredential := range credentials {
|
||||||
|
// A flag to tell us if the keystore contains a credential associated to the input identity credential, i.e. membershipCredential
|
||||||
|
found := -1
|
||||||
|
for i, existingCredentials := range k.Credentials {
|
||||||
|
credentialsBytes, err := keystore.DecryptDataV3(existingCredentials, password)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var credentials MembershipCredentials
|
||||||
|
err = json.Unmarshal(credentialsBytes, &credentials)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if rln.IdentityCredentialEquals(credentials.IdentityCredential, newCredential.IdentityCredential) {
|
||||||
|
// idCredential is present in keystore. We add the input credential membership group to the one contained in the decrypted keystore credential (we deduplicate groups using sets)
|
||||||
|
allMemberships := append(credentials.MembershipGroups, newCredential.MembershipGroups...)
|
||||||
|
|
||||||
|
// we define the updated credential with the updated membership sets
|
||||||
|
updatedCredential := MembershipCredentials{
|
||||||
|
IdentityCredential: newCredential.IdentityCredential,
|
||||||
|
MembershipGroups: allMemberships,
|
||||||
|
}
|
||||||
|
|
||||||
|
// we re-encrypt creating a new keyfile
|
||||||
|
b, err := json.Marshal(updatedCredential)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
encryptedCredentials, err := keystore.EncryptDataV3(b, []byte(password), keystore.StandardScryptN, keystore.StandardScryptP)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// we update the original credential field in keystoreCredentials
|
||||||
|
k.Credentials[i] = encryptedCredentials
|
||||||
|
|
||||||
|
found = i
|
||||||
|
|
||||||
|
// We stop decrypting other credentials in the keystore
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if found == -1 {
|
||||||
|
credentialsToAdd = append(credentialsToAdd, newCredential)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range credentialsToAdd {
|
||||||
|
b, err := json.Marshal(c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
encryptedCredentials, err := keystore.EncryptDataV3(b, []byte(password), keystore.StandardScryptN, keystore.StandardScryptP)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
k.Credentials = append(k.Credentials, encryptedCredentials)
|
||||||
|
}
|
||||||
|
|
||||||
|
return save(k, path, separator)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Safely saves a Keystore's JsonNode to disk.
|
||||||
|
// If exists, the destination file is renamed with extension .bkp; the file is written at its destination and the .bkp file is removed if write is successful, otherwise is restored
|
||||||
|
func save(keystore AppKeystore, path string, separator string) error {
|
||||||
|
// We first backup the current keystore
|
||||||
|
_, err := os.Stat(path)
|
||||||
|
if err == nil {
|
||||||
|
err := os.Rename(path, path+".bkp")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if separator == "" {
|
||||||
|
separator = DefaultSeparator
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := json.Marshal(keystore)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
b = append(b, []byte(separator)...)
|
||||||
|
|
||||||
|
buffer := new(bytes.Buffer)
|
||||||
|
|
||||||
|
err = json.Compact(buffer, b)
|
||||||
|
if err != nil {
|
||||||
|
restoreErr := os.Rename(path, path+".bkp")
|
||||||
|
if restoreErr != nil {
|
||||||
|
return fmt.Errorf("could not restore backup file: %w", restoreErr)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ioutil.WriteFile(path, buffer.Bytes(), 0600)
|
||||||
|
if err != nil {
|
||||||
|
restoreErr := os.Rename(path, path+".bkp")
|
||||||
|
if restoreErr != nil {
|
||||||
|
return fmt.Errorf("could not restore backup file: %w", restoreErr)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@ -21,12 +21,6 @@ import (
|
|||||||
proto "google.golang.org/protobuf/proto"
|
proto "google.golang.org/protobuf/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
var RLNAppInfo = AppInfo{
|
|
||||||
Application: "go-waku-rln-relay",
|
|
||||||
AppIdentifier: "01234567890abcdef",
|
|
||||||
Version: "0.1",
|
|
||||||
}
|
|
||||||
|
|
||||||
type GroupManager interface {
|
type GroupManager interface {
|
||||||
Start(ctx context.Context, rln *rln.RLN, rootTracker *group_manager.MerkleRootTracker) error
|
Start(ctx context.Context, rln *rln.RLN, rootTracker *group_manager.MerkleRootTracker) error
|
||||||
IdentityCredentials() (rln.IdentityCredential, error)
|
IdentityCredentials() (rln.IdentityCredential, error)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user