mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-27 23:26:44 +00:00
This makes the zero Config slightly more useful. It also fixes package node tests because Node detects reuse of the datadir through the NodeDatabase.
651 lines
17 KiB
Go
651 lines
17 KiB
Go
// Copyright 2016 The go-ethereum Authors
|
|
// This file is part of go-ethereum.
|
|
//
|
|
// go-ethereum is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// go-ethereum is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
// This is a simple Whisper node. It could be used as a stand-alone bootstrap node.
|
|
// Also, could be used for different test and diagnostics purposes.
|
|
|
|
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"crypto/ecdsa"
|
|
"crypto/sha512"
|
|
"encoding/binary"
|
|
"encoding/hex"
|
|
"flag"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/ethereum/go-ethereum/console"
|
|
"github.com/ethereum/go-ethereum/crypto"
|
|
"github.com/ethereum/go-ethereum/log"
|
|
"github.com/ethereum/go-ethereum/p2p"
|
|
"github.com/ethereum/go-ethereum/p2p/discover"
|
|
"github.com/ethereum/go-ethereum/p2p/nat"
|
|
"github.com/ethereum/go-ethereum/whisper/mailserver"
|
|
whisper "github.com/ethereum/go-ethereum/whisper/whisperv5"
|
|
"golang.org/x/crypto/pbkdf2"
|
|
)
|
|
|
|
const quitCommand = "~Q"
|
|
|
|
// singletons
|
|
var (
|
|
server *p2p.Server
|
|
shh *whisper.Whisper
|
|
done chan struct{}
|
|
mailServer mailserver.WMailServer
|
|
|
|
input = bufio.NewReader(os.Stdin)
|
|
)
|
|
|
|
// encryption
|
|
var (
|
|
symKey []byte
|
|
pub *ecdsa.PublicKey
|
|
asymKey *ecdsa.PrivateKey
|
|
nodeid *ecdsa.PrivateKey
|
|
topic []byte
|
|
asymKeyID string
|
|
filterID string
|
|
symPass string
|
|
msPassword string
|
|
)
|
|
|
|
// cmd arguments
|
|
var (
|
|
bootstrapMode = flag.Bool("standalone", false, "boostrap node: don't actively connect to peers, wait for incoming connections")
|
|
forwarderMode = flag.Bool("forwarder", false, "forwarder mode: only forward messages, neither send nor decrypt messages")
|
|
mailServerMode = flag.Bool("mailserver", false, "mail server mode: delivers expired messages on demand")
|
|
requestMail = flag.Bool("mailclient", false, "request expired messages from the bootstrap server")
|
|
asymmetricMode = flag.Bool("asym", false, "use asymmetric encryption")
|
|
generateKey = flag.Bool("generatekey", false, "generate and show the private key")
|
|
fileExMode = flag.Bool("fileexchange", false, "file exchange mode")
|
|
testMode = flag.Bool("test", false, "use of predefined parameters for diagnostics")
|
|
echoMode = flag.Bool("echo", false, "echo mode: prints some arguments for diagnostics")
|
|
|
|
argVerbosity = flag.Int("verbosity", int(log.LvlWarn), "log verbosity level")
|
|
argTTL = flag.Uint("ttl", 30, "time-to-live for messages in seconds")
|
|
argWorkTime = flag.Uint("work", 5, "work time in seconds")
|
|
argMaxSize = flag.Int("maxsize", whisper.DefaultMaxMessageLength, "max size of message")
|
|
argPoW = flag.Float64("pow", whisper.DefaultMinimumPoW, "PoW for normal messages in float format (e.g. 2.7)")
|
|
argServerPoW = flag.Float64("mspow", whisper.DefaultMinimumPoW, "PoW requirement for Mail Server request")
|
|
|
|
argIP = flag.String("ip", "", "IP address and port of this node (e.g. 127.0.0.1:30303)")
|
|
argPub = flag.String("pub", "", "public key for asymmetric encryption")
|
|
argDBPath = flag.String("dbpath", "", "path to the server's DB directory")
|
|
argIDFile = flag.String("idfile", "", "file name with node id (private key)")
|
|
argEnode = flag.String("boot", "", "bootstrap node you want to connect to (e.g. enode://e454......08d50@52.176.211.200:16428)")
|
|
argTopic = flag.String("topic", "", "topic in hexadecimal format (e.g. 70a4beef)")
|
|
argSaveDir = flag.String("savedir", "", "directory where incoming messages will be saved as files")
|
|
)
|
|
|
|
func main() {
|
|
processArgs()
|
|
initialize()
|
|
run()
|
|
}
|
|
|
|
func processArgs() {
|
|
flag.Parse()
|
|
|
|
if len(*argIDFile) > 0 {
|
|
var err error
|
|
nodeid, err = crypto.LoadECDSA(*argIDFile)
|
|
if err != nil {
|
|
utils.Fatalf("Failed to load file [%s]: %s.", *argIDFile, err)
|
|
}
|
|
}
|
|
|
|
const enodePrefix = "enode://"
|
|
if len(*argEnode) > 0 {
|
|
if (*argEnode)[:len(enodePrefix)] != enodePrefix {
|
|
*argEnode = enodePrefix + *argEnode
|
|
}
|
|
}
|
|
|
|
if len(*argTopic) > 0 {
|
|
x, err := hex.DecodeString(*argTopic)
|
|
if err != nil {
|
|
utils.Fatalf("Failed to parse the topic: %s", err)
|
|
}
|
|
topic = x
|
|
}
|
|
|
|
if *asymmetricMode && len(*argPub) > 0 {
|
|
pub = crypto.ToECDSAPub(common.FromHex(*argPub))
|
|
if !isKeyValid(pub) {
|
|
utils.Fatalf("invalid public key")
|
|
}
|
|
}
|
|
|
|
if len(*argSaveDir) > 0 {
|
|
if _, err := os.Stat(*argSaveDir); os.IsNotExist(err) {
|
|
utils.Fatalf("Download directory '%s' does not exist", *argSaveDir)
|
|
}
|
|
} else if *fileExMode {
|
|
utils.Fatalf("Parameter 'savedir' is mandatory for file exchange mode")
|
|
}
|
|
|
|
if *echoMode {
|
|
echo()
|
|
}
|
|
}
|
|
|
|
func echo() {
|
|
fmt.Printf("ttl = %d \n", *argTTL)
|
|
fmt.Printf("workTime = %d \n", *argWorkTime)
|
|
fmt.Printf("pow = %f \n", *argPoW)
|
|
fmt.Printf("mspow = %f \n", *argServerPoW)
|
|
fmt.Printf("ip = %s \n", *argIP)
|
|
fmt.Printf("pub = %s \n", common.ToHex(crypto.FromECDSAPub(pub)))
|
|
fmt.Printf("idfile = %s \n", *argIDFile)
|
|
fmt.Printf("dbpath = %s \n", *argDBPath)
|
|
fmt.Printf("boot = %s \n", *argEnode)
|
|
}
|
|
|
|
func initialize() {
|
|
log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(*argVerbosity), log.StreamHandler(os.Stderr, log.TerminalFormat(false))))
|
|
|
|
done = make(chan struct{})
|
|
var peers []*discover.Node
|
|
var err error
|
|
|
|
if *generateKey {
|
|
key, err := crypto.GenerateKey()
|
|
if err != nil {
|
|
utils.Fatalf("Failed to generate private key: %s", err)
|
|
}
|
|
k := hex.EncodeToString(crypto.FromECDSA(key))
|
|
fmt.Printf("Random private key: %s \n", k)
|
|
os.Exit(0)
|
|
}
|
|
|
|
if *testMode {
|
|
symPass = "wwww" // ascii code: 0x77777777
|
|
msPassword = "mail server test password"
|
|
}
|
|
|
|
if *bootstrapMode {
|
|
if len(*argIP) == 0 {
|
|
argIP = scanLineA("Please enter your IP and port (e.g. 127.0.0.1:30348): ")
|
|
}
|
|
} else {
|
|
if len(*argEnode) == 0 {
|
|
argEnode = scanLineA("Please enter the peer's enode: ")
|
|
}
|
|
peer := discover.MustParseNode(*argEnode)
|
|
peers = append(peers, peer)
|
|
}
|
|
|
|
if *mailServerMode {
|
|
if len(msPassword) == 0 {
|
|
msPassword, err = console.Stdin.PromptPassword("Please enter the Mail Server password: ")
|
|
if err != nil {
|
|
utils.Fatalf("Failed to read Mail Server password: %s", err)
|
|
}
|
|
}
|
|
shh = whisper.New()
|
|
shh.RegisterServer(&mailServer)
|
|
mailServer.Init(shh, *argDBPath, msPassword, *argServerPoW)
|
|
} else {
|
|
shh = whisper.New()
|
|
}
|
|
|
|
if *argPoW != whisper.DefaultMinimumPoW {
|
|
err := shh.SetMinimumPoW(*argPoW)
|
|
if err != nil {
|
|
utils.Fatalf("Failed to set PoW: %s", err)
|
|
}
|
|
}
|
|
|
|
if *argMaxSize != whisper.DefaultMaxMessageLength {
|
|
err := shh.SetMaxMessageLength(*argMaxSize)
|
|
if err != nil {
|
|
utils.Fatalf("Failed to set max message size: %s", err)
|
|
}
|
|
}
|
|
|
|
asymKeyID, err = shh.NewKeyPair()
|
|
if err != nil {
|
|
utils.Fatalf("Failed to generate a new key pair: %s", err)
|
|
}
|
|
|
|
asymKey, err = shh.GetPrivateKey(asymKeyID)
|
|
if err != nil {
|
|
utils.Fatalf("Failed to retrieve a new key pair: %s", err)
|
|
}
|
|
|
|
if nodeid == nil {
|
|
tmpID, err := shh.NewKeyPair()
|
|
if err != nil {
|
|
utils.Fatalf("Failed to generate a new key pair: %s", err)
|
|
}
|
|
|
|
nodeid, err = shh.GetPrivateKey(tmpID)
|
|
if err != nil {
|
|
utils.Fatalf("Failed to retrieve a new key pair: %s", err)
|
|
}
|
|
}
|
|
|
|
maxPeers := 80
|
|
if *bootstrapMode {
|
|
maxPeers = 800
|
|
}
|
|
|
|
server = &p2p.Server{
|
|
Config: p2p.Config{
|
|
PrivateKey: nodeid,
|
|
MaxPeers: maxPeers,
|
|
Name: common.MakeName("wnode", "5.0"),
|
|
Protocols: shh.Protocols(),
|
|
ListenAddr: *argIP,
|
|
NAT: nat.Any(),
|
|
BootstrapNodes: peers,
|
|
StaticNodes: peers,
|
|
TrustedNodes: peers,
|
|
},
|
|
}
|
|
}
|
|
|
|
func startServer() {
|
|
err := server.Start()
|
|
if err != nil {
|
|
utils.Fatalf("Failed to start Whisper peer: %s.", err)
|
|
}
|
|
|
|
fmt.Printf("my public key: %s \n", common.ToHex(crypto.FromECDSAPub(&asymKey.PublicKey)))
|
|
fmt.Println(server.NodeInfo().Enode)
|
|
|
|
if *bootstrapMode {
|
|
configureNode()
|
|
fmt.Println("Bootstrap Whisper node started")
|
|
} else {
|
|
fmt.Println("Whisper node started")
|
|
// first see if we can establish connection, then ask for user input
|
|
waitForConnection(true)
|
|
configureNode()
|
|
}
|
|
|
|
if !*forwarderMode {
|
|
fmt.Printf("Please type the message. To quit type: '%s'\n", quitCommand)
|
|
}
|
|
}
|
|
|
|
func isKeyValid(k *ecdsa.PublicKey) bool {
|
|
return k.X != nil && k.Y != nil
|
|
}
|
|
|
|
func configureNode() {
|
|
var err error
|
|
var p2pAccept bool
|
|
|
|
if *forwarderMode {
|
|
return
|
|
}
|
|
|
|
if *asymmetricMode {
|
|
if len(*argPub) == 0 {
|
|
s := scanLine("Please enter the peer's public key: ")
|
|
pub = crypto.ToECDSAPub(common.FromHex(s))
|
|
if !isKeyValid(pub) {
|
|
utils.Fatalf("Error: invalid public key")
|
|
}
|
|
}
|
|
}
|
|
|
|
if *requestMail {
|
|
p2pAccept = true
|
|
if len(msPassword) == 0 {
|
|
msPassword, err = console.Stdin.PromptPassword("Please enter the Mail Server password: ")
|
|
if err != nil {
|
|
utils.Fatalf("Failed to read Mail Server password: %s", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
if !*asymmetricMode && !*forwarderMode {
|
|
if len(symPass) == 0 {
|
|
symPass, err = console.Stdin.PromptPassword("Please enter the password: ")
|
|
if err != nil {
|
|
utils.Fatalf("Failed to read passphrase: %v", err)
|
|
}
|
|
}
|
|
|
|
symKeyID, err := shh.AddSymKeyFromPassword(symPass)
|
|
if err != nil {
|
|
utils.Fatalf("Failed to create symmetric key: %s", err)
|
|
}
|
|
symKey, err = shh.GetSymKey(symKeyID)
|
|
if err != nil {
|
|
utils.Fatalf("Failed to save symmetric key: %s", err)
|
|
}
|
|
if len(*argTopic) == 0 {
|
|
generateTopic([]byte(symPass))
|
|
}
|
|
}
|
|
|
|
if *mailServerMode {
|
|
if len(*argDBPath) == 0 {
|
|
argDBPath = scanLineA("Please enter the path to DB file: ")
|
|
}
|
|
}
|
|
|
|
filter := whisper.Filter{
|
|
KeySym: symKey,
|
|
KeyAsym: asymKey,
|
|
Topics: [][]byte{topic},
|
|
AllowP2P: p2pAccept,
|
|
}
|
|
filterID, err = shh.Subscribe(&filter)
|
|
if err != nil {
|
|
utils.Fatalf("Failed to install filter: %s", err)
|
|
}
|
|
fmt.Printf("Filter is configured for the topic: %x \n", topic)
|
|
}
|
|
|
|
func generateTopic(password []byte) {
|
|
x := pbkdf2.Key(password, password, 8196, 128, sha512.New)
|
|
for i := 0; i < len(x); i++ {
|
|
topic[i%whisper.TopicLength] ^= x[i]
|
|
}
|
|
}
|
|
|
|
func waitForConnection(timeout bool) {
|
|
var cnt int
|
|
var connected bool
|
|
for !connected {
|
|
time.Sleep(time.Millisecond * 50)
|
|
connected = server.PeerCount() > 0
|
|
if timeout {
|
|
cnt++
|
|
if cnt > 1000 {
|
|
utils.Fatalf("Timeout expired, failed to connect")
|
|
}
|
|
}
|
|
}
|
|
|
|
fmt.Println("Connected to peer.")
|
|
}
|
|
|
|
func run() {
|
|
defer mailServer.Close()
|
|
startServer()
|
|
defer server.Stop()
|
|
shh.Start(nil)
|
|
defer shh.Stop()
|
|
|
|
if !*forwarderMode {
|
|
go messageLoop()
|
|
}
|
|
|
|
if *requestMail {
|
|
requestExpiredMessagesLoop()
|
|
} else if *fileExMode {
|
|
sendFilesLoop()
|
|
} else {
|
|
sendLoop()
|
|
}
|
|
}
|
|
|
|
func sendLoop() {
|
|
for {
|
|
s := scanLine("")
|
|
if s == quitCommand {
|
|
fmt.Println("Quit command received")
|
|
close(done)
|
|
break
|
|
}
|
|
sendMsg([]byte(s))
|
|
|
|
if *asymmetricMode {
|
|
// print your own message for convenience,
|
|
// because in asymmetric mode it is impossible to decrypt it
|
|
timestamp := time.Now().Unix()
|
|
from := crypto.PubkeyToAddress(asymKey.PublicKey)
|
|
fmt.Printf("\n%d <%x>: %s\n", timestamp, from, s)
|
|
}
|
|
}
|
|
}
|
|
|
|
func sendFilesLoop() {
|
|
for {
|
|
s := scanLine("")
|
|
if s == quitCommand {
|
|
fmt.Println("Quit command received")
|
|
close(done)
|
|
break
|
|
}
|
|
b, err := ioutil.ReadFile(s)
|
|
if err != nil {
|
|
fmt.Printf(">>> Error: %s \n", err)
|
|
continue
|
|
} else {
|
|
h := sendMsg(b)
|
|
if (h == common.Hash{}) {
|
|
fmt.Printf(">>> Error: message was not sent \n")
|
|
} else {
|
|
timestamp := time.Now().Unix()
|
|
from := crypto.PubkeyToAddress(asymKey.PublicKey)
|
|
fmt.Printf("\n%d <%x>: sent message with hash %x\n", timestamp, from, h)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func scanLine(prompt string) string {
|
|
if len(prompt) > 0 {
|
|
fmt.Print(prompt)
|
|
}
|
|
txt, err := input.ReadString('\n')
|
|
if err != nil {
|
|
utils.Fatalf("input error: %s", err)
|
|
}
|
|
txt = strings.TrimRight(txt, "\n\r")
|
|
return txt
|
|
}
|
|
|
|
func scanLineA(prompt string) *string {
|
|
s := scanLine(prompt)
|
|
return &s
|
|
}
|
|
|
|
func scanUint(prompt string) uint32 {
|
|
s := scanLine(prompt)
|
|
i, err := strconv.Atoi(s)
|
|
if err != nil {
|
|
utils.Fatalf("Fail to parse the lower time limit: %s", err)
|
|
}
|
|
return uint32(i)
|
|
}
|
|
|
|
func sendMsg(payload []byte) common.Hash {
|
|
params := whisper.MessageParams{
|
|
Src: asymKey,
|
|
Dst: pub,
|
|
KeySym: symKey,
|
|
Payload: payload,
|
|
Topic: whisper.BytesToTopic(topic),
|
|
TTL: uint32(*argTTL),
|
|
PoW: *argPoW,
|
|
WorkTime: uint32(*argWorkTime),
|
|
}
|
|
|
|
msg := whisper.NewSentMessage(¶ms)
|
|
if msg == nil {
|
|
fmt.Printf("failed to create new message (OS level error)")
|
|
os.Exit(0)
|
|
}
|
|
envelope, err := msg.Wrap(¶ms)
|
|
if err != nil {
|
|
fmt.Printf("failed to seal message: %v \n", err)
|
|
return common.Hash{}
|
|
}
|
|
|
|
err = shh.Send(envelope)
|
|
if err != nil {
|
|
fmt.Printf("failed to send message: %v \n", err)
|
|
return common.Hash{}
|
|
}
|
|
|
|
return envelope.Hash()
|
|
}
|
|
|
|
func messageLoop() {
|
|
f := shh.GetFilter(filterID)
|
|
if f == nil {
|
|
utils.Fatalf("filter is not installed")
|
|
}
|
|
|
|
ticker := time.NewTicker(time.Millisecond * 50)
|
|
|
|
for {
|
|
select {
|
|
case <-ticker.C:
|
|
messages := f.Retrieve()
|
|
for _, msg := range messages {
|
|
if *fileExMode || len(msg.Payload) > 2048 {
|
|
writeMessageToFile(*argSaveDir, msg)
|
|
} else {
|
|
printMessageInfo(msg)
|
|
}
|
|
}
|
|
case <-done:
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func printMessageInfo(msg *whisper.ReceivedMessage) {
|
|
timestamp := fmt.Sprintf("%d", msg.Sent) // unix timestamp for diagnostics
|
|
text := string(msg.Payload)
|
|
|
|
var address common.Address
|
|
if msg.Src != nil {
|
|
address = crypto.PubkeyToAddress(*msg.Src)
|
|
}
|
|
|
|
if whisper.IsPubKeyEqual(msg.Src, &asymKey.PublicKey) {
|
|
fmt.Printf("\n%s <%x>: %s\n", timestamp, address, text) // message from myself
|
|
} else {
|
|
fmt.Printf("\n%s [%x]: %s\n", timestamp, address, text) // message from a peer
|
|
}
|
|
}
|
|
|
|
func writeMessageToFile(dir string, msg *whisper.ReceivedMessage) {
|
|
timestamp := fmt.Sprintf("%d", msg.Sent)
|
|
name := fmt.Sprintf("%x", msg.EnvelopeHash)
|
|
|
|
var address common.Address
|
|
if msg.Src != nil {
|
|
address = crypto.PubkeyToAddress(*msg.Src)
|
|
}
|
|
|
|
if whisper.IsPubKeyEqual(msg.Src, &asymKey.PublicKey) {
|
|
// message from myself: don't save, only report
|
|
fmt.Printf("\n%s <%x>: message received: '%s'\n", timestamp, address, name)
|
|
} else if len(dir) > 0 {
|
|
fullpath := filepath.Join(dir, name)
|
|
err := ioutil.WriteFile(fullpath, msg.Payload, 0644)
|
|
if err != nil {
|
|
fmt.Printf("\n%s {%x}: message received but not saved: %s\n", timestamp, address, err)
|
|
} else {
|
|
fmt.Printf("\n%s {%x}: message received and saved as '%s' (%d bytes)\n", timestamp, address, name, len(msg.Payload))
|
|
}
|
|
} else {
|
|
fmt.Printf("\n%s {%x}: big message received (%d bytes), but not saved: %s\n", timestamp, address, len(msg.Payload), name)
|
|
}
|
|
}
|
|
|
|
func requestExpiredMessagesLoop() {
|
|
var key, peerID []byte
|
|
var timeLow, timeUpp uint32
|
|
var t string
|
|
var xt, empty whisper.TopicType
|
|
|
|
keyID, err := shh.AddSymKeyFromPassword(msPassword)
|
|
if err != nil {
|
|
utils.Fatalf("Failed to create symmetric key for mail request: %s", err)
|
|
}
|
|
key, err = shh.GetSymKey(keyID)
|
|
if err != nil {
|
|
utils.Fatalf("Failed to save symmetric key for mail request: %s", err)
|
|
}
|
|
peerID = extractIdFromEnode(*argEnode)
|
|
shh.AllowP2PMessagesFromPeer(peerID)
|
|
|
|
for {
|
|
timeLow = scanUint("Please enter the lower limit of the time range (unix timestamp): ")
|
|
timeUpp = scanUint("Please enter the upper limit of the time range (unix timestamp): ")
|
|
t = scanLine("Please enter the topic (hexadecimal): ")
|
|
if len(t) >= whisper.TopicLength*2 {
|
|
x, err := hex.DecodeString(t)
|
|
if err != nil {
|
|
utils.Fatalf("Failed to parse the topic: %s", err)
|
|
}
|
|
xt = whisper.BytesToTopic(x)
|
|
}
|
|
if timeUpp == 0 {
|
|
timeUpp = 0xFFFFFFFF
|
|
}
|
|
|
|
data := make([]byte, 8+whisper.TopicLength)
|
|
binary.BigEndian.PutUint32(data, timeLow)
|
|
binary.BigEndian.PutUint32(data[4:], timeUpp)
|
|
copy(data[8:], xt[:])
|
|
if xt == empty {
|
|
data = data[:8]
|
|
}
|
|
|
|
var params whisper.MessageParams
|
|
params.PoW = *argServerPoW
|
|
params.Payload = data
|
|
params.KeySym = key
|
|
params.Src = nodeid
|
|
params.WorkTime = 5
|
|
|
|
msg := whisper.NewSentMessage(¶ms)
|
|
if msg == nil {
|
|
utils.Fatalf("failed to create new message (OS level error)")
|
|
}
|
|
env, err := msg.Wrap(¶ms)
|
|
if err != nil {
|
|
utils.Fatalf("Wrap failed: %s", err)
|
|
}
|
|
|
|
err = shh.RequestHistoricMessages(peerID, env)
|
|
if err != nil {
|
|
utils.Fatalf("Failed to send P2P message: %s", err)
|
|
}
|
|
|
|
time.Sleep(time.Second * 5)
|
|
}
|
|
}
|
|
|
|
func extractIdFromEnode(s string) []byte {
|
|
n, err := discover.ParseNode(s)
|
|
if err != nil {
|
|
utils.Fatalf("Failed to parse enode: %s", err)
|
|
}
|
|
return n.ID[:]
|
|
}
|