Skip to content

Commit

Permalink
Add ACP signaling (#2476)
Browse files Browse the repository at this point in the history
  • Loading branch information
StephenButtolph committed Dec 12, 2023
1 parent ac5a00e commit 82fbc97
Show file tree
Hide file tree
Showing 15 changed files with 406 additions and 206 deletions.
63 changes: 63 additions & 0 deletions api/info/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ import (
"github.com/ava-labs/avalanchego/network"
"github.com/ava-labs/avalanchego/network/peer"
"github.com/ava-labs/avalanchego/snow/networking/benchlist"
"github.com/ava-labs/avalanchego/snow/validators"
"github.com/ava-labs/avalanchego/utils/constants"
"github.com/ava-labs/avalanchego/utils/ips"
"github.com/ava-labs/avalanchego/utils/json"
"github.com/ava-labs/avalanchego/utils/logging"
"github.com/ava-labs/avalanchego/utils/set"
"github.com/ava-labs/avalanchego/version"
"github.com/ava-labs/avalanchego/vms"
"github.com/ava-labs/avalanchego/vms/platformvm/signer"
Expand All @@ -32,6 +34,7 @@ var errNoChainProvided = errors.New("argument 'chain' not given")
type Info struct {
Parameters
log logging.Logger
validators validators.Manager
myIP ips.DynamicIPPort
networking network.Network
chainManager chains.Manager
Expand Down Expand Up @@ -59,6 +62,7 @@ type Parameters struct {
func NewService(
parameters Parameters,
log logging.Logger,
validators validators.Manager,
chainManager chains.Manager,
vmManager vms.Manager,
myIP ips.DynamicIPPort,
Expand All @@ -73,6 +77,7 @@ func NewService(
&Info{
Parameters: parameters,
log: log,
validators: validators,
chainManager: chainManager,
vmManager: vmManager,
myIP: myIP,
Expand Down Expand Up @@ -319,6 +324,64 @@ func (i *Info) Uptime(_ *http.Request, args *UptimeRequest, reply *UptimeRespons
return nil
}

type ACP struct {
SupportWeight json.Uint64 `json:"supportWeight"`
Supporters set.Set[ids.NodeID] `json:"supporters"`
ObjectWeight json.Uint64 `json:"objectWeight"`
Objectors set.Set[ids.NodeID] `json:"objectors"`
AbstainWeight json.Uint64 `json:"abstainWeight"`
}

type ACPsReply struct {
ACPs map[uint32]*ACP `json:"acps"`
}

func (a *ACPsReply) getACP(acpNum uint32) *ACP {
acp, ok := a.ACPs[acpNum]
if !ok {
acp = &ACP{}
a.ACPs[acpNum] = acp
}
return acp
}

func (i *Info) Acps(_ *http.Request, _ *struct{}, reply *ACPsReply) error {
i.log.Debug("API called",
zap.String("service", "info"),
zap.String("method", "acps"),
)

reply.ACPs = make(map[uint32]*ACP, constants.CurrentACPs.Len())
peers := i.networking.PeerInfo(nil)
for _, peer := range peers {
weight := json.Uint64(i.validators.GetWeight(constants.PrimaryNetworkID, peer.ID))
if weight == 0 {
continue
}

for acpNum := range peer.SupportedACPs {
acp := reply.getACP(acpNum)
acp.Supporters.Add(peer.ID)
acp.SupportWeight += weight
}
for acpNum := range peer.ObjectedACPs {
acp := reply.getACP(acpNum)
acp.Objectors.Add(peer.ID)
acp.ObjectWeight += weight
}
}

totalWeight, err := i.validators.TotalWeight(constants.PrimaryNetworkID)
if err != nil {
return err
}
for acpNum := range constants.CurrentACPs {
acp := reply.getACP(acpNum)
acp.AbstainWeight = json.Uint64(totalWeight) - acp.SupportWeight - acp.ObjectWeight
}
return nil
}

type GetTxFeeResponse struct {
TxFee json.Uint64 `json:"txFee"`
CreateAssetTxFee json.Uint64 `json:"createAssetTxFee"`
Expand Down
23 changes: 23 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ var (
ConsensusGossipOnAcceptPeerSizeKey: acceptedFrontierGossipDeprecationMsg,
}

errConflictingACPOpinion = errors.New("supporting and objecting to the same ACP")
errSybilProtectionDisabledStakerWeights = errors.New("sybil protection disabled weights must be positive")
errSybilProtectionDisabledOnPublicNetwork = errors.New("sybil protection disabled on public network")
errAuthPasswordTooWeak = errors.New("API auth password is not strong enough")
Expand Down Expand Up @@ -346,6 +347,25 @@ func getNetworkConfig(
allowPrivateIPs = v.GetBool(NetworkAllowPrivateIPsKey)
}

var supportedACPs set.Set[uint32]
for _, acp := range v.GetIntSlice(ACPSupportKey) {
if acp < 0 || acp > math.MaxInt32 {
return network.Config{}, fmt.Errorf("invalid ACP: %d", acp)
}
supportedACPs.Add(uint32(acp))
}

var objectedACPs set.Set[uint32]
for _, acp := range v.GetIntSlice(ACPObjectKey) {
if acp < 0 || acp > math.MaxInt32 {
return network.Config{}, fmt.Errorf("invalid ACP: %d", acp)
}
objectedACPs.Add(uint32(acp))
}
if supportedACPs.Overlaps(objectedACPs) {
return network.Config{}, errConflictingACPOpinion
}

config := network.Config{
ThrottlerConfig: network.ThrottlerConfig{
MaxInboundConnsPerSec: maxInboundConnsPerSec,
Expand Down Expand Up @@ -425,6 +445,9 @@ func getNetworkConfig(
UptimeMetricFreq: v.GetDuration(UptimeMetricFreqKey),
MaximumInboundMessageTimeout: v.GetDuration(NetworkMaximumInboundTimeoutKey),

SupportedACPs: supportedACPs,
ObjectedACPs: objectedACPs,

RequireValidatorToConnect: v.GetBool(NetworkRequireValidatorToConnectKey),
PeerReadBufferSize: int(v.GetUint(NetworkPeerReadBufferSizeKey)),
PeerWriteBufferSize: int(v.GetUint(NetworkPeerWriteBufferSizeKey)),
Expand Down
4 changes: 4 additions & 0 deletions config/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ func addNodeFlags(fs *pflag.FlagSet) {
// Network ID
fs.String(NetworkNameKey, constants.MainnetName, "Network ID this node will connect to")

// ACP flagging
fs.IntSlice(ACPSupportKey, nil, "ACPs to support adoption")
fs.IntSlice(ACPObjectKey, nil, "ACPs to object adoption")

// AVAX fees
fs.Uint64(TxFeeKey, genesis.LocalParams.TxFee, "Transaction fee, in nAVAX")
fs.Uint64(CreateAssetTxFeeKey, genesis.LocalParams.CreateAssetTxFee, "Transaction fee, in nAVAX, for transactions that create new assets")
Expand Down
2 changes: 2 additions & 0 deletions config/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ const (
GenesisFileKey = "genesis-file"
GenesisFileContentKey = "genesis-file-content"
NetworkNameKey = "network-id"
ACPSupportKey = "acp-support"
ACPObjectKey = "acp-object"
TxFeeKey = "tx-fee"
CreateAssetTxFeeKey = "create-asset-tx-fee"
CreateSubnetTxFeeKey = "create-subnet-tx-fee"
Expand Down
8 changes: 4 additions & 4 deletions message/mock_outbound_message_builder.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions message/outbound_msg_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ type OutboundMsgBuilder interface {
myVersionTime uint64,
sig []byte,
trackedSubnets []ids.ID,
supportedACPs []uint32,
objectedACPs []uint32,
) (OutboundMessage, error)

PeerList(
Expand Down Expand Up @@ -240,6 +242,8 @@ func (b *outMsgBuilder) Version(
myVersionTime uint64,
sig []byte,
trackedSubnets []ids.ID,
supportedACPs []uint32,
objectedACPs []uint32,
) (OutboundMessage, error) {
subnetIDBytes := make([][]byte, len(trackedSubnets))
encodeIDs(trackedSubnets, subnetIDBytes)
Expand All @@ -261,6 +265,8 @@ func (b *outMsgBuilder) Version(
Minor: minor,
Patch: patch,
},
SupportedAcps: supportedACPs,
ObjectedAcps: objectedACPs,
},
},
},
Expand Down
3 changes: 3 additions & 0 deletions network/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ type Config struct {
PingFrequency time.Duration `json:"pingFrequency"`
AllowPrivateIPs bool `json:"allowPrivateIPs"`

SupportedACPs set.Set[uint32] `json:"supportedACPs"`
ObjectedACPs set.Set[uint32] `json:"objectedACPs"`

// The compression type to use when compressing outbound messages.
// Assumes all peers support this compression type.
CompressionType compression.Type `json:"compressionType"`
Expand Down
2 changes: 2 additions & 0 deletions network/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,8 @@ func NewNetwork(
PingFrequency: config.PingFrequency,
PongTimeout: config.PingPongTimeout,
MaxClockDifference: config.MaxClockDifference,
SupportedACPs: config.SupportedACPs.List(),
ObjectedACPs: config.ObjectedACPs.List(),
ResourceTracker: config.ResourceTracker,
UptimeCalculator: config.UptimeCalculator,
IPSigner: peer.NewIPSigner(config.MyIPPort, config.TLSKey),
Expand Down
3 changes: 3 additions & 0 deletions network/peer/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ type Config struct {
PongTimeout time.Duration
MaxClockDifference time.Duration

SupportedACPs []uint32
ObjectedACPs []uint32

// Unix time of the last message sent and received respectively
// Must only be accessed atomically
LastSent, LastReceived int64
Expand Down
5 changes: 4 additions & 1 deletion network/peer/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/utils/json"
"github.com/ava-labs/avalanchego/utils/set"
)

type Info struct {
Expand All @@ -19,5 +20,7 @@ type Info struct {
LastReceived time.Time `json:"lastReceived"`
ObservedUptime json.Uint32 `json:"observedUptime"`
ObservedSubnetUptimes map[ids.ID]json.Uint32 `json:"observedSubnetUptimes"`
TrackedSubnets []ids.ID `json:"trackedSubnets"`
TrackedSubnets set.Set[ids.ID] `json:"trackedSubnets"`
SupportedACPs set.Set[uint32] `json:"supportedACPs"`
ObjectedACPs set.Set[uint32] `json:"objectedACPs"`
}
37 changes: 33 additions & 4 deletions network/peer/peer.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,9 @@ type peer struct {
// trackedSubnets is the subset of subnetIDs the peer sent us in the Version
// message that we are also tracking.
trackedSubnets set.Set[ids.ID]
// options of ACPs provided in the Version message.
supportedACPs set.Set[uint32]
objectedACPs set.Set[uint32]

observedUptimesLock sync.RWMutex
// [observedUptimesLock] must be held while accessing [observedUptime]
Expand Down Expand Up @@ -246,10 +249,9 @@ func (p *peer) Info() Info {
publicIPStr = p.ip.IPPort.String()
}

trackedSubnets := p.trackedSubnets.List()
uptimes := make(map[ids.ID]json.Uint32, len(trackedSubnets))
uptimes := make(map[ids.ID]json.Uint32, p.trackedSubnets.Len())

for _, subnetID := range trackedSubnets {
for subnetID := range p.trackedSubnets {
uptime, exist := p.ObservedUptime(subnetID)
if !exist {
continue
Expand All @@ -271,7 +273,9 @@ func (p *peer) Info() Info {
LastReceived: p.LastReceived(),
ObservedUptime: json.Uint32(primaryUptime),
ObservedSubnetUptimes: uptimes,
TrackedSubnets: trackedSubnets,
TrackedSubnets: p.trackedSubnets,
SupportedACPs: p.supportedACPs,
ObjectedACPs: p.objectedACPs,
}
}

Expand Down Expand Up @@ -517,6 +521,8 @@ func (p *peer) writeMessages() {
mySignedIP.Timestamp,
mySignedIP.Signature,
p.MySubnets.List(),
p.SupportedACPs,
p.ObjectedACPs,
)
if err != nil {
p.Log.Error("failed to create message",
Expand Down Expand Up @@ -956,6 +962,29 @@ func (p *peer) handleVersion(msg *p2p.Version) {
}
}

for _, acp := range msg.SupportedAcps {
if constants.CurrentACPs.Contains(acp) {
p.supportedACPs.Add(acp)
}
}
for _, acp := range msg.ObjectedAcps {
if constants.CurrentACPs.Contains(acp) {
p.objectedACPs.Add(acp)
}
}

if p.supportedACPs.Overlaps(p.objectedACPs) {
p.Log.Debug("message with invalid field",
zap.Stringer("nodeID", p.id),
zap.Stringer("messageOp", message.VersionOp),
zap.String("field", "ACPs"),
zap.Reflect("supportedACPs", p.supportedACPs),
zap.Reflect("objectedACPs", p.objectedACPs),
)
p.StartClose()
return
}

// "net.IP" type in Golang is 16-byte
if ipLen := len(msg.IpAddr); ipLen != net.IPv6len {
p.Log.Debug("message with invalid field",
Expand Down
20 changes: 20 additions & 0 deletions node/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,25 @@ func (n *Node) initNetworking() error {
)
}

// We allow nodes to gossip unknown ACPs in case the current ACPs constant
// becomes out of date.
var unknownACPs set.Set[uint32]
for acp := range n.Config.NetworkConfig.SupportedACPs {
if !constants.CurrentACPs.Contains(acp) {
unknownACPs.Add(acp)
}
}
for acp := range n.Config.NetworkConfig.ObjectedACPs {
if !constants.CurrentACPs.Contains(acp) {
unknownACPs.Add(acp)
}
}
if unknownACPs.Len() > 0 {
n.Log.Warn("gossipping unknown ACPs",
zap.Reflect("acps", unknownACPs),
)
}

tlsConfig := peer.TLSConfig(n.Config.StakingTLSCert, n.tlsKeyLogWriterCloser)

// Configure benchlist
Expand Down Expand Up @@ -1272,6 +1291,7 @@ func (n *Node) initInfoAPI() error {
VMManager: n.VMManager,
},
n.Log,
n.vdrs,
n.chainManager,
n.VMManager,
n.Config.NetworkConfig.MyIPPort,
Expand Down
2 changes: 2 additions & 0 deletions proto/p2p/p2p.proto
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ message Version {
// Subnets the peer is tracking
repeated bytes tracked_subnets = 8;
Client client = 9;
repeated uint32 supported_acps = 10;
repeated uint32 objected_acps = 11;
}

// Metadata about a peer's P2P client used to determine compatibility
Expand Down
Loading

0 comments on commit 82fbc97

Please sign in to comment.