Last active
September 6, 2020 21:47
-
-
Save nolash/9434ea24a71474bebcd0341c98685ff4 to your computer and use it in GitHub Desktop.
POC bzzkey over ENR
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"bytes" | |
"crypto/ecdsa" | |
"flag" | |
"fmt" | |
"io" | |
"math/rand" | |
"net" | |
"os" | |
"time" | |
"github.com/ethereum/go-ethereum/common/hexutil" | |
"github.com/ethereum/go-ethereum/crypto" | |
"github.com/ethereum/go-ethereum/log" | |
"github.com/ethereum/go-ethereum/node" | |
"github.com/ethereum/go-ethereum/p2p" | |
"github.com/ethereum/go-ethereum/p2p/enode" | |
"github.com/ethereum/go-ethereum/p2p/enr" | |
"github.com/ethereum/go-ethereum/p2p/simulations" | |
"github.com/ethereum/go-ethereum/p2p/simulations/adapters" | |
"github.com/ethereum/go-ethereum/rlp" | |
"github.com/ethereum/go-ethereum/rpc" | |
) | |
var ( | |
verbose = flag.Bool("v", false, "make verbose") | |
debugLevel = 3 | |
) | |
func init() { | |
flag.Parse() | |
if *verbose { | |
debugLevel = 4 | |
} | |
rand.Seed(42) | |
log.Root().SetHandler( | |
log.CallerFileHandler( | |
log.LvlFilterHandler( | |
log.Lvl(debugLevel), | |
log.StreamHandler( | |
os.Stderr, log.TerminalFormat(true), | |
), | |
), | |
), | |
) | |
} | |
// randReader is used to generate predictable keys | |
type randReader struct { | |
} | |
func (r randReader) Read(b []byte) (int, error) { | |
i, err := rand.Read(b) | |
return i, err | |
} | |
// auxEntry is a control entry to check that an arbitrary record are transmitted to the peer | |
type auxEntry string | |
// ENRKey implements enr.Entry | |
func (a auxEntry) ENRKey() string { | |
return "aux" | |
} | |
func (a auxEntry) EncodeRLP(w io.Writer) error { | |
log.Debug("in encoderlp aux", "a", a, "p", fmt.Sprintf("%p", &a)) | |
return rlp.Encode(w, (*string)(&a)) | |
} | |
func (a *auxEntry) DecodeRLP(s *rlp.Stream) error { | |
byt, err := s.Bytes() | |
if err != nil { | |
return err | |
} | |
*a = auxEntry(byt) | |
log.Debug("in decoderlp aux", "a", a, "p", fmt.Sprintf("%p", &a)) | |
return nil | |
} | |
// bzzKeyEntry is the entry type to store the bzz key in the enode | |
type bzzKeyEntry struct { | |
data []byte | |
} | |
// ENRKey implements enr.Entry | |
func (b bzzKeyEntry) ENRKey() string { | |
return "bzzkey" | |
} | |
// EncodeRLP implements rlp.Encoder | |
func (b bzzKeyEntry) EncodeRLP(w io.Writer) error { | |
log.Debug("in encoderlp", "b", b, "p", fmt.Sprintf("%p", &b)) | |
return rlp.Encode(w, &b.data) | |
} | |
// DecodeRLP implements rlp.Decoder | |
func (b *bzzKeyEntry) DecodeRLP(s *rlp.Stream) error { | |
byt, err := s.Bytes() | |
if err != nil { | |
return err | |
} | |
b.data = byt | |
log.Debug("in decoderlp", "b", b, "p", fmt.Sprintf("%p", &b)) | |
return nil | |
} | |
func getRelevantEntries(record *enr.Record) map[string]string { | |
// recover relevant entries in record | |
var bzzkey bzzKeyEntry | |
var aux auxEntry | |
record.Load(&bzzkey) | |
record.Load(&aux) | |
return map[string]string{ | |
"bzzkey": hexutil.Encode(bzzkey.data), | |
"aux": string(aux), | |
} | |
} | |
type bzzMsg struct { | |
seq uint64 | |
} | |
func newBzzTestProtocol(r enr.Record) p2p.Protocol { | |
var bzzkey bzzKeyEntry | |
var aux auxEntry | |
r.Load(&bzzkey) | |
r.Load(&aux) | |
return p2p.Protocol{ | |
Name: "bzztest", | |
Version: 1, | |
Length: 1, | |
Run: func(peer *p2p.Peer, rw p2p.MsgReadWriter) error { | |
log.Info("result in protocol", "entries", getRelevantEntries(peer.Node().Record())) | |
payload := []byte(fmt.Sprintf("%p", peer)) | |
err := rw.WriteMsg(p2p.Msg{ | |
Code: 0, | |
Size: uint32(len(payload)), | |
Payload: bytes.NewReader(payload), | |
}) | |
log.Debug("writemsg attempt", "err", err) | |
for { | |
msg, err := rw.ReadMsg() | |
if err != nil { | |
return err | |
} | |
log.Debug("got msg", "r", peer, "msg", msg) | |
} | |
}, | |
Attributes: []enr.Entry{ | |
bzzkey, | |
aux, | |
}, | |
} | |
} | |
type bzzTestAPI struct { | |
} | |
func newBzzTestAPI(client *adapters.RPCDialer) bzzTestAPI { | |
return bzzTestAPI{} | |
} | |
// noopService fills the space of the required service object for creating a simulation node | |
type noopService struct { | |
record enr.Record | |
} | |
func (n noopService) APIs() []rpc.API { | |
return []rpc.API{} | |
} | |
func (n noopService) Protocols() []p2p.Protocol { | |
return []p2p.Protocol{ | |
newBzzTestProtocol(n.record), | |
} | |
} | |
func (n noopService) Start(p *p2p.Server) error { | |
log.Debug("starting noopservice") | |
return nil | |
} | |
func (n noopService) Stop() error { | |
log.Debug("stopping noopservice") | |
return nil | |
} | |
func noopServiceFunc(s *adapters.ServiceContext) (node.Service, error) { | |
return noopService{ | |
record: s.Config.Record, | |
}, nil | |
} | |
// newNodeConfig generates a simulations adapter configuration using the enr scheme | |
func newNodeConfig(pk *ecdsa.PrivateKey, bzzaddr []byte, v []string) (*adapters.NodeConfig, error) { | |
var record enr.Record | |
entry_bzzkey := bzzKeyEntry{ | |
data: bzzaddr, | |
} | |
record.Set(entry_bzzkey) | |
entry_aux := auxEntry(v[0]) | |
record.Set(entry_aux) | |
entry_pubkey := enode.Secp256k1(pk.PublicKey) | |
record.Set(entry_pubkey) | |
entry_ip := enr.IP(net.IPv4(127, 0, 0, 1)) | |
record.Set(entry_ip) | |
entry_tcp := enr.TCP(0) | |
record.Set(entry_tcp) | |
err := enode.SignV4(&record, pk) | |
if err != nil { | |
return nil, err | |
} | |
nod, err := enode.New(enode.ValidSchemes["v4"], &record) | |
if err != nil { | |
return nil, err | |
} | |
return &adapters.NodeConfig{ | |
ID: nod.ID(), | |
PrivateKey: pk, | |
Name: v[0], | |
Record: record, | |
Services: []string{"noop"}, | |
}, nil | |
} | |
// newNodeConfigParams returns the private key and swarm address to create a node with | |
func newNodeConfigParams() (*ecdsa.PrivateKey, []byte, error) { | |
pk, err := ecdsa.GenerateKey(crypto.S256(), randReader{}) | |
if err != nil { | |
return nil, nil, err | |
} | |
pubkeybytes := crypto.FromECDSAPub(&pk.PublicKey) | |
bzzkeyhash := crypto.Keccak256Hash(pubkeybytes) | |
log.Info("config pubkey", "bytes", hexutil.Encode(pubkeybytes)) | |
log.Info("config bzz", "bytes", hexutil.Encode(bzzkeyhash.Bytes())) | |
return pk, bzzkeyhash.Bytes(), nil | |
} | |
func main() { | |
// create node configuations | |
pk_l, addr_l, err := newNodeConfigParams() | |
if err != nil { | |
log.Crit(err.Error()) | |
} | |
cfg_l, err := newNodeConfig(pk_l, addr_l, []string{"foo"}) | |
if err != nil { | |
log.Crit(err.Error()) | |
} | |
pk_r, addr_r, err := newNodeConfigParams() | |
if err != nil { | |
log.Crit(err.Error()) | |
} | |
cfg_r, err := newNodeConfig(pk_r, addr_r, []string{"bar"}) | |
if err != nil { | |
log.Crit(err.Error()) | |
} | |
log.Info("addrs", "left", hexutil.Encode(addr_l), "right", hexutil.Encode(addr_r)) | |
log.Debug("privkey left", "key", hexutil.Encode(crypto.FromECDSA(cfg_l.PrivateKey)), "cfg", cfg_l) | |
log.Debug("privkey right", "key", hexutil.Encode(crypto.FromECDSA(cfg_r.PrivateKey)), "cfg", cfg_r) | |
// create the nodes | |
servicesMap := map[string]adapters.ServiceFunc{ | |
"noop": noopServiceFunc, | |
} | |
simAdapter := adapters.NewSimAdapter(servicesMap) | |
// create network | |
ncfg := &simulations.NetworkConfig{ | |
ID: "enrtest", | |
DefaultService: "noop", | |
} | |
simnet := simulations.NewNetwork(simAdapter, ncfg) | |
nod_l, err := simnet.NewNodeWithConfig(cfg_l) | |
if err != nil { | |
log.Crit(err.Error()) | |
} | |
nod_r, err := simnet.NewNodeWithConfig(cfg_r) | |
if err != nil { | |
log.Crit(err.Error()) | |
} | |
log.Info("node info left", "n", nod_l.ID()) | |
log.Info("node info right", "n", nod_r.ID()) | |
// start nodes | |
err = simnet.Start(nod_l.ID()) | |
if err != nil { | |
log.Crit(err.Error()) | |
} | |
defer simnet.Stop(nod_l.ID()) | |
err = simnet.Start(nod_r.ID()) | |
if err != nil { | |
log.Crit(err.Error()) | |
} | |
defer simnet.Stop(nod_r.ID()) | |
// connect nodes | |
nid_l := nod_l.ID() | |
nid_r := nod_r.ID() | |
err = simnet.Connect(nid_l, nid_r) | |
if err != nil { | |
log.Crit(err.Error()) | |
} | |
// wait a bit to allow connection to complete | |
time.Sleep(1 * time.Second) | |
// retrieve results | |
for _, n := range simnet.GetNodes() { | |
// get peerinfo available on node | |
cli, err := n.Client() | |
if err != nil { | |
log.Crit(err.Error()) | |
} | |
var peerInfo []p2p.PeerInfo | |
err = cli.Call(&peerInfo, "admin_peers") | |
if err != nil { | |
log.Crit(err.Error()) | |
} | |
// decode peerinfo ENR entry | |
var record enr.Record | |
enrInfoBytes, err := hexutil.Decode(peerInfo[0].ENR) | |
if err != nil { | |
log.Crit(err.Error()) | |
} | |
err = rlp.DecodeBytes(enrInfoBytes, &record) | |
if err != nil { | |
log.Crit(err.Error()) | |
} | |
results := getRelevantEntries(&record) | |
log.Info("result in peerinfo", "node", n, "entries", results) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment