Documentation
¶
Index ¶
- Constants
- Variables
- func BucketIndexUpperBounds(sr protocol.StateReader) ([]eracow.ContractBucketLimit, error)
- func OwnerIndexEnabled(ctx context.Context) bool
- func ParseLSDVoterIndexKey(key []byte) (address.Address, bool)
- type Bucket
- type BucketType
- type ContractBucketRef
- type ContractBucketRefs
- func (refs ContractBucketRefs) Contains(ref ContractBucketRef) bool
- func (refs *ContractBucketRefs) Decode(gv systemcontracts.GenericValue) error
- func (refs *ContractBucketRefs) Deserialize(data []byte) error
- func (refs *ContractBucketRefs) Encode() (systemcontracts.GenericValue, error)
- func (refs *ContractBucketRefs) LoadProto(pb *stakingpb.ContractBucketRefs) error
- func (refs *ContractBucketRefs) Proto() *stakingpb.ContractBucketRefs
- func (refs *ContractBucketRefs) Serialize() ([]byte, error)
- type ContractStakingStateManager
- func (cs *ContractStakingStateManager) AddOwnerRefs(ctx context.Context, owner address.Address, refs []ContractBucketRef) error
- func (cs *ContractStakingStateManager) DeleteBucket(ctx context.Context, contractAddr address.Address, bucketID uint64) error
- func (cs *ContractStakingStateManager) RaiseNumOfBuckets(contractAddr address.Address, bucketID uint64) error
- func (cs *ContractStakingStateManager) UpdateNumOfBuckets(contractAddr address.Address, numOfBuckets uint64) error
- func (cs *ContractStakingStateManager) UpsertBucket(ctx context.Context, contractAddr address.Address, bid uint64, bucket *Bucket) error
- func (cs *ContractStakingStateManager) UpsertBucketType(contractAddr address.Address, bucketID uint64, bucketType *BucketType) error
- type ContractStakingStateReader
- func (r *ContractStakingStateReader) Bucket(contractAddr address.Address, bucketID uint64) (*Bucket, error)
- func (r *ContractStakingStateReader) BucketRefsByOwner(owner address.Address) (ContractBucketRefs, uint64, error)
- func (r *ContractStakingStateReader) BucketStateOpts(contractAddr address.Address, bucketID uint64) []protocol.StateOption
- func (r *ContractStakingStateReader) BucketType(contractAddr address.Address, tID uint64) (*BucketType, error)
- func (r *ContractStakingStateReader) BucketTypes(contractAddr address.Address) ([]uint64, []*BucketType, error)
- func (r *ContractStakingStateReader) Buckets(contractAddr address.Address) ([]uint64, []*Bucket, error)
- func (r *ContractStakingStateReader) FrozenBucket(window eracow.Window, contractAddr address.Address, bucketID uint64) (*Bucket, error)
- func (r *ContractStakingStateReader) FrozenBucketRefs(window eracow.Window, owner address.Address) (ContractBucketRefs, error)
- func (r *ContractStakingStateReader) NumOfBuckets(contractAddr address.Address) (uint64, error)
- func (r *ContractStakingStateReader) OwnerIndexStateOpts(owner address.Address) []protocol.StateOption
- type StakingContract
Constants ¶
const LSDVoterIndexPrefix = byte(6)
LSDVoterIndexPrefix is the 1-byte tag of the owner -> contract-staking bucket index inside state.StakingNamespace.
Key shape is {LSDVoterIndexPrefix} || owner(20) = 21 bytes. That is the same length as the native _voterIndex / _candIndex / _candidateRewardSnapshot keys, which is fine because those carry a different leading tag; readers that scan the shared namespace discriminate on tag *and* length (see parseVoterWeightKey in the staking package), and no existing tag uses this value.
Variables ¶
var ErrBucketNotExist = errors.New("bucket does not exist")
ErrBucketNotExist is the error when bucket does not exist
var ErrOwnerIndexNotExist = errors.New("contract-staking owner index does not exist")
ErrOwnerIndexNotExist is returned when an address owns no contract-staking bucket, i.e. the index key is absent. The empty list is never stored.
Functions ¶
func BucketIndexUpperBounds ¶
func BucketIndexUpperBounds(sr protocol.StateReader) ([]eracow.ContractBucketLimit, error)
BucketIndexUpperBounds returns every staking contract's exclusive bucket index upper bound, sorted ascending by contract address, for freezing into an IIP-59 era window.
The values come from the contract meta namespace, which holds exactly one small record per registered staking contract and nothing else, so this is a bounded scan rather than a state walk. The stored number is the max bucket id ever seen for the contract: it is only ever raised, never lowered, and contract bucket ids are minted from a strictly monotonic counter that burning does not touch. A bucket id above the frozen mark therefore cannot have existed at the freeze height. This function converts that inclusive mark to an exclusive upper bound, matching native bucket semantics.
Post-IIP-59 the mark is maintained for every contract by ContractStakingStateManager.RaiseNumOfBuckets, hooked into UpsertBucket. Do not assume the indexers maintain it: only the V1 indexer ever did, which is exactly why RaiseNumOfBuckets exists.
A contract that is missing from the result has never had a bucket written through UpsertBucket post-activation. eracow.Window.ContractBucketExisted rejects every bucket of such a contract, so a contract silently going missing here costs its owners their share -- staking.FrozenContractBucket logs that case rather than letting it pass unnoticed.
Sorted output matters: this ends up in a consensus record, and map iteration order would make two nodes disagree byte-for-byte on identical state.
func OwnerIndexEnabled ¶
OwnerIndexEnabled reports whether the owner -> contract-staking bucket index may be written to the state trie.
Pre-activation it must stay out of it: nodes upgrade over days, and one that wrote these keys early would diverge from every node still on the old binary -- a split at deployment time rather than at activation. It is bound to protocol.FeatureCtx.NoVoterRewardDistribution, the IIP-59 fork gate.
A context with no feature context at all (indexer bootstraps, tests) is treated as pre-activation, so nothing is written by accident.
func ParseLSDVoterIndexKey ¶
ParseLSDVoterIndexKey reverses lsdVoterIndexKey. ok is false for any key in the staking namespace that is not an owner index entry -- buckets, native bucket indices, endorsements, reward snapshots and voter weights all share the namespace, so a scan must discriminate by key rather than by whether the value happens to deserialize.
Types ¶
type Bucket ¶
type Bucket struct {
// Candidate is the address of the candidate that this bucket is staking for.
Candidate address.Address
// Owner is the address of the owner of this bucket.
Owner address.Address
// StakedAmount is the amount of tokens staked in this bucket.
StakedAmount *big.Int
// StakedDuration is the duration for which the tokens have been staked.
StakedDuration uint64 // in seconds if timestamped, in block number if not
// CreatedAt is the time when the bucket was created.
CreatedAt uint64 // in unix timestamp if timestamped, in block height if not
// UnlockedAt is the time when the bucket was unlocked.
UnlockedAt uint64 // in unix timestamp if timestamped, in block height if not
// UnstakedAt is the time when the bucket was unstaked.
UnstakedAt uint64 // in unix timestamp if timestamped, in block height if not
// IsTimestampBased indicates whether the bucket is timestamp-based
IsTimestampBased bool
// Muted indicates whether the bucket is vote weight muted
Muted bool
}
Bucket is the structure that holds information about a staking bucket.
func LoadBucketFromProto ¶
func LoadBucketFromProto(pb *stakingpb.SystemStakingBucket) (*Bucket, error)
LoadBucketFromProto converts a protobuf representation of a staking bucket to a Bucket struct.
func (*Bucket) Decode ¶
func (b *Bucket) Decode(gv systemcontracts.GenericValue) error
Decode decodes the bucket from a GenericValue
func (*Bucket) Deserialize ¶
Deserialize deserializes the bucket from a byte slice.
func (*Bucket) Encode ¶
func (b *Bucket) Encode() (systemcontracts.GenericValue, error)
Encode encodes the bucket into a GenericValue
type BucketType ¶
BucketType defines the type of contract staking bucket
func LoadBucketTypeFromProto ¶
func LoadBucketTypeFromProto(pb *stakingpb.BucketType) (*BucketType, error)
LoadBucketTypeFromProto converts a protobuf representation of a staking bucket type to a BucketType struct.
func (*BucketType) Decode ¶
func (bt *BucketType) Decode(gv systemcontracts.GenericValue) error
Decode decodes the bucket type from a GenericValue
func (*BucketType) Deserialize ¶
func (bt *BucketType) Deserialize(b []byte) error
Deserialize deserializes the bucket type
func (*BucketType) Encode ¶
func (bt *BucketType) Encode() (systemcontracts.GenericValue, error)
Encode encodes the bucket type into a GenericValue
func (*BucketType) Serialize ¶
func (bt *BucketType) Serialize() ([]byte, error)
Serialize serializes the bucket type
type ContractBucketRef ¶
ContractBucketRef names one contract-staking bucket. Bucket ids are only unique per staking contract, so the contract address is part of the identity, not context.
type ContractBucketRefs ¶
type ContractBucketRefs []ContractBucketRef
ContractBucketRefs is the value stored under an owner index key: every contract-staking bucket an address owns, across all staking contracts.
The slice is kept sorted ascending by (contract bytes, bucket id) at all times. This is consensus state: two nodes that applied the same set of bucket writes must produce byte-identical values, so the order can never come from map iteration.
func (ContractBucketRefs) Contains ¶
func (refs ContractBucketRefs) Contains(ref ContractBucketRef) bool
Contains reports whether the ref is in the list.
func (*ContractBucketRefs) Decode ¶
func (refs *ContractBucketRefs) Decode(gv systemcontracts.GenericValue) error
Decode decodes the refs from a GenericValue.
func (*ContractBucketRefs) Deserialize ¶
func (refs *ContractBucketRefs) Deserialize(data []byte) error
Deserialize deserializes bytes into refs.
func (*ContractBucketRefs) Encode ¶
func (refs *ContractBucketRefs) Encode() (systemcontracts.GenericValue, error)
Encode encodes the refs into a GenericValue for Erigon dual-storage.
func (*ContractBucketRefs) LoadProto ¶
func (refs *ContractBucketRefs) LoadProto(pb *stakingpb.ContractBucketRefs) error
LoadProto converts protobuf to refs.
func (*ContractBucketRefs) Proto ¶
func (refs *ContractBucketRefs) Proto() *stakingpb.ContractBucketRefs
Proto converts the refs to protobuf.
func (*ContractBucketRefs) Serialize ¶
func (refs *ContractBucketRefs) Serialize() ([]byte, error)
Serialize serializes the refs into bytes. The list is sorted first so a caller that built one by hand cannot write a value that differs only in order from the one a replaying node would produce.
type ContractStakingStateManager ¶
type ContractStakingStateManager struct {
ContractStakingStateReader
// contains filtered or unexported fields
}
ContractStakingStateManager wraps a state manager to provide staking contract-specific writes.
func NewContractStakingStateManager ¶
func NewContractStakingStateManager(sm protocol.StateManager, opts ...protocol.StateOption) *ContractStakingStateManager
NewContractStakingStateManager creates a new ContractStakingStateManager
func (*ContractStakingStateManager) AddOwnerRefs ¶
func (cs *ContractStakingStateManager) AddOwnerRefs(ctx context.Context, owner address.Address, refs []ContractBucketRef) error
AddOwnerRefs records that owner owns every ref in refs.
Semantically identical to calling addOwnerRef once per ref, including its idempotence and its "a no-op write produces no era copy either" rule. The difference is cost: the per-ref form is a read-modify-write of the same trie key for each ref, which is what made the IIP-59 activation backfill expensive for owners holding many buckets. This reads once, copies aside at most once, and writes once.
Exported because the backfill lives in the staking package (see staking.backfillOwnerIndex); nothing else needs it — the live paths add one ref at a time.
func (*ContractStakingStateManager) DeleteBucket ¶
func (cs *ContractStakingStateManager) DeleteBucket(ctx context.Context, contractAddr address.Address, bucketID uint64) error
DeleteBucket removes a bucket for a given contract and bucket ID.
ctx carries the fork gate for the owner index only; the bucket write itself is unconditional and byte-for-byte what it was before the index existed.
func (*ContractStakingStateManager) RaiseNumOfBuckets ¶
func (cs *ContractStakingStateManager) RaiseNumOfBuckets(contractAddr address.Address, bucketID uint64) error
RaiseNumOfBuckets raises the contract's bucket high-water mark to cover bucketID, and does nothing if it already does.
Why this exists ¶
StakingContract.NumOfBuckets is the max bucket id ever seen for a contract (see BackfillContract for why that is not a count). It is the only bound the IIP-59 era window has on contract bucket ids -- eracow.Window. ContractBucketExisted answers "did this id exist at H" purely from it -- and a contract with no record at all is rejected outright.
Before IIP-59 exactly one code path maintained it: blockindex/contractstaking/cache.go Commit, i.e. the V1 indexer only. The V2 and V3 indexers in systemcontractindex/stakingindex never wrote it, and neither did staking.nftEventHandler, the shared trie-write path all three indexers funnel their bucket writes through. So V2/V3 buckets had no frozen mark and were silently dropped from every frozen weight, no matter what the owner index said about them.
Hooking the raise here rather than in each indexer covers all three at once, because UpsertBucket is the single writer of contract bucket state.
Raise-only, and gated ¶
Raise-only because the mark's whole meaning is "no id above this existed before now": lowering it would let a post-freeze bucket into a frozen era. It is also what makes this safe to run alongside the V1 indexer's own unconditional write of the same key -- V1's value is monotone too, so the two agree and the extra write is a no-op.
Gated behind the IIP-59 fork by its only caller, because writing a meta record for a contract that has none today changes the state root.
func (*ContractStakingStateManager) UpdateNumOfBuckets ¶
func (cs *ContractStakingStateManager) UpdateNumOfBuckets(contractAddr address.Address, numOfBuckets uint64) error
UpdateNumOfBuckets updates the number of buckets.
This is an unconditional write, used by the V1 indexer which tracks the mark itself. Prefer RaiseNumOfBuckets anywhere the value is not already known to be monotone.
func (*ContractStakingStateManager) UpsertBucket ¶
func (cs *ContractStakingStateManager) UpsertBucket(ctx context.Context, contractAddr address.Address, bid uint64, bucket *Bucket) error
UpsertBucket inserts or updates a bucket for a given contract and bid.
This and DeleteBucket are the only writers of contract-staking bucket state, which is what makes them the single choke point for the owner index.
func (*ContractStakingStateManager) UpsertBucketType ¶
func (cs *ContractStakingStateManager) UpsertBucketType(contractAddr address.Address, bucketID uint64, bucketType *BucketType) error
UpsertBucketType inserts or updates a bucket type for a given contract and bucket ID.
type ContractStakingStateReader ¶
type ContractStakingStateReader struct {
// contains filtered or unexported fields
}
ContractStakingStateReader wraps a state reader to provide staking contract-specific reads.
func NewStateReader ¶
func NewStateReader(sr protocol.StateReader, opts ...protocol.StateOption) *ContractStakingStateReader
NewStateReader creates a new ContractStakingStateReader.
func (*ContractStakingStateReader) Bucket ¶
func (r *ContractStakingStateReader) Bucket(contractAddr address.Address, bucketID uint64) (*Bucket, error)
Bucket returns the Bucket for a given contract and bucket id.
func (*ContractStakingStateReader) BucketRefsByOwner ¶
func (r *ContractStakingStateReader) BucketRefsByOwner(owner address.Address) (ContractBucketRefs, uint64, error)
BucketRefsByOwner returns every contract-staking bucket the address owns, sorted by (contract, bucket id), plus the height the state was read at.
This lives in the contractstaking package rather than next to NativeBucketIndicesByVoter because the value type names a staking contract: `staking` already imports `contractstaking`, so the ref type cannot live on the other side without inverting the dependency.
An owner with no contract-staking buckets has no key at all; the error is state.ErrStateNotExist, wrapped as ErrOwnerIndexNotExist.
func (*ContractStakingStateReader) BucketStateOpts ¶
func (r *ContractStakingStateReader) BucketStateOpts(contractAddr address.Address, bucketID uint64) []protocol.StateOption
BucketStateOpts addresses one contract-staking bucket.
This is the single expression for that address in the repository. Every live read and write of a bucket goes through it, and so does the IIP-59 era copy-on-write resolver's live-value fallback (staking.FrozenContractBucket), which would otherwise re-derive the same address by hand and drift from this one — silently, since a frozen read that misses is skipped, not failed.
func (*ContractStakingStateReader) BucketType ¶
func (r *ContractStakingStateReader) BucketType(contractAddr address.Address, tID uint64) (*BucketType, error)
BucketType returns the BucketType for a given contract and bucket id.
func (*ContractStakingStateReader) BucketTypes ¶
func (r *ContractStakingStateReader) BucketTypes(contractAddr address.Address) ([]uint64, []*BucketType, error)
BucketTypes returns all BucketType for a given contract and bucket id.
func (*ContractStakingStateReader) Buckets ¶
func (r *ContractStakingStateReader) Buckets(contractAddr address.Address) ([]uint64, []*Bucket, error)
Buckets returns all BucketInfo for a given contract.
func (*ContractStakingStateReader) FrozenBucket ¶
func (r *ContractStakingStateReader) FrozenBucket( window eracow.Window, contractAddr address.Address, bucketID uint64, ) (*Bucket, error)
FrozenBucket reads a contract-staking bucket as of the era freeze height.
func (*ContractStakingStateReader) FrozenBucketRefs ¶
func (r *ContractStakingStateReader) FrozenBucketRefs(window eracow.Window, owner address.Address) (ContractBucketRefs, error)
FrozenBucketRefs reads an owner's contract-staking bucket list as of the era freeze height. An absent list is returned as nil without an error.
func (*ContractStakingStateReader) NumOfBuckets ¶
func (r *ContractStakingStateReader) NumOfBuckets(contractAddr address.Address) (uint64, error)
NumOfBuckets returns the total number of buckets for a contract.
func (*ContractStakingStateReader) OwnerIndexStateOpts ¶
func (r *ContractStakingStateReader) OwnerIndexStateOpts(owner address.Address) []protocol.StateOption
OwnerIndexStateOpts addresses one owner's contract-staking bucket ref list.
This is the single expression for that address in the repository; see BucketStateOpts for why frozen and live reads must share one.
type StakingContract ¶
type StakingContract struct {
// NumOfBuckets is the number of buckets in the staking contract
NumOfBuckets uint64
}
StakingContract represents the staking contract in the system
func LoadStakingContractFromProto ¶
func LoadStakingContractFromProto(pb *stakingpb.SystemStakingContract) (*StakingContract, error)
LoadStakingContractFromProto converts a protobuf representation of a staking contract to a StakingContract struct.
func (*StakingContract) Decode ¶
func (sc *StakingContract) Decode(gv systemcontracts.GenericValue) error
Decode decodes the staking contract from a GenericValue
func (*StakingContract) Deserialize ¶
func (sc *StakingContract) Deserialize(b []byte) error
Deserialize deserializes the staking contract
func (*StakingContract) Encode ¶
func (sc *StakingContract) Encode() (systemcontracts.GenericValue, error)
Encode encodes the staking contract into a GenericValue
func (*StakingContract) Serialize ¶
func (sc *StakingContract) Serialize() ([]byte, error)
Serialize serializes the staking contract