mirror of
https://github.com/QuilibriumNetwork/ceremonyclient.git
synced 2026-02-21 10:27:26 +08:00
v2.1.0.11, the later half
This commit is contained in:
parent
8fb97c355f
commit
4a382fc12a
@ -41,6 +41,7 @@ import (
|
||||
"source.quilibrium.com/quilibrium/monorepo/node/dispatch"
|
||||
"source.quilibrium.com/quilibrium/monorepo/node/execution/manager"
|
||||
hgstate "source.quilibrium.com/quilibrium/monorepo/node/execution/state/hypergraph"
|
||||
keyedaggregator "source.quilibrium.com/quilibrium/monorepo/node/keyedaggregator"
|
||||
"source.quilibrium.com/quilibrium/monorepo/node/keys"
|
||||
"source.quilibrium.com/quilibrium/monorepo/node/p2p"
|
||||
"source.quilibrium.com/quilibrium/monorepo/node/p2p/onion"
|
||||
@ -107,8 +108,10 @@ type AppConsensusEngine struct {
|
||||
peerInfoManager tp2p.PeerInfoManager
|
||||
currentDifficulty uint32
|
||||
currentDifficultyMu sync.RWMutex
|
||||
pendingMessages []*protobufs.Message
|
||||
pendingMessagesMu sync.RWMutex
|
||||
messageCollectors *keyedaggregator.SequencedCollectors[sequencedAppMessage]
|
||||
messageAggregator *keyedaggregator.SequencedAggregator[sequencedAppMessage]
|
||||
lastProposalRank uint64
|
||||
lastProposalRankMu sync.RWMutex
|
||||
collectedMessages []*protobufs.Message
|
||||
collectedMessagesMu sync.RWMutex
|
||||
provingMessages []*protobufs.Message
|
||||
@ -256,7 +259,6 @@ func NewAppConsensusEngine(
|
||||
proposalCache: make(map[uint64]*protobufs.AppShardProposal),
|
||||
pendingCertifiedParents: make(map[uint64]*protobufs.AppShardProposal),
|
||||
proofCache: make(map[uint64][516]byte),
|
||||
pendingMessages: []*protobufs.Message{},
|
||||
collectedMessages: []*protobufs.Message{},
|
||||
provingMessages: []*protobufs.Message{},
|
||||
consensusMessageQueue: make(chan *pb.Message, 1000),
|
||||
@ -457,11 +459,16 @@ func NewAppConsensusEngine(
|
||||
executorsRegistered.WithLabelValues(engine.appAddressHex).Set(0)
|
||||
pendingMessagesCount.WithLabelValues(engine.appAddressHex).Set(0)
|
||||
|
||||
if err := engine.initAppMessageAggregator(); err != nil {
|
||||
return nil, errors.Wrap(err, "new app consensus engine")
|
||||
}
|
||||
|
||||
componentBuilder := lifecycle.NewComponentManagerBuilder()
|
||||
// Add execution engines
|
||||
componentBuilder.AddWorker(engine.executionManager.Start)
|
||||
componentBuilder.AddWorker(engine.eventDistributor.Start)
|
||||
componentBuilder.AddWorker(engine.appTimeReel.Start)
|
||||
componentBuilder.AddWorker(engine.startAppMessageAggregator)
|
||||
|
||||
latest, err := engine.consensusStore.GetConsensusState(engine.appAddress)
|
||||
var state *models.CertifiedState[*protobufs.AppShardFrame]
|
||||
@ -518,6 +525,8 @@ func NewAppConsensusEngine(
|
||||
}
|
||||
pending = engine.getPendingProposals(frame.Header.FrameNumber)
|
||||
}
|
||||
|
||||
engine.recordProposalRank(state.Rank())
|
||||
liveness, err := engine.consensusStore.GetLivenessState(appAddress)
|
||||
if err == nil {
|
||||
engine.currentRank = liveness.CurrentRank
|
||||
|
||||
@ -2,10 +2,10 @@ package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"slices"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"go.uber.org/zap"
|
||||
keyedaggregator "source.quilibrium.com/quilibrium/monorepo/node/keyedaggregator"
|
||||
"source.quilibrium.com/quilibrium/monorepo/protobufs"
|
||||
)
|
||||
|
||||
@ -42,23 +42,62 @@ func (p *AppLivenessProvider) Collect(
|
||||
mixnetMessages = p.engine.mixnet.GetMessages()
|
||||
}
|
||||
|
||||
finalizedMessages := []*protobufs.Message{}
|
||||
|
||||
// Get and clear pending messages
|
||||
p.engine.pendingMessagesMu.Lock()
|
||||
pendingMessages := p.engine.pendingMessages
|
||||
p.engine.pendingMessages = []*protobufs.Message{}
|
||||
p.engine.pendingMessagesMu.Unlock()
|
||||
var collectorRecords []*sequencedAppMessage
|
||||
var collector keyedaggregator.Collector[sequencedAppMessage]
|
||||
if p.engine.messageCollectors != nil {
|
||||
var err error
|
||||
var found bool
|
||||
collector, found, err = p.engine.getAppMessageCollector(rank)
|
||||
if err != nil && !errors.Is(err, keyedaggregator.ErrSequenceBelowRetention) {
|
||||
p.engine.logger.Warn(
|
||||
"could not fetch collector for rank",
|
||||
zap.Uint64("rank", rank),
|
||||
zap.Error(err),
|
||||
)
|
||||
} else if found {
|
||||
collectorRecords = collector.Records()
|
||||
}
|
||||
}
|
||||
|
||||
txMap := map[string][][]byte{}
|
||||
for i, message := range slices.Concat(mixnetMessages, pendingMessages) {
|
||||
finalizedMessages := make(
|
||||
[]*protobufs.Message,
|
||||
0,
|
||||
len(collectorRecords)+len(mixnetMessages),
|
||||
)
|
||||
|
||||
for _, record := range collectorRecords {
|
||||
if record == nil || record.message == nil {
|
||||
continue
|
||||
}
|
||||
lockedAddrs, err := p.engine.executionManager.Lock(
|
||||
record.frameNumber,
|
||||
record.message.Address,
|
||||
record.message.Payload,
|
||||
)
|
||||
if err != nil {
|
||||
p.engine.logger.Debug(
|
||||
"message failed lock",
|
||||
zap.Uint64("rank", rank),
|
||||
zap.Error(err),
|
||||
)
|
||||
if collector != nil {
|
||||
collector.Remove(record)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
txMap[string(record.message.Hash)] = lockedAddrs
|
||||
finalizedMessages = append(finalizedMessages, record.message)
|
||||
}
|
||||
|
||||
for i, message := range mixnetMessages {
|
||||
lockedAddrs, err := p.validateAndLockMessage(frameNumber, i, message)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
txMap[string(message.Hash)] = lockedAddrs
|
||||
|
||||
finalizedMessages = append(finalizedMessages, message)
|
||||
}
|
||||
|
||||
@ -71,7 +110,7 @@ func (p *AppLivenessProvider) Collect(
|
||||
"collected messages",
|
||||
zap.Int(
|
||||
"total_message_count",
|
||||
len(mixnetMessages)+len(pendingMessages),
|
||||
len(mixnetMessages)+len(collectorRecords),
|
||||
),
|
||||
zap.Int("valid_message_count", len(finalizedMessages)),
|
||||
zap.Uint64(
|
||||
@ -89,6 +128,11 @@ func (p *AppLivenessProvider) Collect(
|
||||
return CollectedCommitments{}, errors.Wrap(err, "collect")
|
||||
}
|
||||
|
||||
if p.engine.messageAggregator != nil {
|
||||
p.engine.messageAggregator.OnSequenceChange(rank, rank+1)
|
||||
}
|
||||
pendingMessagesCount.WithLabelValues(p.engine.appAddressHex).Set(0)
|
||||
|
||||
p.engine.collectedMessagesMu.Lock()
|
||||
p.engine.collectedMessages = finalizedMessages
|
||||
p.engine.collectedMessagesMu.Unlock()
|
||||
|
||||
269
node/consensus/app/message_collector.go
Normal file
269
node/consensus/app/message_collector.go
Normal file
@ -0,0 +1,269 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/crypto/sha3"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"source.quilibrium.com/quilibrium/monorepo/consensus/models"
|
||||
"source.quilibrium.com/quilibrium/monorepo/lifecycle"
|
||||
"source.quilibrium.com/quilibrium/monorepo/node/consensus/tracing"
|
||||
keyedaggregator "source.quilibrium.com/quilibrium/monorepo/node/keyedaggregator"
|
||||
keyedcollector "source.quilibrium.com/quilibrium/monorepo/node/keyedcollector"
|
||||
"source.quilibrium.com/quilibrium/monorepo/protobufs"
|
||||
)
|
||||
|
||||
const maxAppMessagesPerRank = 100
|
||||
|
||||
type sequencedAppMessage struct {
|
||||
rank uint64
|
||||
frameNumber uint64
|
||||
identity models.Identity
|
||||
message *protobufs.Message
|
||||
}
|
||||
|
||||
func newSequencedAppMessage(
|
||||
rank uint64,
|
||||
message *protobufs.Message,
|
||||
) *sequencedAppMessage {
|
||||
if message == nil {
|
||||
return nil
|
||||
}
|
||||
cloned := proto.Clone(message).(*protobufs.Message)
|
||||
return &sequencedAppMessage{
|
||||
rank: rank,
|
||||
identity: models.Identity(string(cloned.Hash)),
|
||||
message: cloned,
|
||||
}
|
||||
}
|
||||
|
||||
var appMessageTraits = keyedcollector.RecordTraits[sequencedAppMessage]{
|
||||
Sequence: func(m *sequencedAppMessage) uint64 {
|
||||
if m == nil {
|
||||
return 0
|
||||
}
|
||||
return m.rank
|
||||
},
|
||||
Identity: func(m *sequencedAppMessage) models.Identity {
|
||||
if m == nil {
|
||||
return ""
|
||||
}
|
||||
return m.identity
|
||||
},
|
||||
Equals: func(a, b *sequencedAppMessage) bool {
|
||||
if a == nil || b == nil {
|
||||
return a == b
|
||||
}
|
||||
return string(a.identity) == string(b.identity)
|
||||
},
|
||||
}
|
||||
|
||||
type appMessageProcessorFactory struct {
|
||||
engine *AppConsensusEngine
|
||||
}
|
||||
|
||||
func (f *appMessageProcessorFactory) Create(
|
||||
sequence uint64,
|
||||
) (keyedcollector.Processor[sequencedAppMessage], error) {
|
||||
return &appMessageProcessor{
|
||||
engine: f.engine,
|
||||
rank: sequence,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type appMessageProcessor struct {
|
||||
engine *AppConsensusEngine
|
||||
rank uint64
|
||||
}
|
||||
|
||||
func (p *appMessageProcessor) Process(
|
||||
record *sequencedAppMessage,
|
||||
) error {
|
||||
if record == nil || record.message == nil {
|
||||
return keyedcollector.NewInvalidRecordError(
|
||||
record,
|
||||
fmt.Errorf("nil app message"),
|
||||
)
|
||||
}
|
||||
|
||||
if err := p.enforceCollectorLimit(record); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
frameNumber, err := p.frameNumberForRank()
|
||||
if err != nil {
|
||||
return keyedcollector.NewInvalidRecordError(record, err)
|
||||
}
|
||||
|
||||
if err := p.engine.executionManager.ValidateMessage(
|
||||
frameNumber,
|
||||
record.message.Address,
|
||||
record.message.Payload,
|
||||
); err != nil {
|
||||
return keyedcollector.NewInvalidRecordError(record, err)
|
||||
}
|
||||
|
||||
record.frameNumber = frameNumber
|
||||
p.engine.updatePendingMessagesGauge(p.rank)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *appMessageProcessor) frameNumberForRank() (uint64, error) {
|
||||
rank := p.rank
|
||||
if rank == 0 {
|
||||
rank = 1
|
||||
}
|
||||
qc, err := p.engine.clockStore.GetQuorumCertificate(
|
||||
p.engine.appAddress,
|
||||
rank-1,
|
||||
)
|
||||
if err != nil {
|
||||
qc, err = p.engine.clockStore.GetLatestQuorumCertificate(
|
||||
p.engine.appAddress,
|
||||
)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
return qc.GetFrameNumber() + 1, nil
|
||||
}
|
||||
|
||||
func (p *appMessageProcessor) enforceCollectorLimit(
|
||||
record *sequencedAppMessage,
|
||||
) error {
|
||||
collector, found, err := p.engine.getAppMessageCollector(p.rank)
|
||||
if err != nil || !found {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(collector.Records()) >= maxAppMessagesPerRank {
|
||||
collector.Remove(record)
|
||||
return keyedcollector.NewInvalidRecordError(
|
||||
record,
|
||||
fmt.Errorf("message limit reached for rank %d", p.rank),
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *AppConsensusEngine) initAppMessageAggregator() error {
|
||||
tracer := tracing.NewZapTracer(e.logger.Named("app_message_collector"))
|
||||
processorFactory := &appMessageProcessorFactory{engine: e}
|
||||
collectorFactory, err := keyedcollector.NewFactory(
|
||||
tracer,
|
||||
appMessageTraits,
|
||||
nil,
|
||||
processorFactory,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
e.messageCollectors = keyedaggregator.NewSequencedCollectors[sequencedAppMessage](
|
||||
tracer,
|
||||
0,
|
||||
collectorFactory,
|
||||
)
|
||||
|
||||
aggregator, err := keyedaggregator.NewSequencedAggregator[sequencedAppMessage](
|
||||
tracer,
|
||||
0,
|
||||
e.messageCollectors,
|
||||
func(m *sequencedAppMessage) uint64 {
|
||||
if m == nil {
|
||||
return 0
|
||||
}
|
||||
return m.rank
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
e.messageAggregator = aggregator
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *AppConsensusEngine) startAppMessageAggregator(
|
||||
ctx lifecycle.SignalerContext,
|
||||
ready lifecycle.ReadyFunc,
|
||||
) {
|
||||
if e.messageAggregator == nil {
|
||||
ready()
|
||||
<-ctx.Done()
|
||||
return
|
||||
}
|
||||
|
||||
go func() {
|
||||
if err := e.messageAggregator.ComponentManager.Start(ctx); err != nil {
|
||||
ctx.Throw(err)
|
||||
}
|
||||
}()
|
||||
|
||||
<-e.messageAggregator.ComponentManager.Ready()
|
||||
ready()
|
||||
<-e.messageAggregator.ComponentManager.Done()
|
||||
}
|
||||
|
||||
func (e *AppConsensusEngine) addAppMessage(message *protobufs.Message) {
|
||||
if e.messageAggregator == nil || message == nil {
|
||||
return
|
||||
}
|
||||
if len(message.Hash) == 0 {
|
||||
hash := sha3.Sum256(message.Payload)
|
||||
message.Hash = hash[:]
|
||||
}
|
||||
rank := e.nextRank()
|
||||
record := newSequencedAppMessage(rank, message)
|
||||
if record == nil {
|
||||
return
|
||||
}
|
||||
e.messageAggregator.Add(record)
|
||||
}
|
||||
|
||||
func (e *AppConsensusEngine) nextRank() uint64 {
|
||||
e.lastProposalRankMu.RLock()
|
||||
last := e.lastProposalRank
|
||||
e.lastProposalRankMu.RUnlock()
|
||||
if last > 0 {
|
||||
return last + 1
|
||||
}
|
||||
return e.currentRank + 1
|
||||
}
|
||||
|
||||
func (e *AppConsensusEngine) getAppMessageCollector(
|
||||
rank uint64,
|
||||
) (keyedaggregator.Collector[sequencedAppMessage], bool, error) {
|
||||
if e.messageCollectors == nil {
|
||||
return nil, false, nil
|
||||
}
|
||||
return e.messageCollectors.GetCollector(rank)
|
||||
}
|
||||
|
||||
func (e *AppConsensusEngine) recordProposalRank(rank uint64) {
|
||||
if rank == 0 {
|
||||
return
|
||||
}
|
||||
e.lastProposalRankMu.Lock()
|
||||
if rank > e.lastProposalRank {
|
||||
e.lastProposalRank = rank
|
||||
}
|
||||
e.lastProposalRankMu.Unlock()
|
||||
}
|
||||
|
||||
func (e *AppConsensusEngine) updatePendingMessagesGauge(rank uint64) {
|
||||
if e.messageCollectors == nil {
|
||||
return
|
||||
}
|
||||
collector, found, err := e.getAppMessageCollector(rank)
|
||||
if err != nil || !found {
|
||||
return
|
||||
}
|
||||
pendingMessagesCount.WithLabelValues(e.appAddressHex).Set(
|
||||
float64(len(collector.Records())),
|
||||
)
|
||||
}
|
||||
@ -5,6 +5,7 @@ import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"slices"
|
||||
|
||||
"github.com/iden3/go-iden3-crypto/poseidon"
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
@ -376,6 +377,10 @@ func (e *AppConsensusEngine) processProposal(
|
||||
e.trySealParentWithChild(proposal)
|
||||
e.registerPendingCertifiedParent(proposal)
|
||||
|
||||
if proposal.State != nil {
|
||||
e.recordProposalRank(proposal.State.GetRank())
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@ -714,17 +719,12 @@ func (e *AppConsensusEngine) handleProverMessage(message *pb.Message) {
|
||||
)
|
||||
switch typePrefix {
|
||||
case protobufs.MessageBundleType:
|
||||
// MessageBundle messages need to be collected for execution
|
||||
// Store them in pendingMessages to be processed during Collect
|
||||
hash := sha3.Sum256(message.Data)
|
||||
e.pendingMessagesMu.Lock()
|
||||
e.pendingMessages = append(e.pendingMessages, &protobufs.Message{
|
||||
e.addAppMessage(&protobufs.Message{
|
||||
Address: e.appAddress[:32],
|
||||
Hash: hash[:],
|
||||
Payload: message.Data,
|
||||
Payload: slices.Clone(message.Data),
|
||||
})
|
||||
e.pendingMessagesMu.Unlock()
|
||||
|
||||
e.logger.Debug(
|
||||
"collected app request for execution",
|
||||
zap.Uint32("type", typePrefix),
|
||||
|
||||
@ -145,15 +145,6 @@ func (p *GlobalLeaderProvider) ProveNextState(
|
||||
)
|
||||
}
|
||||
|
||||
_, err = p.engine.livenessProvider.Collect(
|
||||
ctx,
|
||||
prior.Header.FrameNumber+1,
|
||||
rank,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, models.NewNoVoteErrorf("could not collect: %+w", err)
|
||||
}
|
||||
|
||||
timer := prometheus.NewTimer(frameProvingDuration)
|
||||
defer timer.ObserveDuration()
|
||||
|
||||
|
||||
@ -10,6 +10,7 @@ import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"go.uber.org/zap"
|
||||
"golang.org/x/crypto/sha3"
|
||||
keyedaggregator "source.quilibrium.com/quilibrium/monorepo/node/keyedaggregator"
|
||||
"source.quilibrium.com/quilibrium/monorepo/protobufs"
|
||||
"source.quilibrium.com/quilibrium/monorepo/types/tries"
|
||||
)
|
||||
@ -42,37 +43,55 @@ func (p *GlobalLivenessProvider) Collect(
|
||||
mixnetMessages = p.engine.mixnet.GetMessages()
|
||||
}
|
||||
|
||||
// Get and clear pending prover messages
|
||||
p.engine.pendingMessagesMu.Lock()
|
||||
pendingMessages := p.engine.pendingMessages
|
||||
p.engine.pendingMessages = [][]byte{}
|
||||
p.engine.pendingMessagesMu.Unlock()
|
||||
|
||||
// Convert pending messages to protobuf.Message format
|
||||
globalAddress := make([]byte, 32)
|
||||
for i := range globalAddress {
|
||||
globalAddress[i] = 0xff
|
||||
var collector keyedaggregator.Collector[sequencedGlobalMessage]
|
||||
var collectorRecords []*sequencedGlobalMessage
|
||||
if p.engine.messageCollectors != nil {
|
||||
var err error
|
||||
var found bool
|
||||
collector, found, err = p.engine.getMessageCollector(rank)
|
||||
if err != nil && !errors.Is(err, keyedaggregator.ErrSequenceBelowRetention) {
|
||||
p.engine.logger.Warn(
|
||||
"could not fetch collector for rank",
|
||||
zap.Uint64("rank", rank),
|
||||
zap.Error(err),
|
||||
)
|
||||
} else if found {
|
||||
collectorRecords = collector.Records()
|
||||
}
|
||||
}
|
||||
|
||||
messages := make(
|
||||
acceptedMessages := make(
|
||||
[]*protobufs.Message,
|
||||
0,
|
||||
len(mixnetMessages)+len(pendingMessages),
|
||||
len(collectorRecords)+len(mixnetMessages),
|
||||
)
|
||||
messages = append(messages, mixnetMessages...)
|
||||
|
||||
for _, msgData := range pendingMessages {
|
||||
messages = append(messages, &protobufs.Message{
|
||||
Address: globalAddress,
|
||||
Payload: msgData,
|
||||
})
|
||||
if collector != nil {
|
||||
for _, record := range collectorRecords {
|
||||
if record == nil || record.message == nil {
|
||||
continue
|
||||
}
|
||||
if err := p.lockCollectorMessage(
|
||||
frameNumber,
|
||||
record.message,
|
||||
); err != nil {
|
||||
p.engine.logger.Debug(
|
||||
"message failed lock",
|
||||
zap.Uint64("frame_number", frameNumber),
|
||||
zap.Error(err),
|
||||
)
|
||||
collector.Remove(record)
|
||||
continue
|
||||
}
|
||||
acceptedMessages = append(acceptedMessages, record.message)
|
||||
}
|
||||
}
|
||||
|
||||
acceptedMessages := []*protobufs.Message{}
|
||||
messages := append([]*protobufs.Message{}, mixnetMessages...)
|
||||
|
||||
p.engine.logger.Debug(
|
||||
"collected messages, validating",
|
||||
zap.Int("message_count", len(messages)),
|
||||
zap.Int("message_count", len(messages)+len(collectorRecords)),
|
||||
)
|
||||
|
||||
for i, message := range messages {
|
||||
@ -84,6 +103,10 @@ func (p *GlobalLivenessProvider) Collect(
|
||||
acceptedMessages = append(acceptedMessages, message)
|
||||
}
|
||||
|
||||
if p.engine.messageAggregator != nil {
|
||||
p.engine.messageAggregator.OnSequenceChange(rank, rank+1)
|
||||
}
|
||||
|
||||
err := p.engine.executionManager.Unlock()
|
||||
if err != nil {
|
||||
p.engine.logger.Error(
|
||||
@ -232,3 +255,18 @@ func (p *GlobalLivenessProvider) validateAndLockMessage(
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *GlobalLivenessProvider) lockCollectorMessage(
|
||||
frameNumber uint64,
|
||||
message *protobufs.Message,
|
||||
) error {
|
||||
if message == nil {
|
||||
return errors.New("nil message")
|
||||
}
|
||||
_, err := p.engine.executionManager.Lock(
|
||||
frameNumber,
|
||||
message.Address,
|
||||
message.Payload,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
pcrypto "github.com/libp2p/go-libp2p/core/crypto"
|
||||
@ -515,6 +516,7 @@ func (e *GlobalConsensusEngine) evaluateForProposals(
|
||||
shardsPaused := 0
|
||||
logicalShards := 0
|
||||
shardDivisions := 0
|
||||
awaitingFrame := map[uint64]struct{}{}
|
||||
for _, info := range shards {
|
||||
resp, err := e.getAppShardsFromProver(
|
||||
client,
|
||||
@ -547,6 +549,7 @@ func (e *GlobalConsensusEngine) evaluateForProposals(
|
||||
allocated = allocation.Status != 4
|
||||
if allocation.Status == typesconsensus.ProverStatusJoining {
|
||||
shardsPending++
|
||||
awaitingFrame[allocation.JoinFrameNumber+360] = struct{}{}
|
||||
}
|
||||
if allocation.Status == typesconsensus.ProverStatusActive {
|
||||
shardsActive++
|
||||
@ -607,9 +610,15 @@ func (e *GlobalConsensusEngine) evaluateForProposals(
|
||||
}
|
||||
}
|
||||
|
||||
awaitingFrames := []string{}
|
||||
for frame := range awaitingFrame {
|
||||
awaitingFrames = append(awaitingFrames, fmt.Sprintf("%d", frame))
|
||||
}
|
||||
|
||||
e.logger.Info(
|
||||
"status for allocations",
|
||||
zap.Int("pending_joins", shardsPending),
|
||||
zap.String("pending_join_frames", strings.Join(awaitingFrames, ", ")),
|
||||
zap.Int("pending_leaves", shardsLeaving),
|
||||
zap.Int("active", shardsActive),
|
||||
zap.Int("paused", shardsPaused),
|
||||
@ -621,7 +630,7 @@ func (e *GlobalConsensusEngine) evaluateForProposals(
|
||||
proposals, err := e.proposer.PlanAndAllocate(
|
||||
uint64(data.Frame.Header.Difficulty),
|
||||
proposalDescriptors,
|
||||
0,
|
||||
100,
|
||||
worldBytes,
|
||||
)
|
||||
if err != nil {
|
||||
|
||||
@ -386,7 +386,14 @@ func (e *GlobalConsensusEngine) createStubGenesis() *protobufs.GlobalFrame {
|
||||
)
|
||||
|
||||
for _, prover := range proverPubKeys {
|
||||
addrbi, err := poseidon.HashBytes(prover)
|
||||
proverAddrBI, err := poseidon.HashBytes(prover)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
addrbi, err := poseidon.HashBytes(slices.Concat(
|
||||
token.QUIL_TOKEN_ADDRESS,
|
||||
proverAddrBI.FillBytes(make([]byte, 32)),
|
||||
))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -396,7 +403,7 @@ func (e *GlobalConsensusEngine) createStubGenesis() *protobufs.GlobalFrame {
|
||||
|
||||
err = rdfMultiprover.Set(
|
||||
globalintrinsics.GLOBAL_RDF_SCHEMA,
|
||||
token.QUIL_TOKEN_ADDRESS,
|
||||
intrinsics.GLOBAL_INTRINSIC_ADDRESS[:],
|
||||
"reward:ProverReward",
|
||||
"DelegateAddress",
|
||||
addrbi.FillBytes(make([]byte, 32)),
|
||||
@ -412,7 +419,7 @@ func (e *GlobalConsensusEngine) createStubGenesis() *protobufs.GlobalFrame {
|
||||
balance = balanceBI.FillBytes(balance)
|
||||
err = rdfMultiprover.Set(
|
||||
globalintrinsics.GLOBAL_RDF_SCHEMA,
|
||||
token.QUIL_TOKEN_ADDRESS,
|
||||
intrinsics.GLOBAL_INTRINSIC_ADDRESS[:],
|
||||
"reward:ProverReward",
|
||||
"Balance",
|
||||
balance,
|
||||
|
||||
@ -22,6 +22,7 @@ import (
|
||||
"github.com/mr-tron/base58"
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/shopspring/decimal"
|
||||
"go.uber.org/zap"
|
||||
"golang.org/x/crypto/sha3"
|
||||
"golang.org/x/sync/errgroup"
|
||||
@ -46,9 +47,11 @@ import (
|
||||
"source.quilibrium.com/quilibrium/monorepo/node/dispatch"
|
||||
"source.quilibrium.com/quilibrium/monorepo/node/execution/intrinsics/global"
|
||||
"source.quilibrium.com/quilibrium/monorepo/node/execution/intrinsics/global/compat"
|
||||
tokenintrinsics "source.quilibrium.com/quilibrium/monorepo/node/execution/intrinsics/token"
|
||||
"source.quilibrium.com/quilibrium/monorepo/node/execution/manager"
|
||||
hgstate "source.quilibrium.com/quilibrium/monorepo/node/execution/state/hypergraph"
|
||||
qgrpc "source.quilibrium.com/quilibrium/monorepo/node/internal/grpc"
|
||||
keyedaggregator "source.quilibrium.com/quilibrium/monorepo/node/keyedaggregator"
|
||||
"source.quilibrium.com/quilibrium/monorepo/node/keys"
|
||||
"source.quilibrium.com/quilibrium/monorepo/node/p2p"
|
||||
"source.quilibrium.com/quilibrium/monorepo/node/p2p/onion"
|
||||
@ -154,8 +157,8 @@ type GlobalConsensusEngine struct {
|
||||
minimumProvers func() uint64
|
||||
blacklistMap map[string]bool
|
||||
blacklistMu sync.RWMutex
|
||||
pendingMessages [][]byte
|
||||
pendingMessagesMu sync.RWMutex
|
||||
messageCollectors *keyedaggregator.SequencedCollectors[sequencedGlobalMessage]
|
||||
messageAggregator *keyedaggregator.SequencedAggregator[sequencedGlobalMessage]
|
||||
currentDifficulty uint32
|
||||
currentDifficultyMu sync.RWMutex
|
||||
lastProvenFrameTime time.Time
|
||||
@ -296,7 +299,6 @@ func NewGlobalConsensusEngine(
|
||||
currentDifficulty: config.Engine.Difficulty,
|
||||
lastProvenFrameTime: time.Now(),
|
||||
blacklistMap: make(map[string]bool),
|
||||
pendingMessages: [][]byte{},
|
||||
peerInfoDigestCache: make(map[string]struct{}),
|
||||
keyRegistryDigestCache: make(map[string]struct{}),
|
||||
peerAuthCache: make(map[string]time.Time),
|
||||
@ -305,6 +307,10 @@ func NewGlobalConsensusEngine(
|
||||
appShardCache: make(map[string]*appShardCacheEntry),
|
||||
}
|
||||
|
||||
if err := engine.initGlobalMessageAggregator(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if config.Engine.AlertKey != "" {
|
||||
alertPublicKey, err := hex.DecodeString(config.Engine.AlertKey)
|
||||
if err != nil {
|
||||
@ -521,6 +527,7 @@ func NewGlobalConsensusEngine(
|
||||
// Add execution engines
|
||||
componentBuilder.AddWorker(engine.executionManager.Start)
|
||||
componentBuilder.AddWorker(engine.globalTimeReel.Start)
|
||||
componentBuilder.AddWorker(engine.startGlobalMessageAggregator)
|
||||
|
||||
if engine.config.P2P.Network == 99 || engine.config.Engine.ArchiveMode {
|
||||
latest, err := engine.consensusStore.GetConsensusState(nil)
|
||||
@ -846,6 +853,16 @@ func NewGlobalConsensusEngine(
|
||||
engine.updateMetrics(ctx)
|
||||
})
|
||||
|
||||
if !engine.config.Engine.ArchiveMode {
|
||||
componentBuilder.AddWorker(func(
|
||||
ctx lifecycle.SignalerContext,
|
||||
ready lifecycle.ReadyFunc,
|
||||
) {
|
||||
ready()
|
||||
engine.monitorNodeHealth(ctx)
|
||||
})
|
||||
}
|
||||
|
||||
// Start periodic tx lock pruning
|
||||
componentBuilder.AddWorker(func(
|
||||
ctx lifecycle.SignalerContext,
|
||||
@ -2037,6 +2054,177 @@ func (e *GlobalConsensusEngine) pruneTxLocks() {
|
||||
}
|
||||
}
|
||||
|
||||
func (e *GlobalConsensusEngine) monitorNodeHealth(
|
||||
ctx lifecycle.SignalerContext,
|
||||
) {
|
||||
ticker := time.NewTicker(time.Minute)
|
||||
defer ticker.Stop()
|
||||
|
||||
e.runNodeHealthCheck()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-ticker.C:
|
||||
e.runNodeHealthCheck()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (e *GlobalConsensusEngine) runNodeHealthCheck() {
|
||||
if e.workerManager == nil {
|
||||
return
|
||||
}
|
||||
|
||||
workers, err := e.workerManager.RangeWorkers()
|
||||
if err != nil {
|
||||
e.logger.Warn("node health check failed to load workers", zap.Error(err))
|
||||
return
|
||||
}
|
||||
|
||||
allocated := 0
|
||||
for _, worker := range workers {
|
||||
if worker.Allocated {
|
||||
allocated++
|
||||
}
|
||||
}
|
||||
|
||||
baseFields := []zap.Field{
|
||||
zap.Int("total_workers", len(workers)),
|
||||
zap.Int("allocated_workers", allocated),
|
||||
}
|
||||
|
||||
unreachable, err := e.workerManager.CheckWorkersConnected()
|
||||
if err != nil {
|
||||
e.logger.Warn(
|
||||
"node health check could not verify worker connectivity",
|
||||
append(baseFields, zap.Error(err))...,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
if len(unreachable) != 0 {
|
||||
unreachable64 := make([]uint64, len(unreachable))
|
||||
for i, id := range unreachable {
|
||||
unreachable64[i] = uint64(id)
|
||||
}
|
||||
e.logger.Warn(
|
||||
"workers unreachable",
|
||||
append(
|
||||
baseFields,
|
||||
zap.Uint64s("unreachable_workers", unreachable64),
|
||||
)...,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
headFrame, err := e.globalTimeReel.GetHead()
|
||||
if err != nil {
|
||||
e.logger.Warn(
|
||||
"global head not yet available",
|
||||
append(baseFields, zap.Error(err))...,
|
||||
)
|
||||
return
|
||||
}
|
||||
if headFrame == nil || headFrame.Header == nil {
|
||||
e.logger.Warn("global head not yet available", baseFields...)
|
||||
return
|
||||
}
|
||||
|
||||
headTime := time.UnixMilli(headFrame.Header.Timestamp)
|
||||
if time.Since(headTime) > time.Minute {
|
||||
e.logger.Warn(
|
||||
"latest frame is older than 60 seconds; node may still be synchronizing",
|
||||
append(
|
||||
baseFields,
|
||||
zap.Uint64("head_frame_number", headFrame.Header.FrameNumber),
|
||||
zap.Time("head_frame_time", headTime),
|
||||
)...,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
units, readable, err := e.getUnmintedRewardBalance()
|
||||
if err != nil {
|
||||
e.logger.Warn(
|
||||
"unable to read prover reward balance",
|
||||
append(baseFields, zap.Error(err))...,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
e.logger.Info(
|
||||
"node health check passed",
|
||||
append(
|
||||
baseFields,
|
||||
zap.Uint64("head_frame_number", headFrame.Header.FrameNumber),
|
||||
zap.Time("head_frame_time", headTime),
|
||||
zap.String("unminted_reward_quil", readable),
|
||||
zap.String("unminted_reward_raw_units", units.String()),
|
||||
)...,
|
||||
)
|
||||
}
|
||||
|
||||
const rewardUnitsPerInterval int64 = 8_000_000_000
|
||||
|
||||
func (e *GlobalConsensusEngine) getUnmintedRewardBalance() (
|
||||
*big.Int,
|
||||
string,
|
||||
error,
|
||||
) {
|
||||
rewardAddress, err := e.deriveRewardAddress()
|
||||
if err != nil {
|
||||
return nil, "", errors.Wrap(err, "derive reward address")
|
||||
}
|
||||
|
||||
var vertexID [64]byte
|
||||
copy(vertexID[:32], intrinsics.GLOBAL_INTRINSIC_ADDRESS[:])
|
||||
copy(vertexID[32:], rewardAddress)
|
||||
|
||||
tree, err := e.hypergraph.GetVertexData(vertexID)
|
||||
if err != nil {
|
||||
return big.NewInt(0), "0", nil
|
||||
}
|
||||
if tree == nil {
|
||||
return big.NewInt(0), "0", nil
|
||||
}
|
||||
|
||||
rdf := schema.NewRDFMultiprover(&schema.TurtleRDFParser{}, e.inclusionProver)
|
||||
balanceBytes, err := rdf.Get(
|
||||
global.GLOBAL_RDF_SCHEMA,
|
||||
"reward:ProverReward",
|
||||
"Balance",
|
||||
tree,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, "", errors.Wrap(err, "read reward balance")
|
||||
}
|
||||
|
||||
units := new(big.Int).SetBytes(balanceBytes)
|
||||
rewardReadable := decimal.NewFromBigInt(units, 0).Div(
|
||||
decimal.NewFromInt(rewardUnitsPerInterval),
|
||||
).String()
|
||||
|
||||
return units, rewardReadable, nil
|
||||
}
|
||||
|
||||
func (e *GlobalConsensusEngine) deriveRewardAddress() ([]byte, error) {
|
||||
proverAddr := e.getProverAddress()
|
||||
if len(proverAddr) == 0 {
|
||||
return nil, errors.New("missing prover address")
|
||||
}
|
||||
|
||||
hash, err := poseidon.HashBytes(
|
||||
slices.Concat(tokenintrinsics.QUIL_TOKEN_ADDRESS[:], proverAddr),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return hash.FillBytes(make([]byte, 32)), nil
|
||||
}
|
||||
|
||||
// validatePeerInfoSignature validates the signature of a peer info message
|
||||
func (e *GlobalConsensusEngine) validatePeerInfoSignature(
|
||||
peerInfo *protobufs.PeerInfo,
|
||||
@ -3000,6 +3188,20 @@ func (e *GlobalConsensusEngine) OnQuorumCertificateTriggeredRankChange(
|
||||
// OnRankChange implements consensus.Consumer.
|
||||
func (e *GlobalConsensusEngine) OnRankChange(oldRank uint64, newRank uint64) {
|
||||
e.currentRank = newRank
|
||||
|
||||
prior, err := e.clockStore.GetLatestGlobalClockFrame()
|
||||
if err != nil {
|
||||
frameProvingTotal.WithLabelValues("error").Inc()
|
||||
return
|
||||
}
|
||||
_, err = e.livenessProvider.Collect(
|
||||
context.TODO(),
|
||||
prior.Header.FrameNumber+1,
|
||||
newRank,
|
||||
)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// OnReceiveProposal implements consensus.Consumer.
|
||||
|
||||
230
node/consensus/global/message_collector.go
Normal file
230
node/consensus/global/message_collector.go
Normal file
@ -0,0 +1,230 @@
|
||||
package global
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"slices"
|
||||
|
||||
"golang.org/x/crypto/sha3"
|
||||
|
||||
"source.quilibrium.com/quilibrium/monorepo/consensus/models"
|
||||
"source.quilibrium.com/quilibrium/monorepo/lifecycle"
|
||||
"source.quilibrium.com/quilibrium/monorepo/node/consensus/tracing"
|
||||
keyedaggregator "source.quilibrium.com/quilibrium/monorepo/node/keyedaggregator"
|
||||
keyedcollector "source.quilibrium.com/quilibrium/monorepo/node/keyedcollector"
|
||||
"source.quilibrium.com/quilibrium/monorepo/protobufs"
|
||||
)
|
||||
|
||||
const maxGlobalMessagesPerFrame = 100
|
||||
|
||||
var globalMessageAddress = bytes.Repeat([]byte{0xff}, 32)
|
||||
|
||||
type sequencedGlobalMessage struct {
|
||||
sequence uint64
|
||||
identity models.Identity
|
||||
payload []byte
|
||||
message *protobufs.Message
|
||||
}
|
||||
|
||||
func newSequencedGlobalMessage(
|
||||
sequence uint64,
|
||||
payload []byte,
|
||||
) *sequencedGlobalMessage {
|
||||
copyPayload := slices.Clone(payload)
|
||||
hash := sha3.Sum256(copyPayload)
|
||||
return &sequencedGlobalMessage{
|
||||
sequence: sequence,
|
||||
identity: models.Identity(string(hash[:])),
|
||||
payload: copyPayload,
|
||||
}
|
||||
}
|
||||
|
||||
var globalMessageTraits = keyedcollector.RecordTraits[sequencedGlobalMessage]{
|
||||
Sequence: func(m *sequencedGlobalMessage) uint64 {
|
||||
if m == nil {
|
||||
return 0
|
||||
}
|
||||
return m.sequence
|
||||
},
|
||||
Identity: func(m *sequencedGlobalMessage) models.Identity {
|
||||
if m == nil {
|
||||
return ""
|
||||
}
|
||||
return m.identity
|
||||
},
|
||||
Equals: func(a, b *sequencedGlobalMessage) bool {
|
||||
if a == nil || b == nil {
|
||||
return a == b
|
||||
}
|
||||
return slices.Equal(a.payload, b.payload)
|
||||
},
|
||||
}
|
||||
|
||||
type globalMessageProcessorFactory struct {
|
||||
engine *GlobalConsensusEngine
|
||||
}
|
||||
|
||||
func (f *globalMessageProcessorFactory) Create(
|
||||
sequence uint64,
|
||||
) (keyedcollector.Processor[sequencedGlobalMessage], error) {
|
||||
return &globalMessageProcessor{
|
||||
engine: f.engine,
|
||||
sequence: sequence,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type globalMessageProcessor struct {
|
||||
engine *GlobalConsensusEngine
|
||||
sequence uint64
|
||||
}
|
||||
|
||||
func (p *globalMessageProcessor) Process(
|
||||
record *sequencedGlobalMessage,
|
||||
) error {
|
||||
if record == nil {
|
||||
return keyedcollector.NewInvalidRecordError(
|
||||
record,
|
||||
errors.New("nil global message"),
|
||||
)
|
||||
}
|
||||
|
||||
if len(record.payload) < 4 {
|
||||
return keyedcollector.NewInvalidRecordError(
|
||||
record,
|
||||
errors.New("global message payload too short"),
|
||||
)
|
||||
}
|
||||
|
||||
typePrefix := binary.BigEndian.Uint32(record.payload[:4])
|
||||
if typePrefix != protobufs.MessageBundleType {
|
||||
return keyedcollector.NewInvalidRecordError(
|
||||
record,
|
||||
fmt.Errorf("unexpected message type: %d", typePrefix),
|
||||
)
|
||||
}
|
||||
|
||||
message := &protobufs.Message{
|
||||
Address: globalMessageAddress,
|
||||
Payload: record.payload,
|
||||
}
|
||||
|
||||
if err := p.enforceCollectorLimit(record); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
qc, err := p.engine.clockStore.GetQuorumCertificate(nil, record.sequence-1)
|
||||
if err != nil {
|
||||
qc, err = p.engine.clockStore.GetLatestQuorumCertificate(nil)
|
||||
}
|
||||
if err != nil {
|
||||
return keyedcollector.NewInvalidRecordError(record, err)
|
||||
}
|
||||
|
||||
if err := p.engine.executionManager.ValidateMessage(
|
||||
qc.FrameNumber+1,
|
||||
message.Address,
|
||||
message.Payload,
|
||||
); err != nil {
|
||||
return keyedcollector.NewInvalidRecordError(record, err)
|
||||
}
|
||||
|
||||
record.message = message
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *globalMessageProcessor) enforceCollectorLimit(
|
||||
record *sequencedGlobalMessage,
|
||||
) error {
|
||||
collector, found, err := p.engine.getMessageCollector(p.sequence)
|
||||
if err != nil || !found {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(collector.Records()) >= maxGlobalMessagesPerFrame {
|
||||
collector.Remove(record)
|
||||
return keyedcollector.NewInvalidRecordError(
|
||||
record,
|
||||
fmt.Errorf("message limit reached for frame %d", p.sequence),
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *GlobalConsensusEngine) initGlobalMessageAggregator() error {
|
||||
tracer := tracing.NewZapTracer(e.logger.Named("global_message_collector"))
|
||||
processorFactory := &globalMessageProcessorFactory{engine: e}
|
||||
collectorFactory, err := keyedcollector.NewFactory(
|
||||
tracer,
|
||||
globalMessageTraits,
|
||||
nil,
|
||||
processorFactory,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("global message collector factory: %w", err)
|
||||
}
|
||||
|
||||
e.messageCollectors = keyedaggregator.NewSequencedCollectors[sequencedGlobalMessage](
|
||||
tracer,
|
||||
0,
|
||||
collectorFactory,
|
||||
)
|
||||
|
||||
aggregator, err := keyedaggregator.NewSequencedAggregator[sequencedGlobalMessage](
|
||||
tracer,
|
||||
0,
|
||||
e.messageCollectors,
|
||||
func(m *sequencedGlobalMessage) uint64 {
|
||||
if m == nil {
|
||||
return 0
|
||||
}
|
||||
return m.sequence
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("global message aggregator: %w", err)
|
||||
}
|
||||
|
||||
e.messageAggregator = aggregator
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *GlobalConsensusEngine) startGlobalMessageAggregator(
|
||||
ctx lifecycle.SignalerContext,
|
||||
ready lifecycle.ReadyFunc,
|
||||
) {
|
||||
if e.messageAggregator == nil {
|
||||
ready()
|
||||
<-ctx.Done()
|
||||
return
|
||||
}
|
||||
|
||||
go func() {
|
||||
if err := e.messageAggregator.ComponentManager.Start(ctx); err != nil {
|
||||
ctx.Throw(err)
|
||||
}
|
||||
}()
|
||||
|
||||
<-e.messageAggregator.ComponentManager.Ready()
|
||||
ready()
|
||||
<-e.messageAggregator.ComponentManager.Done()
|
||||
}
|
||||
|
||||
func (e *GlobalConsensusEngine) addGlobalMessage(data []byte) {
|
||||
if e.messageAggregator == nil {
|
||||
return
|
||||
}
|
||||
record := newSequencedGlobalMessage(e.currentRank+1, data)
|
||||
e.messageAggregator.Add(record)
|
||||
}
|
||||
|
||||
func (e *GlobalConsensusEngine) getMessageCollector(
|
||||
rank uint64,
|
||||
) (keyedaggregator.Collector[sequencedGlobalMessage], bool, error) {
|
||||
if e.messageCollectors == nil {
|
||||
return nil, false, nil
|
||||
}
|
||||
return e.messageCollectors.GetCollector(rank)
|
||||
}
|
||||
@ -219,11 +219,7 @@ func (e *GlobalConsensusEngine) handleProverMessage(message *pb.Message) {
|
||||
|
||||
switch typePrefix {
|
||||
case protobufs.MessageBundleType:
|
||||
// MessageBundle messages need to be collected for execution
|
||||
// Store them in pendingMessages to be processed during Collect
|
||||
e.pendingMessagesMu.Lock()
|
||||
e.pendingMessages = append(e.pendingMessages, message.Data)
|
||||
e.pendingMessagesMu.Unlock()
|
||||
e.addGlobalMessage(message.Data)
|
||||
|
||||
e.logger.Debug(
|
||||
"collected global request for execution",
|
||||
@ -338,7 +334,6 @@ func (e *GlobalConsensusEngine) handleAppFrameMessage(message *pb.Message) {
|
||||
shardFramesProcessedTotal.WithLabelValues("error").Inc()
|
||||
}
|
||||
|
||||
e.pendingMessagesMu.Lock()
|
||||
bundle := &protobufs.MessageBundle{
|
||||
Requests: []*protobufs.MessageRequest{
|
||||
&protobufs.MessageRequest{
|
||||
@ -353,12 +348,10 @@ func (e *GlobalConsensusEngine) handleAppFrameMessage(message *pb.Message) {
|
||||
bundleBytes, err := bundle.ToCanonicalBytes()
|
||||
if err != nil {
|
||||
e.logger.Error("failed to add shard bundle", zap.Error(err))
|
||||
e.pendingMessagesMu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
e.pendingMessages = append(e.pendingMessages, bundleBytes)
|
||||
e.pendingMessagesMu.Unlock()
|
||||
e.addGlobalMessage(bundleBytes)
|
||||
e.frameStoreMu.Lock()
|
||||
defer e.frameStoreMu.Unlock()
|
||||
e.appFrameStore[string(frame.Header.Address)] = frame
|
||||
@ -1528,6 +1521,7 @@ func (e *GlobalConsensusEngine) handleVote(message *pb.Message) {
|
||||
}
|
||||
|
||||
e.voteAggregator.AddVote(&vote)
|
||||
|
||||
voteProcessedTotal.WithLabelValues("success").Inc()
|
||||
}
|
||||
|
||||
@ -1596,12 +1590,7 @@ func (e *GlobalConsensusEngine) handleTimeoutState(message *pb.Message) {
|
||||
}
|
||||
|
||||
func (e *GlobalConsensusEngine) handleMessageBundle(message *pb.Message) {
|
||||
// MessageBundle messages need to be collected for execution
|
||||
// Store them in pendingMessages to be processed during Collect
|
||||
e.pendingMessagesMu.Lock()
|
||||
e.pendingMessages = append(e.pendingMessages, message.Data)
|
||||
e.pendingMessagesMu.Unlock()
|
||||
|
||||
e.addGlobalMessage(message.Data)
|
||||
e.logger.Debug("collected global request for execution")
|
||||
}
|
||||
|
||||
|
||||
@ -387,7 +387,7 @@ func (e *GlobalConsensusEngine) validateAppFrameMessage(
|
||||
return tp2p.ValidationResultReject
|
||||
}
|
||||
|
||||
if frametime.AppFrameSince(frame) > 20*time.Second {
|
||||
if frametime.AppFrameSince(frame) > 120*time.Second {
|
||||
shardFrameValidationTotal.WithLabelValues("ignore").Inc()
|
||||
return tp2p.ValidationResultIgnore
|
||||
}
|
||||
@ -457,6 +457,11 @@ func (e *GlobalConsensusEngine) validateFrameMessage(
|
||||
return tp2p.ValidationResultIgnore
|
||||
}
|
||||
|
||||
if frametime.GlobalFrameSince(frame) > 120*time.Second {
|
||||
frameValidationTotal.WithLabelValues("ignore").Inc()
|
||||
return tp2p.ValidationResultIgnore
|
||||
}
|
||||
|
||||
frameValidationTotal.WithLabelValues("accept").Inc()
|
||||
default:
|
||||
e.logger.Debug("received unknown type", zap.Uint32("type", typePrefix))
|
||||
|
||||
@ -125,10 +125,7 @@ func (r *DataWorkerIPCServer) Respawn(
|
||||
ctx context.Context,
|
||||
req *protobufs.RespawnRequest,
|
||||
) (*protobufs.RespawnResponse, error) {
|
||||
err := r.RespawnServer(req.Filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
go r.RespawnServer(req.Filter)
|
||||
return &protobufs.RespawnResponse{}, nil
|
||||
}
|
||||
|
||||
|
||||
@ -735,6 +735,7 @@ func (e *ComputeExecutionEngine) handleBundle(
|
||||
if err := fees.SanityCheck(feeQueue, consumers); err != nil {
|
||||
return nil, errors.Wrap(err, "handle bundle")
|
||||
}
|
||||
responses.State = state
|
||||
|
||||
// Process each operation in the bundle sequentially
|
||||
for i, op := range bundle.Requests {
|
||||
@ -787,7 +788,7 @@ func (e *ComputeExecutionEngine) handleBundle(
|
||||
movingAddress,
|
||||
op,
|
||||
true,
|
||||
state,
|
||||
responses.State,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "handle bundle: operation %d failed", i)
|
||||
@ -807,7 +808,7 @@ func (e *ComputeExecutionEngine) handleBundle(
|
||||
|
||||
// Collect responses
|
||||
responses.Messages = append(responses.Messages, opResponses.Messages...)
|
||||
responses.State = state
|
||||
responses.State = opResponses.State
|
||||
}
|
||||
|
||||
e.logger.Info(
|
||||
|
||||
@ -346,6 +346,7 @@ func (e *GlobalExecutionEngine) handleBundle(
|
||||
}
|
||||
|
||||
responses := &execution.ProcessMessageResult{}
|
||||
responses.State = state
|
||||
|
||||
// Process each operation in the bundle sequentially
|
||||
for i, op := range bundle.Requests {
|
||||
@ -356,7 +357,7 @@ func (e *GlobalExecutionEngine) handleBundle(
|
||||
address,
|
||||
op,
|
||||
true,
|
||||
state,
|
||||
responses.State,
|
||||
)
|
||||
if err != nil {
|
||||
// Skip non-global operations (e.g., token payments, compute ops)
|
||||
@ -370,7 +371,7 @@ func (e *GlobalExecutionEngine) handleBundle(
|
||||
|
||||
// Collect responses
|
||||
responses.Messages = append(responses.Messages, opResponses.Messages...)
|
||||
responses.State = state
|
||||
responses.State = opResponses.State
|
||||
}
|
||||
|
||||
e.logger.Info(
|
||||
|
||||
@ -512,6 +512,7 @@ func (e *HypergraphExecutionEngine) handleBundle(
|
||||
return nil, errors.Wrap(err, "handle bundle")
|
||||
}
|
||||
|
||||
responses.State = state
|
||||
// Process each operation in the bundle sequentially
|
||||
for i, op := range bundle.Requests {
|
||||
e.logger.Debug(
|
||||
@ -564,7 +565,7 @@ func (e *HypergraphExecutionEngine) handleBundle(
|
||||
address,
|
||||
op,
|
||||
true,
|
||||
state,
|
||||
responses.State,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "handle bundle: operation %d failed", i)
|
||||
@ -572,7 +573,7 @@ func (e *HypergraphExecutionEngine) handleBundle(
|
||||
|
||||
// Collect responses
|
||||
responses.Messages = append(responses.Messages, opResponses.Messages...)
|
||||
responses.State = state
|
||||
responses.State = opResponses.State
|
||||
}
|
||||
|
||||
e.logger.Info(
|
||||
|
||||
@ -578,6 +578,7 @@ func (e *TokenExecutionEngine) handleBundle(
|
||||
}
|
||||
|
||||
responses := &execution.ProcessMessageResult{}
|
||||
responses.State = state
|
||||
|
||||
// Process each operation in the bundle sequentially
|
||||
for i, op := range bundle.Requests {
|
||||
@ -630,7 +631,7 @@ func (e *TokenExecutionEngine) handleBundle(
|
||||
address,
|
||||
op,
|
||||
true,
|
||||
state,
|
||||
responses.State,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "handle bundle: operation %d failed", i)
|
||||
@ -638,7 +639,7 @@ func (e *TokenExecutionEngine) handleBundle(
|
||||
|
||||
// Collect responses
|
||||
responses.Messages = append(responses.Messages, opResponses.Messages...)
|
||||
responses.State = state
|
||||
responses.State = opResponses.State
|
||||
}
|
||||
|
||||
e.logger.Info(
|
||||
|
||||
266
node/keyedaggregator/aggregator.go
Normal file
266
node/keyedaggregator/aggregator.go
Normal file
@ -0,0 +1,266 @@
|
||||
package keyedaggregator
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"source.quilibrium.com/quilibrium/monorepo/consensus"
|
||||
"source.quilibrium.com/quilibrium/monorepo/consensus/counters"
|
||||
"source.quilibrium.com/quilibrium/monorepo/lifecycle"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultWorkerCount = 4
|
||||
defaultQueueSize = 1000
|
||||
)
|
||||
|
||||
// SequenceExtractor returns the sequence identifier for a given item. The
|
||||
// sequence is typically the logical round/rank/height that an item belongs to.
|
||||
type SequenceExtractor[ItemT any] func(*ItemT) uint64
|
||||
|
||||
// SequencedAggregator is a generic event dispatcher that fans out sequenced
|
||||
// items to lazily-created collectors keyed by the item's sequence. Items are
|
||||
// processed asynchronously by worker goroutines. The aggregator drops stale
|
||||
// items (items whose sequence is below the currently retained threshold) and
|
||||
// relies on the CollectorCache implementation to prune old collectors.
|
||||
type SequencedAggregator[ItemT any] struct {
|
||||
*lifecycle.ComponentManager
|
||||
|
||||
tracer consensus.TraceLogger
|
||||
lowestRetained counters.StrictMonotonicCounter
|
||||
collectors CollectorCache[ItemT]
|
||||
sequenceExtractor SequenceExtractor[ItemT]
|
||||
queuedItems chan *ItemT
|
||||
itemsNotifier chan struct{}
|
||||
sequenceNotifier chan struct{}
|
||||
wg sync.WaitGroup
|
||||
workerCount int
|
||||
queueCapacity int
|
||||
}
|
||||
|
||||
// AggregatorOption customizes the behaviour of the SequencedAggregator.
|
||||
type AggregatorOption func(*aggregatorConfig)
|
||||
|
||||
type aggregatorConfig struct {
|
||||
workerCount int
|
||||
queueCapacity int
|
||||
}
|
||||
|
||||
// WithWorkerCount overrides the default number of worker goroutines used to
|
||||
// drain the inbound queue. Values smaller than one are ignored.
|
||||
func WithWorkerCount(count int) AggregatorOption {
|
||||
return func(cfg *aggregatorConfig) {
|
||||
if count > 0 {
|
||||
cfg.workerCount = count
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithQueueCapacity overrides the size of the buffered queue that stores
|
||||
// pending items. Values smaller than one are ignored.
|
||||
func WithQueueCapacity(capacity int) AggregatorOption {
|
||||
return func(cfg *aggregatorConfig) {
|
||||
if capacity > 0 {
|
||||
cfg.queueCapacity = capacity
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NewSequencedAggregator wires a SequencedAggregator using the provided
|
||||
// CollectorCache and SequenceExtractor. The aggregator starts workers via the
|
||||
// lifecycle.ComponentManager built during construction.
|
||||
func NewSequencedAggregator[ItemT any](
|
||||
tracer consensus.TraceLogger,
|
||||
lowestRetained uint64,
|
||||
collectors CollectorCache[ItemT],
|
||||
extractor SequenceExtractor[ItemT],
|
||||
opts ...AggregatorOption,
|
||||
) (*SequencedAggregator[ItemT], error) {
|
||||
if collectors == nil {
|
||||
return nil, fmt.Errorf("collector cache is required")
|
||||
}
|
||||
if extractor == nil {
|
||||
return nil, fmt.Errorf("sequence extractor is required")
|
||||
}
|
||||
|
||||
cfg := aggregatorConfig{
|
||||
workerCount: defaultWorkerCount,
|
||||
queueCapacity: defaultQueueSize,
|
||||
}
|
||||
for _, opt := range opts {
|
||||
if opt != nil {
|
||||
opt(&cfg)
|
||||
}
|
||||
}
|
||||
if cfg.workerCount <= 0 {
|
||||
cfg.workerCount = defaultWorkerCount
|
||||
}
|
||||
if cfg.queueCapacity <= 0 {
|
||||
cfg.queueCapacity = defaultQueueSize
|
||||
}
|
||||
|
||||
aggregator := &SequencedAggregator[ItemT]{
|
||||
tracer: tracer,
|
||||
lowestRetained: counters.NewMonotonicCounter(lowestRetained),
|
||||
collectors: collectors,
|
||||
sequenceExtractor: extractor,
|
||||
queuedItems: make(chan *ItemT, cfg.queueCapacity),
|
||||
itemsNotifier: make(chan struct{}, 1),
|
||||
sequenceNotifier: make(chan struct{}, 1),
|
||||
workerCount: cfg.workerCount,
|
||||
queueCapacity: cfg.queueCapacity,
|
||||
}
|
||||
|
||||
aggregator.wg.Add(aggregator.workerCount + 1)
|
||||
builder := lifecycle.NewComponentManagerBuilder()
|
||||
for i := 0; i < aggregator.workerCount; i++ {
|
||||
builder.AddWorker(func(
|
||||
ctx lifecycle.SignalerContext,
|
||||
ready lifecycle.ReadyFunc,
|
||||
) {
|
||||
ready()
|
||||
aggregator.queuedItemsProcessingLoop(ctx)
|
||||
})
|
||||
}
|
||||
builder.AddWorker(func(
|
||||
ctx lifecycle.SignalerContext,
|
||||
ready lifecycle.ReadyFunc,
|
||||
) {
|
||||
ready()
|
||||
aggregator.sequenceProcessingLoop(ctx)
|
||||
})
|
||||
|
||||
aggregator.ComponentManager = builder.Build()
|
||||
return aggregator, nil
|
||||
}
|
||||
|
||||
// Add enqueues an item for asynchronous processing. Items whose sequence is
|
||||
// below the retained threshold are silently discarded.
|
||||
func (a *SequencedAggregator[ItemT]) Add(item *ItemT) {
|
||||
if item == nil {
|
||||
return
|
||||
}
|
||||
sequence := a.sequenceExtractor(item)
|
||||
if sequence < a.lowestRetained.Value() {
|
||||
a.tracer.Trace(
|
||||
"dropping item added below lowest retained value",
|
||||
consensus.Uint64Param("lowest_retained", a.lowestRetained.Value()),
|
||||
consensus.Uint64Param("sequence", sequence),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case a.queuedItems <- item:
|
||||
select {
|
||||
case a.itemsNotifier <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
default:
|
||||
a.tracer.Trace("dropping sequenced item: queue at capacity")
|
||||
}
|
||||
}
|
||||
|
||||
// PruneUpToSequence prunes all collectors with sequence lower than the provided
|
||||
// threshold. If the provided threshold is behind the current value, this call
|
||||
// is treated as a no-op.
|
||||
func (a *SequencedAggregator[ItemT]) PruneUpToSequence(sequence uint64) {
|
||||
a.collectors.PruneUpToSequence(sequence)
|
||||
}
|
||||
|
||||
// OnSequenceChange notifies the aggregator that the active sequence advanced.
|
||||
// When the internal counter is updated the pruning worker is notified to prune
|
||||
// the collector cache.
|
||||
func (a *SequencedAggregator[ItemT]) OnSequenceChange(oldSeq, newSeq uint64) {
|
||||
if a.lowestRetained.Set(newSeq) {
|
||||
select {
|
||||
case a.sequenceNotifier <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (a *SequencedAggregator[ItemT]) queuedItemsProcessingLoop(
|
||||
ctx lifecycle.SignalerContext,
|
||||
) {
|
||||
defer a.wg.Done()
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-a.itemsNotifier:
|
||||
a.tracer.Trace("processing queued sequenced items")
|
||||
if err := a.processQueuedItems(ctx); err != nil {
|
||||
ctx.Throw(fmt.Errorf("processing queued items failed: %w", err))
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (a *SequencedAggregator[ItemT]) processQueuedItems(
|
||||
ctx context.Context,
|
||||
) error {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
case item, ok := <-a.queuedItems:
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
if item == nil {
|
||||
continue
|
||||
}
|
||||
if err := a.processQueuedItem(item); err != nil {
|
||||
return err
|
||||
}
|
||||
a.tracer.Trace("sequenced item processed successfully")
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (a *SequencedAggregator[ItemT]) processQueuedItem(item *ItemT) error {
|
||||
sequence := a.sequenceExtractor(item)
|
||||
collector, _, err := a.collectors.GetOrCreateCollector(sequence)
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, ErrSequenceUnknown):
|
||||
a.tracer.Error("dropping item for unknown sequence", err)
|
||||
return nil
|
||||
case errors.Is(err, ErrSequenceBelowRetention):
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("could not get collector for sequence %d: %w",
|
||||
sequence,
|
||||
err,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if err := collector.Add(item); err != nil {
|
||||
return fmt.Errorf("collector processing failed for sequence %d: %w",
|
||||
sequence,
|
||||
err,
|
||||
)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *SequencedAggregator[ItemT]) sequenceProcessingLoop(
|
||||
ctx context.Context,
|
||||
) {
|
||||
defer a.wg.Done()
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-a.sequenceNotifier:
|
||||
a.PruneUpToSequence(a.lowestRetained.Value())
|
||||
}
|
||||
}
|
||||
}
|
||||
201
node/keyedaggregator/aggregator_test.go
Normal file
201
node/keyedaggregator/aggregator_test.go
Normal file
@ -0,0 +1,201 @@
|
||||
package keyedaggregator
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"source.quilibrium.com/quilibrium/monorepo/consensus"
|
||||
"source.quilibrium.com/quilibrium/monorepo/lifecycle"
|
||||
)
|
||||
|
||||
type testItem struct {
|
||||
sequence uint64
|
||||
value string
|
||||
}
|
||||
|
||||
type stubCollector struct {
|
||||
mu sync.Mutex
|
||||
items []*testItem
|
||||
}
|
||||
|
||||
func (c *stubCollector) Add(item *testItem) error {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
c.items = append(c.items, item)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *stubCollector) Records() []*testItem {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
out := make([]*testItem, len(c.items))
|
||||
copy(out, c.items)
|
||||
return out
|
||||
}
|
||||
|
||||
func (c *stubCollector) Remove(item *testItem) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
for i, existing := range c.items {
|
||||
if existing == item {
|
||||
c.items = append(c.items[:i], c.items[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type trackingFactory struct {
|
||||
mu sync.Mutex
|
||||
collectors map[uint64]*stubCollector
|
||||
}
|
||||
|
||||
func newTrackingFactory() *trackingFactory {
|
||||
return &trackingFactory{
|
||||
collectors: make(map[uint64]*stubCollector),
|
||||
}
|
||||
}
|
||||
|
||||
func (f *trackingFactory) Create(
|
||||
sequence uint64,
|
||||
) (Collector[testItem], error) {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
collector := &stubCollector{}
|
||||
f.collectors[sequence] = collector
|
||||
return collector, nil
|
||||
}
|
||||
|
||||
func (f *trackingFactory) collector(sequence uint64) *stubCollector {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
return f.collectors[sequence]
|
||||
}
|
||||
|
||||
type noopTracer struct{}
|
||||
|
||||
func (noopTracer) Trace(string, ...consensus.LogParam) {}
|
||||
func (noopTracer) Error(string, error, ...consensus.LogParam) {}
|
||||
func (noopTracer) With(...consensus.LogParam) consensus.TraceLogger { return noopTracer{} }
|
||||
|
||||
func startTestAggregator(
|
||||
t *testing.T,
|
||||
aggregator *SequencedAggregator[testItem],
|
||||
) func() {
|
||||
t.Helper()
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
signalCtx, _ := lifecycle.WithSignaler(ctx)
|
||||
require.NoError(t, aggregator.ComponentManager.Start(signalCtx))
|
||||
<-aggregator.ComponentManager.Ready()
|
||||
return func() {
|
||||
cancel()
|
||||
<-aggregator.ComponentManager.Done()
|
||||
}
|
||||
}
|
||||
|
||||
func TestSequencedAggregatorDispatchesItems(t *testing.T) {
|
||||
t.Parallel()
|
||||
factory := newTrackingFactory()
|
||||
collectors := NewSequencedCollectors[testItem](noopTracer{}, 0, factory)
|
||||
aggregator, err := NewSequencedAggregator[testItem](
|
||||
noopTracer{},
|
||||
0,
|
||||
collectors,
|
||||
func(item *testItem) uint64 { return item.sequence },
|
||||
)
|
||||
require.NoError(t, err)
|
||||
stop := startTestAggregator(t, aggregator)
|
||||
defer stop()
|
||||
|
||||
expected := &testItem{sequence: 2, value: "payload"}
|
||||
aggregator.Add(expected)
|
||||
|
||||
require.Eventually(t, func() bool {
|
||||
c := factory.collector(2)
|
||||
if c == nil {
|
||||
return false
|
||||
}
|
||||
items := c.Records()
|
||||
return len(items) == 1 && items[0] == expected
|
||||
}, time.Second, 10*time.Millisecond)
|
||||
}
|
||||
|
||||
func TestSequencedAggregatorDropsStaleItems(t *testing.T) {
|
||||
t.Parallel()
|
||||
factory := newTrackingFactory()
|
||||
collectors := NewSequencedCollectors[testItem](noopTracer{}, 5, factory)
|
||||
aggregator, err := NewSequencedAggregator[testItem](
|
||||
noopTracer{},
|
||||
5,
|
||||
collectors,
|
||||
func(item *testItem) uint64 { return item.sequence },
|
||||
)
|
||||
require.NoError(t, err)
|
||||
stop := startTestAggregator(t, aggregator)
|
||||
defer stop()
|
||||
|
||||
aggregator.Add(&testItem{sequence: 2})
|
||||
|
||||
// Item is dropped before it ever enters the queue, so no collector
|
||||
// should have been created for the stale sequence.
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
require.Nil(t, factory.collector(2))
|
||||
}
|
||||
|
||||
func TestSequencedAggregatorPrunesCollectorsOnSequenceChange(t *testing.T) {
|
||||
t.Parallel()
|
||||
factory := newTrackingFactory()
|
||||
collectors := NewSequencedCollectors[testItem](noopTracer{}, 0, factory)
|
||||
aggregator, err := NewSequencedAggregator[testItem](
|
||||
noopTracer{},
|
||||
0,
|
||||
collectors,
|
||||
func(item *testItem) uint64 { return item.sequence },
|
||||
)
|
||||
require.NoError(t, err)
|
||||
stop := startTestAggregator(t, aggregator)
|
||||
defer stop()
|
||||
|
||||
aggregator.Add(&testItem{sequence: 1})
|
||||
require.Eventually(t, func() bool {
|
||||
_, found, err := collectors.getCollector(1)
|
||||
return err == nil && found
|
||||
}, time.Second, 10*time.Millisecond)
|
||||
|
||||
aggregator.OnSequenceChange(0, 3)
|
||||
require.Eventually(t, func() bool {
|
||||
_, _, err := collectors.getCollector(1)
|
||||
return errors.Is(err, ErrSequenceBelowRetention)
|
||||
}, time.Second, 10*time.Millisecond)
|
||||
}
|
||||
|
||||
func TestSequencedCollectorsGetOrCreateReusesInstances(t *testing.T) {
|
||||
t.Parallel()
|
||||
collectors := NewSequencedCollectors[testItem](noopTracer{}, 0, newTrackingFactory())
|
||||
|
||||
first, created, err := collectors.GetOrCreateCollector(4)
|
||||
require.NoError(t, err)
|
||||
require.True(t, created)
|
||||
|
||||
second, created, err := collectors.GetOrCreateCollector(4)
|
||||
require.NoError(t, err)
|
||||
require.False(t, created)
|
||||
require.Equal(t, first, second)
|
||||
}
|
||||
|
||||
func TestSequencedCollectorsPruneRemovesOldSequences(t *testing.T) {
|
||||
t.Parallel()
|
||||
collectors := NewSequencedCollectors[testItem](noopTracer{}, 0, newTrackingFactory())
|
||||
|
||||
_, _, err := collectors.GetOrCreateCollector(2)
|
||||
require.NoError(t, err)
|
||||
|
||||
collectors.PruneUpToSequence(5)
|
||||
|
||||
_, _, err = collectors.GetOrCreateCollector(2)
|
||||
require.ErrorIs(t, err, ErrSequenceBelowRetention)
|
||||
}
|
||||
144
node/keyedaggregator/collectors.go
Normal file
144
node/keyedaggregator/collectors.go
Normal file
@ -0,0 +1,144 @@
|
||||
package keyedaggregator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"source.quilibrium.com/quilibrium/monorepo/consensus"
|
||||
)
|
||||
|
||||
// Collector represents a per-sequence worker that processes items belonging to
|
||||
// that sequence.
|
||||
type Collector[ItemT any] interface {
|
||||
Add(*ItemT) error
|
||||
Records() []*ItemT
|
||||
Remove(*ItemT)
|
||||
}
|
||||
|
||||
// CollectorFactory constructs collectors for a specific sequence.
|
||||
type CollectorFactory[ItemT any] interface {
|
||||
Create(sequence uint64) (Collector[ItemT], error)
|
||||
}
|
||||
|
||||
// CollectorCache provides lazy access to collectors keyed by sequence and
|
||||
// pruning of stale sequences.
|
||||
type CollectorCache[ItemT any] interface {
|
||||
GetOrCreateCollector(sequence uint64) (Collector[ItemT], bool, error)
|
||||
GetCollector(sequence uint64) (Collector[ItemT], bool, error)
|
||||
PruneUpToSequence(sequence uint64)
|
||||
}
|
||||
|
||||
// SequencedCollectors is a threadsafe CollectorCache implementation that
|
||||
// lazily instantiates collectors and prunes them when the retained sequence
|
||||
// advances.
|
||||
type SequencedCollectors[ItemT any] struct {
|
||||
tracer consensus.TraceLogger
|
||||
lock sync.RWMutex
|
||||
lowestRetained uint64
|
||||
highestCached uint64
|
||||
collectors map[uint64]Collector[ItemT]
|
||||
collectorFactory CollectorFactory[ItemT]
|
||||
}
|
||||
|
||||
// NewSequencedCollectors creates a SequencedCollectors backed by the provided
|
||||
// factory. The lowestRetained sequence is kept even if pruning is invoked with
|
||||
// smaller values.
|
||||
func NewSequencedCollectors[ItemT any](
|
||||
tracer consensus.TraceLogger,
|
||||
lowestRetained uint64,
|
||||
factory CollectorFactory[ItemT],
|
||||
) *SequencedCollectors[ItemT] {
|
||||
if factory == nil {
|
||||
panic("collector factory is required")
|
||||
}
|
||||
return &SequencedCollectors[ItemT]{
|
||||
tracer: tracer,
|
||||
lowestRetained: lowestRetained,
|
||||
highestCached: lowestRetained,
|
||||
collectors: make(map[uint64]Collector[ItemT]),
|
||||
collectorFactory: factory,
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrCreateCollector retrieves the collector for the provided sequence. If no
|
||||
// collector exists, one is created using the factory.
|
||||
func (c *SequencedCollectors[ItemT]) GetOrCreateCollector(
|
||||
sequence uint64,
|
||||
) (Collector[ItemT], bool, error) {
|
||||
cached, found, err := c.getCollector(sequence)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
if found {
|
||||
return cached, false, nil
|
||||
}
|
||||
|
||||
col, err := c.collectorFactory.Create(sequence)
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf(
|
||||
"could not create collector for sequence %d: %w",
|
||||
sequence,
|
||||
err,
|
||||
)
|
||||
}
|
||||
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
if existing, ok := c.collectors[sequence]; ok {
|
||||
return existing, false, nil
|
||||
}
|
||||
c.collectors[sequence] = col
|
||||
if c.highestCached < sequence {
|
||||
c.highestCached = sequence
|
||||
}
|
||||
return col, true, nil
|
||||
}
|
||||
|
||||
func (c *SequencedCollectors[ItemT]) getCollector(
|
||||
sequence uint64,
|
||||
) (Collector[ItemT], bool, error) {
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
if sequence < c.lowestRetained {
|
||||
return nil, false, ErrSequenceBelowRetention
|
||||
}
|
||||
col, found := c.collectors[sequence]
|
||||
return col, found, nil
|
||||
}
|
||||
|
||||
// GetCollector retrieves a collector for the provided sequence without creating
|
||||
// a new one.
|
||||
func (c *SequencedCollectors[ItemT]) GetCollector(
|
||||
sequence uint64,
|
||||
) (Collector[ItemT], bool, error) {
|
||||
return c.getCollector(sequence)
|
||||
}
|
||||
|
||||
// PruneUpToSequence removes collectors whose sequence is below the provided
|
||||
// threshold.
|
||||
func (c *SequencedCollectors[ItemT]) PruneUpToSequence(sequence uint64) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
if c.lowestRetained >= sequence {
|
||||
return
|
||||
}
|
||||
before := len(c.collectors)
|
||||
if before == 0 {
|
||||
c.lowestRetained = sequence
|
||||
return
|
||||
}
|
||||
|
||||
if uint64(before) < sequence-c.lowestRetained {
|
||||
for seq := range c.collectors {
|
||||
if seq < sequence {
|
||||
delete(c.collectors, seq)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for seq := c.lowestRetained; seq < sequence; seq++ {
|
||||
delete(c.collectors, seq)
|
||||
}
|
||||
}
|
||||
c.lowestRetained = sequence
|
||||
}
|
||||
13
node/keyedaggregator/errors.go
Normal file
13
node/keyedaggregator/errors.go
Normal file
@ -0,0 +1,13 @@
|
||||
package keyedaggregator
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
// ErrSequenceBelowRetention indicates that a collector for the requested
|
||||
// sequence can no longer be accessed because it was pruned.
|
||||
ErrSequenceBelowRetention = errors.New("sequence below retention threshold")
|
||||
|
||||
// ErrSequenceUnknown indicates that the requested sequence is not known to
|
||||
// the collaborating components and should be dropped.
|
||||
ErrSequenceUnknown = errors.New("unknown sequence")
|
||||
)
|
||||
116
node/keyedcollector/cache.go
Normal file
116
node/keyedcollector/cache.go
Normal file
@ -0,0 +1,116 @@
|
||||
package keyedcollector
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"source.quilibrium.com/quilibrium/monorepo/consensus/models"
|
||||
)
|
||||
|
||||
// RecordTraits specifies how to extract attributes from a record that are
|
||||
// required by the collector infrastructure.
|
||||
type RecordTraits[RecordT any] struct {
|
||||
Sequence func(*RecordT) uint64
|
||||
Identity func(*RecordT) models.Identity
|
||||
Equals func(*RecordT, *RecordT) bool
|
||||
}
|
||||
|
||||
func (t RecordTraits[RecordT]) validate() error {
|
||||
switch {
|
||||
case t.Sequence == nil:
|
||||
return fmt.Errorf("sequence accessor is required")
|
||||
case t.Identity == nil:
|
||||
return fmt.Errorf("identity accessor is required")
|
||||
case t.Equals == nil:
|
||||
return fmt.Errorf("equality comparator is required")
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// RecordCache stores the first record per identity for a particular sequence.
|
||||
// Subsequent duplicates are ignored, while conflicting records produce a
|
||||
// ConflictingRecordError.
|
||||
type RecordCache[RecordT any] struct {
|
||||
lock sync.RWMutex
|
||||
sequence uint64
|
||||
entries map[models.Identity]*RecordT
|
||||
traits RecordTraits[RecordT]
|
||||
}
|
||||
|
||||
func NewRecordCache[RecordT any](
|
||||
sequence uint64,
|
||||
traits RecordTraits[RecordT],
|
||||
) *RecordCache[RecordT] {
|
||||
return &RecordCache[RecordT]{
|
||||
sequence: sequence,
|
||||
entries: make(map[models.Identity]*RecordT),
|
||||
traits: traits,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *RecordCache[RecordT]) Sequence() uint64 { return c.sequence }
|
||||
|
||||
// Add stores the record in the cache, returning ErrRepeatedRecord when the
|
||||
// record already exists (same identity and equal contents) and
|
||||
// ConflictingRecordError when the record already exists but with different
|
||||
// contents. When an error is returned the record is not stored.
|
||||
func (c *RecordCache[RecordT]) Add(record *RecordT) error {
|
||||
if c.traits.Sequence(record) != c.sequence {
|
||||
return ErrRecordForDifferentSequence
|
||||
}
|
||||
|
||||
identity := c.traits.Identity(record)
|
||||
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
if existing, ok := c.entries[identity]; ok {
|
||||
if c.traits.Equals(existing, record) {
|
||||
return ErrRepeatedRecord
|
||||
}
|
||||
return NewConflictingRecordError(existing, record)
|
||||
}
|
||||
|
||||
c.entries[identity] = record
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get returns the stored record for the given identity.
|
||||
func (c *RecordCache[RecordT]) Get(
|
||||
identity models.Identity,
|
||||
) (*RecordT, bool) {
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
record, ok := c.entries[identity]
|
||||
return record, ok
|
||||
}
|
||||
|
||||
// Size returns the number of cached records.
|
||||
func (c *RecordCache[RecordT]) Size() int {
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
return len(c.entries)
|
||||
}
|
||||
|
||||
// All returns a snapshot of all cached records.
|
||||
func (c *RecordCache[RecordT]) All() []*RecordT {
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
result := make([]*RecordT, 0, len(c.entries))
|
||||
for _, record := range c.entries {
|
||||
result = append(result, record)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Remove deletes the record from the cache.
|
||||
func (c *RecordCache[RecordT]) Remove(record *RecordT) {
|
||||
if record == nil {
|
||||
return
|
||||
}
|
||||
identity := c.traits.Identity(record)
|
||||
c.lock.Lock()
|
||||
delete(c.entries, identity)
|
||||
c.lock.Unlock()
|
||||
}
|
||||
111
node/keyedcollector/collector.go
Normal file
111
node/keyedcollector/collector.go
Normal file
@ -0,0 +1,111 @@
|
||||
package keyedcollector
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"source.quilibrium.com/quilibrium/monorepo/consensus"
|
||||
)
|
||||
|
||||
// Processor handles validated records. Implementations are expected to be
|
||||
// concurrency-safe.
|
||||
type Processor[RecordT any] interface {
|
||||
Process(record *RecordT) error
|
||||
}
|
||||
|
||||
// CollectorConsumer receives notifications about collector events, such as
|
||||
// conflicting records or invalid records detected by the Processor.
|
||||
type CollectorConsumer[RecordT any] interface {
|
||||
OnRecordProcessed(record *RecordT)
|
||||
OnConflictingRecords(first, second *RecordT)
|
||||
OnInvalidRecord(err *InvalidRecordError[RecordT])
|
||||
}
|
||||
|
||||
// Collector implements the record caching/deduplication flow for a single
|
||||
// sequence. It stores the first record per identity, detects equivocations, and
|
||||
// delegates valid records to the configured Processor.
|
||||
type Collector[RecordT any] struct {
|
||||
tracer consensus.TraceLogger
|
||||
cache *RecordCache[RecordT]
|
||||
processor Processor[RecordT]
|
||||
consumer CollectorConsumer[RecordT]
|
||||
traits RecordTraits[RecordT]
|
||||
}
|
||||
|
||||
func NewCollector[RecordT any](
|
||||
tracer consensus.TraceLogger,
|
||||
sequence uint64,
|
||||
traits RecordTraits[RecordT],
|
||||
processor Processor[RecordT],
|
||||
consumer CollectorConsumer[RecordT],
|
||||
) (*Collector[RecordT], error) {
|
||||
if err := traits.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if processor == nil {
|
||||
return nil, fmt.Errorf("processor is required")
|
||||
}
|
||||
cache := NewRecordCache(sequence, traits)
|
||||
return &Collector[RecordT]{
|
||||
tracer: tracer,
|
||||
cache: cache,
|
||||
processor: processor,
|
||||
consumer: consumer,
|
||||
traits: traits,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Collector[RecordT]) Sequence() uint64 {
|
||||
return c.cache.Sequence()
|
||||
}
|
||||
|
||||
// Add inserts the record into the cache and triggers processing. Duplicate
|
||||
// records are ignored, conflicting records are surfaced to the consumer, and
|
||||
// invalid records (as indicated by the Processor) are reported via
|
||||
// CollectorConsumer.OnInvalidRecord.
|
||||
func (c *Collector[RecordT]) Add(record *RecordT) error {
|
||||
err := c.cache.Add(record)
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, ErrRepeatedRecord):
|
||||
return nil
|
||||
case errors.Is(err, ErrRecordForDifferentSequence):
|
||||
return fmt.Errorf("record sequence mismatch: %w", err)
|
||||
default:
|
||||
var conflict *ConflictingRecordError[RecordT]
|
||||
if errors.As(err, &conflict) {
|
||||
if c.consumer != nil {
|
||||
c.consumer.OnConflictingRecords(conflict.First(), conflict.Second())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("adding record to cache failed: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := c.processor.Process(record); err != nil {
|
||||
if invalid, ok := AsInvalidRecordError[RecordT](err); ok {
|
||||
c.tracer.Error("invalid record detected", err)
|
||||
if c.consumer != nil {
|
||||
c.consumer.OnInvalidRecord(invalid)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("processing record failed: %w", err)
|
||||
}
|
||||
|
||||
if c.consumer != nil {
|
||||
c.consumer.OnRecordProcessed(record)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Records returns a snapshot of all cached records.
|
||||
func (c *Collector[RecordT]) Records() []*RecordT {
|
||||
return c.cache.All()
|
||||
}
|
||||
|
||||
// Remove deletes the provided record from the cache.
|
||||
func (c *Collector[RecordT]) Remove(record *RecordT) {
|
||||
c.cache.Remove(record)
|
||||
}
|
||||
272
node/keyedcollector/collector_test.go
Normal file
272
node/keyedcollector/collector_test.go
Normal file
@ -0,0 +1,272 @@
|
||||
package keyedcollector
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"source.quilibrium.com/quilibrium/monorepo/consensus"
|
||||
"source.quilibrium.com/quilibrium/monorepo/consensus/models"
|
||||
"source.quilibrium.com/quilibrium/monorepo/lifecycle"
|
||||
"source.quilibrium.com/quilibrium/monorepo/node/keyedaggregator"
|
||||
)
|
||||
|
||||
type fakeRecord struct {
|
||||
sequence uint64
|
||||
identity models.Identity
|
||||
payload string
|
||||
}
|
||||
|
||||
func recordTraits() RecordTraits[fakeRecord] {
|
||||
return RecordTraits[fakeRecord]{
|
||||
Sequence: func(r *fakeRecord) uint64 { return r.sequence },
|
||||
Identity: func(r *fakeRecord) models.Identity { return r.identity },
|
||||
Equals: func(a, b *fakeRecord) bool {
|
||||
if a == nil || b == nil {
|
||||
return a == b
|
||||
}
|
||||
return a.payload == b.payload
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type noopProcessor struct {
|
||||
mu sync.Mutex
|
||||
records []*fakeRecord
|
||||
err error
|
||||
}
|
||||
|
||||
func (p *noopProcessor) Process(record *fakeRecord) error {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
p.records = append(p.records, record)
|
||||
return p.err
|
||||
}
|
||||
|
||||
type capturingConsumer struct {
|
||||
mu sync.Mutex
|
||||
processed []*fakeRecord
|
||||
conflicts [][2]*fakeRecord
|
||||
invalid []*InvalidRecordError[fakeRecord]
|
||||
}
|
||||
|
||||
func (c *capturingConsumer) OnRecordProcessed(record *fakeRecord) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
c.processed = append(c.processed, record)
|
||||
}
|
||||
|
||||
func (c *capturingConsumer) OnConflictingRecords(first, second *fakeRecord) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
c.conflicts = append(c.conflicts, [2]*fakeRecord{first, second})
|
||||
}
|
||||
|
||||
func (c *capturingConsumer) OnInvalidRecord(err *InvalidRecordError[fakeRecord]) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
c.invalid = append(c.invalid, err)
|
||||
}
|
||||
|
||||
type noopTracer struct{}
|
||||
|
||||
func (noopTracer) Trace(string, ...consensus.LogParam) {}
|
||||
func (noopTracer) Error(string, error, ...consensus.LogParam) {}
|
||||
func (noopTracer) With(...consensus.LogParam) consensus.TraceLogger { return noopTracer{} }
|
||||
|
||||
func TestCollectorProcessesRecord(t *testing.T) {
|
||||
t.Parallel()
|
||||
processor := &noopProcessor{}
|
||||
consumer := &capturingConsumer{}
|
||||
collector, err := NewCollector[fakeRecord](
|
||||
noopTracer{},
|
||||
1,
|
||||
recordTraits(),
|
||||
processor,
|
||||
consumer,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
record := &fakeRecord{sequence: 1, identity: "id", payload: "a"}
|
||||
require.NoError(t, collector.Add(record))
|
||||
|
||||
require.Len(t, consumer.processed, 1)
|
||||
require.Equal(t, record, consumer.processed[0])
|
||||
require.Len(t, processor.records, 1)
|
||||
require.Equal(t, record, processor.records[0])
|
||||
}
|
||||
|
||||
func TestCollectorIgnoresDuplicates(t *testing.T) {
|
||||
t.Parallel()
|
||||
processor := &noopProcessor{}
|
||||
collector, err := NewCollector[fakeRecord](
|
||||
noopTracer{},
|
||||
1,
|
||||
recordTraits(),
|
||||
processor,
|
||||
nil,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
record := &fakeRecord{sequence: 1, identity: "id", payload: "a"}
|
||||
require.NoError(t, collector.Add(record))
|
||||
require.NoError(t, collector.Add(&fakeRecord{sequence: 1, identity: "id", payload: "a"}))
|
||||
require.Len(t, processor.records, 1)
|
||||
}
|
||||
|
||||
func TestCollectorNotifiesConflicts(t *testing.T) {
|
||||
t.Parallel()
|
||||
processor := &noopProcessor{}
|
||||
consumer := &capturingConsumer{}
|
||||
collector, err := NewCollector[fakeRecord](
|
||||
noopTracer{},
|
||||
1,
|
||||
recordTraits(),
|
||||
processor,
|
||||
consumer,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, collector.Add(&fakeRecord{sequence: 1, identity: "id", payload: "a"}))
|
||||
require.NoError(t, collector.Add(&fakeRecord{sequence: 1, identity: "id", payload: "b"}))
|
||||
|
||||
require.Len(t, consumer.conflicts, 1)
|
||||
require.Equal(t, "a", consumer.conflicts[0][0].payload)
|
||||
require.Equal(t, "b", consumer.conflicts[0][1].payload)
|
||||
require.Len(t, processor.records, 1)
|
||||
}
|
||||
|
||||
func TestCollectorHandlesInvalidRecords(t *testing.T) {
|
||||
t.Parallel()
|
||||
invalid := NewInvalidRecordError(&fakeRecord{sequence: 1}, errors.New("boom"))
|
||||
processor := &noopProcessor{err: invalid}
|
||||
consumer := &capturingConsumer{}
|
||||
collector, err := NewCollector[fakeRecord](
|
||||
noopTracer{},
|
||||
1,
|
||||
recordTraits(),
|
||||
processor,
|
||||
consumer,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, collector.Add(&fakeRecord{sequence: 1, identity: "id"}))
|
||||
require.Len(t, consumer.invalid, 1)
|
||||
}
|
||||
|
||||
func TestCollectorPropagatesProcessorErrors(t *testing.T) {
|
||||
t.Parallel()
|
||||
processor := &noopProcessor{err: errors.New("fatal")}
|
||||
collector, err := NewCollector[fakeRecord](
|
||||
noopTracer{},
|
||||
1,
|
||||
recordTraits(),
|
||||
processor,
|
||||
nil,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = collector.Add(&fakeRecord{sequence: 1, identity: "id"})
|
||||
require.Error(t, err)
|
||||
require.ErrorContains(t, err, "processing record failed")
|
||||
}
|
||||
|
||||
func TestCollectorRejectsIncompatibleSequence(t *testing.T) {
|
||||
t.Parallel()
|
||||
processor := &noopProcessor{}
|
||||
collector, err := NewCollector[fakeRecord](
|
||||
noopTracer{},
|
||||
2,
|
||||
recordTraits(),
|
||||
processor,
|
||||
nil,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = collector.Add(&fakeRecord{sequence: 1, identity: "id"})
|
||||
require.Error(t, err)
|
||||
require.ErrorIs(t, err, ErrRecordForDifferentSequence)
|
||||
}
|
||||
|
||||
type mockProcessorFactory struct {
|
||||
mu sync.Mutex
|
||||
sequences []uint64
|
||||
processor Processor[fakeRecord]
|
||||
err error
|
||||
}
|
||||
|
||||
func (f *mockProcessorFactory) Create(sequence uint64) (Processor[fakeRecord], error) {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
if f.err != nil {
|
||||
return nil, f.err
|
||||
}
|
||||
f.sequences = append(f.sequences, sequence)
|
||||
if f.processor != nil {
|
||||
return f.processor, nil
|
||||
}
|
||||
return &noopProcessor{}, nil
|
||||
}
|
||||
|
||||
func TestFactoryCreatesCollector(t *testing.T) {
|
||||
t.Parallel()
|
||||
processorFactory := &mockProcessorFactory{}
|
||||
factory, err := NewFactory[fakeRecord](
|
||||
noopTracer{},
|
||||
recordTraits(),
|
||||
nil,
|
||||
processorFactory,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
collectorIface, err := factory.Create(3)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, collectorIface)
|
||||
require.Len(t, processorFactory.sequences, 1)
|
||||
require.Equal(t, uint64(3), processorFactory.sequences[0])
|
||||
}
|
||||
|
||||
func TestFactorySatisfiesKeyedAggregatorInterface(t *testing.T) {
|
||||
t.Parallel()
|
||||
processorFactory := &mockProcessorFactory{}
|
||||
factory, err := NewFactory[fakeRecord](
|
||||
noopTracer{},
|
||||
recordTraits(),
|
||||
nil,
|
||||
processorFactory,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
collectors := keyedaggregator.NewSequencedCollectors[fakeRecord](
|
||||
noopTracer{},
|
||||
0,
|
||||
factory,
|
||||
)
|
||||
aggregator, err := keyedaggregator.NewSequencedAggregator(
|
||||
noopTracer{},
|
||||
0,
|
||||
collectors,
|
||||
func(r *fakeRecord) uint64 { return r.sequence },
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
signalCtx, _ := lifecycle.WithSignaler(ctx)
|
||||
require.NoError(t, aggregator.ComponentManager.Start(signalCtx))
|
||||
<-aggregator.ComponentManager.Ready()
|
||||
|
||||
record := &fakeRecord{sequence: 0, identity: "id"}
|
||||
aggregator.Add(record)
|
||||
|
||||
require.Eventually(t, func() bool {
|
||||
return len(processorFactory.sequences) == 1
|
||||
}, time.Second, 10*time.Millisecond)
|
||||
|
||||
cancel()
|
||||
<-aggregator.ComponentManager.Done()
|
||||
}
|
||||
74
node/keyedcollector/errors.go
Normal file
74
node/keyedcollector/errors.go
Normal file
@ -0,0 +1,74 @@
|
||||
package keyedcollector
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrRepeatedRecord indicates that a record from the same identity with the
|
||||
// same content was added multiple times.
|
||||
ErrRepeatedRecord = errors.New("duplicated record")
|
||||
// ErrRecordForDifferentSequence indicates that a record belongs to a
|
||||
// different sequence than the collector handles.
|
||||
ErrRecordForDifferentSequence = errors.New("record for incompatible sequence")
|
||||
)
|
||||
|
||||
// ConflictingRecordError is emitted when two records for the same sequence and
|
||||
// identity contain different contents, signaling equivocation.
|
||||
type ConflictingRecordError[RecordT any] struct {
|
||||
first *RecordT
|
||||
second *RecordT
|
||||
}
|
||||
|
||||
func NewConflictingRecordError[RecordT any](
|
||||
first *RecordT,
|
||||
second *RecordT,
|
||||
) *ConflictingRecordError[RecordT] {
|
||||
return &ConflictingRecordError[RecordT]{first: first, second: second}
|
||||
}
|
||||
|
||||
func (e *ConflictingRecordError[RecordT]) Error() string {
|
||||
return "conflicting records detected"
|
||||
}
|
||||
|
||||
func (e *ConflictingRecordError[RecordT]) First() *RecordT { return e.first }
|
||||
func (e *ConflictingRecordError[RecordT]) Second() *RecordT { return e.second }
|
||||
|
||||
// InvalidRecordError indicates that a record failed validation. Processor
|
||||
// implementations should wrap contextual information in this error type to
|
||||
// signal recoverable invalid inputs to the collector.
|
||||
type InvalidRecordError[RecordT any] struct {
|
||||
Record *RecordT
|
||||
Cause error
|
||||
}
|
||||
|
||||
func NewInvalidRecordError[RecordT any](
|
||||
record *RecordT,
|
||||
cause error,
|
||||
) *InvalidRecordError[RecordT] {
|
||||
return &InvalidRecordError[RecordT]{Record: record, Cause: cause}
|
||||
}
|
||||
|
||||
func (e *InvalidRecordError[RecordT]) Error() string {
|
||||
if e.Cause == nil {
|
||||
return "invalid record"
|
||||
}
|
||||
return fmt.Sprintf("invalid record: %v", e.Cause)
|
||||
}
|
||||
|
||||
func (e *InvalidRecordError[RecordT]) Unwrap() error {
|
||||
return e.Cause
|
||||
}
|
||||
|
||||
// AsInvalidRecordError performs a typed errors.As lookup for
|
||||
// InvalidRecordError[RecordT].
|
||||
func AsInvalidRecordError[RecordT any](
|
||||
err error,
|
||||
) (*InvalidRecordError[RecordT], bool) {
|
||||
var invalid *InvalidRecordError[RecordT]
|
||||
if errors.As(err, &invalid) {
|
||||
return invalid, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
58
node/keyedcollector/factory.go
Normal file
58
node/keyedcollector/factory.go
Normal file
@ -0,0 +1,58 @@
|
||||
package keyedcollector
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"source.quilibrium.com/quilibrium/monorepo/consensus"
|
||||
"source.quilibrium.com/quilibrium/monorepo/node/keyedaggregator"
|
||||
)
|
||||
|
||||
// ProcessorFactory creates processors for a given sequence.
|
||||
type ProcessorFactory[RecordT any] interface {
|
||||
Create(sequence uint64) (Processor[RecordT], error)
|
||||
}
|
||||
|
||||
// Factory produces collectors for a given sequence, satisfying the
|
||||
// keyedaggregator.CollectorFactory interface.
|
||||
type Factory[RecordT any] struct {
|
||||
tracer consensus.TraceLogger
|
||||
traits RecordTraits[RecordT]
|
||||
consumer CollectorConsumer[RecordT]
|
||||
processorFactory ProcessorFactory[RecordT]
|
||||
}
|
||||
|
||||
func NewFactory[RecordT any](
|
||||
tracer consensus.TraceLogger,
|
||||
traits RecordTraits[RecordT],
|
||||
consumer CollectorConsumer[RecordT],
|
||||
processorFactory ProcessorFactory[RecordT],
|
||||
) (*Factory[RecordT], error) {
|
||||
if err := traits.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if processorFactory == nil {
|
||||
return nil, fmt.Errorf("processor factory is required")
|
||||
}
|
||||
return &Factory[RecordT]{
|
||||
tracer: tracer,
|
||||
traits: traits,
|
||||
consumer: consumer,
|
||||
processorFactory: processorFactory,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (f *Factory[RecordT]) Create(
|
||||
sequence uint64,
|
||||
) (keyedaggregator.Collector[RecordT], error) {
|
||||
processor, err := f.processorFactory.Create(sequence)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not create processor for sequence %d: %w", sequence, err)
|
||||
}
|
||||
return NewCollector(
|
||||
f.tracer,
|
||||
sequence,
|
||||
f.traits,
|
||||
processor,
|
||||
f.consumer,
|
||||
)
|
||||
}
|
||||
@ -13,6 +13,7 @@ import (
|
||||
"math/bits"
|
||||
"net"
|
||||
"runtime/debug"
|
||||
"slices"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
@ -55,7 +56,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
DecayInterval = 10 * time.Second
|
||||
DecayInterval = 10 * time.Minute
|
||||
AppDecay = .9
|
||||
)
|
||||
|
||||
@ -309,11 +310,11 @@ func NewBlossomSubWithHost(
|
||||
},
|
||||
&blossomsub.PeerScoreThresholds{
|
||||
SkipAtomicValidation: false,
|
||||
GossipThreshold: -2000,
|
||||
PublishThreshold: -5000,
|
||||
GraylistThreshold: -10000,
|
||||
AcceptPXThreshold: 1,
|
||||
OpportunisticGraftThreshold: 2,
|
||||
GossipThreshold: -500,
|
||||
PublishThreshold: -1000,
|
||||
GraylistThreshold: -2500,
|
||||
AcceptPXThreshold: 1000,
|
||||
OpportunisticGraftThreshold: 3.5,
|
||||
},
|
||||
))
|
||||
blossomOpts = append(blossomOpts, observability.WithPrometheusRawTracer())
|
||||
@ -711,14 +712,48 @@ func NewBlossomSub(
|
||||
if tracer != nil {
|
||||
blossomOpts = append(blossomOpts, blossomsub.WithEventTracer(tracer))
|
||||
}
|
||||
|
||||
GLOBAL_CONSENSUS_BITMASK := []byte{0x00}
|
||||
GLOBAL_FRAME_BITMASK := []byte{0x00, 0x00}
|
||||
GLOBAL_PROVER_BITMASK := []byte{0x00, 0x00, 0x00}
|
||||
GLOBAL_PEER_INFO_BITMASK := []byte{0x00, 0x00, 0x00, 0x00}
|
||||
GLOBAL_ALERT_BITMASK := bytes.Repeat([]byte{0x00}, 16)
|
||||
sets := getBitmaskSets(bytes.Repeat([]byte{0xff}, 32))
|
||||
sets = slices.Concat([][]byte{
|
||||
GLOBAL_CONSENSUS_BITMASK,
|
||||
GLOBAL_FRAME_BITMASK,
|
||||
GLOBAL_PROVER_BITMASK,
|
||||
GLOBAL_PEER_INFO_BITMASK,
|
||||
GLOBAL_ALERT_BITMASK,
|
||||
}, sets)
|
||||
bitmasksScoring := map[string]*blossomsub.BitmaskScoreParams{}
|
||||
for _, set := range sets {
|
||||
bitmasksScoring[string(set)] = &blossomsub.BitmaskScoreParams{
|
||||
SkipAtomicValidation: false,
|
||||
BitmaskWeight: 0.1,
|
||||
TimeInMeshWeight: 0.00027,
|
||||
TimeInMeshQuantum: time.Second,
|
||||
TimeInMeshCap: 1,
|
||||
FirstMessageDeliveriesWeight: 5,
|
||||
FirstMessageDeliveriesDecay: blossomsub.ScoreParameterDecay(
|
||||
10 * time.Minute,
|
||||
),
|
||||
FirstMessageDeliveriesCap: 10000,
|
||||
InvalidMessageDeliveriesWeight: -1000,
|
||||
InvalidMessageDeliveriesDecay: blossomsub.ScoreParameterDecay(time.Hour),
|
||||
}
|
||||
}
|
||||
|
||||
if p2pConfig.Network != 0 {
|
||||
blossomOpts = append(blossomOpts, blossomsub.WithPeerScore(
|
||||
&blossomsub.PeerScoreParams{
|
||||
SkipAtomicValidation: false,
|
||||
Bitmasks: bitmasksScoring,
|
||||
BitmaskScoreCap: 0,
|
||||
IPColocationFactorWeight: 0,
|
||||
IPColocationFactorThreshold: 6,
|
||||
BehaviourPenaltyWeight: -10,
|
||||
BehaviourPenaltyThreshold: 100,
|
||||
BehaviourPenaltyThreshold: 6,
|
||||
BehaviourPenaltyDecay: .5,
|
||||
DecayInterval: DecayInterval,
|
||||
DecayToZero: .1,
|
||||
@ -730,13 +765,58 @@ func NewBlossomSub(
|
||||
},
|
||||
&blossomsub.PeerScoreThresholds{
|
||||
SkipAtomicValidation: false,
|
||||
GossipThreshold: -2000,
|
||||
PublishThreshold: -5000,
|
||||
GraylistThreshold: -10000,
|
||||
AcceptPXThreshold: 1,
|
||||
OpportunisticGraftThreshold: 2,
|
||||
GossipThreshold: -500,
|
||||
PublishThreshold: -1000,
|
||||
GraylistThreshold: -2500,
|
||||
AcceptPXThreshold: 1000,
|
||||
OpportunisticGraftThreshold: 3.5,
|
||||
},
|
||||
))
|
||||
|
||||
} else {
|
||||
whitelist := []*net.IPNet{}
|
||||
for _, p := range directPeers {
|
||||
for _, i := range p.Addrs {
|
||||
ipnet, err := MultiaddrToIPNet(i)
|
||||
if err != nil {
|
||||
logger.Error(
|
||||
"could not convert direct peer for ip colocation whitelist",
|
||||
zap.String("peer_addr", i.String()),
|
||||
zap.Error(err),
|
||||
)
|
||||
}
|
||||
whitelist = append(whitelist, ipnet)
|
||||
}
|
||||
}
|
||||
blossomOpts = append(blossomOpts, blossomsub.WithPeerScore(
|
||||
&blossomsub.PeerScoreParams{
|
||||
SkipAtomicValidation: false,
|
||||
Bitmasks: bitmasksScoring,
|
||||
BitmaskScoreCap: 0,
|
||||
IPColocationFactorWeight: -100,
|
||||
IPColocationFactorThreshold: 6,
|
||||
IPColocationFactorWhitelist: whitelist,
|
||||
BehaviourPenaltyWeight: -10,
|
||||
BehaviourPenaltyThreshold: 6,
|
||||
BehaviourPenaltyDecay: .5,
|
||||
DecayInterval: DecayInterval,
|
||||
DecayToZero: .1,
|
||||
RetainScore: 60 * time.Minute,
|
||||
AppSpecificScore: func(p peer.ID) float64 {
|
||||
return float64(bs.GetPeerScore([]byte(p)))
|
||||
},
|
||||
AppSpecificWeight: 10.0,
|
||||
},
|
||||
&blossomsub.PeerScoreThresholds{
|
||||
SkipAtomicValidation: false,
|
||||
GossipThreshold: -500,
|
||||
PublishThreshold: -1000,
|
||||
GraylistThreshold: -2500,
|
||||
AcceptPXThreshold: 1000,
|
||||
OpportunisticGraftThreshold: 3.5,
|
||||
},
|
||||
))
|
||||
}
|
||||
blossomOpts = append(blossomOpts,
|
||||
blossomsub.WithValidateQueueSize(p2pConfig.ValidateQueueSize),
|
||||
blossomsub.WithValidateWorkers(p2pConfig.ValidateWorkers),
|
||||
@ -1611,3 +1691,83 @@ func getNetworkNamespace(network uint8) string {
|
||||
func (b *BlossomSub) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MultiaddrToIPNet converts a multiaddr containing /ip4 or /ip6
|
||||
// into a *net.IPNet with a host mask (/32 or /128).
|
||||
func MultiaddrToIPNet(m ma.Multiaddr) (*net.IPNet, error) {
|
||||
var (
|
||||
ip net.IP
|
||||
ipBits int
|
||||
)
|
||||
|
||||
// Walk components and grab the first IP we see.
|
||||
ma.ForEach(m, func(c ma.Component, err error) bool {
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
switch c.Protocol().Code {
|
||||
case ma.P_IP4:
|
||||
if ip == nil {
|
||||
ip = net.IP(c.RawValue()).To4()
|
||||
ipBits = 32
|
||||
}
|
||||
return false
|
||||
|
||||
case ma.P_IP6:
|
||||
if ip == nil {
|
||||
ip = net.IP(c.RawValue()).To16()
|
||||
ipBits = 128
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
if ip == nil {
|
||||
return nil, fmt.Errorf("multiaddr has no ip4/ip6 component: %s", m)
|
||||
}
|
||||
|
||||
mask := net.CIDRMask(ipBits, ipBits)
|
||||
|
||||
return &net.IPNet{
|
||||
IP: ip.Mask(mask),
|
||||
Mask: mask,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getBitmaskSets(bitmask []byte) [][]byte {
|
||||
sliced := [][]byte{}
|
||||
if bytes.Equal(bitmask, make([]byte, len(bitmask))) {
|
||||
sliced = append(sliced, bitmask)
|
||||
} else {
|
||||
for i, b := range bitmask {
|
||||
if b == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// fast: one bit in byte
|
||||
if b&(b-1) == 0 {
|
||||
slice := make([]byte, len(bitmask))
|
||||
slice[i] = b
|
||||
sliced = append(sliced, slice)
|
||||
sliced = append(sliced, slices.Concat([]byte{0}, slice))
|
||||
sliced = append(sliced, slices.Concat([]byte{0, 0}, slice))
|
||||
sliced = append(sliced, slices.Concat([]byte{0, 0, 0}, slice))
|
||||
continue
|
||||
}
|
||||
|
||||
for j := 7; j >= 0; j-- {
|
||||
if (b>>j)&1 == 1 {
|
||||
slice := make([]byte, len(bitmask))
|
||||
slice[i] = 1 << j
|
||||
sliced = append(sliced, slice)
|
||||
sliced = append(sliced, slices.Concat([]byte{0}, slice))
|
||||
sliced = append(sliced, slices.Concat([]byte{0, 0}, slice))
|
||||
sliced = append(sliced, slices.Concat([]byte{0, 0, 0}, slice))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sliced
|
||||
}
|
||||
|
||||
@ -31,6 +31,7 @@ import (
|
||||
"source.quilibrium.com/quilibrium/monorepo/protobufs"
|
||||
application "source.quilibrium.com/quilibrium/monorepo/types/hypergraph"
|
||||
"source.quilibrium.com/quilibrium/monorepo/types/tries"
|
||||
crypto "source.quilibrium.com/quilibrium/monorepo/types/tries"
|
||||
"source.quilibrium.com/quilibrium/monorepo/verenc"
|
||||
)
|
||||
|
||||
@ -58,7 +59,7 @@ func TestHypergraphSyncServer(t *testing.T) {
|
||||
data1 := enc.Encrypt(make([]byte, 20), pub)
|
||||
verenc1 := data1[0].Compress()
|
||||
vertices1 := make([]application.Vertex, numOperations)
|
||||
dataTree1 := &tries.VectorCommitmentTree{}
|
||||
dataTree1 := &crypto.VectorCommitmentTree{}
|
||||
logger, _ := zap.NewDevelopment()
|
||||
inclusionProver := bls48581.NewKZGInclusionProver(logger)
|
||||
for _, d := range []application.Encrypted{verenc1} {
|
||||
@ -272,7 +273,7 @@ func TestHypergraphSyncServer(t *testing.T) {
|
||||
}
|
||||
time.Sleep(10 * time.Second)
|
||||
str.CloseSend()
|
||||
leaves := tries.CompareLeaves(
|
||||
leaves := crypto.CompareLeaves(
|
||||
crdts[0].(*hgcrdt.HypergraphCRDT).GetVertexAddsSet(shardKey).GetTree(),
|
||||
crdts[1].(*hgcrdt.HypergraphCRDT).GetVertexAddsSet(shardKey).GetTree(),
|
||||
)
|
||||
@ -297,7 +298,7 @@ func TestHypergraphSyncServer(t *testing.T) {
|
||||
crdts[0].(*hgcrdt.HypergraphCRDT).GetVertexAddsSet(shardKey).GetTree().Commit(false),
|
||||
crdts[1].(*hgcrdt.HypergraphCRDT).GetVertexAddsSet(shardKey).GetTree().Commit(false),
|
||||
) {
|
||||
leaves := tries.CompareLeaves(
|
||||
leaves := crypto.CompareLeaves(
|
||||
crdts[0].(*hgcrdt.HypergraphCRDT).GetVertexAddsSet(shardKey).GetTree(),
|
||||
crdts[1].(*hgcrdt.HypergraphCRDT).GetVertexAddsSet(shardKey).GetTree(),
|
||||
)
|
||||
@ -330,7 +331,7 @@ func TestHypergraphPartialSync(t *testing.T) {
|
||||
data1 := enc.Encrypt(make([]byte, 20), pub)
|
||||
verenc1 := data1[0].Compress()
|
||||
vertices1 := make([]application.Vertex, numOperations)
|
||||
dataTree1 := &tries.VectorCommitmentTree{}
|
||||
dataTree1 := &crypto.VectorCommitmentTree{}
|
||||
logger, _ := zap.NewDevelopment()
|
||||
inclusionProver := bls48581.NewKZGInclusionProver(logger)
|
||||
domain := make([]byte, 32)
|
||||
@ -627,7 +628,7 @@ func TestHypergraphPartialSync(t *testing.T) {
|
||||
log.Fatalf("Client: failed to sync 1: %v", err)
|
||||
}
|
||||
str.CloseSend()
|
||||
leaves := tries.CompareLeaves(
|
||||
leaves := crypto.CompareLeaves(
|
||||
crdts[0].(*hgcrdt.HypergraphCRDT).GetVertexAddsSet(shardKey).GetTree(),
|
||||
crdts[1].(*hgcrdt.HypergraphCRDT).GetVertexAddsSet(shardKey).GetTree(),
|
||||
)
|
||||
@ -750,7 +751,7 @@ func TestHypergraphPartialSync(t *testing.T) {
|
||||
crdts[0].(*hgcrdt.HypergraphCRDT).GetVertexAddsSet(shardKey).GetTree().Commit(false),
|
||||
crdts[1].(*hgcrdt.HypergraphCRDT).GetVertexAddsSet(shardKey).GetTree().Commit(false),
|
||||
) {
|
||||
leaves := tries.CompareLeaves(
|
||||
leaves := crypto.CompareLeaves(
|
||||
crdts[0].(*hgcrdt.HypergraphCRDT).GetVertexAddsSet(shardKey).GetTree(),
|
||||
crdts[1].(*hgcrdt.HypergraphCRDT).GetVertexAddsSet(shardKey).GetTree(),
|
||||
)
|
||||
|
||||
@ -214,6 +214,7 @@ func (r *RPCServer) GetNodeInfo(
|
||||
PeerId: peer.ID(peerID).String(),
|
||||
PeerScore: uint64(r.pubSub.GetPeerScore(peerID)),
|
||||
Version: append([]byte{}, config.GetVersion()...),
|
||||
PatchVersion: []byte{config.GetPatchNumber()},
|
||||
PeerSeniority: seniority.FillBytes(make([]byte, 8)),
|
||||
RunningWorkers: uint32(len(workers)),
|
||||
AllocatedWorkers: allocated,
|
||||
|
||||
@ -416,18 +416,7 @@ func (w *WorkerManager) AllocateWorker(coreId uint, filter []byte) error {
|
||||
w.setWorkerAllocation(coreId, true)
|
||||
|
||||
// Refresh worker
|
||||
svc, err := w.getIPCOfWorker(coreId)
|
||||
if err != nil {
|
||||
w.logger.Error("could not get ipc of worker", zap.Error(err))
|
||||
return errors.Wrap(err, "allocate worker")
|
||||
}
|
||||
|
||||
ctx := w.currentContext()
|
||||
_, err = svc.Respawn(ctx, &protobufs.RespawnRequest{
|
||||
Filter: worker.Filter,
|
||||
})
|
||||
if err != nil {
|
||||
w.logger.Error("could not respawn worker", zap.Error(err))
|
||||
if err := w.respawnWorker(coreId, worker.Filter); err != nil {
|
||||
return errors.Wrap(err, "allocate worker")
|
||||
}
|
||||
|
||||
@ -496,18 +485,7 @@ func (w *WorkerManager) DeallocateWorker(coreId uint) error {
|
||||
}
|
||||
|
||||
// Refresh worker
|
||||
svc, err := w.getIPCOfWorker(coreId)
|
||||
if err != nil {
|
||||
w.logger.Error("could not get ipc of worker", zap.Error(err))
|
||||
return errors.Wrap(err, "allocate worker")
|
||||
}
|
||||
|
||||
ctx := w.currentContext()
|
||||
_, err = svc.Respawn(ctx, &protobufs.RespawnRequest{
|
||||
Filter: []byte{},
|
||||
})
|
||||
if err != nil {
|
||||
w.logger.Error("could not respawn worker", zap.Error(err))
|
||||
if err := w.respawnWorker(coreId, []byte{}); err != nil {
|
||||
return errors.Wrap(err, "allocate worker")
|
||||
}
|
||||
|
||||
@ -630,6 +608,34 @@ func (w *WorkerManager) RangeWorkers() ([]*typesStore.WorkerInfo, error) {
|
||||
return workers, nil
|
||||
}
|
||||
|
||||
const workerConnectivityTimeout = 5 * time.Second
|
||||
|
||||
func (w *WorkerManager) CheckWorkersConnected() ([]uint, error) {
|
||||
workers, err := w.store.RangeWorkers()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "check worker connectivity")
|
||||
}
|
||||
|
||||
unreachable := make([]uint, 0)
|
||||
for _, worker := range workers {
|
||||
_, err := w.getIPCOfWorkerWithTimeout(
|
||||
worker.CoreId,
|
||||
workerConnectivityTimeout,
|
||||
)
|
||||
if err != nil {
|
||||
w.logger.Debug(
|
||||
"worker unreachable during connectivity check",
|
||||
zap.Uint("core_id", worker.CoreId),
|
||||
zap.Error(err),
|
||||
)
|
||||
w.closeServiceClient(worker.CoreId)
|
||||
unreachable = append(unreachable, worker.CoreId)
|
||||
}
|
||||
}
|
||||
|
||||
return unreachable, nil
|
||||
}
|
||||
|
||||
// ProposeAllocations invokes a proposal function set by the parent of the
|
||||
// manager.
|
||||
func (w *WorkerManager) ProposeAllocations(
|
||||
@ -737,18 +743,12 @@ func (w *WorkerManager) loadWorkersFromStore() error {
|
||||
w.setWorkerAllocation(worker.CoreId, false)
|
||||
}
|
||||
totalStorage += uint64(worker.TotalStorage)
|
||||
svc, err := w.getIPCOfWorker(worker.CoreId)
|
||||
if err != nil {
|
||||
w.logger.Error("could not obtain IPC for worker", zap.Error(err))
|
||||
continue
|
||||
}
|
||||
|
||||
ctx := w.currentContext()
|
||||
_, err = svc.Respawn(ctx, &protobufs.RespawnRequest{
|
||||
Filter: worker.Filter,
|
||||
})
|
||||
if err != nil {
|
||||
w.logger.Error("could not respawn worker", zap.Error(err))
|
||||
if err := w.respawnWorker(worker.CoreId, worker.Filter); err != nil {
|
||||
w.logger.Error(
|
||||
"could not respawn worker",
|
||||
zap.Uint("core_id", worker.CoreId),
|
||||
zap.Error(err),
|
||||
)
|
||||
continue
|
||||
}
|
||||
}
|
||||
@ -800,21 +800,84 @@ func (w *WorkerManager) getP2PMultiaddrOfWorker(coreId uint) (
|
||||
return ma, errors.Wrap(err, "get p2p multiaddr of worker")
|
||||
}
|
||||
|
||||
func (w *WorkerManager) ensureWorkerRegistered(
|
||||
coreId uint,
|
||||
p2pAddr multiaddr.Multiaddr,
|
||||
streamAddr multiaddr.Multiaddr,
|
||||
) error {
|
||||
_, err := w.store.GetWorker(coreId)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
if err != nil && !errors.Is(err, store.ErrNotFound) {
|
||||
return err
|
||||
}
|
||||
|
||||
return w.registerWorker(&typesStore.WorkerInfo{
|
||||
CoreId: coreId,
|
||||
ListenMultiaddr: p2pAddr.String(),
|
||||
StreamListenMultiaddr: streamAddr.String(),
|
||||
Filter: nil,
|
||||
TotalStorage: 0,
|
||||
Automatic: len(w.config.Engine.DataWorkerP2PMultiaddrs) == 0,
|
||||
Allocated: false,
|
||||
})
|
||||
}
|
||||
|
||||
func (w *WorkerManager) getIPCOfWorker(coreId uint) (
|
||||
protobufs.DataIPCServiceClient,
|
||||
error,
|
||||
) {
|
||||
ctx := w.currentContext()
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
return w.getIPCOfWorkerWithContext(ctx, coreId)
|
||||
}
|
||||
|
||||
func (w *WorkerManager) getIPCOfWorkerWithTimeout(
|
||||
coreId uint,
|
||||
timeout time.Duration,
|
||||
) (protobufs.DataIPCServiceClient, error) {
|
||||
ctx := w.currentContext()
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
if timeout > 0 {
|
||||
var cancel context.CancelFunc
|
||||
ctx, cancel = context.WithTimeout(ctx, timeout)
|
||||
defer cancel()
|
||||
}
|
||||
return w.getIPCOfWorkerWithContext(ctx, coreId)
|
||||
}
|
||||
|
||||
func (w *WorkerManager) getIPCOfWorkerWithContext(
|
||||
ctx context.Context,
|
||||
coreId uint,
|
||||
) (
|
||||
protobufs.DataIPCServiceClient,
|
||||
error,
|
||||
) {
|
||||
if client, ok := w.getServiceClient(coreId); ok {
|
||||
return protobufs.NewDataIPCServiceClient(client), nil
|
||||
}
|
||||
|
||||
w.logger.Info("reconnecting to worker", zap.Uint("core_id", coreId))
|
||||
addr, err := w.getMultiaddrOfWorker(coreId)
|
||||
streamAddr, err := w.getMultiaddrOfWorker(coreId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "get ipc of worker")
|
||||
}
|
||||
|
||||
mga, err := mn.ToNetAddr(addr)
|
||||
p2pAddr, err := w.getP2PMultiaddrOfWorker(coreId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "get ipc of worker")
|
||||
}
|
||||
|
||||
if err := w.ensureWorkerRegistered(coreId, p2pAddr, streamAddr); err != nil {
|
||||
return nil, errors.Wrap(err, "get ipc of worker")
|
||||
}
|
||||
|
||||
mga, err := mn.ToNetAddr(streamAddr)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "get ipc of worker")
|
||||
}
|
||||
@ -825,25 +888,6 @@ func (w *WorkerManager) getIPCOfWorker(coreId uint) (
|
||||
return nil, errors.Wrap(err, "get ipc of worker")
|
||||
}
|
||||
|
||||
if !w.hasWorkerFilter(coreId) {
|
||||
p2pAddr, err := w.getP2PMultiaddrOfWorker(coreId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "get ipc of worker")
|
||||
}
|
||||
err = w.registerWorker(&typesStore.WorkerInfo{
|
||||
CoreId: coreId,
|
||||
ListenMultiaddr: p2pAddr.String(),
|
||||
StreamListenMultiaddr: addr.String(),
|
||||
Filter: nil,
|
||||
TotalStorage: 0,
|
||||
Automatic: len(w.config.Engine.DataWorkerP2PMultiaddrs) == 0,
|
||||
Allocated: false,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "get ipc of worker")
|
||||
}
|
||||
}
|
||||
|
||||
privKey, err := crypto.UnmarshalEd448PrivateKey(peerPrivKey)
|
||||
if err != nil {
|
||||
w.logger.Error("error unmarshaling peerkey", zap.Error(err))
|
||||
@ -874,16 +918,120 @@ func (w *WorkerManager) getIPCOfWorker(coreId uint) (
|
||||
return nil, errors.Wrap(err, "get ipc of worker")
|
||||
}
|
||||
|
||||
client, err := grpc.NewClient(
|
||||
return w.dialWorkerWithRetry(
|
||||
ctx,
|
||||
coreId,
|
||||
mga.String(),
|
||||
grpc.WithTransportCredentials(creds),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "get ipc of worker")
|
||||
}
|
||||
|
||||
func (w *WorkerManager) dialWorkerWithRetry(
|
||||
ctx context.Context,
|
||||
coreId uint,
|
||||
target string,
|
||||
opts ...grpc.DialOption,
|
||||
) (protobufs.DataIPCServiceClient, error) {
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
|
||||
const (
|
||||
initialBackoff = 50 * time.Millisecond
|
||||
maxBackoff = 5 * time.Second
|
||||
)
|
||||
|
||||
backoff := initialBackoff
|
||||
for {
|
||||
client, err := grpc.NewClient(target, opts...)
|
||||
if err == nil {
|
||||
w.setServiceClient(coreId, client)
|
||||
return protobufs.NewDataIPCServiceClient(client), nil
|
||||
}
|
||||
|
||||
w.logger.Info(
|
||||
"worker dial failed, retrying",
|
||||
zap.Uint("core_id", coreId),
|
||||
zap.String("target", target),
|
||||
zap.Duration("backoff", backoff),
|
||||
zap.Error(err),
|
||||
)
|
||||
|
||||
select {
|
||||
case <-time.After(backoff):
|
||||
case <-ctx.Done():
|
||||
return nil, errors.Wrap(ctx.Err(), "get ipc of worker")
|
||||
}
|
||||
|
||||
if backoff < maxBackoff {
|
||||
backoff *= 2
|
||||
if backoff > maxBackoff {
|
||||
backoff = maxBackoff
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (w *WorkerManager) respawnWorker(
|
||||
coreId uint,
|
||||
filter []byte,
|
||||
) error {
|
||||
const (
|
||||
respawnTimeout = 5 * time.Second
|
||||
initialBackoff = 50 * time.Millisecond
|
||||
maxRespawnBackoff = 2 * time.Second
|
||||
)
|
||||
|
||||
managerCtx := w.currentContext()
|
||||
if managerCtx == nil {
|
||||
managerCtx = context.Background()
|
||||
}
|
||||
|
||||
backoff := initialBackoff
|
||||
for {
|
||||
svc, err := w.getIPCOfWorker(coreId)
|
||||
if err != nil {
|
||||
w.logger.Error(
|
||||
"could not get ipc of worker",
|
||||
zap.Uint("core_id", coreId),
|
||||
zap.Error(err),
|
||||
)
|
||||
select {
|
||||
case <-time.After(backoff):
|
||||
case <-managerCtx.Done():
|
||||
return errors.Wrap(managerCtx.Err(), "respawn worker")
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(managerCtx, respawnTimeout)
|
||||
_, err = svc.Respawn(ctx, &protobufs.RespawnRequest{Filter: filter})
|
||||
cancel()
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
w.logger.Warn(
|
||||
"worker respawn failed, retrying",
|
||||
zap.Uint("core_id", coreId),
|
||||
zap.Duration("backoff", backoff),
|
||||
zap.Error(err),
|
||||
)
|
||||
w.closeServiceClient(coreId)
|
||||
|
||||
select {
|
||||
case <-time.After(backoff):
|
||||
case <-managerCtx.Done():
|
||||
return errors.Wrap(managerCtx.Err(), "respawn worker")
|
||||
}
|
||||
|
||||
if backoff < maxRespawnBackoff {
|
||||
backoff *= 2
|
||||
if backoff > maxRespawnBackoff {
|
||||
backoff = maxRespawnBackoff
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (w *WorkerManager) spawnDataWorkers() {
|
||||
|
||||
@ -80,6 +80,12 @@ func (m *InboxMessage) FromCanonicalBytes(data []byte) error {
|
||||
if err := binary.Read(buf, binary.BigEndian, &addressLen); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
}
|
||||
if addressLen > 64 {
|
||||
return errors.Wrap(
|
||||
errors.New("invalid address length"),
|
||||
"from canonical bytes",
|
||||
)
|
||||
}
|
||||
m.Address = make([]byte, addressLen)
|
||||
if _, err := buf.Read(m.Address); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
@ -95,6 +101,12 @@ func (m *InboxMessage) FromCanonicalBytes(data []byte) error {
|
||||
if err := binary.Read(buf, binary.BigEndian, &ephemeralKeyLen); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
}
|
||||
if ephemeralKeyLen > 57 {
|
||||
return errors.Wrap(
|
||||
errors.New("invalid ephemeral key length"),
|
||||
"from canonical bytes",
|
||||
)
|
||||
}
|
||||
m.EphemeralPublicKey = make([]byte, ephemeralKeyLen)
|
||||
if _, err := buf.Read(m.EphemeralPublicKey); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
@ -105,6 +117,12 @@ func (m *InboxMessage) FromCanonicalBytes(data []byte) error {
|
||||
if err := binary.Read(buf, binary.BigEndian, &messageLen); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
}
|
||||
if messageLen > 5*1024*1024 {
|
||||
return errors.Wrap(
|
||||
errors.New("invalid message length"),
|
||||
"from canonical bytes",
|
||||
)
|
||||
}
|
||||
m.Message = make([]byte, messageLen)
|
||||
if _, err := buf.Read(m.Message); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
@ -224,6 +242,12 @@ func (m *HubAddInboxMessage) FromCanonicalBytes(data []byte) error {
|
||||
if err := binary.Read(buf, binary.BigEndian, &addressLen); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
}
|
||||
if addressLen > 64 {
|
||||
return errors.Wrap(
|
||||
errors.New("invalid address length"),
|
||||
"from canonical bytes",
|
||||
)
|
||||
}
|
||||
m.Address = make([]byte, addressLen)
|
||||
if _, err := buf.Read(m.Address); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
@ -234,6 +258,12 @@ func (m *HubAddInboxMessage) FromCanonicalBytes(data []byte) error {
|
||||
if err := binary.Read(buf, binary.BigEndian, &inboxKeyLen); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
}
|
||||
if inboxKeyLen > 57 {
|
||||
return errors.Wrap(
|
||||
errors.New("invalid inbox key length"),
|
||||
"from canonical bytes",
|
||||
)
|
||||
}
|
||||
m.InboxPublicKey = make([]byte, inboxKeyLen)
|
||||
if _, err := buf.Read(m.InboxPublicKey); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
@ -244,6 +274,12 @@ func (m *HubAddInboxMessage) FromCanonicalBytes(data []byte) error {
|
||||
if err := binary.Read(buf, binary.BigEndian, &hubKeyLen); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
}
|
||||
if hubKeyLen > 57 {
|
||||
return errors.Wrap(
|
||||
errors.New("invalid hub key length"),
|
||||
"from canonical bytes",
|
||||
)
|
||||
}
|
||||
m.HubPublicKey = make([]byte, hubKeyLen)
|
||||
if _, err := buf.Read(m.HubPublicKey); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
@ -254,6 +290,12 @@ func (m *HubAddInboxMessage) FromCanonicalBytes(data []byte) error {
|
||||
if err := binary.Read(buf, binary.BigEndian, &inboxSignatureLen); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
}
|
||||
if inboxSignatureLen > 114 {
|
||||
return errors.Wrap(
|
||||
errors.New("invalid inbox signature length"),
|
||||
"from canonical bytes",
|
||||
)
|
||||
}
|
||||
m.InboxSignature = make([]byte, inboxSignatureLen)
|
||||
if _, err := buf.Read(m.InboxSignature); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
@ -264,6 +306,12 @@ func (m *HubAddInboxMessage) FromCanonicalBytes(data []byte) error {
|
||||
if err := binary.Read(buf, binary.BigEndian, &hubSignatureLen); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
}
|
||||
if hubSignatureLen > 114 {
|
||||
return errors.Wrap(
|
||||
errors.New("invalid hub signature length"),
|
||||
"from canonical bytes",
|
||||
)
|
||||
}
|
||||
m.HubSignature = make([]byte, hubSignatureLen)
|
||||
if _, err := buf.Read(m.HubSignature); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
@ -386,6 +434,12 @@ func (m *HubDeleteInboxMessage) FromCanonicalBytes(data []byte) error {
|
||||
if err := binary.Read(buf, binary.BigEndian, &addressLen); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
}
|
||||
if addressLen > 64 {
|
||||
return errors.Wrap(
|
||||
errors.New("invalid address length"),
|
||||
"from canonical bytes",
|
||||
)
|
||||
}
|
||||
m.Address = make([]byte, addressLen)
|
||||
if _, err := buf.Read(m.Address); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
@ -396,6 +450,12 @@ func (m *HubDeleteInboxMessage) FromCanonicalBytes(data []byte) error {
|
||||
if err := binary.Read(buf, binary.BigEndian, &inboxKeyLen); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
}
|
||||
if inboxKeyLen > 57 {
|
||||
return errors.Wrap(
|
||||
errors.New("invalid inbox key length"),
|
||||
"from canonical bytes",
|
||||
)
|
||||
}
|
||||
m.InboxPublicKey = make([]byte, inboxKeyLen)
|
||||
if _, err := buf.Read(m.InboxPublicKey); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
@ -406,6 +466,12 @@ func (m *HubDeleteInboxMessage) FromCanonicalBytes(data []byte) error {
|
||||
if err := binary.Read(buf, binary.BigEndian, &hubKeyLen); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
}
|
||||
if hubKeyLen > 57 {
|
||||
return errors.Wrap(
|
||||
errors.New("invalid hub key length"),
|
||||
"from canonical bytes",
|
||||
)
|
||||
}
|
||||
m.HubPublicKey = make([]byte, hubKeyLen)
|
||||
if _, err := buf.Read(m.HubPublicKey); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
@ -416,6 +482,12 @@ func (m *HubDeleteInboxMessage) FromCanonicalBytes(data []byte) error {
|
||||
if err := binary.Read(buf, binary.BigEndian, &inboxSignatureLen); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
}
|
||||
if inboxSignatureLen > 114 {
|
||||
return errors.Wrap(
|
||||
errors.New("invalid inbox signature length"),
|
||||
"from canonical bytes",
|
||||
)
|
||||
}
|
||||
m.InboxSignature = make([]byte, inboxSignatureLen)
|
||||
if _, err := buf.Read(m.InboxSignature); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
@ -426,6 +498,12 @@ func (m *HubDeleteInboxMessage) FromCanonicalBytes(data []byte) error {
|
||||
if err := binary.Read(buf, binary.BigEndian, &hubSignatureLen); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
}
|
||||
if hubSignatureLen > 114 {
|
||||
return errors.Wrap(
|
||||
errors.New("invalid hub signature length"),
|
||||
"from canonical bytes",
|
||||
)
|
||||
}
|
||||
m.HubSignature = make([]byte, hubSignatureLen)
|
||||
if _, err := buf.Read(m.HubSignature); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
|
||||
@ -37,7 +37,7 @@ func TestInboxMessage_Serialization(t *testing.T) {
|
||||
msg: &InboxMessage{
|
||||
Address: []byte{0xAA, 0xBB, 0xCC},
|
||||
Timestamp: uint64(time.Now().UnixMilli()),
|
||||
EphemeralPublicKey: randomBytesDispatch(t, 64),
|
||||
EphemeralPublicKey: randomBytesDispatch(t, 57),
|
||||
Message: randomBytesDispatch(t, 1024),
|
||||
},
|
||||
},
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -165,14 +165,19 @@ func TestFrameHeader_Serialization(t *testing.T) {
|
||||
{
|
||||
name: "complete frame header",
|
||||
header: &FrameHeader{
|
||||
Address: make([]byte, 32),
|
||||
Address: make([]byte, 64),
|
||||
FrameNumber: 99999,
|
||||
Timestamp: 1234567890123,
|
||||
Difficulty: 1000000,
|
||||
Output: make([]byte, 516), // VDF output: 258 + 258 bytes
|
||||
ParentSelector: make([]byte, 32),
|
||||
RequestsRoot: make([]byte, 32),
|
||||
StateRoots: [][]byte{make([]byte, 32), make([]byte, 32)},
|
||||
RequestsRoot: make([]byte, 74),
|
||||
StateRoots: [][]byte{
|
||||
make([]byte, 74),
|
||||
make([]byte, 74),
|
||||
make([]byte, 74),
|
||||
make([]byte, 74),
|
||||
},
|
||||
Prover: make([]byte, 32),
|
||||
FeeMultiplierVote: 500,
|
||||
PublicKeySignatureBls48581: &BLS48581AggregateSignature{
|
||||
@ -184,22 +189,6 @@ func TestFrameHeader_Serialization(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "minimal frame header",
|
||||
header: &FrameHeader{
|
||||
Address: []byte{},
|
||||
FrameNumber: 0,
|
||||
Timestamp: 0,
|
||||
Difficulty: 0,
|
||||
Output: []byte{},
|
||||
ParentSelector: []byte{},
|
||||
RequestsRoot: []byte{},
|
||||
StateRoots: [][]byte{},
|
||||
Prover: []byte{},
|
||||
FeeMultiplierVote: 0,
|
||||
PublicKeySignatureBls48581: nil,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
@ -626,15 +615,15 @@ func TestProverKick_Serialization(t *testing.T) {
|
||||
name: "complete prover kick",
|
||||
kick: &ProverKick{
|
||||
FrameNumber: 66666,
|
||||
KickedProverPublicKey: make([]byte, 57), // Ed448 public key
|
||||
KickedProverPublicKey: make([]byte, 585), // BLS48-581 public key
|
||||
ConflictingFrame_1: make([]byte, 32),
|
||||
ConflictingFrame_2: make([]byte, 32),
|
||||
Commitment: make([]byte, 32),
|
||||
Proof: make([]byte, 128),
|
||||
Proof: make([]byte, 160),
|
||||
TraversalProof: &TraversalProof{
|
||||
Multiproof: &Multiproof{
|
||||
Multicommitment: make([]byte, 32),
|
||||
Proof: make([]byte, 64),
|
||||
Multicommitment: make([]byte, 74),
|
||||
Proof: make([]byte, 74),
|
||||
},
|
||||
SubProofs: []*TraversalSubProof{
|
||||
{
|
||||
@ -646,18 +635,6 @@ func TestProverKick_Serialization(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "minimal prover kick",
|
||||
kick: &ProverKick{
|
||||
FrameNumber: 0,
|
||||
KickedProverPublicKey: []byte{},
|
||||
ConflictingFrame_1: []byte{},
|
||||
ConflictingFrame_2: []byte{},
|
||||
Commitment: []byte{},
|
||||
Proof: []byte{},
|
||||
TraversalProof: nil,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
@ -1030,7 +1007,12 @@ func TestAppShardFrame_Serialization(t *testing.T) {
|
||||
Output: make([]byte, 516),
|
||||
ParentSelector: make([]byte, 32),
|
||||
RequestsRoot: make([]byte, 32),
|
||||
StateRoots: [][]byte{make([]byte, 32), make([]byte, 32)},
|
||||
StateRoots: [][]byte{
|
||||
make([]byte, 74),
|
||||
make([]byte, 74),
|
||||
make([]byte, 74),
|
||||
make([]byte, 74),
|
||||
},
|
||||
Prover: make([]byte, 32),
|
||||
FeeMultiplierVote: 250,
|
||||
PublicKeySignatureBls48581: &BLS48581AggregateSignature{
|
||||
@ -1071,13 +1053,6 @@ func TestAppShardFrame_Serialization(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "minimal app shard frame",
|
||||
frame: &AppShardFrame{
|
||||
Header: nil,
|
||||
Requests: []*MessageBundle{},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
@ -1216,22 +1191,15 @@ func TestMultiproof_Serialization(t *testing.T) {
|
||||
{
|
||||
name: "complete multiproof",
|
||||
multiproof: &Multiproof{
|
||||
Multicommitment: make([]byte, 32),
|
||||
Proof: make([]byte, 256),
|
||||
Multicommitment: make([]byte, 74),
|
||||
Proof: make([]byte, 74),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multiproof with different sizes",
|
||||
multiproof: &Multiproof{
|
||||
Multicommitment: append([]byte{0xAA}, make([]byte, 31)...),
|
||||
Proof: append([]byte{0xBB}, make([]byte, 255)...),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "minimal multiproof",
|
||||
multiproof: &Multiproof{
|
||||
Multicommitment: []byte{},
|
||||
Proof: []byte{},
|
||||
Multicommitment: append([]byte{0xAA}, make([]byte, 73)...),
|
||||
Proof: append([]byte{0xBB}, make([]byte, 73)...),
|
||||
},
|
||||
},
|
||||
}
|
||||
@ -1359,33 +1327,23 @@ func TestTraversalProof_Serialization(t *testing.T) {
|
||||
name: "complete traversal proof",
|
||||
proof: &TraversalProof{
|
||||
Multiproof: &Multiproof{
|
||||
Multicommitment: make([]byte, 32),
|
||||
Proof: make([]byte, 128),
|
||||
Multicommitment: make([]byte, 74),
|
||||
Proof: make([]byte, 74),
|
||||
},
|
||||
SubProofs: []*TraversalSubProof{
|
||||
{
|
||||
Commits: [][]byte{make([]byte, 32)},
|
||||
Commits: [][]byte{make([]byte, 74)},
|
||||
Ys: [][]byte{make([]byte, 48)},
|
||||
Paths: []*Path{{Indices: []uint64{1, 2}}},
|
||||
},
|
||||
{
|
||||
Commits: [][]byte{make([]byte, 32), make([]byte, 32)},
|
||||
Commits: [][]byte{make([]byte, 74), make([]byte, 74)},
|
||||
Ys: [][]byte{make([]byte, 48), make([]byte, 48)},
|
||||
Paths: []*Path{{Indices: []uint64{3, 4, 5}}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "minimal traversal proof",
|
||||
proof: &TraversalProof{
|
||||
Multiproof: &Multiproof{
|
||||
Multicommitment: []byte{},
|
||||
Proof: []byte{},
|
||||
},
|
||||
SubProofs: []*TraversalSubProof{},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
||||
@ -87,7 +87,7 @@ func (s *SignedX448Key) Validate() error {
|
||||
}
|
||||
|
||||
// Parent key address should be non-zero bytes
|
||||
if len(s.ParentKeyAddress) == 0 {
|
||||
if len(s.ParentKeyAddress) == 0 || len(s.ParentKeyAddress) > 64 {
|
||||
return errors.Wrap(
|
||||
errors.New("invalid parent key address length"),
|
||||
"validate",
|
||||
@ -173,7 +173,7 @@ func (s *SignedDecaf448Key) Validate() error {
|
||||
}
|
||||
|
||||
// Parent key address should be non-zero bytes
|
||||
if len(s.ParentKeyAddress) == 0 {
|
||||
if len(s.ParentKeyAddress) == 0 || len(s.ParentKeyAddress) > 64 {
|
||||
return errors.Wrap(
|
||||
errors.New("invalid parent key address length"),
|
||||
"validate",
|
||||
@ -245,8 +245,12 @@ func (k *KeyCollection) Validate() error {
|
||||
}
|
||||
|
||||
// KeyPurpose should not be empty
|
||||
if k.KeyPurpose == "" {
|
||||
return errors.Wrap(errors.New("empty key purpose"), "validate")
|
||||
if k.KeyPurpose == "" || len(k.KeyPurpose) > 32 {
|
||||
return errors.Wrap(errors.New("invalid key purpose length"), "validate")
|
||||
}
|
||||
|
||||
if len(k.X448Keys) > 20 {
|
||||
return errors.Wrap(errors.New("invalid key collection length"), "validate")
|
||||
}
|
||||
|
||||
// Validate all x448 keys
|
||||
@ -256,6 +260,10 @@ func (k *KeyCollection) Validate() error {
|
||||
}
|
||||
}
|
||||
|
||||
if len(k.Decaf448Keys) > 20 {
|
||||
return errors.Wrap(errors.New("invalid key collection length"), "validate")
|
||||
}
|
||||
|
||||
// Validate all decaf448 keys
|
||||
for i, key := range k.Decaf448Keys {
|
||||
if err := key.Validate(); err != nil {
|
||||
@ -274,6 +282,10 @@ func (k *KeyRegistry) Validate() error {
|
||||
)
|
||||
}
|
||||
|
||||
if len(k.KeysByPurpose) > 20 {
|
||||
return errors.Wrap(errors.New("invalid purpose set length"), "validate")
|
||||
}
|
||||
|
||||
// Validate keys by purpose map
|
||||
for purpose, collection := range k.KeysByPurpose {
|
||||
if err := collection.Validate(); err != nil {
|
||||
@ -389,8 +401,11 @@ func (s *SignedX448Key) Verify(
|
||||
return errors.Wrap(errors.New("invalid length for key"), "verify")
|
||||
}
|
||||
|
||||
if len(s.ParentKeyAddress) == 0 {
|
||||
return errors.Wrap(errors.New("parent key address required"), "verify")
|
||||
if len(s.ParentKeyAddress) == 0 || len(s.ParentKeyAddress) > 64 {
|
||||
return errors.Wrap(
|
||||
errors.New("invalid parent key address length"),
|
||||
"verify",
|
||||
)
|
||||
}
|
||||
|
||||
// Verify signature and check that parent key address matches
|
||||
@ -515,8 +530,11 @@ func (s *SignedDecaf448Key) Verify(
|
||||
return errors.Wrap(errors.New("invalid length for key"), "verify")
|
||||
}
|
||||
|
||||
if len(s.ParentKeyAddress) == 0 {
|
||||
return errors.Wrap(errors.New("parent key address required"), "verify")
|
||||
if len(s.ParentKeyAddress) == 0 || len(s.ParentKeyAddress) > 64 {
|
||||
return errors.Wrap(
|
||||
errors.New("invalid parent key address length"),
|
||||
"verify",
|
||||
)
|
||||
}
|
||||
|
||||
// Verify signature and check that parent key address matches
|
||||
@ -746,6 +764,9 @@ func (e *Ed448Signature) FromCanonicalBytes(data []byte) error {
|
||||
if err := binary.Read(buf, binary.BigEndian, &keyLen); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
}
|
||||
if keyLen > 61 {
|
||||
return errors.Wrap(errors.New("invalid key length"), "from canonical bytes")
|
||||
}
|
||||
if keyLen > 0 {
|
||||
keyBytes := make([]byte, keyLen)
|
||||
if _, err := buf.Read(keyBytes); err != nil {
|
||||
@ -762,6 +783,12 @@ func (e *Ed448Signature) FromCanonicalBytes(data []byte) error {
|
||||
if err := binary.Read(buf, binary.BigEndian, &sigLen); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
}
|
||||
if sigLen > 114 {
|
||||
return errors.Wrap(
|
||||
errors.New("invalid signature length"),
|
||||
"from canonical bytes",
|
||||
)
|
||||
}
|
||||
e.Signature = make([]byte, sigLen)
|
||||
if _, err := buf.Read(e.Signature); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
@ -893,6 +920,9 @@ func (b *BLS48581Signature) FromCanonicalBytes(data []byte) error {
|
||||
if err := binary.Read(buf, binary.BigEndian, &keyLen); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
}
|
||||
if keyLen > 589 {
|
||||
return errors.Wrap(errors.New("invalid key length"), "from canonical bytes")
|
||||
}
|
||||
if keyLen > 0 {
|
||||
keyBytes := make([]byte, keyLen)
|
||||
if _, err := buf.Read(keyBytes); err != nil {
|
||||
@ -909,6 +939,12 @@ func (b *BLS48581Signature) FromCanonicalBytes(data []byte) error {
|
||||
if err := binary.Read(buf, binary.BigEndian, &sigLen); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
}
|
||||
if sigLen > 74 {
|
||||
return errors.Wrap(
|
||||
errors.New("invalid signature length"),
|
||||
"from canonical bytes",
|
||||
)
|
||||
}
|
||||
b.Signature = make([]byte, sigLen)
|
||||
if _, err := buf.Read(b.Signature); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
@ -1007,6 +1043,12 @@ func (b *BLS48581SignatureWithProofOfPossession) FromCanonicalBytes(
|
||||
if err := binary.Read(buf, binary.BigEndian, &sigLen); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
}
|
||||
if sigLen != 74 {
|
||||
return errors.Wrap(
|
||||
errors.New("invalid signature length"),
|
||||
"from canonical bytes",
|
||||
)
|
||||
}
|
||||
b.Signature = make([]byte, sigLen)
|
||||
if _, err := buf.Read(b.Signature); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
@ -1018,6 +1060,12 @@ func (b *BLS48581SignatureWithProofOfPossession) FromCanonicalBytes(
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
}
|
||||
if pubKeyLen > 0 {
|
||||
if pubKeyLen != 589 {
|
||||
return errors.Wrap(
|
||||
errors.New("invalid pubkey length"),
|
||||
"from canonical bytes",
|
||||
)
|
||||
}
|
||||
pubKeyBytes := make([]byte, pubKeyLen)
|
||||
if _, err := buf.Read(pubKeyBytes); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
@ -1033,6 +1081,12 @@ func (b *BLS48581SignatureWithProofOfPossession) FromCanonicalBytes(
|
||||
if err := binary.Read(buf, binary.BigEndian, &popSigLen); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
}
|
||||
if popSigLen != 74 {
|
||||
return errors.Wrap(
|
||||
errors.New("invalid pop length"),
|
||||
"from canonical bytes",
|
||||
)
|
||||
}
|
||||
b.PopSignature = make([]byte, popSigLen)
|
||||
if _, err := buf.Read(b.PopSignature); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
@ -1103,6 +1157,12 @@ func (b *BLS48581AddressedSignature) FromCanonicalBytes(data []byte) error {
|
||||
if err := binary.Read(buf, binary.BigEndian, &sigLen); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
}
|
||||
if sigLen != 74 && sigLen != (74+516) {
|
||||
return errors.Wrap(
|
||||
errors.New("invalid signature length"),
|
||||
"from canonical bytes",
|
||||
)
|
||||
}
|
||||
b.Signature = make([]byte, sigLen)
|
||||
if _, err := buf.Read(b.Signature); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
@ -1113,6 +1173,12 @@ func (b *BLS48581AddressedSignature) FromCanonicalBytes(data []byte) error {
|
||||
if err := binary.Read(buf, binary.BigEndian, &addrLen); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
}
|
||||
if addrLen != 32 {
|
||||
return errors.Wrap(
|
||||
errors.New("invalid address length"),
|
||||
"from canonical bytes",
|
||||
)
|
||||
}
|
||||
b.Address = make([]byte, addrLen)
|
||||
if _, err := buf.Read(b.Address); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
@ -1205,6 +1271,12 @@ func (b *BLS48581AggregateSignature) FromCanonicalBytes(data []byte) error {
|
||||
if err := binary.Read(buf, binary.BigEndian, &sigLen); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
}
|
||||
if sigLen != 74 && (sigLen > 74+(516*64) || ((sigLen-74)%516) != 0) {
|
||||
return errors.Wrap(
|
||||
errors.New("invalid signature length"),
|
||||
"from canonical bytes",
|
||||
)
|
||||
}
|
||||
b.Signature = make([]byte, sigLen)
|
||||
if _, err := buf.Read(b.Signature); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
@ -1215,6 +1287,12 @@ func (b *BLS48581AggregateSignature) FromCanonicalBytes(data []byte) error {
|
||||
if err := binary.Read(buf, binary.BigEndian, &pubKeyLen); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
}
|
||||
if pubKeyLen != 0 && pubKeyLen != 589 {
|
||||
return errors.Wrap(
|
||||
errors.New("invalid pubkey length"),
|
||||
"from canonical bytes",
|
||||
)
|
||||
}
|
||||
if pubKeyLen > 0 {
|
||||
pubKeyBytes := make([]byte, pubKeyLen)
|
||||
if _, err := buf.Read(pubKeyBytes); err != nil {
|
||||
@ -1231,6 +1309,12 @@ func (b *BLS48581AggregateSignature) FromCanonicalBytes(data []byte) error {
|
||||
if err := binary.Read(buf, binary.BigEndian, &bitmaskLen); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
}
|
||||
if bitmaskLen > 32 {
|
||||
return errors.Wrap(
|
||||
errors.New("invalid bitmask length"),
|
||||
"from canonical bytes",
|
||||
)
|
||||
}
|
||||
b.Bitmask = make([]byte, bitmaskLen)
|
||||
if _, err := buf.Read(b.Bitmask); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
@ -1414,6 +1498,12 @@ func (d *Decaf448Signature) FromCanonicalBytes(data []byte) error {
|
||||
if err := binary.Read(buf, binary.BigEndian, &keyLen); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
}
|
||||
if keyLen != 60 && keyLen != 0 {
|
||||
return errors.Wrap(
|
||||
errors.New("invalid pubkey length"),
|
||||
"from canonical bytes",
|
||||
)
|
||||
}
|
||||
if keyLen > 0 {
|
||||
keyBytes := make([]byte, keyLen)
|
||||
if _, err := buf.Read(keyBytes); err != nil {
|
||||
@ -1430,6 +1520,12 @@ func (d *Decaf448Signature) FromCanonicalBytes(data []byte) error {
|
||||
if err := binary.Read(buf, binary.BigEndian, &sigLen); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
}
|
||||
if sigLen > 336 {
|
||||
return errors.Wrap(
|
||||
errors.New("invalid signature length"),
|
||||
"from canonical bytes",
|
||||
)
|
||||
}
|
||||
d.Signature = make([]byte, sigLen)
|
||||
if _, err := buf.Read(d.Signature); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
@ -1598,6 +1694,12 @@ func (s *SignedX448Key) FromCanonicalBytes(data []byte) error {
|
||||
if err := binary.Read(buf, binary.BigEndian, &keyLen); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
}
|
||||
if keyLen > 61 {
|
||||
return errors.Wrap(
|
||||
errors.New("invalid pubkey length"),
|
||||
"from canonical bytes",
|
||||
)
|
||||
}
|
||||
if keyLen > 0 {
|
||||
keyBytes := make([]byte, keyLen)
|
||||
if _, err := buf.Read(keyBytes); err != nil {
|
||||
@ -1618,6 +1720,12 @@ func (s *SignedX448Key) FromCanonicalBytes(data []byte) error {
|
||||
); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
}
|
||||
if parentKeyAddressLen > 64 {
|
||||
return errors.Wrap(
|
||||
errors.New("invalid address length"),
|
||||
"from canonical bytes",
|
||||
)
|
||||
}
|
||||
s.ParentKeyAddress = make([]byte, parentKeyAddressLen)
|
||||
if _, err := buf.Read(s.ParentKeyAddress); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
@ -1635,6 +1743,13 @@ func (s *SignedX448Key) FromCanonicalBytes(data []byte) error {
|
||||
if err := binary.Read(buf, binary.BigEndian, &sigLen); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
}
|
||||
// largest possible signature size
|
||||
if sigLen > 675 {
|
||||
return errors.Wrap(
|
||||
errors.New("invalid signature length"),
|
||||
"from canonical bytes",
|
||||
)
|
||||
}
|
||||
sigBytes := make([]byte, sigLen)
|
||||
if _, err := buf.Read(sigBytes); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
@ -1679,6 +1794,12 @@ func (s *SignedX448Key) FromCanonicalBytes(data []byte) error {
|
||||
if err := binary.Read(buf, binary.BigEndian, &purposeLen); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
}
|
||||
if purposeLen > 32 {
|
||||
return errors.Wrap(
|
||||
errors.New("invalid purpose length"),
|
||||
"from canonical bytes",
|
||||
)
|
||||
}
|
||||
purposeBytes := make([]byte, purposeLen)
|
||||
if _, err := buf.Read(purposeBytes); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
@ -1848,6 +1969,12 @@ func (s *SignedDecaf448Key) FromCanonicalBytes(data []byte) error {
|
||||
if err := binary.Read(buf, binary.BigEndian, &keyLen); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
}
|
||||
if keyLen > 60 {
|
||||
return errors.Wrap(
|
||||
errors.New("invalid pubkey length"),
|
||||
"from canonical bytes",
|
||||
)
|
||||
}
|
||||
if keyLen > 0 {
|
||||
keyBytes := make([]byte, keyLen)
|
||||
if _, err := buf.Read(keyBytes); err != nil {
|
||||
@ -1868,6 +1995,12 @@ func (s *SignedDecaf448Key) FromCanonicalBytes(data []byte) error {
|
||||
); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
}
|
||||
if parentKeyAddressLen > 64 {
|
||||
return errors.Wrap(
|
||||
errors.New("invalid address length"),
|
||||
"from canonical bytes",
|
||||
)
|
||||
}
|
||||
s.ParentKeyAddress = make([]byte, parentKeyAddressLen)
|
||||
if _, err := buf.Read(s.ParentKeyAddress); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
@ -1885,6 +2018,13 @@ func (s *SignedDecaf448Key) FromCanonicalBytes(data []byte) error {
|
||||
if err := binary.Read(buf, binary.BigEndian, &sigLen); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
}
|
||||
// longest possible signature length
|
||||
if sigLen > 675 {
|
||||
return errors.Wrap(
|
||||
errors.New("invalid signature length"),
|
||||
"from canonical bytes",
|
||||
)
|
||||
}
|
||||
sigBytes := make([]byte, sigLen)
|
||||
if _, err := buf.Read(sigBytes); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
@ -1929,6 +2069,12 @@ func (s *SignedDecaf448Key) FromCanonicalBytes(data []byte) error {
|
||||
if err := binary.Read(buf, binary.BigEndian, &purposeLen); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
}
|
||||
if purposeLen > 32 {
|
||||
return errors.Wrap(
|
||||
errors.New("invalid purpose length"),
|
||||
"from canonical bytes",
|
||||
)
|
||||
}
|
||||
purposeBytes := make([]byte, purposeLen)
|
||||
if _, err := buf.Read(purposeBytes); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
@ -2113,7 +2259,13 @@ func (b *BLS48581AggregateSignature) Validate() error {
|
||||
}
|
||||
}
|
||||
|
||||
// Bitmask can be variable length
|
||||
// Bitmask can be variable length, but should not exceed 32
|
||||
if len(b.Bitmask) > 32 {
|
||||
return errors.Wrap(
|
||||
errors.New("invalid bitmask length"),
|
||||
"validate",
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -2316,6 +2468,12 @@ func (k *KeyCollection) FromCanonicalBytes(data []byte) error {
|
||||
if err := binary.Read(buf, binary.BigEndian, &purposeLen); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
}
|
||||
if purposeLen > 32 {
|
||||
return errors.Wrap(
|
||||
errors.New("invalid purpose length"),
|
||||
"from canonical bytes",
|
||||
)
|
||||
}
|
||||
purposeBytes := make([]byte, purposeLen)
|
||||
if _, err := buf.Read(purposeBytes); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
@ -2327,7 +2485,12 @@ func (k *KeyCollection) FromCanonicalBytes(data []byte) error {
|
||||
if err := binary.Read(buf, binary.BigEndian, &x448KeysCount); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
}
|
||||
|
||||
if x448KeysCount > 20 {
|
||||
return errors.Wrap(
|
||||
errors.New("invalid x448 keys length"),
|
||||
"from canonical bytes",
|
||||
)
|
||||
}
|
||||
// Read each key
|
||||
k.X448Keys = make([]*SignedX448Key, x448KeysCount)
|
||||
for i := uint32(0); i < x448KeysCount; i++ {
|
||||
@ -2335,6 +2498,12 @@ func (k *KeyCollection) FromCanonicalBytes(data []byte) error {
|
||||
if err := binary.Read(buf, binary.BigEndian, &keyLen); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
}
|
||||
if keyLen > 869 {
|
||||
return errors.Wrap(
|
||||
errors.New("invalid key length"),
|
||||
"from canonical bytes",
|
||||
)
|
||||
}
|
||||
keyBytes := make([]byte, keyLen)
|
||||
if _, err := buf.Read(keyBytes); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
@ -2350,7 +2519,12 @@ func (k *KeyCollection) FromCanonicalBytes(data []byte) error {
|
||||
if err := binary.Read(buf, binary.BigEndian, &decaf448KeysCount); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
}
|
||||
|
||||
if decaf448KeysCount > 20 {
|
||||
return errors.Wrap(
|
||||
errors.New("invalid decaf448 keys length"),
|
||||
"from canonical bytes",
|
||||
)
|
||||
}
|
||||
// Read each key
|
||||
k.Decaf448Keys = make([]*SignedDecaf448Key, decaf448KeysCount)
|
||||
for i := uint32(0); i < decaf448KeysCount; i++ {
|
||||
@ -2358,6 +2532,12 @@ func (k *KeyCollection) FromCanonicalBytes(data []byte) error {
|
||||
if err := binary.Read(buf, binary.BigEndian, &keyLen); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
}
|
||||
if keyLen > 869 {
|
||||
return errors.Wrap(
|
||||
errors.New("invalid key length"),
|
||||
"from canonical bytes",
|
||||
)
|
||||
}
|
||||
keyBytes := make([]byte, keyLen)
|
||||
if _, err := buf.Read(keyBytes); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
@ -2542,6 +2722,12 @@ func (k *KeyRegistry) FromCanonicalBytes(data []byte) error {
|
||||
if err := binary.Read(buf, binary.BigEndian, &identityKeyLen); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
}
|
||||
if identityKeyLen > 61 {
|
||||
return errors.Wrap(
|
||||
errors.New("invalid identity key length"),
|
||||
"from canonical bytes",
|
||||
)
|
||||
}
|
||||
if identityKeyLen > 0 {
|
||||
keyBytes := make([]byte, identityKeyLen)
|
||||
if _, err := buf.Read(keyBytes); err != nil {
|
||||
@ -2558,6 +2744,12 @@ func (k *KeyRegistry) FromCanonicalBytes(data []byte) error {
|
||||
if err := binary.Read(buf, binary.BigEndian, &proverKeyLen); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
}
|
||||
if proverKeyLen > 589 {
|
||||
return errors.Wrap(
|
||||
errors.New("invalid prover key length"),
|
||||
"from canonical bytes",
|
||||
)
|
||||
}
|
||||
if proverKeyLen > 0 {
|
||||
keyBytes := make([]byte, proverKeyLen)
|
||||
if _, err := buf.Read(keyBytes); err != nil {
|
||||
@ -2578,6 +2770,12 @@ func (k *KeyRegistry) FromCanonicalBytes(data []byte) error {
|
||||
); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
}
|
||||
if identityToProverLen > 187 {
|
||||
return errors.Wrap(
|
||||
errors.New("invalid key length"),
|
||||
"from canonical bytes",
|
||||
)
|
||||
}
|
||||
if identityToProverLen > 0 {
|
||||
sigBytes := make([]byte, identityToProverLen)
|
||||
if _, err := buf.Read(sigBytes); err != nil {
|
||||
@ -2598,6 +2796,12 @@ func (k *KeyRegistry) FromCanonicalBytes(data []byte) error {
|
||||
); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
}
|
||||
if proverToIdentityLen > 675 {
|
||||
return errors.Wrap(
|
||||
errors.New("invalid key length"),
|
||||
"from canonical bytes",
|
||||
)
|
||||
}
|
||||
if proverToIdentityLen > 0 {
|
||||
sigBytes := make([]byte, proverToIdentityLen)
|
||||
if _, err := buf.Read(sigBytes); err != nil {
|
||||
@ -2614,6 +2818,12 @@ func (k *KeyRegistry) FromCanonicalBytes(data []byte) error {
|
||||
if err := binary.Read(buf, binary.BigEndian, &mapCount); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
}
|
||||
if mapCount > 20 {
|
||||
return errors.Wrap(
|
||||
errors.New("invalid key map length"),
|
||||
"from canonical bytes",
|
||||
)
|
||||
}
|
||||
|
||||
// Read each key collection in the map
|
||||
k.KeysByPurpose = make(map[string]*KeyCollection)
|
||||
@ -2623,6 +2833,12 @@ func (k *KeyRegistry) FromCanonicalBytes(data []byte) error {
|
||||
if err := binary.Read(buf, binary.BigEndian, &purposeLen); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
}
|
||||
if purposeLen > 32 {
|
||||
return errors.Wrap(
|
||||
errors.New("invalid purpose length"),
|
||||
"from canonical bytes",
|
||||
)
|
||||
}
|
||||
purposeBytes := make([]byte, purposeLen)
|
||||
if _, err := buf.Read(purposeBytes); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
@ -2634,6 +2850,12 @@ func (k *KeyRegistry) FromCanonicalBytes(data []byte) error {
|
||||
if err := binary.Read(buf, binary.BigEndian, &collectionLen); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
}
|
||||
if collectionLen > 27604 {
|
||||
return errors.Wrap(
|
||||
errors.New("invalid collection length"),
|
||||
"from canonical bytes",
|
||||
)
|
||||
}
|
||||
collectionBytes := make([]byte, collectionLen)
|
||||
if _, err := buf.Read(collectionBytes); err != nil {
|
||||
return errors.Wrap(err, "from canonical bytes")
|
||||
|
||||
@ -29,13 +29,6 @@ func TestBLS48581SignatureWithProofOfPossession_Serialization(t *testing.T) {
|
||||
PopSignature: make([]byte, 74),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty fields",
|
||||
sig: &BLS48581SignatureWithProofOfPossession{
|
||||
Signature: []byte{},
|
||||
PopSignature: []byte{},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
@ -76,17 +69,6 @@ func TestBLS48581AddressedSignature_Serialization(t *testing.T) {
|
||||
Address: make([]byte, 32),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty fields",
|
||||
sig: &BLS48581AddressedSignature{
|
||||
Signature: []byte{},
|
||||
Address: []byte{},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "nil fields",
|
||||
sig: &BLS48581AddressedSignature{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
@ -130,13 +112,6 @@ func TestBLS48581AggregateSignature_Serialization(t *testing.T) {
|
||||
Bitmask: []byte{0x00, 0x00, 0x00, 0x00},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty fields",
|
||||
sig: &BLS48581AggregateSignature{
|
||||
Signature: []byte{},
|
||||
Bitmask: []byte{},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
@ -384,12 +359,6 @@ func TestDecaf448Signature_Serialization(t *testing.T) {
|
||||
Signature: make([]byte, 112),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty fields",
|
||||
sig: &Decaf448Signature{
|
||||
Signature: []byte{},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
||||
@ -371,6 +371,7 @@ type NodeInfoResponse struct {
|
||||
PeerSeniority []byte `protobuf:"bytes,4,opt,name=peer_seniority,json=peerSeniority,proto3" json:"peer_seniority,omitempty"`
|
||||
RunningWorkers uint32 `protobuf:"varint,5,opt,name=running_workers,json=runningWorkers,proto3" json:"running_workers,omitempty"`
|
||||
AllocatedWorkers uint32 `protobuf:"varint,6,opt,name=allocated_workers,json=allocatedWorkers,proto3" json:"allocated_workers,omitempty"`
|
||||
PatchVersion []byte `protobuf:"bytes,7,opt,name=patch_version,json=patchVersion,proto3" json:"patch_version,omitempty"`
|
||||
}
|
||||
|
||||
func (x *NodeInfoResponse) Reset() {
|
||||
@ -447,6 +448,13 @@ func (x *NodeInfoResponse) GetAllocatedWorkers() uint32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *NodeInfoResponse) GetPatchVersion() []byte {
|
||||
if x != nil {
|
||||
return x.PatchVersion
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type WorkerInfo struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
@ -1948,7 +1956,7 @@ var file_node_proto_rawDesc = []byte{
|
||||
0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x71, 0x75, 0x69, 0x6c, 0x69, 0x62, 0x72, 0x69, 0x75,
|
||||
0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x50,
|
||||
0x65, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x70, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x66,
|
||||
0x6f, 0x22, 0xe1, 0x01, 0x0a, 0x10, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65,
|
||||
0x6f, 0x22, 0x86, 0x02, 0x0a, 0x10, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65,
|
||||
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69,
|
||||
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x65, 0x65, 0x72, 0x49, 0x64, 0x12,
|
||||
0x1d, 0x0a, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x02, 0x20,
|
||||
@ -1962,271 +1970,273 @@ var file_node_proto_rawDesc = []byte{
|
||||
0x67, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x61, 0x6c, 0x6c, 0x6f,
|
||||
0x63, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x18, 0x06, 0x20,
|
||||
0x01, 0x28, 0x0d, 0x52, 0x10, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x64, 0x57, 0x6f,
|
||||
0x72, 0x6b, 0x65, 0x72, 0x73, 0x22, 0x8f, 0x01, 0x0a, 0x0a, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72,
|
||||
0x49, 0x6e, 0x66, 0x6f, 0x12, 0x17, 0x0a, 0x07, 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x69, 0x64, 0x18,
|
||||
0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x63, 0x6f, 0x72, 0x65, 0x49, 0x64, 0x12, 0x16, 0x0a,
|
||||
0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x66,
|
||||
0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x2b, 0x0a, 0x11, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62,
|
||||
0x6c, 0x65, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04,
|
||||
0x52, 0x10, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x61,
|
||||
0x67, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x73, 0x74, 0x6f, 0x72,
|
||||
0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x74, 0x6f, 0x74, 0x61, 0x6c,
|
||||
0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x22, 0x5a, 0x0a, 0x12, 0x57, 0x6f, 0x72, 0x6b, 0x65,
|
||||
0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a,
|
||||
0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x03,
|
||||
0x28, 0x0b, 0x32, 0x23, 0x2e, 0x71, 0x75, 0x69, 0x6c, 0x69, 0x62, 0x72, 0x69, 0x75, 0x6d, 0x2e,
|
||||
0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x57, 0x6f, 0x72,
|
||||
0x6b, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0a, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49,
|
||||
0x6e, 0x66, 0x6f, 0x22, 0x6e, 0x0a, 0x0a, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74,
|
||||
0x79, 0x12, 0x2f, 0x0a, 0x13, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x5f, 0x69, 0x64,
|
||||
0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69,
|
||||
0x65, 0x72, 0x12, 0x2f, 0x0a, 0x13, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c,
|
||||
0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52,
|
||||
0x12, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x4d, 0x65, 0x74, 0x61, 0x64,
|
||||
0x61, 0x74, 0x61, 0x22, 0x2f, 0x0a, 0x09, 0x49, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x4b, 0x65, 0x79,
|
||||
0x12, 0x10, 0x0a, 0x03, 0x72, 0x65, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x72,
|
||||
0x65, 0x66, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52,
|
||||
0x03, 0x6b, 0x65, 0x79, 0x22, 0x41, 0x0a, 0x07, 0x4b, 0x65, 0x79, 0x52, 0x69, 0x6e, 0x67, 0x12,
|
||||
0x36, 0x0a, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e,
|
||||
0x71, 0x75, 0x69, 0x6c, 0x69, 0x62, 0x72, 0x69, 0x75, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e,
|
||||
0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x49, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x4b, 0x65,
|
||||
0x79, 0x52, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x22, 0x4f, 0x0a, 0x0e, 0x44, 0x65, 0x6c, 0x69, 0x76,
|
||||
0x65, 0x72, 0x79, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x64, 0x65, 0x6c,
|
||||
0x69, 0x76, 0x65, 0x72, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d,
|
||||
0x52, 0x0c, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x18,
|
||||
0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52,
|
||||
0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x54, 0x0a, 0x0c, 0x44, 0x65, 0x6c, 0x69,
|
||||
0x76, 0x65, 0x72, 0x79, 0x44, 0x61, 0x74, 0x61, 0x12, 0x44, 0x0a, 0x08, 0x6d, 0x65, 0x73, 0x73,
|
||||
0x61, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x71, 0x75, 0x69,
|
||||
0x6c, 0x69, 0x62, 0x72, 0x69, 0x75, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x63, 0x68, 0x61,
|
||||
0x6e, 0x6e, 0x65, 0x6c, 0x2e, 0x70, 0x62, 0x2e, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x4d, 0x65, 0x73,
|
||||
0x73, 0x61, 0x67, 0x65, 0x52, 0x08, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x22, 0xdd,
|
||||
0x01, 0x0a, 0x0b, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16,
|
||||
0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06,
|
||||
0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x42, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||
0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x71, 0x75, 0x69, 0x6c, 0x69, 0x62,
|
||||
0x72, 0x69, 0x75, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c,
|
||||
0x2e, 0x70, 0x62, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x75, 0x6e, 0x64, 0x6c,
|
||||
0x65, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0e, 0x61, 0x75,
|
||||
0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01,
|
||||
0x28, 0x0c, 0x52, 0x0e, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x12, 0x4a, 0x0a, 0x0d, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x5f, 0x64,
|
||||
0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x71, 0x75, 0x69, 0x6c,
|
||||
0x69, 0x62, 0x72, 0x69, 0x75, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x6f, 0x64, 0x65,
|
||||
0x2e, 0x70, 0x62, 0x2e, 0x44, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x44, 0x61, 0x74, 0x61,
|
||||
0x52, 0x0c, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x44, 0x61, 0x74, 0x61, 0x22, 0x5a,
|
||||
0x0a, 0x0c, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a,
|
||||
0x0a, 0x0d, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18,
|
||||
0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x71, 0x75, 0x69, 0x6c, 0x69, 0x62, 0x72, 0x69,
|
||||
0x75, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x70, 0x62, 0x2e,
|
||||
0x44, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x44, 0x61, 0x74, 0x61, 0x52, 0x0c, 0x64, 0x65,
|
||||
0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x44, 0x61, 0x74, 0x61, 0x22, 0x30, 0x0a, 0x14, 0x4f, 0x72,
|
||||
0x69, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x64, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52,
|
||||
0x65, 0x66, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x68, 0x0a, 0x0f,
|
||||
0x49, 0x6d, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12,
|
||||
0x23, 0x0a, 0x0d, 0x69, 0x6d, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x69, 0x6d, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74,
|
||||
0x54, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18,
|
||||
0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x16,
|
||||
0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06,
|
||||
0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x22, 0xce, 0x01, 0x0a, 0x0a, 0x41, 0x63, 0x63, 0x6f, 0x75,
|
||||
0x6e, 0x74, 0x52, 0x65, 0x66, 0x12, 0x5e, 0x0a, 0x12, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61,
|
||||
0x74, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x0b, 0x32, 0x2d, 0x2e, 0x71, 0x75, 0x69, 0x6c, 0x69, 0x62, 0x72, 0x69, 0x75, 0x6d, 0x2e, 0x6e,
|
||||
0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x4f, 0x72, 0x69, 0x67,
|
||||
0x69, 0x6e, 0x61, 0x74, 0x65, 0x64, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x66,
|
||||
0x48, 0x00, 0x52, 0x11, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x64, 0x41, 0x63,
|
||||
0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x55, 0x0a, 0x10, 0x69, 0x6d, 0x70, 0x6c, 0x69, 0x63, 0x69,
|
||||
0x74, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||
0x72, 0x6b, 0x65, 0x72, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x76,
|
||||
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x70, 0x61,
|
||||
0x74, 0x63, 0x68, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x8f, 0x01, 0x0a, 0x0a, 0x57,
|
||||
0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x17, 0x0a, 0x07, 0x63, 0x6f, 0x72,
|
||||
0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x63, 0x6f, 0x72, 0x65,
|
||||
0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01,
|
||||
0x28, 0x0c, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x2b, 0x0a, 0x11, 0x61, 0x76,
|
||||
0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x18,
|
||||
0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x10, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65,
|
||||
0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x6f, 0x74, 0x61, 0x6c,
|
||||
0x5f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c,
|
||||
0x74, 0x6f, 0x74, 0x61, 0x6c, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x22, 0x5a, 0x0a, 0x12,
|
||||
0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||
0x73, 0x65, 0x12, 0x44, 0x0a, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x66,
|
||||
0x6f, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x71, 0x75, 0x69, 0x6c, 0x69, 0x62,
|
||||
0x72, 0x69, 0x75, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x70,
|
||||
0x62, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0a, 0x77, 0x6f,
|
||||
0x72, 0x6b, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x6e, 0x0a, 0x0a, 0x43, 0x61, 0x70, 0x61,
|
||||
0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x2f, 0x0a, 0x13, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63,
|
||||
0x6f, 0x6c, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x0d, 0x52, 0x12, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x49, 0x64, 0x65,
|
||||
0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x2f, 0x0a, 0x13, 0x61, 0x64, 0x64, 0x69, 0x74,
|
||||
0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02,
|
||||
0x20, 0x01, 0x28, 0x0c, 0x52, 0x12, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c,
|
||||
0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0x2f, 0x0a, 0x09, 0x49, 0x6e, 0x6c, 0x69,
|
||||
0x6e, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x65, 0x66, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x0c, 0x52, 0x03, 0x72, 0x65, 0x66, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02,
|
||||
0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0x41, 0x0a, 0x07, 0x4b, 0x65, 0x79,
|
||||
0x52, 0x69, 0x6e, 0x67, 0x12, 0x36, 0x0a, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x01, 0x20, 0x03,
|
||||
0x28, 0x0b, 0x32, 0x22, 0x2e, 0x71, 0x75, 0x69, 0x6c, 0x69, 0x62, 0x72, 0x69, 0x75, 0x6d, 0x2e,
|
||||
0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x49, 0x6e, 0x6c,
|
||||
0x69, 0x6e, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x22, 0x4f, 0x0a, 0x0e,
|
||||
0x44, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x23,
|
||||
0x0a, 0x0d, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18,
|
||||
0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x54,
|
||||
0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02,
|
||||
0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x54, 0x0a,
|
||||
0x0c, 0x44, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x44, 0x61, 0x74, 0x61, 0x12, 0x44, 0x0a,
|
||||
0x08, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32,
|
||||
0x28, 0x2e, 0x71, 0x75, 0x69, 0x6c, 0x69, 0x62, 0x72, 0x69, 0x75, 0x6d, 0x2e, 0x6e, 0x6f, 0x64,
|
||||
0x65, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x49, 0x6d, 0x70, 0x6c, 0x69, 0x63,
|
||||
0x69, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0f, 0x69, 0x6d, 0x70,
|
||||
0x6c, 0x69, 0x63, 0x69, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x42, 0x09, 0x0a, 0x07,
|
||||
0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x7d, 0x0a, 0x04, 0x43, 0x6f, 0x69, 0x6e, 0x12,
|
||||
0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52,
|
||||
0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x22, 0x0a, 0x0c, 0x69, 0x6e, 0x74, 0x65, 0x72,
|
||||
0x73, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x69,
|
||||
0x6e, 0x74, 0x65, 0x72, 0x73, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x39, 0x0a, 0x05, 0x6f,
|
||||
0x77, 0x6e, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x71, 0x75, 0x69,
|
||||
0x6c, 0x69, 0x62, 0x72, 0x69, 0x75, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x6f, 0x64,
|
||||
0x65, 0x2e, 0x70, 0x62, 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x66, 0x52,
|
||||
0x05, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x22, 0x7c, 0x0a, 0x0a, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79,
|
||||
0x43, 0x6f, 0x69, 0x6e, 0x12, 0x31, 0x0a, 0x04, 0x63, 0x6f, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x71, 0x75, 0x69, 0x6c, 0x69, 0x62, 0x72, 0x69, 0x75, 0x6d, 0x2e,
|
||||
0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x69,
|
||||
0x6e, 0x52, 0x04, 0x63, 0x6f, 0x69, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x66, 0x72, 0x61, 0x6d, 0x65,
|
||||
0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x66,
|
||||
0x72, 0x61, 0x6d, 0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64,
|
||||
0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, 0x64, 0x64,
|
||||
0x72, 0x65, 0x73, 0x73, 0x22, 0x88, 0x03, 0x0a, 0x17, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61,
|
||||
0x6c, 0x69, 0x7a, 0x65, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x0c, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x61,
|
||||
0x77, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52,
|
||||
0x0a, 0x72, 0x61, 0x77, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x66,
|
||||
0x72, 0x61, 0x6d, 0x65, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28,
|
||||
0x04, 0x52, 0x0b, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x1e,
|
||||
0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01,
|
||||
0x28, 0x0c, 0x52, 0x0a, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x20,
|
||||
0x0a, 0x0c, 0x6f, 0x6e, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x05,
|
||||
0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x6f, 0x6e, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x4b, 0x65, 0x79,
|
||||
0x12, 0x29, 0x0a, 0x10, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x5f, 0x6b, 0x65, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x76, 0x65, 0x72, 0x69,
|
||||
0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x63,
|
||||
0x6f, 0x69, 0x6e, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28,
|
||||
0x0c, 0x52, 0x0b, 0x63, 0x6f, 0x69, 0x6e, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x12,
|
||||
0x0a, 0x04, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x6d, 0x61,
|
||||
0x73, 0x6b, 0x12, 0x31, 0x0a, 0x14, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c,
|
||||
0x5f, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0c,
|
||||
0x52, 0x13, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x52, 0x65, 0x66, 0x65,
|
||||
0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x38, 0x0a, 0x18, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x61, 0x6c, 0x5f, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x5f, 0x6b, 0x65,
|
||||
0x79, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x16, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x61, 0x6c, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x22,
|
||||
0x8c, 0x06, 0x0a, 0x1e, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64,
|
||||
0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1f, 0x0a, 0x0b,
|
||||
0x72, 0x61, 0x77, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||
0x0c, 0x52, 0x0a, 0x72, 0x61, 0x77, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x21, 0x0a,
|
||||
0x0c, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x03, 0x20,
|
||||
0x01, 0x28, 0x04, 0x52, 0x0b, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72,
|
||||
0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x04,
|
||||
0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74,
|
||||
0x12, 0x25, 0x0a, 0x0f, 0x74, 0x6f, 0x5f, 0x6f, 0x6e, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f,
|
||||
0x6b, 0x65, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x74, 0x6f, 0x4f, 0x6e, 0x65,
|
||||
0x54, 0x69, 0x6d, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x2d, 0x0a, 0x13, 0x72, 0x65, 0x66, 0x75, 0x6e,
|
||||
0x64, 0x5f, 0x6f, 0x6e, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x06,
|
||||
0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, 0x72, 0x65, 0x66, 0x75, 0x6e, 0x64, 0x4f, 0x6e, 0x65, 0x54,
|
||||
0x69, 0x6d, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x2e, 0x0a, 0x13, 0x74, 0x6f, 0x5f, 0x76, 0x65, 0x72,
|
||||
0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x07, 0x20,
|
||||
0x01, 0x28, 0x0c, 0x52, 0x11, 0x74, 0x6f, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
|
||||
0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x12, 0x36, 0x0a, 0x17, 0x72, 0x65, 0x66, 0x75, 0x6e, 0x64,
|
||||
0x5f, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6b, 0x65,
|
||||
0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x15, 0x72, 0x65, 0x66, 0x75, 0x6e, 0x64, 0x56,
|
||||
0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x12, 0x26,
|
||||
0x0a, 0x0f, 0x74, 0x6f, 0x5f, 0x63, 0x6f, 0x69, 0x6e, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63,
|
||||
0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x74, 0x6f, 0x43, 0x6f, 0x69, 0x6e, 0x42,
|
||||
0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x2e, 0x0a, 0x13, 0x72, 0x65, 0x66, 0x75, 0x6e, 0x64,
|
||||
0x5f, 0x63, 0x6f, 0x69, 0x6e, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x0a, 0x20,
|
||||
0x01, 0x28, 0x0c, 0x52, 0x11, 0x72, 0x65, 0x66, 0x75, 0x6e, 0x64, 0x43, 0x6f, 0x69, 0x6e, 0x42,
|
||||
0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x6f, 0x5f, 0x6d, 0x61, 0x73,
|
||||
0x6b, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x74, 0x6f, 0x4d, 0x61, 0x73, 0x6b, 0x12,
|
||||
0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x66, 0x75, 0x6e, 0x64, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x0c,
|
||||
0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x72, 0x65, 0x66, 0x75, 0x6e, 0x64, 0x4d, 0x61, 0x73, 0x6b,
|
||||
0x12, 0x36, 0x0a, 0x17, 0x74, 0x6f, 0x5f, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61,
|
||||
0x6c, 0x5f, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28,
|
||||
0x0c, 0x52, 0x15, 0x74, 0x6f, 0x41, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x52,
|
||||
0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x3d, 0x0a, 0x1b, 0x74, 0x6f, 0x5f, 0x61,
|
||||
0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65,
|
||||
0x6e, 0x63, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x18, 0x74,
|
||||
0x6f, 0x41, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x52, 0x65, 0x66, 0x65, 0x72,
|
||||
0x65, 0x6e, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x3e, 0x0a, 0x1b, 0x72, 0x65, 0x66, 0x75, 0x6e,
|
||||
0x64, 0x5f, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x72, 0x65, 0x66,
|
||||
0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x19, 0x72, 0x65,
|
||||
0x66, 0x75, 0x6e, 0x64, 0x41, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x52, 0x65,
|
||||
0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x45, 0x0a, 0x1f, 0x72, 0x65, 0x66, 0x75, 0x6e,
|
||||
0x64, 0x5f, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x72, 0x65, 0x66,
|
||||
0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0c,
|
||||
0x52, 0x1c, 0x72, 0x65, 0x66, 0x75, 0x6e, 0x64, 0x41, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x61, 0x6c, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x1e,
|
||||
0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x11, 0x20, 0x01,
|
||||
0x28, 0x04, 0x52, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x4d,
|
||||
0x0a, 0x19, 0x47, 0x65, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x42, 0x79, 0x41, 0x63, 0x63,
|
||||
0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61,
|
||||
0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, 0x64,
|
||||
0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18,
|
||||
0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x22, 0xa6, 0x02,
|
||||
0x0a, 0x1a, 0x47, 0x65, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x42, 0x79, 0x41, 0x63, 0x63,
|
||||
0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x0c,
|
||||
0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x5f, 0x63, 0x6f, 0x69, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03,
|
||||
0x28, 0x0b, 0x32, 0x23, 0x2e, 0x71, 0x75, 0x69, 0x6c, 0x69, 0x62, 0x72, 0x69, 0x75, 0x6d, 0x2e,
|
||||
0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x4c, 0x65, 0x67,
|
||||
0x61, 0x63, 0x79, 0x43, 0x6f, 0x69, 0x6e, 0x52, 0x0b, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x43,
|
||||
0x6f, 0x69, 0x6e, 0x73, 0x12, 0x54, 0x0a, 0x0c, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74,
|
||||
0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x71, 0x75, 0x69,
|
||||
0x6c, 0x69, 0x62, 0x72, 0x69, 0x75, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x6f, 0x64,
|
||||
0x65, 0x2e, 0x70, 0x62, 0x2e, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65,
|
||||
0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x74, 0x72,
|
||||
0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x6a, 0x0a, 0x14, 0x70, 0x65,
|
||||
0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x37, 0x2e, 0x71, 0x75, 0x69, 0x6c, 0x69,
|
||||
0x62, 0x72, 0x69, 0x75, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e,
|
||||
0x70, 0x62, 0x2e, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x50,
|
||||
0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x52, 0x13, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61,
|
||||
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x28, 0x0a, 0x0e, 0x52, 0x65, 0x73, 0x70, 0x61, 0x77,
|
||||
0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74,
|
||||
0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72,
|
||||
0x22, 0x11, 0x0a, 0x0f, 0x52, 0x65, 0x73, 0x70, 0x61, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||
0x6e, 0x73, 0x65, 0x22, 0x8b, 0x01, 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4a, 0x6f,
|
||||
0x69, 0x6e, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c,
|
||||
0x0a, 0x09, 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x0c, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x12, 0x1e, 0x0a, 0x0a,
|
||||
0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d,
|
||||
0x52, 0x0a, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x12, 0x10, 0x0a, 0x03,
|
||||
0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x03, 0x69, 0x64, 0x73, 0x12, 0x21,
|
||||
0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x04,
|
||||
0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65,
|
||||
0x78, 0x22, 0x35, 0x0a, 0x17, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x69, 0x6e, 0x50,
|
||||
0x72, 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08,
|
||||
0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08,
|
||||
0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x9c, 0x04, 0x0a, 0x0b, 0x4e, 0x6f, 0x64,
|
||||
0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x65, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x50,
|
||||
0x65, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2b, 0x2e, 0x71, 0x75, 0x69, 0x6c, 0x69, 0x62,
|
||||
0x72, 0x69, 0x75, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x70,
|
||||
0x62, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71,
|
||||
0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x71, 0x75, 0x69, 0x6c, 0x69, 0x62, 0x72, 0x69, 0x75,
|
||||
0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x50,
|
||||
0x65, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
|
||||
0x65, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2b,
|
||||
0x65, 0x2e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x2e, 0x70, 0x62, 0x2e, 0x49, 0x6e, 0x62,
|
||||
0x6f, 0x78, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x08, 0x6d, 0x65, 0x73, 0x73, 0x61,
|
||||
0x67, 0x65, 0x73, 0x22, 0xdd, 0x01, 0x0a, 0x0b, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75,
|
||||
0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x0c, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x42, 0x0a, 0x07, 0x72,
|
||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x71,
|
||||
0x75, 0x69, 0x6c, 0x69, 0x62, 0x72, 0x69, 0x75, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x67,
|
||||
0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x2e, 0x70, 0x62, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
|
||||
0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
|
||||
0x26, 0x0a, 0x0e, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74,
|
||||
0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x4a, 0x0a, 0x0d, 0x64, 0x65, 0x6c, 0x69, 0x76,
|
||||
0x65, 0x72, 0x79, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25,
|
||||
0x2e, 0x71, 0x75, 0x69, 0x6c, 0x69, 0x62, 0x72, 0x69, 0x75, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65,
|
||||
0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65,
|
||||
0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x71, 0x75,
|
||||
0x69, 0x6c, 0x69, 0x62, 0x72, 0x69, 0x75, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x6f,
|
||||
0x64, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65,
|
||||
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6b, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72,
|
||||
0x6b, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2d, 0x2e, 0x71, 0x75, 0x69, 0x6c, 0x69, 0x62,
|
||||
0x72, 0x69, 0x75, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x70,
|
||||
0x62, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52,
|
||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x71, 0x75, 0x69, 0x6c, 0x69, 0x62, 0x72,
|
||||
0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x44, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72,
|
||||
0x79, 0x44, 0x61, 0x74, 0x61, 0x52, 0x0c, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x44,
|
||||
0x61, 0x74, 0x61, 0x22, 0x5a, 0x0a, 0x0c, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||
0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x0d, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x5f,
|
||||
0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x71, 0x75, 0x69,
|
||||
0x6c, 0x69, 0x62, 0x72, 0x69, 0x75, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x6f, 0x64,
|
||||
0x65, 0x2e, 0x70, 0x62, 0x2e, 0x44, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x44, 0x61, 0x74,
|
||||
0x61, 0x52, 0x0c, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x44, 0x61, 0x74, 0x61, 0x22,
|
||||
0x30, 0x0a, 0x14, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x64, 0x41, 0x63, 0x63,
|
||||
0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x66, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65,
|
||||
0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73,
|
||||
0x73, 0x22, 0x68, 0x0a, 0x0f, 0x49, 0x6d, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x41, 0x63, 0x63,
|
||||
0x6f, 0x75, 0x6e, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x6d, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74,
|
||||
0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x69, 0x6d, 0x70,
|
||||
0x6c, 0x69, 0x63, 0x69, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64,
|
||||
0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72,
|
||||
0x65, 0x73, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x03, 0x20,
|
||||
0x01, 0x28, 0x0c, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x22, 0xce, 0x01, 0x0a, 0x0a,
|
||||
0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x66, 0x12, 0x5e, 0x0a, 0x12, 0x6f, 0x72,
|
||||
0x69, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x71, 0x75, 0x69, 0x6c, 0x69, 0x62, 0x72,
|
||||
0x69, 0x75, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x70, 0x62,
|
||||
0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||
0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x04, 0x53, 0x65, 0x6e, 0x64, 0x12, 0x24, 0x2e, 0x71, 0x75,
|
||||
0x69, 0x6c, 0x69, 0x62, 0x72, 0x69, 0x75, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x6f,
|
||||
0x64, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||
0x74, 0x1a, 0x25, 0x2e, 0x71, 0x75, 0x69, 0x6c, 0x69, 0x62, 0x72, 0x69, 0x75, 0x6d, 0x2e, 0x6e,
|
||||
0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x6e, 0x64,
|
||||
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7d, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x54,
|
||||
0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x42, 0x79, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x32,
|
||||
0x2e, 0x71, 0x75, 0x69, 0x6c, 0x69, 0x62, 0x72, 0x69, 0x75, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65,
|
||||
0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x6f, 0x6b, 0x65,
|
||||
0x6e, 0x73, 0x42, 0x79, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||
0x73, 0x74, 0x1a, 0x33, 0x2e, 0x71, 0x75, 0x69, 0x6c, 0x69, 0x62, 0x72, 0x69, 0x75, 0x6d, 0x2e,
|
||||
0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74,
|
||||
0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x42, 0x79, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52,
|
||||
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xe4, 0x01, 0x0a, 0x0e, 0x44, 0x61, 0x74, 0x61,
|
||||
0x49, 0x50, 0x43, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x5c, 0x0a, 0x07, 0x52, 0x65,
|
||||
0x73, 0x70, 0x61, 0x77, 0x6e, 0x12, 0x27, 0x2e, 0x71, 0x75, 0x69, 0x6c, 0x69, 0x62, 0x72, 0x69,
|
||||
0x75, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x70, 0x62, 0x2e,
|
||||
0x52, 0x65, 0x73, 0x70, 0x61, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28,
|
||||
0x2e, 0x71, 0x75, 0x69, 0x6c, 0x69, 0x62, 0x72, 0x69, 0x75, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65,
|
||||
0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x61, 0x77, 0x6e,
|
||||
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x74, 0x0a, 0x0f, 0x43, 0x72, 0x65, 0x61,
|
||||
0x74, 0x65, 0x4a, 0x6f, 0x69, 0x6e, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x12, 0x2f, 0x2e, 0x71, 0x75,
|
||||
0x69, 0x6c, 0x69, 0x62, 0x72, 0x69, 0x75, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x6f,
|
||||
0x64, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x69, 0x6e,
|
||||
0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x71,
|
||||
0x2e, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x64, 0x41, 0x63, 0x63, 0x6f, 0x75,
|
||||
0x6e, 0x74, 0x52, 0x65, 0x66, 0x48, 0x00, 0x52, 0x11, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61,
|
||||
0x74, 0x65, 0x64, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x55, 0x0a, 0x10, 0x69, 0x6d,
|
||||
0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02,
|
||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x71, 0x75, 0x69, 0x6c, 0x69, 0x62, 0x72, 0x69, 0x75,
|
||||
0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x49,
|
||||
0x6d, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x48, 0x00,
|
||||
0x52, 0x0f, 0x69, 0x6d, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e,
|
||||
0x74, 0x42, 0x09, 0x0a, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x7d, 0x0a, 0x04,
|
||||
0x43, 0x6f, 0x69, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x22, 0x0a, 0x0c,
|
||||
0x69, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01,
|
||||
0x28, 0x0c, 0x52, 0x0c, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x12, 0x39, 0x0a, 0x05, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||
0x23, 0x2e, 0x71, 0x75, 0x69, 0x6c, 0x69, 0x62, 0x72, 0x69, 0x75, 0x6d, 0x2e, 0x6e, 0x6f, 0x64,
|
||||
0x65, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e,
|
||||
0x74, 0x52, 0x65, 0x66, 0x52, 0x05, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x22, 0x7c, 0x0a, 0x0a, 0x4c,
|
||||
0x65, 0x67, 0x61, 0x63, 0x79, 0x43, 0x6f, 0x69, 0x6e, 0x12, 0x31, 0x0a, 0x04, 0x63, 0x6f, 0x69,
|
||||
0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x71, 0x75, 0x69, 0x6c, 0x69, 0x62,
|
||||
0x72, 0x69, 0x75, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x70,
|
||||
0x62, 0x2e, 0x43, 0x6f, 0x69, 0x6e, 0x52, 0x04, 0x63, 0x6f, 0x69, 0x6e, 0x12, 0x21, 0x0a, 0x0c,
|
||||
0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01,
|
||||
0x28, 0x04, 0x52, 0x0b, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12,
|
||||
0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c,
|
||||
0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x88, 0x03, 0x0a, 0x17, 0x4d, 0x61,
|
||||
0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61,
|
||||
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12,
|
||||
0x1f, 0x0a, 0x0b, 0x72, 0x61, 0x77, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x02,
|
||||
0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x72, 0x61, 0x77, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65,
|
||||
0x12, 0x21, 0x0a, 0x0c, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72,
|
||||
0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x4e, 0x75, 0x6d,
|
||||
0x62, 0x65, 0x72, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e,
|
||||
0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d,
|
||||
0x65, 0x6e, 0x74, 0x12, 0x20, 0x0a, 0x0c, 0x6f, 0x6e, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f,
|
||||
0x6b, 0x65, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x6f, 0x6e, 0x65, 0x54, 0x69,
|
||||
0x6d, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x10, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63,
|
||||
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52,
|
||||
0x0f, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79,
|
||||
0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x69, 0x6e, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65,
|
||||
0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x63, 0x6f, 0x69, 0x6e, 0x42, 0x61, 0x6c, 0x61,
|
||||
0x6e, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x08, 0x20, 0x01, 0x28,
|
||||
0x0c, 0x52, 0x04, 0x6d, 0x61, 0x73, 0x6b, 0x12, 0x31, 0x0a, 0x14, 0x61, 0x64, 0x64, 0x69, 0x74,
|
||||
0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x18,
|
||||
0x09, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x13, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61,
|
||||
0x6c, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x38, 0x0a, 0x18, 0x61, 0x64,
|
||||
0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e,
|
||||
0x63, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x16, 0x61, 0x64,
|
||||
0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63,
|
||||
0x65, 0x4b, 0x65, 0x79, 0x22, 0x8c, 0x06, 0x0a, 0x1e, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61,
|
||||
0x6c, 0x69, 0x7a, 0x65, 0x64, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x61, 0x6e,
|
||||
0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65,
|
||||
0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73,
|
||||
0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x61, 0x77, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65,
|
||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x72, 0x61, 0x77, 0x42, 0x61, 0x6c, 0x61, 0x6e,
|
||||
0x63, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x6e, 0x75, 0x6d, 0x62,
|
||||
0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x4e,
|
||||
0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d,
|
||||
0x65, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x63, 0x6f, 0x6d, 0x6d, 0x69,
|
||||
0x74, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x25, 0x0a, 0x0f, 0x74, 0x6f, 0x5f, 0x6f, 0x6e, 0x65, 0x5f,
|
||||
0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c,
|
||||
0x74, 0x6f, 0x4f, 0x6e, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x2d, 0x0a, 0x13,
|
||||
0x72, 0x65, 0x66, 0x75, 0x6e, 0x64, 0x5f, 0x6f, 0x6e, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f,
|
||||
0x6b, 0x65, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, 0x72, 0x65, 0x66, 0x75, 0x6e,
|
||||
0x64, 0x4f, 0x6e, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x2e, 0x0a, 0x13, 0x74,
|
||||
0x6f, 0x5f, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6b,
|
||||
0x65, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x11, 0x74, 0x6f, 0x56, 0x65, 0x72, 0x69,
|
||||
0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x12, 0x36, 0x0a, 0x17, 0x72,
|
||||
0x65, 0x66, 0x75, 0x6e, 0x64, 0x5f, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x15, 0x72, 0x65,
|
||||
0x66, 0x75, 0x6e, 0x64, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x4b, 0x65, 0x79, 0x12, 0x26, 0x0a, 0x0f, 0x74, 0x6f, 0x5f, 0x63, 0x6f, 0x69, 0x6e, 0x5f, 0x62,
|
||||
0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x74, 0x6f,
|
||||
0x43, 0x6f, 0x69, 0x6e, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x2e, 0x0a, 0x13, 0x72,
|
||||
0x65, 0x66, 0x75, 0x6e, 0x64, 0x5f, 0x63, 0x6f, 0x69, 0x6e, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e,
|
||||
0x63, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x11, 0x72, 0x65, 0x66, 0x75, 0x6e, 0x64,
|
||||
0x43, 0x6f, 0x69, 0x6e, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x74,
|
||||
0x6f, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x74, 0x6f,
|
||||
0x4d, 0x61, 0x73, 0x6b, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x66, 0x75, 0x6e, 0x64, 0x5f, 0x6d,
|
||||
0x61, 0x73, 0x6b, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x72, 0x65, 0x66, 0x75, 0x6e,
|
||||
0x64, 0x4d, 0x61, 0x73, 0x6b, 0x12, 0x36, 0x0a, 0x17, 0x74, 0x6f, 0x5f, 0x61, 0x64, 0x64, 0x69,
|
||||
0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65,
|
||||
0x18, 0x0d, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x15, 0x74, 0x6f, 0x41, 0x64, 0x64, 0x69, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x61, 0x6c, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x3d, 0x0a,
|
||||
0x1b, 0x74, 0x6f, 0x5f, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x72,
|
||||
0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x0e, 0x20, 0x01,
|
||||
0x28, 0x0c, 0x52, 0x18, 0x74, 0x6f, 0x41, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c,
|
||||
0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x3e, 0x0a, 0x1b,
|
||||
0x72, 0x65, 0x66, 0x75, 0x6e, 0x64, 0x5f, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61,
|
||||
0x6c, 0x5f, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28,
|
||||
0x0c, 0x52, 0x19, 0x72, 0x65, 0x66, 0x75, 0x6e, 0x64, 0x41, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x61, 0x6c, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x45, 0x0a, 0x1f,
|
||||
0x72, 0x65, 0x66, 0x75, 0x6e, 0x64, 0x5f, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61,
|
||||
0x6c, 0x5f, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18,
|
||||
0x10, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x1c, 0x72, 0x65, 0x66, 0x75, 0x6e, 0x64, 0x41, 0x64, 0x64,
|
||||
0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65,
|
||||
0x4b, 0x65, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x18, 0x11, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74,
|
||||
0x69, 0x6f, 0x6e, 0x22, 0x4d, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73,
|
||||
0x42, 0x79, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||
0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x0c, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f,
|
||||
0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61,
|
||||
0x69, 0x6e, 0x22, 0xa6, 0x02, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73,
|
||||
0x42, 0x79, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
||||
0x65, 0x12, 0x46, 0x0a, 0x0c, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x5f, 0x63, 0x6f, 0x69, 0x6e,
|
||||
0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x71, 0x75, 0x69, 0x6c, 0x69, 0x62,
|
||||
0x72, 0x69, 0x75, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x70,
|
||||
0x62, 0x2e, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x43, 0x6f, 0x69, 0x6e, 0x52, 0x0b, 0x6c, 0x65,
|
||||
0x67, 0x61, 0x63, 0x79, 0x43, 0x6f, 0x69, 0x6e, 0x73, 0x12, 0x54, 0x0a, 0x0c, 0x74, 0x72, 0x61,
|
||||
0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32,
|
||||
0x30, 0x2e, 0x71, 0x75, 0x69, 0x6c, 0x69, 0x62, 0x72, 0x69, 0x75, 0x6d, 0x2e, 0x6e, 0x6f, 0x64,
|
||||
0x65, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69,
|
||||
0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x52, 0x0c, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12,
|
||||
0x6a, 0x0a, 0x14, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73,
|
||||
0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x37, 0x2e,
|
||||
0x71, 0x75, 0x69, 0x6c, 0x69, 0x62, 0x72, 0x69, 0x75, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e,
|
||||
0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c,
|
||||
0x69, 0x7a, 0x65, 0x64, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x61, 0x6e, 0x73,
|
||||
0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x54,
|
||||
0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x28, 0x0a, 0x0e, 0x52,
|
||||
0x65, 0x73, 0x70, 0x61, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a,
|
||||
0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x66,
|
||||
0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, 0x11, 0x0a, 0x0f, 0x52, 0x65, 0x73, 0x70, 0x61, 0x77, 0x6e,
|
||||
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x8b, 0x01, 0x0a, 0x16, 0x43, 0x72, 0x65,
|
||||
0x61, 0x74, 0x65, 0x4a, 0x6f, 0x69, 0x6e, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x71, 0x75,
|
||||
0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67,
|
||||
0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x18,
|
||||
0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74,
|
||||
0x79, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x03,
|
||||
0x69, 0x64, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x72, 0x5f, 0x69, 0x6e,
|
||||
0x64, 0x65, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x76, 0x65,
|
||||
0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x35, 0x0a, 0x17, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65,
|
||||
0x4a, 0x6f, 0x69, 0x6e, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
||||
0x65, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x0c, 0x52, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x9c, 0x04,
|
||||
0x0a, 0x0b, 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x65, 0x0a,
|
||||
0x0b, 0x47, 0x65, 0x74, 0x50, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2b, 0x2e, 0x71,
|
||||
0x75, 0x69, 0x6c, 0x69, 0x62, 0x72, 0x69, 0x75, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e,
|
||||
0x6f, 0x64, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x69,
|
||||
0x6e, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x3a,
|
||||
0x5a, 0x38, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x71, 0x75, 0x69, 0x6c, 0x69, 0x62, 0x72,
|
||||
0x69, 0x75, 0x6d, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x71, 0x75, 0x69, 0x6c, 0x69, 0x62, 0x72, 0x69,
|
||||
0x75, 0x6d, 0x2f, 0x6d, 0x6f, 0x6e, 0x6f, 0x72, 0x65, 0x70, 0x6f, 0x2f, 0x6e, 0x6f, 0x64, 0x65,
|
||||
0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x33,
|
||||
0x6f, 0x64, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x65, 0x65, 0x72, 0x49, 0x6e,
|
||||
0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x71, 0x75, 0x69, 0x6c,
|
||||
0x69, 0x62, 0x72, 0x69, 0x75, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x6f, 0x64, 0x65,
|
||||
0x2e, 0x70, 0x62, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70,
|
||||
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x65, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x49,
|
||||
0x6e, 0x66, 0x6f, 0x12, 0x2b, 0x2e, 0x71, 0x75, 0x69, 0x6c, 0x69, 0x62, 0x72, 0x69, 0x75, 0x6d,
|
||||
0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x47, 0x65,
|
||||
0x74, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||
0x1a, 0x29, 0x2e, 0x71, 0x75, 0x69, 0x6c, 0x69, 0x62, 0x72, 0x69, 0x75, 0x6d, 0x2e, 0x6e, 0x6f,
|
||||
0x64, 0x65, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49,
|
||||
0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6b, 0x0a, 0x0d, 0x47,
|
||||
0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2d, 0x2e, 0x71,
|
||||
0x75, 0x69, 0x6c, 0x69, 0x62, 0x72, 0x69, 0x75, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e,
|
||||
0x6f, 0x64, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72,
|
||||
0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x71, 0x75,
|
||||
0x69, 0x6c, 0x69, 0x62, 0x72, 0x69, 0x75, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x6f,
|
||||
0x64, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f,
|
||||
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x04, 0x53, 0x65, 0x6e, 0x64,
|
||||
0x12, 0x24, 0x2e, 0x71, 0x75, 0x69, 0x6c, 0x69, 0x62, 0x72, 0x69, 0x75, 0x6d, 0x2e, 0x6e, 0x6f,
|
||||
0x64, 0x65, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52,
|
||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x71, 0x75, 0x69, 0x6c, 0x69, 0x62, 0x72,
|
||||
0x69, 0x75, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x70, 0x62,
|
||||
0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7d, 0x0a,
|
||||
0x12, 0x47, 0x65, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x42, 0x79, 0x41, 0x63, 0x63, 0x6f,
|
||||
0x75, 0x6e, 0x74, 0x12, 0x32, 0x2e, 0x71, 0x75, 0x69, 0x6c, 0x69, 0x62, 0x72, 0x69, 0x75, 0x6d,
|
||||
0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x47, 0x65,
|
||||
0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x42, 0x79, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74,
|
||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x33, 0x2e, 0x71, 0x75, 0x69, 0x6c, 0x69, 0x62,
|
||||
0x72, 0x69, 0x75, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x70,
|
||||
0x62, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x42, 0x79, 0x41, 0x63, 0x63,
|
||||
0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xe4, 0x01, 0x0a,
|
||||
0x0e, 0x44, 0x61, 0x74, 0x61, 0x49, 0x50, 0x43, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12,
|
||||
0x5c, 0x0a, 0x07, 0x52, 0x65, 0x73, 0x70, 0x61, 0x77, 0x6e, 0x12, 0x27, 0x2e, 0x71, 0x75, 0x69,
|
||||
0x6c, 0x69, 0x62, 0x72, 0x69, 0x75, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x6f, 0x64,
|
||||
0x65, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x61, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75,
|
||||
0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x71, 0x75, 0x69, 0x6c, 0x69, 0x62, 0x72, 0x69, 0x75, 0x6d,
|
||||
0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65,
|
||||
0x73, 0x70, 0x61, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x74, 0x0a,
|
||||
0x0f, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x69, 0x6e, 0x50, 0x72, 0x6f, 0x6f, 0x66,
|
||||
0x12, 0x2f, 0x2e, 0x71, 0x75, 0x69, 0x6c, 0x69, 0x62, 0x72, 0x69, 0x75, 0x6d, 0x2e, 0x6e, 0x6f,
|
||||
0x64, 0x65, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74,
|
||||
0x65, 0x4a, 0x6f, 0x69, 0x6e, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||
0x74, 0x1a, 0x30, 0x2e, 0x71, 0x75, 0x69, 0x6c, 0x69, 0x62, 0x72, 0x69, 0x75, 0x6d, 0x2e, 0x6e,
|
||||
0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x65, 0x61,
|
||||
0x74, 0x65, 0x4a, 0x6f, 0x69, 0x6e, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||
0x6e, 0x73, 0x65, 0x42, 0x3a, 0x5a, 0x38, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x71, 0x75,
|
||||
0x69, 0x6c, 0x69, 0x62, 0x72, 0x69, 0x75, 0x6d, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x71, 0x75, 0x69,
|
||||
0x6c, 0x69, 0x62, 0x72, 0x69, 0x75, 0x6d, 0x2f, 0x6d, 0x6f, 0x6e, 0x6f, 0x72, 0x65, 0x70, 0x6f,
|
||||
0x2f, 0x6e, 0x6f, 0x64, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x73, 0x62,
|
||||
0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
|
||||
@ -54,6 +54,7 @@ message NodeInfoResponse {
|
||||
bytes peer_seniority = 4;
|
||||
uint32 running_workers = 5;
|
||||
uint32 allocated_workers = 6;
|
||||
bytes patch_version = 7;
|
||||
}
|
||||
|
||||
message WorkerInfo {
|
||||
|
||||
@ -11,6 +11,7 @@ type WorkerManager interface {
|
||||
Stop() error
|
||||
AllocateWorker(coreId uint, filter []byte) error
|
||||
DeallocateWorker(coreId uint) error
|
||||
CheckWorkersConnected() ([]uint, error)
|
||||
GetWorkerIdByFilter(filter []byte) (uint, error)
|
||||
GetFilterByWorkerId(coreId uint) ([]byte, error)
|
||||
RegisterWorker(info *store.WorkerInfo) error
|
||||
|
||||
Loading…
Reference in New Issue
Block a user