@andrewnguyen22 - Andrew Nguyen
@BenVanGithub - Shawn Regan
@olshansk - Daniel Olshansky
Version 1.0.1 - Jan 2023
- 1. Overview
- 2. Requirements
- 3 Specification
- 4. Launch Sequence
- 5. Attack Vectors
- 5.1 Attack Category Types
- 5.2 Attack Examples
- 5.2.1 Fisherman <> Servicer Collusion
- 5.2.2 Fisherman Assigning bad Servicer TestScores
- 5.2.3 Fishermen falsifying Application Volume Metrics
- 5.2.4 Fisherman DDoS
- 5.2.5 Incognito Fisherman Identified
- 5.2.6 Fisherman or Servicer register Applications for selfish self-dealing attacks
- 5.2.7 Actors intentionally staking in “wrong” GeoZone
- 6. Dissenting Opinions (FAQ)
- References
This document describes Pocket Network’s Utility Module: an account based, state machine protocol that enables applications to permissionlessly access high quality decentralized Web3 services without the need of maintaining clients themselves.
Pocket Network aims to solve the RPC Trilemma:
- Reliability: Uptime, geo-political redundancy, etc...
- Performance: QoS, latency, geographic decentralization, etc...
- Cost: Web3 access, infrastructure maintenance, tokenomic incentivization & penalties etc...
Pocket Network enables a Utilitarian economy that proportionally incentivizes or penalizes the corresponding infrastructure providers based on their quality of service. It is composed of the following actors:
- Staked Applications that purchase Web3 access over a function of volume and time
- Staked Servicers that earn rewards for providing Web3 access over a function of volume and quality
- Elected Fishermen who grade and enforce the quality of the Web3 access provided by Servicers
- Staked Validators responsible for maintaining safety & liveness of the replicated state machine
- Registered Gateways that can be optionally leveraged by Applications through delegated trust
flowchart TD
subgraph Validators
direction LR
V1[Validator1]
V[Validator ...]
VN[Validator N]
end
V1 <--Propose/Vote--> V
V1 <--Propose/Vote--> VN
V <--Propose/Vote--> VN
subgraph Blockchain
direction LR
B1[Block 1]
B[Block ...]
BN[Block N]
end
B1 --Transition--> B
B --Transition--> BN
subgraph Servicers
direction TB
S1[Servicer 1]
SN[Servicer N]
end
subgraph Applications
direction TB
A1[Application 1]
AN[Application N]
end
subgraph Gateways
direction TB
P1[Gateway 1]
PN[Gateway N]
end
subgraph Fishermen
direction TB
F1[Fisherman 1]
FN[Fisherman N]
end
Transactions --> Validators
Validators --Validate State<br>Transition--> Blockchain
Applications <--Delegated RPC--> Gateways
Gateways <--App RPC--> Servicers
Applications <--Trustless RPC--> Servicers
Blockchain --Sync--> Gateways
Blockchain --Sync--> Applications
Blockchain --Sync--> Servicers
Blockchain --Sync--> Fishermen
Fishermen --Monitor--> Servicers
classDef blue fill:#0000FF
classDef brown fill:#A52A2A
classDef red fill:#FF0000
classDef yellow fill:#DC783D
classDef acqua fill:#00A3A3
classDef purple fill:#FF36FF
class V1,V,VN blue
class B1,B,BN brown
class S1,SN yellow
class P1,PN red
class F1,FN acqua
class A1,AN purple
Readers of this document must keep in mind the following:
- This living document is subject to change. Ongoing R&D will shape the specification until it is formalized and finished.
- This document represents one stage of Pocket Network's evolution. Future iterations will aim to iterate on tokenomic incentives, permissionless Fisherman, Gateway incentives, etc.
- This document should not be treated as a complete whitepaper. It is a specification of Utility Module components intended to drive the design and implementation of the technical specifications.
- This document is not an academic paper. Formal proofs and verifications are absent and knowledge of background concepts is implicitly assumed.
- This document does not outline implementation specific interfaces or details. Any interfaces presented are for illustrative purposes only.
The specification must:
- Enable Web3 access through a Utilitarian Economy leveraging native cryptocurrency capabilities
- Account for both inflationary and deflationary economic scenarios
- Incentivize actors within the protocol through competing offerings and economic penalties
- Reward Servicers through a combination of quality of service, application demand volume, and services offered
- Constrain service capacity through a combination of time, volume and cost
- Account for gamification, collusion, and other attack vectors
- Enable Applications to gain permissionless Web3 access without maintaining their own infrastructure
- Enable Applications to optionally delegate trust to Gateways that ease the use of Web3 access
The current iteration of the specification must not necessarily:
- Replace the safety guarantees of Applications maintaining their own infrastructure or using light clients
- Enable permissionless registration of Fisherman actors on initial launch
- Does not quantifiably model the underlying tokenomics of the system
The logical abstraction of the specification system is comprised of multiple sub-protocols listed below. Interfaces and diagrams presented are intended as aiding guidelines rather than definitive implementation details.
A Session
is a time-based mechanism used to regulate the various interactions (Web3 access, monitoring, etc) between protocol actors to enable the Utilitarian economy in a fair and secure manner. A single session may extend multiple Blocks as determined by SessionBlockFrequency
.
Under the Random Oracle Model, a Session can be seeded to deterministically select which group of actors will interact for some duration of time. This enables a random, deterministic and uniform distribution of Web3 access, provisioning and monitoring. It limits what work, and by whom, can be rewarded or penalized at the protocol layer.
The seed data for a session is an implementation detail that could be composed of multiple variables. It includes, but is not limited to, attributes such as LatestBlockHash
, Timestamp
, etc...
To start a new session, or retrieve the metadata for an existing / prior session, the Querier (e.g. Application, Client) can execute a request to any synched Full Node (protocol actor or not).
sequenceDiagram
autonumber
actor Q as Querier
participant N as Full Node
participant S as Session Interface
Q->>N: Who are the Servicers &<br>Fisherman for this (new) Session?
N->>S: seedData = (height, blockHash, geoZone, relayChain, app)
S->>S: sessionKey = hash(transform(seedData))
N->>S: servicerList = Ordered [publicKeys]
S->>S: sessionServicers = pseudoRandomSelect(sessionKey, servicerList, maxSessionServicers)
N->>S: fishermenList = Ordered [publicKeys]
S->>S: sessionFishermen = pseudoRandomSelect(sessionKey, fishermenList, maxSessionFisherman)
S->>Q: ([sessionServicers], [sessionFishermen])
For illustrative purposes, an example implementation of NewSession
could be:
func NewSession(sessionHeight, lastBlockHash, geoZone, relayChain, appPubKey) Session {
key = hash(concat(sessionHeight, lastBlockHash, geoZone, relayChain, appPubKey))
servicers = getClosestServicers(key, geoZone, numServicers)
fishermen = getClosestFishermen(key, geoZone, numFishermen)
return Session{sessionHeight, geoZone, relayChain, appPubKey, servicers, fishermen}
}
Note that a timestamp
is explicitly not used to generate a new session because the process of generating a session (i.e. matching an application to servicers in some geozone at some height), must be a deterministic process executable by full nodes, light nodes and actors alike.
A RelayChain
is an identifier of the specified Web3 data source (i.e. a blockchain) being interacted with for that session.
For example, 0021
represents Ethereum Mainnet
in Pocket Network V0.
A GeoZone
is a representation of a physical geo-location the actors advertise they are in.
For example, GeoZone 0001
could represent US East
, but alternative coordinate systems such as Uber's H3 or PostGIS could be used as well.
There is no formal requirement or validation (e.g. IP verification) for an actor to be physically located in the GeoZone it registers in. However, crypto-economic incentives drive actors to be registered close to where they are physically located to receive and provide the best service possible.
The number of GeoZones an actor can stake for is limited to one to incentivize real geographic distribution. To quote @deblasis: "If an actor is everywhere, they're nowhere."
Since a single Session extends multiple blocks, an actor could potentially send an on-chain transaction to exit (e.g. Unstake, Pause) prematurely. Any rewards for that Session for that actor are invalidated, and penalties may be applied. A replacement actor (e.g. a Servicer) will be found and dynamically added to the session in the closest following block.
Rate limiting limits the amount of work (i.e. Web3 access) a Servicer can provide to an Application throughout the duration of a Session.
During each Session, the amount of POKT an Application has staked (see Application Protocol for more details) is mapped to "Service Tokens" that represent the amount of work a Servicer can provide using the SessionTokenBucketCoefficient
governance parameter. The Token Bucket rate limiting algorithm is used to determine the maximum number of requests a Servicer can relay, and be rewarded for, thereby disincentivizing it to process relays for the Application once the cap is reached.
At the beginning of the session, each Servicer initializes: AppSessionTokens = (AppStakeAmount * SessionTokenBucketCoefficient) / NumServicersPerSession
. When one of the Servicers in the session is out of session tokens, the Application can continue to use other Servicers until every they are all exhausted.
sequenceDiagram
participant App as Application
participant S1 as Servicer 1
participant SN as Servicer N
note over App, SN: Session Initialized
loop
App ->>+ S1: Relay
alt AppSessionTokens > 0
S1 ->> S1: AppSessionTokens -= 1
S1 ->> App: Relay Response
else
S1 ->>- App: Error
end
end
loop
App ->>+ SN: Relay
alt AppSessionTokens > 0
SN ->> SN: AppSessionTokens -= 1
SN ->> App: Relay Response
else
SN ->>- App: Error
end
end
The mechanism described above enables future iterations of the protocol where different types of request may vary the required number of AppSessionTokens
per request.
An illustrative example of the Session interface can be summarized as follows:
type Session interface {
NewSession(seeds ...interface{}) Session
GetApplication() Application # The Application consuming Web3 access
GetRelayChain() RelayChain # The Web3 chain identifier being accessed this session
GetGeoZone() GeoZone # The physical geo-location where all the actors are registered
GetSessionHeight() uint64 # The block height when the session started
GetServicers() []Servicer # The Servicers providing Web3 access
GetFishermen() []Fisherman # The Fisherman monitoring Web3 service
}
A Servicer
is a protocol actor that provisions Web3 access for Pocket Network Applications to consume. Servicers are the supply side of the Utilitarian Economy, who are compensated in the native cryptographic token, POKT, for their work: relaying RPC requests.
In order to participate as a Servicer in Pocket Network, each actor is required to bond a certain amount of tokens in escrow while they are providing Web3 access. These tokens may be burnt or removed from the actor as a result of breaking the Protocol’s Service Level Agreement, a DAO defined set of requirements for the minimum quality of service.
Upon registration (i.e. staking), the Servicer is required to provide the network with sufficient information to be paired with Applications. This includes the GeoZone
it is in, RelayChain
(s) it supports, and ServiceURL
, the Pocket API endpoint exposed where its services can be accessed. An optional OperatorPublicKey
may be provided for non-custodial operation of the Servicer.
This registration message is formally known as the StakeMsg
, and a Servicer can only start providing service once it is registered: the StakeMsg has been validated and included in the following block, thereby modifying the World State. An illustrative example can be summarized like so:
type ServicerStakeMsg interface {
GetPublicKey() PublicKey # The public cryptographic id of the custodial account
GetStakeAmount() BigInt # The amount of uPOKT in escrow (i.e. a security deposit)
GetServiceURL() ServiceURL # The API endpoint where the Web3 service is provided
GetRelayChains() []RelayChain # The flavor(s) of Web3 hosted by this Servicer
GetGeoZone() GeoZone # The physical geo-location identifier this Servicer registered in
GetOperatorPubKey() *PublicKey # OPTIONAL; The non-custodial pubKey operating this node
}
Servicers are paid proportionally to how well their Relay responses meet the standards of the Network SLA. A Relay
is an abstraction of a request/response cycle with additional security guarantees when interacting with Web3 resources. The quality of a service is measured by:
- Availability: The Servicer's uptime
- Latency: The Round-Trip-Time (RTT) of the the Servicer's response relative to when the request was sent
- Data Accuracy: The integrity of the Servicer's response
Since the Fisherman may not necessarily be in the same GeoZone as the Application & Servicers, the Latency will onus will be on them to normalize the TestScores The Fisherman
A TestScore
is a collection of samples by a Fisherman of a Servicer, based on the SLA criteria outlined above, throughout the duration of a Session.
A ReportCard
is the logical aggregation of multiple TestScores
over an Actor's registration lifetime.
The Application's Web3 usage volume is estimated through probabilistic hash collisions. This enables a concise proof of probabilistic volume, without requiring compute or memory intensive storage and aggregation. Similar to Bitcoin's Difficulty, a RelayVolumeDifficulty
governance parameter will be used to determine the "difficulty", and how relay counts must be estimated.
Each relay can be viewed as an independent Bernoulli Trial that is either a volume applicable relay or not. A geometric distribution can be built of the number of relays that need to be serviced until an applicable relay is made. For example, if a SHA256 hash algorithm is used and RelayVolumeDifficulty
represents 3 leading zeroes, the hash of each concat(SignedRelay, SignedRelayResponse)
above 0x000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
will not be applicable for relay rewards. However, any hash below the target will receive RelayVolume
rewards proportional to the likelihood of the hash. For example, 0x000FFF...
would be estimate RelayVolume
relays, 0x0000FF...
would e estimated to 16*RelayVolume
, etc...
A the end of each Session, the volume applicable relays are sent to the Fisherman for validation and salary distribution.
sequenceDiagram
actor Client
actor Servicer
participant IS as Internal State
participant ISS as Internal Storage
participant Chain as Web3 Chain
loop Repeats During Session
Client ->> Client: Sign(Relay)
Client ->> Servicer: Send(Relay)
Servicer ->> +IS: Validate(Relay)
IS ->> -Servicer: IsValid(Relay)
Servicer ->> +Chain: Execute(Relay, RelayChainURL)
Chain ->> -Servicer: RelayResponse = GetResponse(RelayChain)
Servicer ->> Servicer: Sign(RelayResponse)
Servicer ->> ISS: IfValid(Relay) -> Persist(Relay, RelayResponse)
Servicer ->> Client: Send(RelayResponse)
end
loop Repeats Every Session End
Servicer ->> +IS: GetSecretKey(sessionData)
IS ->> -Servicer: HashCollision = SecretKey(govParams)
Servicer ->> +ISS: RelaysThatEndWith(HashCollision)
ISS ->> -Servicer: VolumeApplicableRelays
Servicer ->> Fisherman: Send(VolumeApplicableRelays)
Fisherman ->> Fisherman: Validate Relay & RelayResponse
end
A ServicerSalary
is assigned to each individual Servicer based on their specific ReportCard
, and is distributed every SalaryBlockFrequency
. Salaries are distributed from the TotalAvailableReward
pool, whose inflation is governed by Application volume of each (RelayChain, GeoZone)
pair and scaled by the UsageToRewardCoefficient
governance parameter.
A Servicer must accumulate MinimumTestScoreThreshold
TestScores before it is eligible for salary distribution. A ReportCard can be viewed as a rolling average of the Servicer's performance, where TestScores are removed when either TestScoreExpiration
is passed or the TestScore FIFO queue exceeds MaxTestScores
.
Salary distribution is accomplished by aggregating the total volume estimated (see above) for a specific (RelayChain, GeoZone)
pair (i.e. TotalVolumeUsage
), multiplied by UsageToRewardCoefficient
, and evenly divided into buckets per Servicer that exceed the minimum threshold (i.e. the MinimumReportCardThreshold
). Each Servicer's reward is scaled proportionally to both their stake and their ReportCard. Tokens that are not allocated to a servicer are burnt.
For example, a 100% ReportCard results in zero burning of the maxServicerReward
, while a 80% ReportCard results in 20% burning of the maxServicerReward. The rate of decrease continues linearly until the MinimumReportCardThreshold
is reached. Below the MinimumReportCardThreshold no reward is given to prevent cheap Sybil attacks and freeloading nodes. Unstaking causes the Servicer's ReportCard to be cleared and start from scratch.
The following is pseudo-code to illustrate this business logic:
// Called for each (relayChain, geoZone) pair every SessionBlockFrequency
func DistributeRewards(relayChain, geoZone, height):
totalVolumeUsage = WorldState.RetrieveTotalVolumeEstimate(relayChain, geoZone, height)
totalAvailableReward = totalVolumeUsage * GovParams.UsageToRewardCoefficient(height)
allServicers = WorldState.RetrieveEligibleServicers(relayChain, geoZone, height)
eligibleServicers = filterServicers(allServicers, GovParams.MinimumTestScoreThreshold(height))
maxServicerReward = totalAvailableReward / len(eligibleServicers)
for servicer := eligibleServicers {
stake = WorldState.GetServicerStake(servicer, height)
score = WorldState.GetReportCard(servicer, height)
burnPercent = getBurnPercent(stake, score, height)
burnAmount = burnPercent * maxServicerReward
awardTokens(servicers, maxServicerReward - burnAmount)
burnTokens(relayChain, geoZone, burnAmount)
}
Servicers are able to gracefully pause their service (e.g. for maintenance reasons) without the need to unstake or face downtime penalization. In addition to an Operator initiated PauseMsg
, Fishermen are also able to temporarily pause a Servicer if a faulty or malicious process is detected during sampling (see the Fisherman Protocol for more details). When a Servicer is paused, they are able resume service by submitting an UnpauseMsg
after the MinPauseTime
has elapsed. After a UnpauseMsg
is validated and the World State is updated, the Servicer is eligible to continue providing Web3 service to Applications, and receive rewards.
A Servicer can update any of the values in its on-chain attributes by submitting another StakeMsg
while it is already staked. The only limitation is that it's StakeAmount
must be equal to or greater than its currently staked value. In addition, the Servicer's historical QoS (TestScores, ReportCard, etc...) will be pruned from the state.
A Servicer's stake can be burnt in two situations:
- A Servicer receives a
TestScore
below theTestScoreBurnThreshold
- A Fisherman initiates a
PauseMsg
with the required evidence
A Servicer is able to submit an UnstakeMsg
to exit the network and remove itself from service. After a successful UnstakeMsg, the Servicer is no longer eligible to receive Web3 traffic from Applications or Fisherman. The original stake (i.e. deposit) is returned to the Servicer's custodial account after ServicerUnbondingPeriod
has elapsed.
If a Servicer's stake ever falls below the MinimumServicerStake
stake, the protocol automatically executes an UnstakeMsg on behalf of the custodial or operator accounts, subjecting the Servicer to the unstaking process described above.
A Fisherman
is a protocol actor whose responsibility is to monitor and report the behavior and quality of Servicers' work. Their fundamental unit of work is to periodically sample the Servicers’s Web3 service, record the quality in a TestScore
, and succinctly report the TestScore to the network in a ReportCard
.
In the current version of the specification, Fishermen are not permissionless actors. The DAO must vote in each participant in order for the Fishermen to register themselves with the network on-chain. This requires Fisherman to advertise their identity and use their reputation as collateral against faulty and malicious behavior.
The Fishermen are bound by a DAO defined SLA to conduct incognito sampling services of the Servicers. Detailed requirements and conditions must be defined in the Fisherman SLA document to create an acceptable level of secrecy from the sampling server to ensure sample accuracy collection. It is important to note that the Fisherman StakeMsg
message differs from other permissionless actors in Pocket Network because it is only valid if publicKey is permissioned through the DAO Access Control List (ACL) a prior.
stateDiagram-v2
direction LR
proposal: Propose Fisherman
registration: Register Fisherman
[*] --> proposal
proposal --> proposal: Await DAO Acceptance
proposal --> registration: Submit StakeTx
registration --> registration: Await on-chain Validation
registration --> [*]
In addition to the Proof of Authority (PoA) process described above, Fisherman are also required to participate in a Proof of Stake (PoS) process by bonding (i.e. staking) a certain amount of POKT in escrow, on-chain, while they are providing the Fishermen services.
PoA and PoS are used to filter madmen adversaries who defy economic incentives in order to attack the network. Upon registration, a Fishermen must provide the necessary information to interact with the protocol, as illustrated in the interface below.
type FishermanStakeMsg interface {
GetPublicKey() PublicKey # The public cryptographic id of the Fisherman custodial account
GetStakeAmount() BigInt # The amount of uPOKT in escrow (i.e. a security deposit)
GetServiceURL() ServiceURL # The API endpoint where the Fishermen service is provided
GetGeoZone() GeoZone # The physical geo-location identifier this Fisherman is registered in
}
The Sampling Protocol is required to grade how each Servicer adheres to the network SLA criteria defined in the Servicer Protocol. Registered Fishermen are responsible for monitoring and periodically sampling Servicers during active Sessions.
NumSamplesPerSession
, a governance parameter, defines how many samples a Fisherman is required to make to each Servicer during the duration of a sessions. To ensure a fair assessment across all Servicers in the session, the same request must be sent to all the Servicers at the same time during the time of evaluation. The frequency at which the samples are sent, as long as the NumSamplesPerSession
quota is satisfied, should aim to be evenly distributed throughout the session. The performance of the Servicer will smooth out to the expected value over time through The Law of Large Numbers.
The data collected is:
- Availability: Null sample if the Servicer is down or unresponsive
- Latency: Round-Trip-Time (RTT) in milliseconds
- Data Consistency: The accuracy of the data through Honest Majority consensus
sequenceDiagram
autonumber
actor App
actor Fisherman
actor Servicers
loop Repeats During Session
App ->> +Servicers: RPC Request
Servicers ->> -App: RPC Response
Fisherman ->> +Servicers: Incognito Sampling (RPC) Request
Servicers ->> -Fisherman: RPC Response
end
Servicers ->> Fisherman: Reward Applicable Relay Proofs
Fisherman ->> Fisherman: Sample Aggregation
Fisherman ->> +World State: TestScore Txn
World State ->> -World State: ReportCard Update
Note over Fisherman, World State: Salary Distribution
World State ->> Servicers: Reward For Service<br>(Based on ReportCard & Stake)
World State ->> Fisherman: Reward For Sampling<br>(Based on Nullable Samples & Stake)
Servicers may bias to prioritize responding to requests from Fisherman in order to achieve a higher score. Ring Signatures will be used in order to prevent identifying who signed the request: Fisherman, Application or Gateway.
flowchart
subgraph Ring
Application <--> F1["Fisherman 1"]
F1 <--> F2["Fisherman N"]
F2 <--> Gateway
Gateway <--> Application
end
Ring --Signature--> Servicer
Servicer--Validate Signature-->Servicer
Servicers may still be able to identify actors by aggregating and inspecting IP analytics, so other protocols (e.g. Tor, NYM, HOPR) or networking mechanism (IP rotations, VPNs, etc..) may be necessary to obfuscate the Fisherman as well.
Fisherman salaries are dependant on the quantity and completeness of the TestScoreMsg
s they send, but are agnostic to their contents with regard to the Servicers' Quality of Service.
Report completeness is based on the number of Nullable samples that have a value collected by the Fisherman. NumSamplesPerSession
are expected to be performed by each Fisherman on each Servicer throughout a session.
Similar to the salary distribution algorithm described in the Servicer Protocol, Fisherman salaries are proportional to the estimated Application volume usage. However, Fishermen salary distribution is only based on the TestScoreMsg quantity and its consistence (i.e. # of non Null samples), as opposed to the actual test scores of the samples.
During each session, the Fisherman is expected to make NumSamplesPerSession
of each active Servicer. The time at which they are made should be normally distributed throughout a session with some variance to avoid predictability. Servicers will timestamp and sign the requests to avoid forgery by the Fishermen.
If a Servicer does not respond, the sample is marked as a Null. Since Fisherman salaries are based off of Nullable samples with a value, they are incentivized to exhaust sampling attempts to retrieve the Servicer's score, up to a maximum of NumSamplesPerSession
. If a Servicer exceeds MaxNullableSamples
in a single Session, the Fishermen will opt to submit a PauseMsg of the unavailable Servicer (which partially burns the Servicer's stake), enabling the protocol to introduce a new Servicer into the Session with the following block. The PauseMsg action is limited up to MaxFishermenPauses
per Session to prevent faulty Fishermen behavior and incentivizing Fisherman to keep the Session access as healthy as possible.
Individual Fishermen salaries are then calculated through a combination of Application volume usage in a certain (RelayChain, GeoZone)
pair as well as the quantity of non-Null samples.
The following graph summarizes some of the high-level interactions described above.
graph LR
F(Fisherman)
B[Blockchain]
RC((Report Card))
S(Servicer)
F <-."1. Sample<br>(collect TestScores)".-> S
F -- "2. TestScore Txn<br>(from sampling)" --> B
B -- "3. TestScore Aggregation<br>(nullable amples)" --> RC
RC -- "4. Distribute Rewards<br>(based on work done)" --> S
RC -- "5. Pay Salaries<br>(based on report completeness)" --> F
On-chain test score submission follows an optimistic Commit & Reveal methodology to avoid excessive chain state bloat while maintaining sampling security. It is similar to the Claim & Proof lifecycle designed in Pocket Network V0.
Fisherman TestScoreMsg
s have a TestScoreSubmissionProbability
requirement of being submitted, and TestScoreProofProbability
requirement of needing to be proven. In practice, this means that while all Sessions are monitored, and all samples are made, only some are submitted on-chain, and a subset of those are formally proven. Similar to Session generation, this selection is done under the Random Oracle model, so the Fishermen cannot predict which TestScores will be sampled, which need a commitment, and which need to be proven on-chain. Deterministic but non-predictable on-chain parameters (e.g. BlockHash at CurrentHeight+N) will be used to seed the selection.
pie title Test Scores Distribution
"On-Chain Proven / Revealed" : 5
"On-Chain Committed" : 20
"Off-Chain" : 75
To reduce the amount of on-chain data, Fishermen are only required to prove a single Non-Null sample from their submitted TestScoreCommitMsg
(i.e. claim) with a TestScoreProofMsg
(i.e. reveal). This must be done before MaxTestScoreProofDelay
blocks elapse.
Since the samples need to be normally distributed throughout the session, and the timestamp of each sample is signed by both the requester (i.e. Application/Gateway/Fisherman) and the responder (i.e. Servicer), the time of the sample selected using the seeded data is compared against the number of samples collected and the start time of the session sampling to verify its timestamp is within variance from the expected value.
---
title: Test Score Submission
---
classDiagram
direction LR
note for TestScoreCommitMsg "Used for rewarding & evaluating<br> Fisherman & Servicers"
class TestScoreCommitMsg{
Header: SessionData
Height: 9001
FirstSampleTime: 04:20 EST
NumSamples: 5
NumNullSamples: 2
NulIndices: [2,3]
Latencies: [p10, p25, p50, p75, p90]
DataAccuracy: 69%
EstimateRelaysServe: 1MM Relays
Commit: SamplesRoot
}
note for TestScoreProofMsg "Used for optimistically<br>validating Fishermen work"
class TestScoreProofMsg{
Header: SessionData
Sample: Sample2
SignatureRequest: FishermanSig
SignatureResponse: ServicerSig
Proof: SampleProof
}
class Sample1{
Time: 04:20 EST
Result: [...]
}
class Sample2{
Time: 04:21 EST
Result: [...]
}
class Sample3{
Time: 04:22 EST
Results: NULL
}
class Sample4{
Time: 04:23 EST
Results: NULL
}
class Sample5{
Time: 04:24 EST
Results: [...]
}
TestScoreCommitMsg --> TestScoreProofMsg: N blocks later
Sample2 --> TestScoreProofMsg: Index 1 (sample 2) selected
Sample1 --> Sample2
Sample2 --> Sample3
Sample3 --> Sample4
Sample4 --> Sample5
A Fisherman can update any of the values in its on-chain attributes by submitting another FishermenStakeMsg
while it is already staked. Parameter changes must be permissioned through the DAO ACL and the StakeAmount
must be equal to or greater than its current value.
type FishermenStakeMsg interface {
GetPublicKey() PublicKey # The public cryptographic ID of the Fisherman
GetStakeAmount() BigInt # May be modified to a value greater or equal to the current value
GetServiceURL() ServiceURL # May be modified
GetGeoZone() GeoZone # May be modified
}
A Fisherman can submit a PauseMsg
to gracefully, and temporarily, remove themselves from service (e.g. for maintenance reasons).
Since Fishermen are DAO permissioned, a minimum of MinActiveFisherman
must be registered and active at any point in time for a Fisherman to be able to pause or unstake. Downtime or inability to meet the DAO defined SLA may result in burning of the Fisherman's stake.
Similar to how Fishermen can send a PauseMsg
on behalf of faulty Servicers, the DAO is able to pause faulty or malicious Fishermen as defined in Pocket Network's Constitution.
A Fisherman can submit an UnpauseMsg
after MinPauseTime
has elapsed to resume monitoring services.
Similar to unpausing, a Fisherman can permanently remove themselves from service when MinActiveFisherman
are still active and registered. The stake will only be returned after FishermenUnstakingTime
has elapsed and permissioned through the DAO to permanently end its services.
Enforcement of Fishermen behaviour and quality is an off-chain endeavor, undertaken by the DAO and crowd-sourced through the Good Citizens Protocol of each protocol actor or community member. DAO monitoring consists of public Fishermen audits, statistical analysis, and incognito network actors that police interactions of Fishermen. The details, conditions, and limitations of DAO monitoring are defined further in the 1.0 Constitution.
In practice, the Good Citizens Protocol acts as sanity checks for the network actors, developers, users and community to monitor the Fishermen. It is an opt-in, configurable module, that checks individual interactions against finalized on-chain data. Participants will be able to report suspicious, faulty, or malicious behavior of the Fishermen to off-chain data sites which are analyzed and filtered up to the DAO and public. Individual Fishermen burns and Good Citizen bounties are determined by the DAO and defined in the 1.0 Constitution.
The Good Citizens Protocol is not a "real" protocol. It is a social committment that is often the result of tools being built and focus being shifted to areas where work is being done and money is being made. Since a permissionless network will be earning rewards based on the, initially, permissioned set of Fisherman, low rewards values are likely be noticed and floated to the surface leading to the uncovering of misbehaviour, sooner or later.
An Application
is a protocol actor that consumes Web3 access from Pocket Network Servicers. Applications are the demand side of the Utilitarian Economy, who are paying in the native cryptographic token, POKT, for the utility provided by the decentralized and permissionless network.
In order to participate as a Application in Pocket Network, each actor is required to bond a certain amount of tokens in escrow while they are consuming the Web3 access. Upon registration, the Application is required to provide information necessary to create applicable Sessions (GeoZone(s), RelayChain(s), etc...). The Application will also be able to specify NumServicers
(bounded by MinServicersPerSession
and MaxServicersPerSession
) to specify the number of servicers it would prefer, if available, for that session.
It is important to note that the bond amount is directly proportional to MaxRelaysPerSession
, which limits the number of requests each App can make per Servicer per Session as explained in the Session Protocol.
type ApplicationStakeMsg interface {
GetPublicKey() PublicKey # The public cryptographic id of the Application
GetStakeAmount() BigInt # The amount of uPOKT in escrow (i.e. a security deposit)
GetRelayChains() []RelayChain # The flavor(s) of Web3 hosted by this Application
GetGeoZone() GeoZone # The physical geo-location identifier this Application registered in
GetNumServicers() uint8 # The number of Servicers requested per session
}
An Application can update any of the values in its on-chain attributes by submitting another ApplicationStakeMsg
while it is already staked. Any parameter changes are permissible as long as the StakeAmount
is equal to or greater than its current value.
type ApplicationStakeMsg interface {
GetPublicKey() PublicKey # The public cryptographic ID of the Fisherman
GetStakeAmount() BigInt # May be modified to a value greater or equal to the current value
GetRelayChains() []RelayChain # May be modified
GetGeoZone() GeoZone # May be modified
GetNumServicers() uint8 # May be modified
}
An Application is able to submit an UnstakeMsg
to exit and remove itself from the network. After a successful UnstakeMsg, the Application is no longer eligible to consume Web3 traffic from Servicers. After the ApplicationUnstakingTime
unbonding time elapses, the remaining stake is returned to the custodial account.
Application stake burn is a necessary mechanism to ensure economic equilibrium at network maturity by balancing POKT inflation and deflation.
AppBurnPerSession
is a governance parameter that will dictate the amount of POKT burnt for every session an Application initiates, and AppBurnPerRelay
will govern the amount of POKT burnt based on the amount of work (i.e. number of relays serviced) Servicers provided to the Application throughout the session. Given that the Application's stake will decrease with every session, new rate limiting parameters, dictated via MaxRelaysPerSession
, will be set at the start of each new session.
As of updating this document, these governance parameters are expected to be 0 at the time of launching the next version of the network. More detailed tokenomic models will follow in future iterations of the specifications.
A Gateway
is a permissionless protocol actor to whom the Application can optionally delegate on-chain trust in order to perform off-chain operations.
Pocket Network's Utilitarian Economy incentivizes data redundancy in a multi-chain ecosystem, with cheap, accessible and highly available multi-chain access. Depending on the level of trust, or lack thereof, an Application can optionally use a Gateway for various Pocket-specific operations such as, but not limited to, session dispatching or request signing.
Delegation also enables free market off-chain economics where additional features, guarantees or payments can be made. This could, for example, include a contractual agreement between Applications and Gateways to execute Client Side Validation with every Nth request. It could also enable L2 services, such as data indexing, that are outside the scope of the Pocket ecosystem, but are closely related to the utility it provides.
Applications that requires just-in-time full data integrity guarantees may opt out of delegating to Gateways and operate in a completely permissionless manner. This may require them to maintain their own infrastructure (i.e. synching a full/light Pocket Node). Even with delegation, an Application would be able to continue operating permissionlessly (via a full or light node) as the two are not mutually exclusive.
OAuth is an open (Web2) protocol that authorizes clients or 3rd parties to gain access to restricted resources. It can be summarized via following flow:
sequenceDiagram
actor U as User
participant C as Client<br>(e.g. Smartphone App)
participant S as Authorization Server<br>(e.g. Google)
participant R as Resource Server<br>(e.g. Email)
U ->> C: Request access<br>to protected resource
C ->> +S: Request authorization
S -->> U: Prompt for authorization
U -->> S: Grant authorization<br>(Username & Password)
S ->> C: Return authorization_code
C ->> S: Exchange authorization_code for access_token
S ->> -C: Return access_token
C ->> +R: Request protected resource
R -->> -C: Return protected resource
For the sake of simplicity, we are omitting refresh_token
related considerations.
Some parallels can be drawn between existing centralized, trusted and permissioned systems relative to Pocket's Utilitarian Economy:
- The
Client
remains as theClient
- The
Application
is theUser
- The
Application
is a one-timeAuthorization Server
- The
Gateways
is an ongoingAuthorization Server
- The
Servicer
is theResource Servicer
- The
Fisherman
is a separate monitoring party overlooking theResource Servicer
most often owned by theAuthorization Server
An Application that chooses to operate without a Gateway is responsible for dispatching sessions and signing RPC requests on its own. To do so, it will need to maintain a Pocket Full Node or a Pocket Light Client.
sequenceDiagram
actor AC as Application / Client
actor LN as Local Pocket Node / <br>Local Light Client
actor SN as Servicer
AC ->> +LN: StartSession()
LN ->> -AC: SessionData([ServicerIDs], ...)
loop Session Duration
AC ->> AC: Sign Request
AC ->>+ SN: SignedRequest
SN ->> SN: Validate Signature<br>& Session Limits
SN ->> SN: Handle Request<br>& Sign Response
SN ->>- AC: SignedResponse
AC ->> AC: Process response
end
An Application that chooses to delegate trust to a Gateway will need to submit a one-time DelegateMsg
transaction to delegate trust from the Application to the Gateway. It must include the PublicKey of the Gateway and be signed by the Application.
sequenceDiagram
actor A as Application
participant WS as World State
actor G as Gateway
A ->> A: Prepare Delegate Message
A ->> A: Sign Request
A ->>+ WS: Delegate(GatewayPubKey)
WS ->>- A: ok
The following message will need to be signed by the Application's PrivateKey in order for it to be valid and committed to the world state.
type DelegateMsg interface {
GetApplicationPublicKey() # The cryptographic ID of the Application
GetGatewayPublicKey() # The cryptographic ID of the Gateway
}
Once committed, the Application can be serviced on behalf of the Gateway. Though an Application can delegate to multiple Gateways simultaneously, the rate limiting for each session still remains at the Servicer level.
When an Application chooses to start a new session, the Gateway is responsible for dispatching the StartSession
request using on-chain and use an off-chain mechanism (e.g. AccessTokens) to service the Application. Throughout the duration of the session, validation and communication between the Application and Gateway are done using off-chain mechanisms, which are outside the scope of this document.
Ring Signatures will be used in order to allow both the Application and the Gateway to sign the Relay.
flowchart
subgraph Ring
Application <--> P1["Gateway 1"]
P1 <--> P2["Gateway 2"]
P2 <--> Application
end
Ring --Signature--> Servicer
Servicer--Validate Signature-->Servicer
Similar to to the incognito sampling section of the Fisherman Protocol section, Ring Signatures enable the Servicer to validate the signed request. This enables permissioned (w/ a Gateway) and permissionless (w/o a Gateway) operations to co-exist, without being mutually exclusive, and without the Servicer needing knowledge of the Application's current mode of operation.
---
title: Signature Validation By Servicer
---
stateDiagram-v2
state "Get gateways the App<br>delegated to: [P1, P2]" as getGateways
state "Is relay signed by one of:<br>Application, Gateway1, Gateway2?" as sigCheck
state "Valid (should service relay)" as Valid
state "Invalid (do not service relay)" as Invalid
[*] --> getGateways
getGateways --> sigCheck
sigCheck --> Valid: Yes
sigCheck --> Invalid: No
Servicer's are incentivized to respond to any valid relay since it is applicable for reward distribution. The session tokens used for rate limiting by the Servicer will come out of the same bucket as described in the rate limiting algorithm regardless of who in the ring signed the request.
sequenceDiagram
actor A as Application
participant WS as World State
actor G as Gateway
actor S as Servicer
A ->>+ G: StartSession
G ->> G: StartSession
G ->>- A: AccessToken
loop Session Duration
A ->> +G: Request(AccessToken)
G ->> G: Sign Request
G ->> +S: SignedRequest
S ->> S: Validate Signature<br>& Session Limits
S ->> S: Handle Request<br>& Sign Response
S ->> -G: SignedResponse
G ->> G: ** Gateway specific features ** <br>(altruist, check, challenge, proof, etc...)
G ->> -A: Response
end
Registration differs from staking in the sense that the pubKey is known but there are no economic benefits/penalties in this stage of the protocol's progression.
The Gateway must register on-chain in order for the Servicer to accept its signature as part of the ring. Future versions of the protocol may include on-chain rewards or penalties for the Gateway, but the current iteration will incentivize Gateways to provide a high quality, highly trusted service through free market economics.
When staking, the Gateway must bond a certain amount of POKT to be able to participate in the network. The governance parameter, StakePerAppDelegation
limits the number of Applications that can delegate to it, and it is the Gateway's responsibility to increase its stake as the number of Applications that trust it grow.
For example, if StakePerAppDelegation
is 100 POKT and the Gateway has staked 1000 POKT, a transaction by the 11th Application to delegate to it will be rejected until the stake is increased appropriately. However, if StakePerAppDelegation
is 0 POKT, all Gateways, which are permissionless actors can have an unbounded number of Applications delegate to them.
If StakePerAppDelegation
changes such that a Gateway cannot support the existing numbers of delegating apps, they are all legacied in to continue operating as normal. However, new applications cannot delegate to the Gateway until the stake is sufficiently increased.
type GatewayStakeMsg interface {
GetPublicKey() PublicKey # The public cryptographic id of the Gateway account
GetStakeAmount() BigInt # The amount of uPOKT in escrow (i.e. a security deposit)
GetServiceURL() ServiceURL # The API endpoint where the Gateway service is provided
}
A Gateway is able to submit an UnstakeMsg
to exit and remove itself from the network. After a successful UnstakeMsg, the Gateway is no eligible sign relays on behalf of an Application. On-chain delegation from existing Applications will be removed from the world state. After the GatewayUnstakingTime
unbonding time elapses, the remaining stake is returned to the Gateway's address.
If a staked Application wants to stop using a Gateway, and prevent the Gateway from further signing relays on its behalf, it would simply submit an on-chain UndelegateMsg
. Further relays signed by the Gateway on behalf of the Application would be rejected by the Servicers.
type UndelegateMsg interface {
GetApplicationPublicKey() # The cryptographic ID of the Application
GetGatewayPublicKey() # The cryptographic ID of the Gateway
}
A Validator
is a protocol actor whose responsibility is to securely validate and process state transitions through transactions. It does so through Byzantine Fault Tolerant consensus. See the accompanying Consensus Specification for full details on its internals.
Though most of their functional behavior is described in the external Consensus Specification, a state specific Validator Protocol is needed to allow a dynamic Validator set and provide the necessary incentive layer for their operations.
Validators registration is permissionless. In order to participate in the network as a Validator, each actor is required to bond a certain amount of tokens in escrow while they are validating state transitions. These tokens are used as collateral to disincentive byzantine behavior.
Upon registration, a Validator must inform the network of how many tokens they are staking and the public endpoint where the Validator API is securely exposed. In addition to the required information, an optional OperatorPublicKey may be provided for non-custodial operation of the Validator. This registration message is formally known as the ValidatorStakeMsg
:
type ValidatorStakeMsg interface {
GetPublicKey() PublicKey # The public cryptographic id of the custodial account
GetStakeAmount() BigInt # The amount of uPOKT in escrow (i.e. a security deposit)
GetServiceURL() ServiceURL # The API endpoint where the validator service is provided
GetOperatorPubKey() *PublicKey # OPTIONAL; The non-custodial pubKey operating this node
}
The Block Producer for each height (i.e. a single Validator) will be rewarded BlockProposerAllocation
% of the total TransactionFees
and RelayRewards
held in the new block.
A single Validator's stake relative to the cumulative total of all Validators' stake is directly proportional to the likelihood of them being selected as a block producer. The external Consensus Specification outlines how block producers are selected for each height in greater detail.
The net BlockProducerReward
is derived from the TotalBlockReward
by subtracting the DAOBlockReward
amount. Once a proposal block is finalized into the blockchain, the BlockProducerReward
is sent to the custodial account.
Validators are able to gracefully pause their service (e.g. for maintenance reasons) without the need to unstake or face downtime penalization.
Validators are paused from service by the network if byzantine behaviors are detected. Some examples include:
- Not producing blocks when expected to
- Not signing blocks
- Signing against the majority
- Double signing blocks
Anytime an automatic removal of a Validator occurs, a burn proportional to the violation is applied against the Validator stake. The conditions, limitations, and severity of the Byzantine Validator burns are proposed and voted on by the DAO.
If a Validator is paused
, they are able to reverse the paused state by submitting an UnpauseMsg
after the MinValidatorPauseTime
has elapsed. After a successful UnpauseMsg, the Validator is once again eligible to execute Validator operations.
A Validator can update any of the values in its on-chain attributes by submitting another StakeMsg
while it is already staked. The only limitation is that it's StakeAmount
must be equal to or greater than its currently staked value.
A Validator is able to submit an UnstakeMsg
to exit the network and remove itself from Validator Operations. After a successful UnstakeMsg, the Validator is no longer eligible to participate in the Consensus protocol.
After ValidatorUnstakingTime
elapses, any stake amount left is returned to the custodial account. If a Validator stake amount ever falls below the MinimumValidatorStake
, the protocol automatically executes an UnstakeMsg on behalf of the node, subjecting the Validator to the unstaking process.
An Account
is a structure that maintains the ownership of POKT via a mapping from an Address
to a Balance
.
The summation of all balances of all accounts in the network equals the TotalSupply
.
type Account interface {
GetAddress() Address # The cryptographic ID of the account
GetBalance() Big.Int # The amount of uPOKT
}
The only state modification an Account may execute is a SendMsg
of POKT from one account to another.
type SendMsg interface {
GetFromAddress() Address # Address of sender Account; the signer
GetToAddress() Address # Address of receiver Account
GetAmount() Big.Int # The number of uPOKT transferred
}
A Pool is a special type of Account that is completely autonomous and owned by the network. POKT can be burnt, minted and transmitted to a Pool at the protocol layer at the time of state transition (after a block is validated) without explicit transactions. Pools can be used for tasks such as fee aggregation, reward distribution and other operations based on state machine changes.
type Pool interface {
GetAccount() Account # The cryptographic ID of the account
GetName() string # The name of the pool
}
The list of pools includes, but is not limited to, the DAO
, FeeCollector
, AppStakePool
, ValidatorStakePool
, ServicerStakePool
.
There are two categories of state changes in Pocket Network that may be included in a block:
- Transactions - state changes initiated and signed by any account with or without a balance
- Evidence - state changes and/or signals initiated and signed by registered/staked protocol actors
- Autonomous - operations completed based on the results of the lifecycle of validated and finalized blocks
Transactions are discrete state change operations executed by actors or accounts. The structure of a Transaction includes, at a minimum:
- Payload: A command structured in the form of a Message (e.g. StakeMsg, PauseMsg, SendMsg, etc..)
- Dynamic fee: A fee to incentivize the block producer to include the Transaction in a block and deter sybil attacks
- Authenticator: A digital signature
- Entropy: A Nonce (i.e. a random number) to prevent replay protection
type Transaction interface {
GetPublicKey() PublicKey # Cryptographic identifier of sender account; must be the signer
GetSignature() Signature # Digital signature of the transaction
GetMsg() Message # The payload of the transaction; Unstake, TestScore, etc.
GetFee() Big.Int # The number of tokens (uPOKT) used to incentivize and pay for the execution
GetNonce() Big.Int # Entropy used to prevent replay attacks; upper bounded
}
Evidence is similar to Transactions in creation, structure and handling, but its production and affects are limited based on the actor's role.
For example, only Validators
are eligible to submit and are affected by DoubleSign
evidence. Only Servicers
are effected by the results of a ClientSideChallenge
evidence. Only Fishermen
are effected by the results of evidence that may challenge by the results of its TestScores (e.g. RegradeServicer
)`.
Evidence is a protection mechanism against faulty or malicious consensus participants, but may extend to other protocol actors as well. The full list of types of Evidence will be defined over time, but will often result in the burning of the actor's stake if proven true.
type Evidence interface {
GetMsg() Message # Payload of the evidence; DoubleSign, TimeoutCert, ClientSideChallenge, etc.
GetHeight() Big.Int # Height at which the evidence was collected and submitted
GetAuth() Authenticator # Authentication of evidence, can be a digital signature or certificate
}
Autonomous state change operations are performed by the protocol according to specific lifecycle triggers linked to consensus. These operations include rewarding block producers, distributing relay rewards, etc.
Though most off-chain governance specification is included in Pocket Network's Constitution, the Utility Module’s Governance Protocol defines how the DAO is able to interact with the on-chain protocol on a technical level.
The Access Control List (ACL) is the primary mechanism that enables live parameter configuration. Specifically, the ACL maintains the "feature switches" that allow active modification of the behavior of the protocol without forks. The ACL also maintains the account(s) permissioned to modify said parameters. A value can be modified by a ParamUpdateMsg
from the permissioned owner of that parameter.
type ParamChangeMsg interface {
GetAddress() Address # Address of sender & signer; must be permissioned through ACL
GetParamName() String # The name of the parameter being updated
GetValue() any # The new value of the parameter being modified
GetMemo() *String # Explanation (or URL) of why the change was made
}
In addition to parameter changes, the DAO is able to burn or transfer from a specified DAO Module Account. In practice, this Module Account is used as the on-chain Treasury of the DAO, the limitations and specifics of which are maintained in the DAO 1.0 Constitution document.
type DAOTreasuryMsg interface {
GetSrcAddress() Address # The sender of the funds; must be permissioned through ACL
GetDstAddress() *Address # OPTIONAL; the receiver of the funds if applicable
GetOperation() DAOOp # The identifier of the operation; burn or send
GetAmount() Big.Int # The operation is executed on this amount of tokens
GetMemo() *String # Explanation (or URL) of why the reasoning for the fund transfer
}
As the only permissioned actors in the network, Fishermen are subject to individual burns, pauses, or removals initiated by the DAO. Usages of this message type are a result of the off-chain monitoring mechanisms described in the Fisherman Protocol section of the document. The specifics and limitations of usage of this message type is detailed in the DAO 1.0 Constitution.
type PolicingMsg interface {
GetAddress() Address # Address of sender & signer; must be permissioned on ACL
GetPoliced() Address # Address of the policed actor
Operation() DAOOp # Identifier of the operation; burn, pause, remove, etc
GetAmount() Big.Int # Amount of tokens (if applicable)
GetMemo() *String # Explanation (or URL) of the issue
}
In order to fully understand Pocket Network 1.0 and its place in the project's maturity, a rollout context is a prerequisite.
During the development & development of a v1 TestNet:
- The Fisherman & Gateway models will be will be prototyped and live-tested
- Major Servicer node runners of Pocket Network V0 may have the option to participate as Fisherman and/or Gateway actors
Upon the initial launch of Pocket Network v1 MainNet:
-
The DAO will need to approve at least one PNI owned Fisherman
- The publicKey of the Fisherman will be included in the re-genesis file
-
PNI will need to to register at least one Gateway
- The publicKey of the Gateway will be included in the re-genesis file
- The community and/or DAO will choose the initial value for
StakePerAppDelegation
- Other actors can register their own Gateways shortly after launch
-
Pocket Network scalability issues will be resolved
-
Permissionless Applications will be enabled
-
The economy will enable Network participants to operate their own Gateways
-
AppBurnPerSession
andAppBurnPerRelay
will be set to 0
- Fisherman governance parameters will be tuned
- Additional Fisherman will be approved by the DAO
- Application stake parameters (
AppBurnPerSession
andAppBurnPerRelay
) will be tuned
- A specification for Fisherman Gateway scores will be designed and developed
NOTE: This section does not exhaust all attack vectors considered, rather details the ones that were deemed important.
Primary goal: Long term. Increase the total value of the attacker's assets.
Secondary goals: Avoiding detection and damage to personal or network reputation.
AKA: Don’t kill the goose that lays the golden eggs.
Short term. Increase the total value of the attacker's assets and sell it before the network/DAO can notice and react.
AKA: Take the money and run.
A single active attacker relies on tacit cooperation from a second party that benefits from not reporting the attack.
AKA: Don’t ask. Don’t tell.
Two active players in two different parts of the economic system seek to circumvent the system by actively submitting fraudulent information and/or blocking attempts to punish, penalize or burn the malfeasant actor(s).
AKA: The master plan.
Profit seeking attacks seek to reduce the effectiveness and/or number of competitors, thereby increasing the share of rewards received.
AKA: Kill the competition.
An attack which requires creation, modification and upkeep of the core software in a private repository w/ deep protocol expertise.
AKA: Mr. Robot
An attack whose net effect is an increase or decrease in the rate of production of new tokens.
In most cases, these are likely to be Mad Man Attacks because any benefit/deficit created will be distributed throughout the entire network.
AKA: Make It Rain.
Non-profit seeking seeking attacks/behaviors which cost more to perform than can reasonably be expected to benefit the attacker. Usually results in damage to the network's reputation.
AKA: Kill 'em all.
Attack does not require significant time, energy or resources.
AKA: What happens when I push this button?
Non-profit seeking attack which requires creation, modification and upkeep of the core software in a private repository.
Any attack which, although possible, makes no sense because you could accomplish the same thing without doing all that work.
AKA: Rube Goldberg Attack.
Attack Vectors: Profit Seeking Passive; Collusion Active; Hacker
A Fisherman colluding with one or more Servicers and is in possession of their keys is able to falsify all aspects of that Servicer's report card. Therefore, all of his colluding node partners get A+ report cards and resultantly larger paychecks.
This is the “big one”. It is the primary reason that Fisherman (at this stage) require DAO Approval. It is why off chain data logs, good-citizen reporting and DAO oversight exist. It is also the reason that Fishermen require large stake/deposit, as well as increased destaking period and delayed payments (See attached spreadsheet of projected collusion ROI based on such factors as Node Percentage, Risk Rate, etc.)
The attack is easy to describe, but not easy to perform because a rather large body of work has gone into making sure that it is extremely difficult to perform, a very small payoff, and extremely costly to get caught.
Attack Vectors: Collusion passive; Hacker; Mad Man Lazy; Inflation/Deflation
If a Fisherman falsifies certain aspects of a node’s report card, he can lower that node’s proper payment share (direct attack) and increase the relative profit of all non-attacked nodes (indirect benefit if Fisherman is also a Servicer owner)
This attack is a lot of work with very little reward. The “excess” POKT does not get distributed to the non-attacked nodes. It gets burned. Therefore the benefit to non-attacked nodes is only a relative gain in terms of the overall network inflation rate. This is a highly detectable attack and the victims are highly motivated to report it. Bottom line here is: We’re talking about a highly motivated hacker, who is also Mad Man Lazy.
Attack Vectors: Mad Man Lazy
There is no financial incentive for a Fisherman to report Application volume higher or lower than actual. Servicers are incentivized to check for under reporting. There is no specific disincentive for over reporting. Other than (of course) losing all of your stake and reputation.
Attack Vectors: Mad Man
No one benefits by DDOSing a Fisherman. The relay, dispatch, service and blockchain processes are not dependent on Fishermen. The attack does not change node report cards. It only reduces overall network inflation. The Fisherman loses money, but no one gets the excess.
Attack Vectors: Lazy Mad Man, Hacker
Fishermen act in “incognito” fashion purely as a deterrent to a particular theoretical publicity seeking attack called Mad Man Blogger. Neither honest, nor dishonest nodes gain any advantage by identifying a particular relay request as belonging to a Fisherman. You can only provide your best service, it’s not possible to provide “better” service to a Fisherman than you already provide. However, the Mad Man Blogger could (if he chose to and if he successfully identified all Fishermen) provide service to only Fishermen and not to applications. We consider this an edge case attack in which the attacker has made a significant investment in POKT and in backend node infrastructure solely for the purpose of “proving” that the system can be gamed. Therefore, the contract with the DAO which Fishermen agree to, requires periodic changing of IP address and reasonable efforts to keep it unknown by other actors in the ecosystem.
Attack Vectors: Inflation Attack, Mad Man Lazy
Re Fishermen spamming via owned app:
There is no economic benefit to Fishermen from this activity as their payment is independent of application usage. One could argue that the Fisherman will benefit from the increase in supply caused by spamming the network this way. However, any inflation attack which benefits the majority of the network to a greater degree than the person who pays for and performs the attack is non-profit seeking.
Re Servicer spamming via owned app:
This activity is an inflation attack which is not profitable to the attacker.
It is economically advantageous for actors to stake within their true GeoZone as Session actor pairings are generated specifically for the GeoZone registered. Specifically, Applications will receive worse QOS and worse fidelity of Fishermen QOS monitoring by proxy as the Servicers are farther away but still within the same GeoZone as the Fishermen. Servicers are the same way, as once a GeoZone is mature the Servicers in other GeoZones are no longer competitive.
Public facing Fisherman is a vulnerability for censorship and defeats the purpose of decentralized infra.
Public facing Fishermen are no more subject to censorship than the DAO is. Just like the DAO voters, the more Fishermen participating in the network, the safer the mechanism they control is. The true difference between the DAO voters and Fishermen is the Fishermen’s scope of control is limited only to the quality of service enforcement and the DAO’s control spans the entirety of the system. If the DAO were to ever be compromised by an adversary, the Validators are able to fork the chain and install a new governing body through a social consensus of the ACL. In a parallel situation, if the Fishermen were ever compromised, the DAO would be able to modify the actors without forking the chain and halting service. The architectural separation of powers between Validators, Servicers, Fishermen, and the DAO is what truly ensures the safety of Pocket Network.
Quality of Service is a second order metric thus it is not a good criteria for salaries of Servicers.
Quality of Service is harder to measure than Quantity of Service, but it is not a second order metric. Total value of anything is measured as: (How Much X How Pure) minus the cost of making it pure.
The DAO is not a silver bullet, it’s not trustless, it’s a potential attack vector.
A powerful government might be able to temporarily stop quality of service metrics but the chain continues. There's no realism in thinking that an overtaking of the DAO isn't a catastrophic event for any version of Pocket Network. As long as there are params like 'MaxValidators' or 'RewardForRelays' the DAO may stop Pocket Network until a social fork happens. Fishermen are the same way. Stop the Fishermen, the DAO will instill new ones. Stop the DAO, the Validators will add a new DAO.
Publicly identifying fishermen puts them at risk of attacks in the real world.
Sure, this is a concern that all people in public office deal with.
Fishermen are not truly Applications, rather a proxy, so the accuracy of their TestScores is questionable and not a valid criteria for salaries of Servicers.
From a Servicer’s perspective, Fishermen are indistinguishable from applications and therefore are not only valid criteria, but - in fact - preferable sources of information because they are financially incentivised to collect honest and complete reports. Experience with V0 has shown that although applications have the ability to enforce some aspects of Servicer quality, they do not avail themselves of this opportunity.
The requirements of Fishermen actors are too high. The incentives are oriented properly so POA/POS is overkill.
If the requirements are too high, the DAO can choose to lower them. Better to start high and lower the bar than to start low and risk flooding the initial system with actors who have little to lose.
The off-chain monitoring of Fishermen is not secure enough for the duties they are responsible for.
Off-chain data monitoring and reporting for some aspects of Fishermen activities serves three purposes:
- It reduces blockchain bloat which is one of the key goals of V1 and an absolute necessity if Pocket network is to grow successfully.
- It provides a method by which “mad man” actors and Fisherman/Servicer collusion can be detected and punished.
- Very importantly, it creates a testable, modifiable, tunable environment where large portions of the action and incentive system can be studied without risk of chain state errors. This tested and tuned incentive system is considered a prerequisite for the eventual codification of the system and translation into a fully self-enforced, decentralized solution (Phase II, AKA CastNet)
Keep in mind that good actors by definition do not need to be incentivized to maintain the rules of the system. +2/3 of the network is assumed to want the network to run. Examples of this is how there's no incentive for Validators to propagate Byzantine Evidence in Tendermint and no incentive for Validators to destroy their ephemeral private keys in Algorand's BA. Or even in Hotstuff, there's no incentive for Validators to uphold the integrity of their 'LockedQC' values.
In this case, the people monitoring the Fishermen are the DAO who's largely invested in Network security and integrity and the other network actors who are interacting with the fishermen and are highly incentivized to keep them in-check, like Apps and Servicers. Apps rely on fishermen to enforce the network's decentralized Service Level Agreement. Servicers rely on fishermen to accurately shape their report cards against their peers.
Vitalik on Social Coordination argument:
'This security assumption, the idea of “getting a block hash from a friend”, may seem unrigorous to many; Bitcoin developers often make the point that if the solution to long-range attacks is some alternative deciding mechanism X, then the security of the blockchain ultimately depends on X, and so the algorithm is in reality no more secure than using X directly - implying that most X, including our social-consensus-driven approach, are insecure.
However, this logic ignores why consensus algorithms exist in the first place. Consensus is a social process, and human beings are fairly good at engaging in consensus on our own without any help from algorithms; perhaps the best example is the Rai stones, where a tribe in Yap essentially maintained a blockchain recording changes to the ownership of stones (used as a Bitcoin-like zero-intrinsic-value asset) as part of its collective memory. The reason why consensus algorithms are needed is, quite simply, because humans do not have infinite computational power, and prefer to rely on software agents to maintain consensus for us. Software agents are very smart, in the sense that they can maintain consensus on extremely large states with extremely complex rulesets with perfect precision, but they are also very ignorant, in the sense that they have very little social information, and the challenge of consensus algorithms is that of creating an algorithm that requires as little input of social information as possible.'
Applications handshaking with Fishermen is more burdensome than V0’s requirements for Applications.
The only additional requirement of V1 for Applications is to provide the Fisherman a limited AAT/Key combination. V1 also removes the difficult decision of Applications configuring for Quality or Throughput. V1 comes with QOS out of the box so the burden of challenging Servicers is completely eliminated.
Applications granting an AAT to a Fishermen is completely insecure as they are providing the Fishermen access to their authenticator.
The Fisherman receives a one-time, one-session token which allows the Fisherman to do only one thing: request relays. The Fishermen have no incentive to overuse that ability. Even a mad-man Fisherman is limited in its ability to cause harm if it wished to.
By ‘unburdening’ Applications from monitoring QOS you have effectively removed their ability to challenge their servicers. This is a downgrade from V0.
Applications are and will remain free to report bad service. Unfortunately, even with automated assistance and easy to use SDKs, it is deemed highly likely that applications will continue to act in the future as they have in the past. IE: They don’t spend time and energy fixing our product for us. They just walk away. Self-monitored and Self-enforced QOS is seen as the most viable path forward.
Only requiring Fishermen to prove they executed the (availability) sampling and not produce on-chain evidence of their data accuracy, latency, and volume claims is an exploitable vulnerability which Fishermen can/will take advantage of.
Each and every metric which is sampled is collected and verifiable on the off-chain systems. Storing that data on- chain is one of the current V0 problems that V1 seeks to solve. As to the exploitability of this particular aspect… (see.. Madman and Collusion in the attack vectors section) the incentive structure of V1 Fisherman rewards is such that there is no economic gain from this activity and the potential loss is quite significant.
The optimistic submission of TestScore and Proof transactions degrade the overall security and quality of the network.
Optimistic Submission:
We imagine that an argument could be put together which tries to demonstrate a security reduction because of optimistic submission. However, since the only aspect of security which is being affected is the amount of payment that Servicers receive, the argument would have to restrict itself to only that single aspect of V1 and (in our opinion, at this time) we do not see such an argument as being valid. However, we look forward to review and discussion of any such argument if and when someone presents it.
The approximation of MaxRelays through hash collisions is probabilistic and will always have inaccuracies. Thus, it is an unfit method to determine Application usage and TotalAvailableReward inflation.
Although the first statement (probabilistic=inaccurate) is true, the conclusion (=unfit) does not follow from the premise. V0 is currently probabilistic in selection of Servicers as well as quantity of relays per session. As they apply to node payments, V1 removes the probabilistic components of both node selection and quantity of relays, and replaces them with an amalgamated pool from which they are then distributed in a transparent and protocol enforced manner.
We do see (and are working to mitigate) potential “bad luck” sessions where an application might receive less than an appropriate minimum relay amount. This is not seen as a hard problem to overcome. “Good Luck” sessions will also happen on occasion. This is not seen as a problem at all.
The name of the actor ‘Fishermen’ has other meanings for other projects and should be changed.
Traditionally, Fishermen are monitoring actors in other Web3 projects. They allow minimum on-chain information by providing off-chain double-checks on the participants who make ‘claims’. Similar to Validators, though our flavor of Fishermen is different and unique, the core functionality of the actor remains the same.
Ability of Fishermen to replace servicers mid session makes it necessary that the size of the dispatch list for any given session cannot be fixed. (Open question)
Newly “recruited” nodes need the freedom to respond to application requests even though they are outside of the calculated list for that session. We may be able to limit dispatch list growth to 2 X the computed size however since the Fisherman cannot replace more than the entire original list. This opens a door for applications to double their effective dispatch list but does not allow abuse of relay usage.
Memos might be important (open question)
If people want to keep memos (which are helpful in the case of traceability/auditability), a compromise between the full content of the memos and nothing might be the signature of the text that “would be” on the memo using the private key. Of course, this begs the question of how big is too big, and what’s the average and mean sizes for memos in our current chain.