mirror of
https://github.com/status-im/go-waku.git
synced 2025-02-13 05:56:48 +00:00
feat: store credentials in a file
This commit is contained in:
parent
f486236d0d
commit
fb6aa64442
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,6 +2,7 @@
|
||||
*.db-shm
|
||||
*.db-wal
|
||||
nodekey
|
||||
rlnCredentials.txt
|
||||
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
|
@ -79,6 +79,7 @@ func main() {
|
||||
rlnRelayEthAccountPrivKeyFlag := flag.String("eth-account-privatekey", "", "Account private key for an Ethereum testnet")
|
||||
rlnRelayEthClientAddressFlag := flag.String("eth-client-address", "ws://localhost:8545/", "Ethereum testnet client address")
|
||||
rlnRelayEthMemContractAddressFlag := flag.String("eth-mem-contract-address", "", "Address of membership contract on an Ethereum testnet")
|
||||
rlnRelayCredentialsFile := flag.String("rln-relay-credentials-file", "rlnCredentials.txt", "RLN credentials file")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
@ -127,23 +128,16 @@ func main() {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var idKey *rln.IDKey
|
||||
if *rlnRelayIdKeyFlag != "" {
|
||||
idKey = new(rln.IDKey)
|
||||
copy((*idKey)[:], common.FromHex(*rlnRelayIdKeyFlag))
|
||||
}
|
||||
|
||||
var idCommitment *rln.IDCommitment
|
||||
if *rlnRelayIdCommitmentKeyFlag != "" {
|
||||
idCommitment = new(rln.IDCommitment)
|
||||
copy((*idCommitment)[:], common.FromHex(*rlnRelayIdCommitmentKeyFlag))
|
||||
idKey, idCommitment, index, err := getMembershipCredentials(*rlnRelayCredentialsFile, *rlnRelayIdKeyFlag, *rlnRelayIdCommitmentKeyFlag, *rlnRelayMemIndexFlag)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Println("Setting up dynamic rln")
|
||||
opts = append(opts, node.WithDynamicRLNRelay(
|
||||
*rlnRelayPubsubTopicFlag,
|
||||
*rlnRelayContentTopicFlag,
|
||||
rln.MembershipIndex(*rlnRelayMemIndexFlag),
|
||||
index,
|
||||
idKey,
|
||||
idCommitment,
|
||||
spamHandler,
|
||||
@ -183,6 +177,14 @@ func main() {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if *rlnRelayFlag && *rlnRelayDynamicFlag {
|
||||
err := writeRLNMembershipCredentialsToFile(*rlnRelayCredentialsFile, wakuNode.RLNRelay().MembershipKeyPair(), wakuNode.RLNRelay().MembershipIndex())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Printf("Wrote credentials in file %s\n", *rlnRelayCredentialsFile)
|
||||
}
|
||||
|
||||
// use the nickname from the cli flag, or a default if blank
|
||||
nick := *nickFlag
|
||||
if len(nick) == 0 {
|
||||
|
86
examples/chat2/rln-credentials.go
Normal file
86
examples/chat2/rln-credentials.go
Normal file
@ -0,0 +1,86 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/status-im/go-rln/rln"
|
||||
)
|
||||
|
||||
type membershipCredentials struct {
|
||||
Keypair rln.MembershipKeyPair `json:"keypair"`
|
||||
Index rln.MembershipIndex `json:"index"`
|
||||
}
|
||||
|
||||
func fileExists(path string) bool {
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
return false
|
||||
|
||||
} else if errors.Is(err, os.ErrNotExist) {
|
||||
return false
|
||||
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func writeRLNMembershipCredentialsToFile(path string, keyPair rln.MembershipKeyPair, idx rln.MembershipIndex) error {
|
||||
if fileExists(path) {
|
||||
return nil
|
||||
}
|
||||
|
||||
credentialsJSON, err := json.Marshal(membershipCredentials{
|
||||
Keypair: keyPair,
|
||||
Index: idx,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ioutil.WriteFile(path, credentialsJSON, 0600)
|
||||
}
|
||||
|
||||
func loadMembershipCredentialsFromFile(path string) (rln.MembershipKeyPair, rln.MembershipIndex, error) {
|
||||
src, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return rln.MembershipKeyPair{}, rln.MembershipIndex(0), err
|
||||
}
|
||||
|
||||
var credentials membershipCredentials
|
||||
err = json.Unmarshal(src, &credentials)
|
||||
if err != nil {
|
||||
return rln.MembershipKeyPair{}, rln.MembershipIndex(0), err
|
||||
}
|
||||
|
||||
return credentials.Keypair, credentials.Index, err
|
||||
}
|
||||
|
||||
func getMembershipCredentials(path string, rlnIDKey string, rlnIDCommitment string, rlnMembershipIndex int) (idKey *rln.IDKey, idCommitment *rln.IDCommitment, index rln.MembershipIndex, err error) {
|
||||
if _, err = os.Stat(path); err == nil {
|
||||
if keyPair, index, err := loadMembershipCredentialsFromFile(path); err != nil {
|
||||
return nil, nil, rln.MembershipIndex(0), fmt.Errorf("could not read membership credentials file: %w", err)
|
||||
} else {
|
||||
return &keyPair.IDKey, &keyPair.IDCommitment, index, nil
|
||||
}
|
||||
}
|
||||
|
||||
if os.IsNotExist(err) {
|
||||
if rlnIDKey != "" {
|
||||
idKey = new(rln.IDKey)
|
||||
copy((*idKey)[:], common.FromHex(rlnIDKey))
|
||||
}
|
||||
|
||||
if rlnIDCommitment != "" {
|
||||
idCommitment = new(rln.IDCommitment)
|
||||
copy((*idCommitment)[:], common.FromHex(rlnIDCommitment))
|
||||
}
|
||||
|
||||
return idKey, idCommitment, rln.MembershipIndex(rlnMembershipIndex), nil
|
||||
}
|
||||
|
||||
return nil, nil, rln.MembershipIndex(0), fmt.Errorf("could not read membership credentials file: %w", err)
|
||||
}
|
6
waku.go
6
waku.go
@ -317,6 +317,12 @@ func main() {
|
||||
Usage: "Rln relay identity commitment key as a Hex string",
|
||||
Destination: &options.RLNRelay.IDCommitment,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "rln-relay-membership-credentials-file",
|
||||
Usage: "RLN relay membership credentials file",
|
||||
Value: "rlnCredentials.txt",
|
||||
Destination: &options.RLNRelay.CredentialsFile,
|
||||
},
|
||||
// TODO: this is a good candidate option for subcommands
|
||||
// TODO: consider accepting a private key file and passwd
|
||||
&cli.StringFlag{
|
||||
|
25
waku/node.go
25
waku/node.go
@ -256,6 +256,7 @@ func Execute(options Options) {
|
||||
nodeOpts = append(nodeOpts, node.WithDiscoveryV5(options.DiscV5.Port, bootnodes, options.DiscV5.AutoUpdate, pubsub.WithDiscoveryOpts(discovery.Limit(45), discovery.TTL(time.Duration(20)*time.Second))))
|
||||
}
|
||||
|
||||
loadedCredentialsFromFile := false
|
||||
if options.RLNRelay.Enable {
|
||||
if !options.Relay.Enable {
|
||||
failOnErr(errors.New("relay not available"), "Could not enable RLN Relay")
|
||||
@ -272,22 +273,15 @@ func Execute(options Options) {
|
||||
ethPrivKey = k
|
||||
}
|
||||
|
||||
var idKey *rln.IDKey
|
||||
if options.RLNRelay.IDCommitment != "" {
|
||||
idKey = new(rln.IDKey)
|
||||
copy((*idKey)[:], common.FromHex(options.RLNRelay.IDKey))
|
||||
}
|
||||
loaded, idKey, idCommitment, membershipIndex, err := getMembershipCredentials(options)
|
||||
failOnErr(err, "Invalid membership credentials")
|
||||
|
||||
var idCommitment *rln.IDCommitment
|
||||
if options.RLNRelay.IDCommitment != "" {
|
||||
idCommitment = new(rln.IDCommitment)
|
||||
copy((*idCommitment)[:], common.FromHex(options.RLNRelay.IDCommitment))
|
||||
}
|
||||
loadedCredentialsFromFile = loaded
|
||||
|
||||
nodeOpts = append(nodeOpts, node.WithDynamicRLNRelay(
|
||||
options.RLNRelay.PubsubTopic,
|
||||
options.RLNRelay.ContentTopic,
|
||||
rln.MembershipIndex(options.RLNRelay.MembershipIndex),
|
||||
membershipIndex,
|
||||
idKey,
|
||||
idCommitment,
|
||||
nil,
|
||||
@ -354,6 +348,11 @@ func Execute(options Options) {
|
||||
}
|
||||
}
|
||||
|
||||
if options.RLNRelay.Enable && options.RLNRelay.Dynamic && !loadedCredentialsFromFile {
|
||||
err := writeRLNMembershipCredentialsToFile(wakuNode.RLNRelay().MembershipKeyPair(), wakuNode.RLNRelay().MembershipIndex(), options.RLNRelay.CredentialsFile, []byte(options.KeyPasswd), options.Overwrite)
|
||||
failOnErr(err, "Could not write membership credentials file")
|
||||
}
|
||||
|
||||
var rpcServer *rpc.WakuRpc
|
||||
if options.RPCServer.Enable {
|
||||
rpcServer = rpc.NewWakuRpc(wakuNode, options.RPCServer.Address, options.RPCServer.Port, options.RPCServer.Admin, options.RPCServer.Private, logger)
|
||||
@ -432,7 +431,7 @@ func loadPrivateKeyFromFile(path string, passwd string) (*ecdsa.PrivateKey, erro
|
||||
return crypto.ToECDSA(pKey)
|
||||
}
|
||||
|
||||
func checkForPrivateKeyFile(path string, overwrite bool) error {
|
||||
func checkForFileExistence(path string, overwrite bool) error {
|
||||
_, err := os.Stat(path)
|
||||
|
||||
if err == nil && !overwrite {
|
||||
@ -456,7 +455,7 @@ func generatePrivateKey() ([]byte, error) {
|
||||
}
|
||||
|
||||
func writePrivateKeyToFile(path string, passwd []byte, overwrite bool) error {
|
||||
if err := checkForPrivateKeyFile(path, overwrite); err != nil {
|
||||
if err := checkForFileExistence(path, overwrite); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -41,6 +41,7 @@ type RelayOptions struct {
|
||||
|
||||
type RLNRelayOptions struct {
|
||||
Enable bool
|
||||
CredentialsFile string
|
||||
MembershipIndex int
|
||||
PubsubTopic string
|
||||
ContentTopic string
|
||||
|
96
waku/rln-credentials.go
Normal file
96
waku/rln-credentials.go
Normal file
@ -0,0 +1,96 @@
|
||||
package waku
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/status-im/go-rln/rln"
|
||||
)
|
||||
|
||||
type membershipCredentials struct {
|
||||
Keypair rln.MembershipKeyPair `json:"keypair"`
|
||||
Index rln.MembershipIndex `json:"index"`
|
||||
}
|
||||
|
||||
func writeRLNMembershipCredentialsToFile(keyPair rln.MembershipKeyPair, idx rln.MembershipIndex, path string, passwd []byte, overwrite bool) error {
|
||||
if err := checkForFileExistence(path, overwrite); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
credentialsJSON, err := json.Marshal(membershipCredentials{
|
||||
Keypair: keyPair,
|
||||
Index: idx,
|
||||
})
|
||||
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
|
||||
}
|
||||
|
||||
return ioutil.WriteFile(path, output, 0600)
|
||||
}
|
||||
|
||||
func loadMembershipCredentialsFromFile(path string, passwd string) (rln.MembershipKeyPair, rln.MembershipIndex, error) {
|
||||
src, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return rln.MembershipKeyPair{}, rln.MembershipIndex(0), err
|
||||
}
|
||||
|
||||
var encryptedK keystore.CryptoJSON
|
||||
err = json.Unmarshal(src, &encryptedK)
|
||||
if err != nil {
|
||||
return rln.MembershipKeyPair{}, rln.MembershipIndex(0), err
|
||||
}
|
||||
|
||||
credentialsBytes, err := keystore.DecryptDataV3(encryptedK, passwd)
|
||||
if err != nil {
|
||||
return rln.MembershipKeyPair{}, rln.MembershipIndex(0), err
|
||||
}
|
||||
|
||||
var credentials membershipCredentials
|
||||
err = json.Unmarshal(credentialsBytes, &credentials)
|
||||
if err != nil {
|
||||
return rln.MembershipKeyPair{}, rln.MembershipIndex(0), err
|
||||
}
|
||||
|
||||
return credentials.Keypair, credentials.Index, err
|
||||
}
|
||||
|
||||
func getMembershipCredentials(options Options) (fromFile bool, idKey *rln.IDKey, idCommitment *rln.IDCommitment, index rln.MembershipIndex, err error) {
|
||||
if _, err = os.Stat(options.RLNRelay.CredentialsFile); err == nil {
|
||||
if keyPair, index, err := loadMembershipCredentialsFromFile(options.RLNRelay.CredentialsFile, options.KeyPasswd); err != nil {
|
||||
return false, nil, nil, rln.MembershipIndex(0), fmt.Errorf("could not read membership credentials file: %w", err)
|
||||
} else {
|
||||
return true, &keyPair.IDKey, &keyPair.IDCommitment, index, nil
|
||||
}
|
||||
}
|
||||
|
||||
if os.IsNotExist(err) {
|
||||
if options.RLNRelay.IDKey != "" {
|
||||
idKey = new(rln.IDKey)
|
||||
copy((*idKey)[:], common.FromHex(options.RLNRelay.IDKey))
|
||||
}
|
||||
|
||||
if options.RLNRelay.IDCommitment != "" {
|
||||
idCommitment = new(rln.IDCommitment)
|
||||
copy((*idCommitment)[:], common.FromHex(options.RLNRelay.IDCommitment))
|
||||
}
|
||||
|
||||
return false, idKey, idCommitment, rln.MembershipIndex(options.RLNRelay.MembershipIndex), nil
|
||||
|
||||
}
|
||||
|
||||
return false, nil, nil, rln.MembershipIndex(0), fmt.Errorf("could not read membership credentials file: %w", err)
|
||||
}
|
@ -67,8 +67,6 @@ type WakuNodeParameters struct {
|
||||
shouldResume bool
|
||||
storeMsgs bool
|
||||
messageProvider store.MessageProvider
|
||||
maxMessages int
|
||||
maxDuration time.Duration
|
||||
|
||||
swapMode int
|
||||
swapDisconnectThreshold int
|
||||
|
@ -101,9 +101,9 @@ func (rln *WakuRLNRelay) Register(ctx context.Context) (*r.MembershipIndex, erro
|
||||
// the types of inputs to this handler matches the MemberRegistered event/proc defined in the MembershipContract interface
|
||||
type RegistrationEventHandler = func(pubkey r.IDCommitment, index r.MembershipIndex) error
|
||||
|
||||
func processLogs(evt *contracts.RLNMemberRegistered, handler RegistrationEventHandler) {
|
||||
func processLogs(evt *contracts.RLNMemberRegistered, handler RegistrationEventHandler) error {
|
||||
if evt == nil {
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
var pubkey r.IDCommitment
|
||||
@ -111,7 +111,7 @@ func processLogs(evt *contracts.RLNMemberRegistered, handler RegistrationEventHa
|
||||
|
||||
index := r.MembershipIndex(uint(evt.Index.Int64()))
|
||||
|
||||
handler(pubkey, index)
|
||||
return handler(pubkey, index)
|
||||
}
|
||||
|
||||
// HandleGroupUpdates mounts the supplied handler for the registration events emitting from the membership contract
|
||||
@ -129,11 +129,15 @@ func (rln *WakuRLNRelay) HandleGroupUpdates(handler RegistrationEventHandler) er
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: process log should have a channel that consumes logs and has a buffer to receive a lot of events
|
||||
// TODO: an error channel is required
|
||||
err = rln.loadOldEvents(rlnContract, handler)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rln.loadOldEvents(rlnContract, handler)
|
||||
rln.watchNewEvents(rlnContract, handler, rln.log)
|
||||
err = rln.watchNewEvents(rlnContract, handler, rln.log)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user