Skip to content

Commit f777896

Browse files
feat: add sysvars (#457)
* feat: add sysvars * fix: add checks
1 parent 336881c commit f777896

13 files changed

Lines changed: 2020 additions & 0 deletions

sysvar/clock.go

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
// Copyright 2021 github.com/gagliardetto
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package sysvar
16+
17+
import (
18+
"encoding/binary"
19+
20+
bin "github.com/gagliardetto/binary"
21+
)
22+
23+
// ClockSize is the serialized size, in bytes, of the Clock sysvar (5 x u64/i64).
24+
const ClockSize = 40
25+
26+
// Cluster timing constants from the solana-sdk clock crate.
27+
const (
28+
DefaultTicksPerSecond uint64 = 160
29+
DefaultTicksPerSlot uint64 = 64
30+
DefaultHashesPerSecond uint64 = 10_000_000
31+
SecondsPerDay uint64 = 24 * 60 * 60
32+
TicksPerDay uint64 = DefaultTicksPerSecond * SecondsPerDay
33+
// DefaultSlotsPerEpoch is ~2 days of slots (432000).
34+
DefaultSlotsPerEpoch uint64 = 2 * TicksPerDay / DefaultTicksPerSlot
35+
// DefaultMsPerSlot is the default slot duration in milliseconds (400).
36+
DefaultMsPerSlot uint64 = 1000 * DefaultTicksPerSlot / DefaultTicksPerSecond
37+
// MaxRecentBlockhashes is the number of blockhashes kept in the
38+
// RecentBlockhashes sysvar (300).
39+
MaxRecentBlockhashes uint64 = 120 * DefaultTicksPerSecond / DefaultTicksPerSlot
40+
// MaxProcessingAge is the number of blocks a blockhash is valid for (150).
41+
MaxProcessingAge uint64 = MaxRecentBlockhashes / 2
42+
)
43+
44+
// Clock is the data of the Clock sysvar (account solana.SysVarClockPubkey). It records
45+
// the cluster's notion of time. Mirrors solana-sdk clock::Clock.
46+
type Clock struct {
47+
// Slot is the current slot.
48+
Slot uint64
49+
// EpochStartTimestamp is the Unix timestamp (seconds) of the first slot in
50+
// the current epoch. Signed.
51+
EpochStartTimestamp int64
52+
// Epoch is the current epoch.
53+
Epoch uint64
54+
// LeaderScheduleEpoch is the most recent epoch for which the leader schedule
55+
// has been calculated.
56+
LeaderScheduleEpoch uint64
57+
// UnixTimestamp is the validator-estimated Unix timestamp (seconds) of the
58+
// current slot. Signed.
59+
UnixTimestamp int64
60+
}
61+
62+
func (c Clock) MarshalWithEncoder(encoder *bin.Encoder) error {
63+
if err := encoder.WriteUint64(c.Slot, binary.LittleEndian); err != nil {
64+
return err
65+
}
66+
if err := encoder.WriteUint64(uint64(c.EpochStartTimestamp), binary.LittleEndian); err != nil {
67+
return err
68+
}
69+
if err := encoder.WriteUint64(c.Epoch, binary.LittleEndian); err != nil {
70+
return err
71+
}
72+
if err := encoder.WriteUint64(c.LeaderScheduleEpoch, binary.LittleEndian); err != nil {
73+
return err
74+
}
75+
return encoder.WriteUint64(uint64(c.UnixTimestamp), binary.LittleEndian)
76+
}
77+
78+
func (c *Clock) UnmarshalWithDecoder(decoder *bin.Decoder) error {
79+
var err error
80+
if c.Slot, err = decoder.ReadUint64(binary.LittleEndian); err != nil {
81+
return err
82+
}
83+
v, err := decoder.ReadUint64(binary.LittleEndian)
84+
if err != nil {
85+
return err
86+
}
87+
c.EpochStartTimestamp = int64(v)
88+
if c.Epoch, err = decoder.ReadUint64(binary.LittleEndian); err != nil {
89+
return err
90+
}
91+
if c.LeaderScheduleEpoch, err = decoder.ReadUint64(binary.LittleEndian); err != nil {
92+
return err
93+
}
94+
if v, err = decoder.ReadUint64(binary.LittleEndian); err != nil {
95+
return err
96+
}
97+
c.UnixTimestamp = int64(v)
98+
return nil
99+
}
100+
101+
func (c Clock) MarshalBinary() ([]byte, error) { return encodeSysvar(c) }
102+
func (c *Clock) UnmarshalBinary(data []byte) error {
103+
return c.UnmarshalWithDecoder(bin.NewBinDecoder(data))
104+
}
105+
106+
// DecodeClock decodes Clock sysvar account data.
107+
func DecodeClock(data []byte) (*Clock, error) {
108+
var c Clock
109+
if err := c.UnmarshalBinary(data); err != nil {
110+
return nil, err
111+
}
112+
return &c, nil
113+
}

sysvar/epoch_rewards.go

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
// Copyright 2021 github.com/gagliardetto
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package sysvar
16+
17+
import (
18+
"encoding/binary"
19+
"fmt"
20+
21+
bin "github.com/gagliardetto/binary"
22+
solana "github.com/gagliardetto/solana-go"
23+
)
24+
25+
// EpochRewardsSize is the serialized size, in bytes, of the EpochRewards sysvar.
26+
const EpochRewardsSize = 81
27+
28+
// EpochRewards is the data of the EpochRewards sysvar (account
29+
// solana.SysVarEpochRewardsPubkey): the state of the partitioned epoch-rewards
30+
// distribution. Mirrors solana-sdk epoch_rewards::EpochRewards.
31+
type EpochRewards struct {
32+
// DistributionStartingBlockHeight is the block height at which rewards
33+
// distribution started for the current epoch.
34+
DistributionStartingBlockHeight uint64
35+
// NumPartitions is the number of partitions rewards are split across.
36+
NumPartitions uint64
37+
// ParentBlockhash is the blockhash of the epoch's last block, used to seed
38+
// the partition shuffle.
39+
ParentBlockhash solana.Hash
40+
// TotalPoints is the total rewards points (u128) calculated for the epoch.
41+
TotalPoints bin.Uint128
42+
// TotalRewards is the total rewards, in lamports, for the epoch.
43+
TotalRewards uint64
44+
// DistributedRewards is the rewards, in lamports, distributed so far.
45+
DistributedRewards uint64
46+
// Active reports whether rewards distribution is in progress.
47+
Active bool
48+
}
49+
50+
// Distribute records that amount more lamports of rewards have been distributed.
51+
// It returns an error if that would push DistributedRewards above TotalRewards
52+
// (or overflow), mirroring the assertion in EpochRewards::distribute.
53+
func (e *EpochRewards) Distribute(amount uint64) error {
54+
newDistributed := e.DistributedRewards + amount
55+
if newDistributed < e.DistributedRewards || newDistributed > e.TotalRewards {
56+
return fmt.Errorf("epoch rewards: distributing %d would exceed total rewards %d", amount, e.TotalRewards)
57+
}
58+
e.DistributedRewards = newDistributed
59+
return nil
60+
}
61+
62+
func (e EpochRewards) MarshalWithEncoder(encoder *bin.Encoder) error {
63+
if err := encoder.WriteUint64(e.DistributionStartingBlockHeight, binary.LittleEndian); err != nil {
64+
return err
65+
}
66+
if err := encoder.WriteUint64(e.NumPartitions, binary.LittleEndian); err != nil {
67+
return err
68+
}
69+
if err := encoder.WriteBytes(e.ParentBlockhash[:], false); err != nil {
70+
return err
71+
}
72+
if err := encoder.WriteUint128(e.TotalPoints, binary.LittleEndian); err != nil {
73+
return err
74+
}
75+
if err := encoder.WriteUint64(e.TotalRewards, binary.LittleEndian); err != nil {
76+
return err
77+
}
78+
if err := encoder.WriteUint64(e.DistributedRewards, binary.LittleEndian); err != nil {
79+
return err
80+
}
81+
return encoder.WriteBool(e.Active)
82+
}
83+
84+
func (e *EpochRewards) UnmarshalWithDecoder(decoder *bin.Decoder) error {
85+
var err error
86+
if e.DistributionStartingBlockHeight, err = decoder.ReadUint64(binary.LittleEndian); err != nil {
87+
return err
88+
}
89+
if e.NumPartitions, err = decoder.ReadUint64(binary.LittleEndian); err != nil {
90+
return err
91+
}
92+
buf, err := decoder.ReadNBytes(32)
93+
if err != nil {
94+
return err
95+
}
96+
copy(e.ParentBlockhash[:], buf)
97+
if e.TotalPoints, err = decoder.ReadUint128(binary.LittleEndian); err != nil {
98+
return err
99+
}
100+
if e.TotalRewards, err = decoder.ReadUint64(binary.LittleEndian); err != nil {
101+
return err
102+
}
103+
if e.DistributedRewards, err = decoder.ReadUint64(binary.LittleEndian); err != nil {
104+
return err
105+
}
106+
e.Active, err = decoder.ReadBool()
107+
return err
108+
}
109+
110+
func (e EpochRewards) MarshalBinary() ([]byte, error) { return encodeSysvar(e) }
111+
func (e *EpochRewards) UnmarshalBinary(data []byte) error {
112+
return e.UnmarshalWithDecoder(bin.NewBinDecoder(data))
113+
}
114+
115+
// DecodeEpochRewards decodes EpochRewards sysvar account data.
116+
func DecodeEpochRewards(data []byte) (*EpochRewards, error) {
117+
var e EpochRewards
118+
if err := e.UnmarshalBinary(data); err != nil {
119+
return nil, err
120+
}
121+
return &e, nil
122+
}

0 commit comments

Comments
 (0)