Skip to content
Snippets Groups Projects
Unverified Commit 1134f3fd authored by Luca Moser's avatar Luca Moser Committed by GitHub
Browse files

Adds changes for v0.1.2 (#266)


* New binary tangle using atomic transactions (#239)

* Feat: started porting the new binary stuff

* Refactor: removed unnecessary folder

* Refactor: cleaned up go.mod files

* Fix: removed objectsdb files

* Feat: added transactionrequester package

* Adds the transactionParser as a fliter and pre-processing mechanism of the tangle. (#242)

* Feat: started porting the new binary stuff

* Refactor: removed unnecessary folder

* Refactor: cleaned up go.mod files

* Fix: removed objectsdb files

* Feat: added transactionrequester package

* Feat: added new transactionparser as the filter for the tangle

* Feat: Use hive.go autopeering (#250)

* use autopeering from hive.go
* update hive.go

* update hive.go

* Adds the TransactionRequester and some refactors (#256)

* Feat: started porting the new binary stuff

* Refactor: removed unnecessary folder

* Refactor: cleaned up go.mod files

* Fix: removed objectsdb files

* Feat: added transactionrequester package

* Feat: added new transactionparser as the filter for the tangle

* Refactor: removed duplicate code

* Fix: Log dropping packets every 1000 drops (#255)

* Fix: Log dropping packets every 1000 drops

* use an atomic counter for dropped message because Write() could be called concurrently

* removes redundant zero

* Add external check in test

Co-authored-by: default avatarLuca Moser <moser.luca@gmail.com>

* Fix: Update pprof port (#244)

Since both Hornet and Goshimmer might be running on the same machine, update the port so that we can get debug information for both.

* Remove docker specific config (#245)

* :arrow_up: upgrades hive.go

* :bug: checks meta_tx size

* :heavy_minus_sign: removes dependencies

* :sparkles: enables local testing

* :sparkles: adds config plugin

* Extend remote log message (#260)

* Bump up Logstash and ElasticSearch memory #251

* Add version and if available GIT information to remote log message #251

* :bug:

 fixes flag parsing before loading config file

* Feat: Add --version to cli (#264)

* Adds changelog entry for v0.1.2 and bumps version number (#265)

* updates changelog for v0.1.2 release

* bumps version number to v0.1.2

Co-authored-by: default avatarHans Moog <hm@mkjc.net>
Co-authored-by: default avatarWolfgang Welz <welzwo@gmail.com>
Co-authored-by: default avatarjkrvivian <jkrvivian@gmail.com>
Co-authored-by: default avatarDave <44786846+centercirclesolutions@users.noreply.github.com>
Co-authored-by: default avatarAngelo Capossele <angelocapossele@gmail.com>
Co-authored-by: default avatarJonas Theis <mail@jonastheis.de>
parent 5d92569b
No related branches found
No related tags found
No related merge requests found
Showing
with 0 additions and 1496 deletions
package distance
import (
"crypto/sha256"
"encoding/binary"
)
// BySalt returns the distance (uint32) between x and y
// by xoring the hash of x and y + salt
// xor(hash(x), hash(y+salt))[:4]
func BySalt(x, y, salt []byte) uint32 {
return xorSHA32(x, joinBytes(y, salt))
}
func joinBytes(a, b []byte) (out []byte) {
out = make([]byte, len(a)+len(b))
copy(out[0:], a)
copy(out[len(a):], b)
return out
}
func xorSHA32(a, b []byte) uint32 {
return binary.LittleEndian.Uint32(
xorSHA(sha256.Sum256(a), sha256.Sum256(b))[:4])
}
func xorSHA(a, b [sha256.Size]byte) (out []byte) {
out = make([]byte, sha256.Size)
for i := 0; i < sha256.Size; i++ {
out[i] = a[i] ^ b[i]
}
return out
}
package distance
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestBySalt(t *testing.T) {
type testCase struct {
x []byte
y []byte
salt []byte
zeroDistance bool
}
tests := []testCase{
{
x: []byte("X"),
y: []byte("Y"),
salt: []byte("salt"),
zeroDistance: false,
},
{
x: []byte("X"),
y: []byte("X"),
salt: []byte{},
zeroDistance: true,
},
}
for _, test := range tests {
d := BySalt(test.x, test.y, test.salt)
got := d == 0
assert.Equal(t, test.zeroDistance, got, "Zero Distance")
}
}
package peer
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"strings"
)
// ID is a unique identifier for each peer.
type ID [sha256.Size]byte
// Bytes returns the byte slice representation of the ID
func (id ID) Bytes() []byte {
return id[:]
}
// String returns a shortened version of the ID as a hex encoded string.
func (id ID) String() string {
return hex.EncodeToString(id[:8])
}
// ParseID parses a hex encoded ID.
func ParseID(s string) (ID, error) {
var id ID
b, err := hex.DecodeString(strings.TrimPrefix(s, "0x"))
if err != nil {
return id, err
}
if len(b) != len(ID{}) {
return id, fmt.Errorf("invalid length: need %d hex chars", hex.EncodedLen(len(ID{})))
}
copy(id[:], b)
return id, nil
}
// ID computes the ID corresponding to the given public key.
func (k PublicKey) ID() ID {
return sha256.Sum256(k)
}
package peer
import (
"crypto/ed25519"
"fmt"
"sync"
"github.com/iotaledger/goshimmer/packages/autopeering/peer/service"
"github.com/iotaledger/goshimmer/packages/autopeering/salt"
)
// Local defines the struct of a local peer
type Local struct {
Peer
key PrivateKey
db *DB
// everything below is protected by a lock
mu sync.RWMutex
serviceRecord *service.Record
publicSalt *salt.Salt
privateSalt *salt.Salt
}
// PrivateKey is the type of Ed25519 private keys used for the local peer.
type PrivateKey ed25519.PrivateKey
// Public returns the PublicKey corresponding to priv.
func (priv PrivateKey) Public() PublicKey {
publicKey := ed25519.PrivateKey(priv).Public()
return PublicKey(publicKey.(ed25519.PublicKey))
}
// newLocal creates a new local peer.
func newLocal(key PrivateKey, serviceRecord *service.Record, db *DB) *Local {
return &Local{
Peer: *NewPeer(key.Public(), serviceRecord),
key: key,
db: db,
serviceRecord: serviceRecord,
}
}
// NewLocal creates a new local peer linked to the provided db.
// If an optional seed is provided, the seed is used to generate the private key. Without a seed,
// the provided key is loaded from the provided database and generated if not stored there.
func NewLocal(serviceRecord *service.Record, db *DB, seed ...[]byte) (*Local, error) {
var key PrivateKey
if len(seed) > 0 {
key = PrivateKey(ed25519.NewKeyFromSeed(seed[0]))
if db != nil {
if err := db.UpdateLocalPrivateKey(key); err != nil {
return nil, err
}
}
} else {
var err error
key, err = db.LocalPrivateKey()
if err != nil {
return nil, err
}
}
if l := len(key); l != ed25519.PrivateKeySize {
return nil, fmt.Errorf("invalid key length: %d, need %d", l, ed25519.PrivateKeySize)
}
return newLocal(key, serviceRecord, db), nil
}
// Database returns the node database associated with the local peer.
func (l *Local) Database() *DB {
return l.db
}
// Sign signs the message with the local peer's private key and returns a signature.
func (l *Local) Sign(message []byte) []byte {
return ed25519.Sign(ed25519.PrivateKey(l.key), message)
}
// UpdateService updates the endpoint address of the given local service.
func (l *Local) UpdateService(key service.Key, network string, address string) error {
l.mu.Lock()
defer l.mu.Unlock()
// update the service in the read protected map
l.serviceRecord.Update(key, network, address)
// create a new peer with the corresponding services
l.Peer = *NewPeer(l.key.Public(), l.serviceRecord)
return nil
}
// GetPublicSalt returns the public salt
func (l *Local) GetPublicSalt() *salt.Salt {
l.mu.RLock()
defer l.mu.RUnlock()
return l.publicSalt
}
// SetPublicSalt sets the public salt
func (l *Local) SetPublicSalt(salt *salt.Salt) {
l.mu.Lock()
defer l.mu.Unlock()
l.publicSalt = salt
}
// GetPrivateSalt returns the private salt
func (l *Local) GetPrivateSalt() *salt.Salt {
l.mu.RLock()
defer l.mu.RUnlock()
return l.privateSalt
}
// SetPrivateSalt sets the private salt
func (l *Local) SetPrivateSalt(salt *salt.Salt) {
l.mu.Lock()
defer l.mu.Unlock()
l.privateSalt = salt
}
// generatePrivateKey generates a private key that can be used for Local.
func generatePrivateKey() (PrivateKey, error) {
_, priv, err := ed25519.GenerateKey(nil)
return PrivateKey(priv), err
}
package peer
import (
"crypto/ed25519"
"testing"
"time"
"github.com/iotaledger/goshimmer/packages/autopeering/peer/service"
"github.com/iotaledger/goshimmer/packages/autopeering/salt"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestID(t *testing.T) {
pub, priv, err := ed25519.GenerateKey(nil)
require.NoError(t, err)
local := newLocal(PrivateKey(priv), newTestServiceRecord(), nil)
id := PublicKey(pub).ID()
assert.Equal(t, id, local.ID())
}
func TestPublicKey(t *testing.T) {
pub, priv, err := ed25519.GenerateKey(nil)
require.NoError(t, err)
local := newLocal(PrivateKey(priv), newTestServiceRecord(), nil)
assert.EqualValues(t, pub, local.PublicKey())
}
func TestAddress(t *testing.T) {
local := newTestLocal(t, nil)
address := local.Services().Get(service.PeeringKey).String()
assert.EqualValues(t, address, local.Address())
}
func TestPrivateSalt(t *testing.T) {
p := newTestLocal(t, nil)
s, _ := salt.NewSalt(time.Second * 10)
p.SetPrivateSalt(s)
got := p.GetPrivateSalt()
assert.Equal(t, s, got, "Private salt")
}
func TestPublicSalt(t *testing.T) {
p := newTestLocal(t, nil)
s, _ := salt.NewSalt(time.Second * 10)
p.SetPublicSalt(s)
got := p.GetPublicSalt()
assert.Equal(t, s, got, "Public salt")
}
func newTestLocal(t require.TestingT, db *DB) *Local {
var priv PrivateKey
var err error
if db == nil {
priv, err = generatePrivateKey()
require.NoError(t, err)
} else {
priv, err = db.LocalPrivateKey()
require.NoError(t, err)
}
services := service.New()
services.Update(service.PeeringKey, testNetwork, testAddress)
return newLocal(priv, services, db)
}
package peer
import (
"crypto/ed25519"
"encoding/base64"
"errors"
"fmt"
"net/url"
"github.com/golang/protobuf/proto"
pb "github.com/iotaledger/goshimmer/packages/autopeering/peer/proto"
"github.com/iotaledger/goshimmer/packages/autopeering/peer/service"
)
// Errors in the peer package.
var (
ErrNeedsPeeringService = errors.New("needs peering service")
ErrInvalidSignature = errors.New("invalid signature")
)
// PublicKey is the type of Ed25519 public keys used for peers.
type PublicKey ed25519.PublicKey
// Peer defines the immutable data of a peer.
type Peer struct {
id ID // comparable node identifier
publicKey PublicKey // public key used to verify signatures
services *service.Record // unmodifiable services supported by the peer
}
// ID returns the identifier of the peer.
func (p *Peer) ID() ID {
return p.id
}
// PublicKey returns the public key of the peer.
func (p *Peer) PublicKey() PublicKey {
return p.publicKey
}
// Network returns the autopeering network of the peer.
func (p *Peer) Network() string {
return p.services.Get(service.PeeringKey).Network()
}
// Address returns the autopeering address of a peer.
func (p *Peer) Address() string {
return p.services.Get(service.PeeringKey).String()
}
// Services returns the supported services of the peer.
func (p *Peer) Services() service.Service {
return p.services
}
// String returns a string representation of the peer.
func (p *Peer) String() string {
u := url.URL{
Scheme: "peer",
User: url.User(base64.StdEncoding.EncodeToString(p.PublicKey())),
Host: p.Address(),
}
return u.String()
}
// SignedData is an interface wrapper around data with key and signature.
type SignedData interface {
GetData() []byte
GetPublicKey() []byte
GetSignature() []byte
}
// RecoverKeyFromSignedData validates and returns the key that was used to sign the data.
func RecoverKeyFromSignedData(m SignedData) (PublicKey, error) {
return recoverKey(m.GetPublicKey(), m.GetData(), m.GetSignature())
}
// NewPeer creates a new unmodifiable peer.
func NewPeer(publicKey PublicKey, services service.Service) *Peer {
if services.Get(service.PeeringKey) == nil {
panic("need peering service")
}
return &Peer{
id: publicKey.ID(),
publicKey: publicKey,
services: services.CreateRecord(),
}
}
// ToProto encodes a given peer into a proto buffer Peer message
func (p *Peer) ToProto() *pb.Peer {
return &pb.Peer{
PublicKey: p.publicKey,
Services: p.services.ToProto(),
}
}
// FromProto decodes a given proto buffer Peer message (in) and returns the corresponding Peer.
func FromProto(in *pb.Peer) (*Peer, error) {
if l := len(in.GetPublicKey()); l != ed25519.PublicKeySize {
return nil, fmt.Errorf("invalid key length: %d, need %d", l, ed25519.PublicKeySize)
}
services, err := service.FromProto(in.GetServices())
if err != nil {
return nil, err
}
if services.Get(service.PeeringKey) == nil {
return nil, ErrNeedsPeeringService
}
return NewPeer(in.GetPublicKey(), services), nil
}
// Marshal serializes a given Peer (p) into a slice of bytes.
func (p *Peer) Marshal() ([]byte, error) {
return proto.Marshal(p.ToProto())
}
// Unmarshal de-serializes a given slice of bytes (data) into a Peer.
func Unmarshal(data []byte) (*Peer, error) {
s := &pb.Peer{}
if err := proto.Unmarshal(data, s); err != nil {
return nil, err
}
return FromProto(s)
}
func recoverKey(key, data, sig []byte) (PublicKey, error) {
if l := len(key); l != ed25519.PublicKeySize {
return nil, fmt.Errorf("%w: invalid key length: %d, need %d", ErrInvalidSignature, l, ed25519.PublicKeySize)
}
if l := len(sig); l != ed25519.SignatureSize {
return nil, fmt.Errorf("%w: invalid signature length: %d, need %d", ErrInvalidSignature, l, ed25519.SignatureSize)
}
if !ed25519.Verify(key, data, sig) {
return nil, ErrInvalidSignature
}
publicKey := make([]byte, ed25519.PublicKeySize)
copy(publicKey, key)
return publicKey, nil
}
package peer
import (
"crypto/ed25519"
"testing"
"github.com/iotaledger/goshimmer/packages/autopeering/peer/service"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
const (
testNetwork = "udp"
testAddress = "127.0.0.1:8000"
testMessage = "Hello World!"
)
func newTestServiceRecord() *service.Record {
services := service.New()
services.Update(service.PeeringKey, testNetwork, testAddress)
return services
}
func newTestPeer(name string) *Peer {
key := make([]byte, ed25519.PublicKeySize)
copy(key, name)
return NewPeer(key, newTestServiceRecord())
}
func TestNoServicePeer(t *testing.T) {
key := make([]byte, ed25519.PublicKeySize)
services := service.New()
assert.Panics(t, func() {
_ = NewPeer(key, services)
})
}
func TestInvalidServicePeer(t *testing.T) {
key := make([]byte, ed25519.PublicKeySize)
services := service.New()
services.Update(service.FPCKey, "network", "address")
assert.Panics(t, func() {
_ = NewPeer(key, services)
})
}
func TestMarshalUnmarshal(t *testing.T) {
p := newTestPeer("test")
data, err := p.Marshal()
require.NoError(t, err)
got, err := Unmarshal(data)
require.NoError(t, err)
assert.Equal(t, p, got)
}
func TestRecoverKeyFromSignedData(t *testing.T) {
msg := []byte(testMessage)
pub, priv, err := ed25519.GenerateKey(nil)
require.NoError(t, err)
sig := ed25519.Sign(priv, msg)
d := signedData{pub: pub, msg: msg, sig: sig}
key, err := RecoverKeyFromSignedData(d)
require.NoError(t, err)
assert.Equal(t, PublicKey(pub).ID(), key.ID())
}
type signedData struct {
pub, msg, sig []byte
}
func (d signedData) GetPublicKey() []byte { return d.pub }
func (d signedData) GetData() []byte { return d.msg }
func (d signedData) GetSignature() []byte { return d.sig }
package peer
import (
"bytes"
"encoding/binary"
"math/rand"
"time"
"github.com/iotaledger/goshimmer/packages/database"
)
const (
// remove peers from DB, when the last received ping was older than this
peerExpiration = 24 * time.Hour
// interval in which expired peers are checked
cleanupInterval = time.Hour
// number of peers used for bootstrapping
seedCount = 10
// time after which potential seed peers should expire
seedExpiration = 5 * 24 * time.Hour
)
// Database defines the database functionality required by DB.
type Database interface {
Get(database.Key) (database.Entry, error)
Set(database.Entry) error
ForEachPrefix(database.KeyPrefix, func(database.Entry) bool) error
}
// DB is the peer database, storing previously seen peers and any collected properties of them.
type DB struct {
db Database
}
// Keys in the node database.
const (
dbNodePrefix = "n:" // Identifier to prefix node entries with
dbLocalPrefix = "local:" // Identifier to prefix local entries
// These fields are stored per ID and address. Use nodeFieldKey to create those keys.
dbNodePing = "lastping"
dbNodePong = "lastpong"
// Local information is keyed by ID only. Use localFieldKey to create those keys.
dbLocalKey = "key"
)
// NewDB creates a new peer database.
func NewDB(db Database) (*DB, error) {
pDB := &DB{
db: db,
}
err := pDB.init()
if err != nil {
return nil, err
}
return pDB, nil
}
func (db *DB) init() error {
// get all peers in the DB
peers := db.getPeers(0)
for _, p := range peers {
// if they dont have an associated pong, give them a grace period
if db.LastPong(p.ID(), p.Address()).Unix() == 0 {
if err := db.setPeerWithTTL(p, cleanupInterval); err != nil {
return err
}
}
}
return nil
}
// nodeKey returns the database key for a node record.
func nodeKey(id ID) []byte {
return append([]byte(dbNodePrefix), id.Bytes()...)
}
// nodeFieldKey returns the database key for a node metadata field.
func nodeFieldKey(id ID, address string, field string) []byte {
return bytes.Join([][]byte{nodeKey(id), []byte(address), []byte(field)}, []byte{':'})
}
func localFieldKey(field string) []byte {
return append([]byte(dbLocalPrefix), []byte(field)...)
}
func parseInt64(blob []byte) int64 {
val, read := binary.Varint(blob)
if read <= 0 {
return 0
}
return val
}
// getInt64 retrieves an integer associated with a particular key.
func (db *DB) getInt64(key []byte) int64 {
entry, err := db.db.Get(key)
if err != nil {
return 0
}
return parseInt64(entry.Value)
}
// setInt64 stores an integer in the given key.
func (db *DB) setInt64(key []byte, n int64) error {
blob := make([]byte, binary.MaxVarintLen64)
blob = blob[:binary.PutVarint(blob, n)]
return db.db.Set(database.Entry{Key: key, Value: blob, TTL: peerExpiration})
}
// LocalPrivateKey returns the private key stored in the database or creates a new one.
func (db *DB) LocalPrivateKey() (PrivateKey, error) {
entry, err := db.db.Get(localFieldKey(dbLocalKey))
if err == database.ErrKeyNotFound {
key, genErr := generatePrivateKey()
if genErr == nil {
err = db.UpdateLocalPrivateKey(key)
}
return key, err
}
if err != nil {
return nil, err
}
return PrivateKey(entry.Value), nil
}
// UpdateLocalPrivateKey stores the provided key in the database.
func (db *DB) UpdateLocalPrivateKey(key PrivateKey) error {
return db.db.Set(database.Entry{Key: localFieldKey(dbLocalKey), Value: []byte(key)})
}
// LastPing returns that property for the given peer ID and address.
func (db *DB) LastPing(id ID, address string) time.Time {
return time.Unix(db.getInt64(nodeFieldKey(id, address, dbNodePing)), 0)
}
// UpdateLastPing updates that property for the given peer ID and address.
func (db *DB) UpdateLastPing(id ID, address string, t time.Time) error {
return db.setInt64(nodeFieldKey(id, address, dbNodePing), t.Unix())
}
// LastPong returns that property for the given peer ID and address.
func (db *DB) LastPong(id ID, address string) time.Time {
return time.Unix(db.getInt64(nodeFieldKey(id, address, dbNodePong)), 0)
}
// UpdateLastPong updates that property for the given peer ID and address.
func (db *DB) UpdateLastPong(id ID, address string, t time.Time) error {
return db.setInt64(nodeFieldKey(id, address, dbNodePong), t.Unix())
}
func (db *DB) setPeerWithTTL(p *Peer, ttl time.Duration) error {
data, err := p.Marshal()
if err != nil {
return err
}
return db.db.Set(database.Entry{Key: nodeKey(p.ID()), Value: data, TTL: ttl})
}
// UpdatePeer updates a peer in the database.
func (db *DB) UpdatePeer(p *Peer) error {
return db.setPeerWithTTL(p, peerExpiration)
}
func parsePeer(data []byte) *Peer {
p, err := Unmarshal(data)
if err != nil {
return nil
}
return p
}
// Peer retrieves a peer from the database.
func (db *DB) Peer(id ID) *Peer {
data, err := db.db.Get(nodeKey(id))
if err != nil {
return nil
}
return parsePeer(data.Value)
}
func randomSubset(peers []*Peer, m int) []*Peer {
if len(peers) <= m {
return peers
}
result := make([]*Peer, 0, m)
for i, p := range peers {
if rand.Intn(len(peers)-i) < m-len(result) {
result = append(result, p)
}
}
return result
}
func (db *DB) getPeers(maxAge time.Duration) (peers []*Peer) {
now := time.Now()
err := db.db.ForEachPrefix([]byte(dbNodePrefix), func(entry database.Entry) bool {
var id ID
if len(entry.Key) != len(id) {
return false
}
copy(id[:], entry.Key)
p := parsePeer(entry.Value)
if p == nil || p.ID() != id {
return false
}
if maxAge > 0 && now.Sub(db.LastPong(p.ID(), p.Address())) > maxAge {
return false
}
peers = append(peers, p)
return false
})
if err != nil {
return nil
}
return peers
}
// SeedPeers retrieves random nodes to be used as potential bootstrap peers.
func (db *DB) SeedPeers() []*Peer {
// get all stored peers and select subset
peers := db.getPeers(0)
return randomSubset(peers, seedCount)
}
// PersistSeeds assures that potential bootstrap peers are not garbage collected.
// It returns the number of peers that have been persisted.
func (db *DB) PersistSeeds() int {
// randomly select potential bootstrap peers
peers := randomSubset(db.getPeers(peerExpiration), seedCount)
for i, p := range peers {
err := db.setPeerWithTTL(p, seedExpiration)
if err != nil {
return i
}
}
return len(peers)
}
package peer
import (
"crypto/ed25519"
"fmt"
"testing"
"time"
"github.com/iotaledger/goshimmer/packages/database/mapdb"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestPeerDB(t *testing.T) {
db, err := NewDB(mapdb.NewMapDB())
require.NoError(t, err)
p := newTestPeer("test")
t.Run("LocalPrivateKey", func(t *testing.T) {
key, err := db.LocalPrivateKey()
require.NoError(t, err)
assert.EqualValues(t, ed25519.PrivateKeySize, len(key))
assert.EqualValues(t, ed25519.PublicKeySize, len(key.Public()))
err = db.UpdateLocalPrivateKey(key)
require.NoError(t, err)
key2, err := db.LocalPrivateKey()
require.NoError(t, err)
assert.Equal(t, key, key2)
})
t.Run("Peer", func(t *testing.T) {
err := db.UpdatePeer(p)
require.NoError(t, err)
assert.Equal(t, p, db.Peer(p.ID()))
})
t.Run("LastPing", func(t *testing.T) {
now := time.Now()
err := db.UpdateLastPing(p.ID(), p.Address(), now)
require.NoError(t, err)
assert.Equal(t, now.Unix(), db.LastPing(p.ID(), p.Address()).Unix())
})
t.Run("LastPong", func(t *testing.T) {
now := time.Now()
err := db.UpdateLastPong(p.ID(), p.Address(), now)
require.NoError(t, err)
assert.Equal(t, now.Unix(), db.LastPong(p.ID(), p.Address()).Unix())
})
t.Run("getPeers", func(t *testing.T) {
time.Sleep(time.Second) // let the old peers expire
newPeer := newTestPeer("new")
assert.NoError(t, db.UpdatePeer(newPeer))
assert.NoError(t, db.UpdateLastPong(newPeer.ID(), newPeer.Address(), time.Now()))
peers := db.getPeers(time.Second)
assert.ElementsMatch(t, []*Peer{newPeer}, peers)
})
t.Run("SeedPeers", func(t *testing.T) {
for i := 0; i < seedCount+1; i++ {
p := newTestPeer(fmt.Sprintf("SeedPeers%0d", i))
assert.NoError(t, db.UpdatePeer(p))
assert.NoError(t, db.UpdateLastPong(p.ID(), p.Address(), time.Now()))
}
peers := db.SeedPeers()
assert.EqualValues(t, seedCount, len(peers))
})
t.Run("PersistSeeds", func(t *testing.T) {
for i := 0; i < seedCount+1; i++ {
p := newTestPeer(fmt.Sprintf("PersistSeeds%0d", i))
assert.NoError(t, db.UpdatePeer(p))
assert.NoError(t, db.UpdateLastPong(p.ID(), p.Address(), time.Now()))
}
count := db.PersistSeeds()
assert.EqualValues(t, seedCount, count)
})
}
// Package peertest provides utilities for writing tests with the peer package.
package peertest
import (
"crypto/ed25519"
"log"
"math/rand"
"github.com/iotaledger/goshimmer/packages/autopeering/peer"
"github.com/iotaledger/goshimmer/packages/autopeering/peer/service"
)
func NewPeer(network, address string) *peer.Peer {
services := service.New()
services.Update(service.PeeringKey, network, address)
key := make([]byte, ed25519.PublicKeySize)
copy(key, address)
return peer.NewPeer(key, services)
}
func NewLocal(network, address string, db *peer.DB) *peer.Local {
services := service.New()
services.Update(service.PeeringKey, network, address)
local, err := peer.NewLocal(services, db, randomSeed())
if err != nil {
log.Panic(err)
}
return local
}
func randomSeed() []byte {
seed := make([]byte, ed25519.SeedSize)
rand.Read(seed)
return seed
}
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: peer/proto/peer.proto
package proto
import (
fmt "fmt"
math "math"
proto "github.com/golang/protobuf/proto"
proto1 "github.com/iotaledger/goshimmer/packages/autopeering/peer/service/proto"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
// Minimal encoding of a peer
type Peer struct {
// public key used for signing
PublicKey []byte `protobuf:"bytes,1,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"`
// services supported by the peer
Services *proto1.ServiceMap `protobuf:"bytes,2,opt,name=services,proto3" json:"services,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Peer) Reset() { *m = Peer{} }
func (m *Peer) String() string { return proto.CompactTextString(m) }
func (*Peer) ProtoMessage() {}
func (*Peer) Descriptor() ([]byte, []int) {
return fileDescriptor_155860cd2f47eba7, []int{0}
}
func (m *Peer) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Peer.Unmarshal(m, b)
}
func (m *Peer) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Peer.Marshal(b, m, deterministic)
}
func (m *Peer) XXX_Merge(src proto.Message) {
xxx_messageInfo_Peer.Merge(m, src)
}
func (m *Peer) XXX_Size() int {
return xxx_messageInfo_Peer.Size(m)
}
func (m *Peer) XXX_DiscardUnknown() {
xxx_messageInfo_Peer.DiscardUnknown(m)
}
var xxx_messageInfo_Peer proto.InternalMessageInfo
func (m *Peer) GetPublicKey() []byte {
if m != nil {
return m.PublicKey
}
return nil
}
func (m *Peer) GetServices() *proto1.ServiceMap {
if m != nil {
return m.Services
}
return nil
}
func init() {
proto.RegisterType((*Peer)(nil), "proto.Peer")
}
func init() { proto.RegisterFile("peer/proto/peer.proto", fileDescriptor_155860cd2f47eba7) }
var fileDescriptor_155860cd2f47eba7 = []byte{
// 168 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x12, 0x2d, 0x48, 0x4d, 0x2d,
0xd2, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0xd7, 0x07, 0x31, 0xf5, 0xc0, 0x4c, 0x21, 0x56, 0x30, 0x25,
0xa5, 0x00, 0x96, 0x2d, 0x4e, 0x2d, 0x2a, 0xcb, 0x4c, 0x4e, 0x85, 0xaa, 0x82, 0xf2, 0x20, 0x0a,
0x95, 0x42, 0xb8, 0x58, 0x02, 0x80, 0x6a, 0x84, 0x64, 0xb9, 0xb8, 0x0a, 0x4a, 0x93, 0x72, 0x32,
0x93, 0xe3, 0xb3, 0x53, 0x2b, 0x25, 0x18, 0x15, 0x18, 0x35, 0x78, 0x82, 0x38, 0x21, 0x22, 0xde,
0xa9, 0x95, 0x42, 0xba, 0x5c, 0x1c, 0x50, 0x7d, 0xc5, 0x12, 0x4c, 0x40, 0x49, 0x6e, 0x23, 0x41,
0x88, 0x01, 0x7a, 0xc1, 0x10, 0x61, 0xdf, 0xc4, 0x82, 0x20, 0xb8, 0x12, 0x27, 0xa3, 0x28, 0x83,
0xf4, 0xcc, 0x92, 0x8c, 0xd2, 0x24, 0xbd, 0xe4, 0xfc, 0x5c, 0xfd, 0xcc, 0xfc, 0x92, 0xc4, 0x9c,
0xd4, 0x94, 0x74, 0xa0, 0x53, 0x12, 0x4b, 0x4b, 0xf2, 0x41, 0x6e, 0xca, 0xcc, 0x4b, 0xd7, 0x2d,
0xce, 0xcc, 0xd5, 0x47, 0xb8, 0x3e, 0x89, 0x0d, 0x4c, 0x19, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff,
0x8e, 0xb0, 0x04, 0x1f, 0xd2, 0x00, 0x00, 0x00,
}
syntax = "proto3";
option go_package = "github.com/iotaledger/goshimmer/packages/autopeering/peer/proto";
package proto;
import "peer/service/proto/service.proto";
// Minimal encoding of a peer
message Peer {
// public key used for signing
bytes public_key = 1;
// services supported by the peer
ServiceMap services = 2;
}
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: peer/service/proto/service.proto
package proto
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
math "math"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
// Mapping between a service ID and its tuple network_address
// e.g., map[autopeering:&{tcp, 198.51.100.1:80}]
type ServiceMap struct {
Map map[string]*NetworkAddress `protobuf:"bytes,1,rep,name=map,proto3" json:"map,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ServiceMap) Reset() { *m = ServiceMap{} }
func (m *ServiceMap) String() string { return proto.CompactTextString(m) }
func (*ServiceMap) ProtoMessage() {}
func (*ServiceMap) Descriptor() ([]byte, []int) {
return fileDescriptor_8dd4c5b37d65f758, []int{0}
}
func (m *ServiceMap) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ServiceMap.Unmarshal(m, b)
}
func (m *ServiceMap) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ServiceMap.Marshal(b, m, deterministic)
}
func (m *ServiceMap) XXX_Merge(src proto.Message) {
xxx_messageInfo_ServiceMap.Merge(m, src)
}
func (m *ServiceMap) XXX_Size() int {
return xxx_messageInfo_ServiceMap.Size(m)
}
func (m *ServiceMap) XXX_DiscardUnknown() {
xxx_messageInfo_ServiceMap.DiscardUnknown(m)
}
var xxx_messageInfo_ServiceMap proto.InternalMessageInfo
func (m *ServiceMap) GetMap() map[string]*NetworkAddress {
if m != nil {
return m.Map
}
return nil
}
// The service type (e.g., tcp, upd) and the address (e.g., 198.51.100.1:80)
type NetworkAddress struct {
Network string `protobuf:"bytes,1,opt,name=network,proto3" json:"network,omitempty"`
Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *NetworkAddress) Reset() { *m = NetworkAddress{} }
func (m *NetworkAddress) String() string { return proto.CompactTextString(m) }
func (*NetworkAddress) ProtoMessage() {}
func (*NetworkAddress) Descriptor() ([]byte, []int) {
return fileDescriptor_8dd4c5b37d65f758, []int{1}
}
func (m *NetworkAddress) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_NetworkAddress.Unmarshal(m, b)
}
func (m *NetworkAddress) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_NetworkAddress.Marshal(b, m, deterministic)
}
func (m *NetworkAddress) XXX_Merge(src proto.Message) {
xxx_messageInfo_NetworkAddress.Merge(m, src)
}
func (m *NetworkAddress) XXX_Size() int {
return xxx_messageInfo_NetworkAddress.Size(m)
}
func (m *NetworkAddress) XXX_DiscardUnknown() {
xxx_messageInfo_NetworkAddress.DiscardUnknown(m)
}
var xxx_messageInfo_NetworkAddress proto.InternalMessageInfo
func (m *NetworkAddress) GetNetwork() string {
if m != nil {
return m.Network
}
return ""
}
func (m *NetworkAddress) GetAddress() string {
if m != nil {
return m.Address
}
return ""
}
func init() {
proto.RegisterType((*ServiceMap)(nil), "proto.ServiceMap")
proto.RegisterMapType((map[string]*NetworkAddress)(nil), "proto.ServiceMap.MapEntry")
proto.RegisterType((*NetworkAddress)(nil), "proto.NetworkAddress")
}
func init() { proto.RegisterFile("peer/service/proto/service.proto", fileDescriptor_8dd4c5b37d65f758) }
var fileDescriptor_8dd4c5b37d65f758 = []byte{
// 227 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x52, 0x28, 0x48, 0x4d, 0x2d,
0xd2, 0x2f, 0x4e, 0x2d, 0x2a, 0xcb, 0x4c, 0x4e, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x87, 0xf1,
0xf4, 0xc0, 0x3c, 0x21, 0x56, 0x30, 0xa5, 0xd4, 0xc9, 0xc8, 0xc5, 0x15, 0x0c, 0x91, 0xf0, 0x4d,
0x2c, 0x10, 0xd2, 0xe1, 0x62, 0xce, 0x4d, 0x2c, 0x90, 0x60, 0x54, 0x60, 0xd6, 0xe0, 0x36, 0x92,
0x82, 0x28, 0xd5, 0x43, 0xc8, 0xeb, 0x01, 0xb1, 0x6b, 0x5e, 0x49, 0x51, 0x65, 0x10, 0x48, 0x99,
0x94, 0x2f, 0x17, 0x07, 0x4c, 0x40, 0x48, 0x80, 0x8b, 0x39, 0x3b, 0xb5, 0x12, 0xa8, 0x93, 0x51,
0x83, 0x33, 0x08, 0xc4, 0x14, 0xd2, 0xe6, 0x62, 0x2d, 0x4b, 0xcc, 0x29, 0x4d, 0x95, 0x60, 0x02,
0x8a, 0x71, 0x1b, 0x89, 0x42, 0x4d, 0xf3, 0x4b, 0x2d, 0x29, 0xcf, 0x2f, 0xca, 0x76, 0x4c, 0x49,
0x29, 0x4a, 0x2d, 0x2e, 0x0e, 0x82, 0xa8, 0xb1, 0x62, 0xb2, 0x60, 0x54, 0x72, 0xe1, 0xe2, 0x43,
0x95, 0x14, 0x92, 0xe0, 0x62, 0xcf, 0x83, 0x88, 0x40, 0x0d, 0x86, 0x71, 0x41, 0x32, 0x89, 0x10,
0x45, 0x60, 0xe3, 0x81, 0x32, 0x50, 0xae, 0x93, 0x55, 0x94, 0x45, 0x7a, 0x66, 0x49, 0x46, 0x69,
0x92, 0x5e, 0x72, 0x7e, 0xae, 0x7e, 0x66, 0x7e, 0x49, 0x62, 0x4e, 0x6a, 0x4a, 0x3a, 0x30, 0x34,
0x12, 0x4b, 0x4b, 0xf2, 0x41, 0xc1, 0x92, 0x99, 0x97, 0xae, 0x5b, 0x9c, 0x99, 0xab, 0x8f, 0x19,
0x44, 0x49, 0x6c, 0x60, 0xca, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0x5f, 0xe9, 0x8e, 0x9d, 0x3f,
0x01, 0x00, 0x00,
}
syntax = "proto3";
option go_package = "github.com/iotaledger/goshimmer/packages/autopeering/peer/service/proto";
package proto;
// Mapping between a service ID and its tuple network_address
// e.g., map[autopeering:&{tcp, 198.51.100.1:80}]
message ServiceMap {
map<string, NetworkAddress> map = 1;
}
// The service type (e.g., tcp, upd) and the address (e.g., 198.51.100.1:80)
message NetworkAddress {
string network = 1;
string address = 2;
}
\ No newline at end of file
package service
import (
"fmt"
"net"
"github.com/golang/protobuf/proto"
pb "github.com/iotaledger/goshimmer/packages/autopeering/peer/service/proto"
)
// Record defines the mapping between a service ID and its tuple TypePort
// e.g., map[autopeering:&{TCP, 8000}]
type Record struct {
m map[string]*networkAddress
}
// networkAddress implements net.Addr
type networkAddress struct {
network string
address string
}
// Network returns the service's network name.
func (a *networkAddress) Network() string {
return a.network
}
// String returns the service's address in string form.
func (a *networkAddress) String() string {
return a.address
}
// New initializes and returns an empty new Record
func New() *Record {
return &Record{
m: make(map[string]*networkAddress),
}
}
// Get returns the network end point address of the service with the given name.
func (s *Record) Get(key Key) net.Addr {
val, ok := s.m[string(key)]
if !ok {
return nil
}
return val
}
// CreateRecord creates a modifyable Record from the services.
func (s *Record) CreateRecord() *Record {
result := New()
for k, v := range s.m {
result.m[k] = v
}
return result
}
// Update adds a new service to the map.
func (s *Record) Update(key Key, network string, address string) {
s.m[string(key)] = &networkAddress{
network: network, address: address,
}
}
// String returns a string representation of the service record.
func (s *Record) String() string {
return fmt.Sprintf("%v", s.m)
}
// FromProto creates a Record from the provided protobuf struct.
func FromProto(in *pb.ServiceMap) (*Record, error) {
m := in.GetMap()
if m == nil {
return nil, nil
}
services := New()
for service, addr := range m {
services.m[service] = &networkAddress{
network: addr.GetNetwork(),
address: addr.GetAddress(),
}
}
return services, nil
}
// ToProto returns the corresponding protobuf struct.
func (s *Record) ToProto() *pb.ServiceMap {
if len(s.m) == 0 {
return nil
}
services := make(map[string]*pb.NetworkAddress, len(s.m))
for service, addr := range s.m {
services[service] = &pb.NetworkAddress{
Network: addr.network,
Address: addr.address,
}
}
return &pb.ServiceMap{
Map: services,
}
}
// Marshal serializes a given Peer (p) into a slice of bytes.
func (s *Record) Marshal() ([]byte, error) {
return proto.Marshal(s.ToProto())
}
// Unmarshal de-serializes a given slice of bytes (data) into a Peer.
func Unmarshal(data []byte) (*Record, error) {
s := &pb.ServiceMap{}
if err := proto.Unmarshal(data, s); err != nil {
return nil, err
}
return FromProto(s)
}
package service
import (
"net"
)
// Service is a read-only interface to access services.
type Service interface {
// Get returns the network end point address of the given service or nil if not supported.
Get(Key) net.Addr
// CreateRecord creates a modifyable Record from the services.
CreateRecord() *Record
}
// Key is the type of keys used to look-up a service.
type Key string
const (
// PeeringKey is the key for the auto peering service.
PeeringKey Key = "peering"
// FPCKey is the key for the FPC service.
FPCKey Key = "fpc"
// GossipKey is the key for the gossip service.
GossipKey Key = "gossip"
)
package peer
import (
"sort"
"github.com/iotaledger/goshimmer/packages/autopeering/distance"
)
// PeerDistance defines the relative distance wrt a remote peer
type PeerDistance struct {
Remote *Peer
Distance uint32
}
// byDistance is a slice of PeerDistance used to sort
type byDistance []PeerDistance
func (a byDistance) Len() int { return len(a) }
func (a byDistance) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a byDistance) Less(i, j int) bool { return a[i].Distance < a[j].Distance }
// NewPeerDistance returns a new PeerDistance
func NewPeerDistance(anchorID, salt []byte, remote *Peer) PeerDistance {
return PeerDistance{
Remote: remote,
Distance: distance.BySalt(anchorID, remote.ID().Bytes(), salt),
}
}
// SortBySalt returns a slice of PeerDistance given a list of remote peers
func SortBySalt(anchor, salt []byte, remotePeers []*Peer) (result []PeerDistance) {
result = make(byDistance, len(remotePeers))
for i, remote := range remotePeers {
result[i] = NewPeerDistance(anchor, salt, remote)
}
sort.Sort(byDistance(result))
return result
}
package peer
import (
"crypto/ed25519"
"testing"
"github.com/stretchr/testify/assert"
)
func newTestPeerWithID(ID byte) *Peer {
key := make([]byte, ed25519.PublicKeySize)
key[0] = ID
return NewPeer(key, newTestServiceRecord())
}
func TestOrderedDistanceList(t *testing.T) {
type testCase struct {
anchor []byte
salt []byte
ordered bool
}
tests := []testCase{
{
anchor: []byte("X"),
salt: []byte("salt"),
ordered: true,
},
}
remotePeers := make([]*Peer, 10)
for i := range remotePeers {
remotePeers[i] = newTestPeerWithID(byte(i + 1))
}
for _, test := range tests {
d := SortBySalt(test.anchor, test.salt, remotePeers)
prev := d[0]
for _, next := range d[1:] {
got := prev.Distance < next.Distance
assert.Equal(t, test.ordered, got, "Ordered distance list")
prev = next
}
}
}
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: salt/proto/salt.proto
package proto
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
math "math"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type Salt struct {
// value of the salt
Bytes []byte `protobuf:"bytes,1,opt,name=bytes,proto3" json:"bytes,omitempty"`
// expiration time of the salt
ExpTime uint64 `protobuf:"fixed64,2,opt,name=exp_time,json=expTime,proto3" json:"exp_time,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Salt) Reset() { *m = Salt{} }
func (m *Salt) String() string { return proto.CompactTextString(m) }
func (*Salt) ProtoMessage() {}
func (*Salt) Descriptor() ([]byte, []int) {
return fileDescriptor_d46762658484b01f, []int{0}
}
func (m *Salt) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Salt.Unmarshal(m, b)
}
func (m *Salt) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Salt.Marshal(b, m, deterministic)
}
func (m *Salt) XXX_Merge(src proto.Message) {
xxx_messageInfo_Salt.Merge(m, src)
}
func (m *Salt) XXX_Size() int {
return xxx_messageInfo_Salt.Size(m)
}
func (m *Salt) XXX_DiscardUnknown() {
xxx_messageInfo_Salt.DiscardUnknown(m)
}
var xxx_messageInfo_Salt proto.InternalMessageInfo
func (m *Salt) GetBytes() []byte {
if m != nil {
return m.Bytes
}
return nil
}
func (m *Salt) GetExpTime() uint64 {
if m != nil {
return m.ExpTime
}
return 0
}
func init() {
proto.RegisterType((*Salt)(nil), "proto.Salt")
}
func init() { proto.RegisterFile("salt/proto/salt.proto", fileDescriptor_d46762658484b01f) }
var fileDescriptor_d46762658484b01f = []byte{
// 142 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x12, 0x2d, 0x4e, 0xcc, 0x29,
0xd1, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0xd7, 0x07, 0x31, 0xf5, 0xc0, 0x4c, 0x21, 0x56, 0x30, 0xa5,
0x64, 0xce, 0xc5, 0x12, 0x0c, 0x14, 0x14, 0x12, 0xe1, 0x62, 0x4d, 0xaa, 0x2c, 0x49, 0x2d, 0x96,
0x60, 0x54, 0x60, 0xd4, 0xe0, 0x09, 0x82, 0x70, 0x84, 0x24, 0xb9, 0x38, 0x52, 0x2b, 0x0a, 0xe2,
0x4b, 0x32, 0x73, 0x53, 0x25, 0x98, 0x80, 0x12, 0x6c, 0x41, 0xec, 0x40, 0x7e, 0x08, 0x90, 0xeb,
0x64, 0x14, 0x65, 0x90, 0x9e, 0x59, 0x92, 0x51, 0x9a, 0xa4, 0x97, 0x9c, 0x9f, 0xab, 0x9f, 0x99,
0x5f, 0x92, 0x98, 0x93, 0x9a, 0x92, 0x9e, 0x5a, 0xa4, 0x9f, 0x58, 0x5a, 0x92, 0x5f, 0x90, 0x9a,
0x5a, 0x94, 0x99, 0x97, 0xae, 0x5b, 0x9c, 0x99, 0xab, 0x8f, 0xb0, 0x3e, 0x89, 0x0d, 0x4c, 0x19,
0x03, 0x02, 0x00, 0x00, 0xff, 0xff, 0x47, 0x07, 0x7d, 0x5f, 0x93, 0x00, 0x00, 0x00,
}
syntax = "proto3";
option go_package = "github.com/iotaledger/goshimmer/packages/autopeering/salt/proto";
package proto;
message Salt {
// value of the salt
bytes bytes = 1;
// expiration time of the salt
fixed64 exp_time = 2;
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment