Skip to content

Commit

Permalink
feat(): add harvester protocol
Browse files Browse the repository at this point in the history
  • Loading branch information
n33pm committed Jun 5, 2024
1 parent ebbd417 commit cb08424
Show file tree
Hide file tree
Showing 14 changed files with 343 additions and 92 deletions.
41 changes: 34 additions & 7 deletions pkg/peerprotocol/connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,16 @@ import (
type Connection struct {
chiaConfig *config.ChiaConfig

networkID string
peerIP *net.IP
peerPort uint16
peerKeyPair *tls.Certificate
peerDialer *websocket.Dialer
networkID string
peerIP *net.IP
peerPort uint16
peerKeyPair *tls.Certificate
peerDialer *websocket.Dialer
PeerCertHash []byte

handshakeTimeout time.Duration
conn *websocket.Conn
serverPort uint16
}

// PeerResponseHandlerFunc is a function that will be called when a response is returned from a peer
Expand All @@ -48,13 +50,21 @@ func NewConnection(ip *net.IP, options ...ConnectionOptionFunc) (*Connection, er
}
}

// peerPort != local Full Node Port
if c.peerPort == 0 {
if err := c.loadChiaConfig(); err != nil {
return nil, err
}
c.peerPort = c.chiaConfig.FullNode.Port
}

if c.serverPort == 0 {
if err := c.loadChiaConfig(); err != nil {
return nil, err
}
c.serverPort = c.chiaConfig.FullNode.Port
}

if c.peerKeyPair == nil {
if err := c.loadChiaConfig(); err != nil {
return nil, err
Expand All @@ -79,6 +89,23 @@ func NewConnection(ip *net.IP, options ...ConnectionOptionFunc) (*Connection, er
return c, nil
}

// NewServerConnection creates a new connection object with the specified peer
func NewServerConnection(conn *websocket.Conn, options ...ConnectionOptionFunc) (*Connection, error) {
c := &Connection{
conn: conn,
}

for _, fn := range options {
if fn == nil {
continue
}
if err := fn(c); err != nil {
return nil, err
}
}
return c, nil
}

func (c *Connection) loadChiaConfig() error {
if c.chiaConfig != nil {
return nil
Expand Down Expand Up @@ -176,8 +203,8 @@ func (c *Connection) handshake(nodeType protocols.NodeType) error {
handshake := &protocols.Handshake{
NetworkID: c.networkID,
ProtocolVersion: protocols.ProtocolVersion,
SoftwareVersion: "2.0.0",
ServerPort: c.peerPort,
SoftwareVersion: "2.2.1",
ServerPort: c.serverPort,
NodeType: nodeType,
Capabilities: []protocols.Capability{
{
Expand Down
8 changes: 8 additions & 0 deletions pkg/peerprotocol/connectionoptions.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@ func WithPeerPort(port uint16) ConnectionOptionFunc {
}
}

// WithServerPort sets the port for the peer
func WithServerPort(port uint16) ConnectionOptionFunc {
return func(c *Connection) error {
c.serverPort = port
return nil
}
}

// WithPeerKeyPair sets the keypair for the peer
func WithPeerKeyPair(keypair tls.Certificate) ConnectionOptionFunc {
return func(c *Connection) error {
Expand Down
10 changes: 10 additions & 0 deletions pkg/peerprotocol/farmer.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,13 @@ func NewFarmerProtocol(connection *Connection) (*FarmerProtocol, error) {
func (c *FarmerProtocol) Handshake() error {
return c.handshake(protocols.NodeTypeFarmer)
}

// DeclareProofOfSpace sends a DeclareProofOfSpace message to the peer
func (c *FarmerProtocol) DeclareProofOfSpace(data *protocols.DeclareProofOfSpace) error {
return c.Do(protocols.ProtocolMessageTypeDeclareProofOfSpace, data)
}

func (c *FarmerProtocol) SignedValues(data *protocols.SignedValues) error {
return c.Do(protocols.ProtocolMessageTypeSignedValues, data)

}
45 changes: 39 additions & 6 deletions pkg/peerprotocol/harvester.go
Original file line number Diff line number Diff line change
@@ -1,23 +1,56 @@
package peerprotocol

import "github.com/chia-network/go-chia-libs/pkg/protocols"
import (
"errors"
"github.com/chia-network/go-chia-libs/pkg/protocols"
"time"
)

// ErrInvalidNodeType is returned when the node type is invalid
var ErrInvalidNodeType = errors.New("invalid node type")

type IHarvesterProtocol interface {
Handshake(nodeType protocols.NodeType) error
HarvesterHandshake(data *protocols.HarvesterHandshake) error
NewSignagePointHarvester(data *protocols.NewSignagePointHarvester) error
RequestSignatures(data *protocols.RequestSignatures) error
ReadOne(timeout time.Duration) (*protocols.Message, error)
}

// HarvesterProtocol is for interfacing with full nodes via the peer protocol
type HarvesterProtocol struct {
*Connection
conn *Connection
}

// NewHarvesterProtocol returns a new instance of the full node protocol
func NewHarvesterProtocol(connection *Connection) (*HarvesterProtocol, error) {
func NewHarvesterProtocol(connection *Connection) (IHarvesterProtocol, error) {
return &HarvesterProtocol{connection}, nil
}

// Handshake performs the handshake with the peer
func (c *HarvesterProtocol) Handshake(data *protocols.HarvesterHandshake) error {
return c.Do(protocols.ProtocolMessageTypeHarvesterHandshake, data)
func (c *HarvesterProtocol) Handshake(nodeType protocols.NodeType) error {
if nodeType != protocols.NodeTypeHarvester && nodeType != protocols.NodeTypeFarmer {
return ErrInvalidNodeType
}
return c.conn.handshake(nodeType)
}

// HarvesterHandshake performs the handshake with the peer
func (c *HarvesterProtocol) HarvesterHandshake(data *protocols.HarvesterHandshake) error {
return c.conn.Do(protocols.ProtocolMessageTypeHarvesterHandshake, data)
}

// NewSignagePointHarvester sends a new signage point to the harvester
func (c *HarvesterProtocol) NewSignagePointHarvester(data *protocols.NewSignagePointHarvester) error {
return c.Do(protocols.ProtocolMessageTypeNewSignagePointHarvester, data)
return c.conn.Do(protocols.ProtocolMessageTypeNewSignagePointHarvester, data)
}

// RequestSignatures sends a request for signatures to the harvester
func (c *HarvesterProtocol) RequestSignatures(data *protocols.RequestSignatures) error {
return c.conn.Do(protocols.ProtocolMessageTypeRequestSignatures, data)
}

// ReadOne reads a single message from the connection
func (c *HarvesterProtocol) ReadOne(timeout time.Duration) (*protocols.Message, error) {
return c.conn.ReadOne(timeout)
}
47 changes: 47 additions & 0 deletions pkg/protocols/farmer.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,50 @@ type SignagePointSourceData struct {
SubSlotData mo.Option[SPSubSlotSourceData] `streamable:""`
VDFData mo.Option[SPVDFSourceData] `streamable:""`
}

// DeclareProofOfSpace matches to the farmer protocol type
// https://github.com/Chia-Network/chia-blockchain/blob/main/chia/protocols/farmer_protocol.py#L60
type DeclareProofOfSpace struct {
ChallengeHash types.Bytes32 `json:"challenge_hash" streamable:""`
ChallengeChainSP types.Bytes32 `json:"challenge_chain_sp" streamable:""`
SignagePointIndex uint8 `json:"signage_point_index" streamable:""`
RewardChainSP types.Bytes32 `json:"reward_chain_sp" streamable:""`
ProofOfSpace types.ProofOfSpace `json:"proof_of_space" streamable:""`
ChallengeChainSPSignature types.G2Element `json:"challenge_chain_sp_signature" streamable:""`
RewardChainSPSignature types.G2Element `json:"reward_chain_sp_signature" streamable:""`
FarmerPuzzleHash types.Bytes32 `json:"farmer_puzzle_hash" streamable:""`
PoolTarget mo.Option[types.PoolTarget] `json:"pool_target,omitempty" streamable:""`
PoolSignature mo.Option[types.G2Element] `json:"pool_signature,omitempty" streamable:""`
IncludeSignatureSourceData bool `json:"include_signature_source_data" streamable:""`
}

// RequestSignedValues is the format for the request_signed_values response
// https://github.com/Chia-Network/chia-blockchain/blob/main/chia/protocols/farmer_protocol.py#L76
type RequestSignedValues struct {
QualityString types.Bytes32 `streamable:""`
FoliageBlockDataHash types.Bytes32 `streamable:""`
FoliageTransactionBlockHash types.Bytes32 `streamable:""`
FoliageBlockData mo.Option[types.FoliageBlockData] `streamable:""`
FoliageTransactionBlockData mo.Option[types.FoliageTransactionBlock] `streamable:""`
RCBlockUnfinished mo.Option[types.RewardChainBlockUnfinished] `streamable:""`
}

// FarmingInfo is the format for the farming_info response
// https://github.com/Chia-Network/chia-blockchain/blob/main/chia/protocols/farmer_protocol.py#L87
type FarmingInfo struct {
ChallengeHash types.Bytes32 `streamable:""`
SPHash types.Bytes32 `streamable:""`
Timestamp uint64 `streamable:""`
Passed uint32 `streamable:""`
Proofs uint32 `streamable:""`
TotalPlots uint32 `streamable:""`
LookupTime uint64 `streamable:""`
}

// SignedValues is the format for the signed_values response
// https://github.com/Chia-Network/chia-blockchain/blob/main/chia/protocols/farmer_protocol.py#L99
type SignedValues struct {
QualityString types.Bytes32 `streamable:""`
FoliageBlockDataSignature types.G2Element `streamable:""`
FoliageTransactionBlockSignature types.G2Element `streamable:""`
}
20 changes: 0 additions & 20 deletions pkg/protocols/farmer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,26 +80,6 @@ func TestNewSignagePoint(t *testing.T) {
}
})
}
//hexStr := ""
//
//// Hex to bytes
//encodedBytes, err := hex.DecodeString(hexStr)
//assert.NoError(t, err)
//
//rp := &protocols.NewSignagePoint{}
//
//err = streamable.Unmarshal(encodedBytes, rp)
//assert.NoError(t, err)

//assert.Equal(t, "69171fb97a11a983e1c45f01393a0755e3b65016be6e92ea776ab8ee5b24b66a", hex.EncodeToString(rp.ChallengeHash[:]))
//assert.Equal(t, "73165326a79bf653220e33573ef2cace709b31e8f9939cd9e7e7df7f009d0881", hex.EncodeToString(rp.ChallengeChainSP[:]))
//assert.Equal(t, "5c0f452f044d025bf0bb4d51744737e09723ab323731a9bbeed9c687e369ec17", hex.EncodeToString(rp.RewardChainSP[:]))
//assert.Equal(t, uint64(11776), rp.Difficulty)
//assert.Equal(t, uint64(578813952), rp.SubSlotIters)
//assert.Equal(t, uint8(26), rp.SignagePointIndex)
//assert.Equal(t, uint32(5198109), rp.PeakHeight)
//
//assert.NotNil(t, rp.SPSourceData)

// todo test SPSourceData
}
104 changes: 101 additions & 3 deletions pkg/protocols/harvester.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,31 @@ import (
"github.com/chia-network/go-chia-libs/pkg/types"
)

// PoolDifficulty
// HarvesterMode is the harvester mode
type HarvesterMode uint8

// https://github.com/Chia-Network/chia-blockchain/blob/main/chia/farmer/farmer_api.py#L97
const (
HarvesterModeCPU HarvesterMode = 1
HarvesterModeGPU HarvesterMode = 2
)

// PoolDifficulty is the pool difficulty in the harvester protocol
// https://github.com/Chia-Network/chia-blockchain/blob/main/chia/protocols/harvester_protocol.py#L23
type PoolDifficulty struct {
Difficulty uint64 `streamable:""`
SubSlotIters uint64 `streamable:""`
PoolContractPuzzleHash types.Bytes32 `streamable:""`
}

// HarvesterHandshake
// HarvesterHandshake is the handshake message in the harvester protocol
// https://github.com/Chia-Network/chia-blockchain/blob/main/chia/protocols/harvester_protocol.py#L31
type HarvesterHandshake struct {
FarmerPublicKeys []types.G1Element `streamable:""`
PoolPublicKeys []types.G1Element `streamable:""`
}

// NewSignagePointHarvester
// NewSignagePointHarvester is the new signage point message in the harvester protocol
// https://github.com/Chia-Network/chia-blockchain/blob/main/chia/protocols/harvester_protocol.py#L38
type NewSignagePointHarvester struct {
ChallengeHash types.Bytes32 `streamable:""`
Expand All @@ -33,6 +42,77 @@ type NewSignagePointHarvester struct {
FilterPrefixBits uint8 `streamable:""`
}

// ProofOfSpaceFeeInfo is the fee info in the harvester protocol
// https://github.com/Chia-Network/chia-blockchain/blob/main/chia/protocols/harvester_protocol.py#L50
type ProofOfSpaceFeeInfo struct {
AppliedFeeThreshold uint32 `streamable:""`
}

// NewProofOfSpace is the new proof of space message in the harvester protocol
// https://github.com/Chia-Network/chia-blockchain/blob/main/chia/protocols/harvester_protocol.py#L56
type NewProofOfSpace struct {
ChallengeHash types.Bytes32 `streamable:""`
SPHash types.Bytes32 `streamable:""`
PlotIdentifier string `streamable:""`
Proof types.ProofOfSpace `streamable:""`
SignagePointIndex uint8 `streamable:""`
IncludeSourceSignatureData bool `streamable:""`
FarmerRewardAddressOverride mo.Option[types.Bytes32] `streamable:""`
FeeInfo mo.Option[ProofOfSpaceFeeInfo] `streamable:""`
}

// SigningDataKind is the kind of signing data
// https://github.com/Chia-Network/chia-blockchain/blob/main/chia/protocols/harvester_protocol.py#L68
type SigningDataKind uint8

// SigningDataKind values
const (
SigningDataKindFoliageBlockData SigningDataKind = iota + 1
SigningDataKindFoliageTransactionBlock
SigningDataKindChallengeChainVdf
SigningDataKindRewardChainVdf
SigningDataKindChallengeChainSubSlot
SigningDataKindRewardChainSubSlot
SigningDataKindPartial
)

// SignatureRequestSourceData is the source data for the signature request
// https://github.com/Chia-Network/chia-blockchain/blob/main/chia/protocols/harvester_protocol.py#L80
type SignatureRequestSourceData struct {
Kind SigningDataKind `streamable:""`
Data []byte `streamable:""`
}

// RequestSignatures is the request signatures message in the harvester protocol
// https://github.com/Chia-Network/chia-blockchain/blob/main/chia/protocols/harvester_protocol.py#L89
type RequestSignatures struct {
PlotIdentifier string `streamable:""`
ChallengeHash types.Bytes32 `streamable:""`
SPHash types.Bytes32 `streamable:""`
Messages []types.Bytes32 `streamable:""`
MessageData mo.Option[[]mo.Option[SignatureRequestSourceData]] `streamable:""`
RCBlockUnfinished mo.Option[types.RewardChainBlockUnfinished] `streamable:""`
}

// MessageSignature is the message signature in the harvester protocol
type MessageSignature struct {
types.Bytes32 `streamable:""`
types.G2Element `streamable:""`
}

// RespondSignatures is the respond signatures message in the harvester protocol
// https://github.com/Chia-Network/chia-blockchain/blob/main/chia/protocols/harvester_protocol.py#L101
type RespondSignatures struct {
PlotIdentifier string `streamable:""`
ChallengeHash types.Bytes32 `streamable:""`
SPHash types.Bytes32 `streamable:""`
LocalPK types.G1Element `streamable:""`
FarmerPK types.G1Element `streamable:""`
MessageSignatures []MessageSignature `streamable:""`
IncludeSourceSignatureData bool `streamable:""`
FarmerRewardAddressOverride mo.Option[types.Bytes32] `streamable:""`
}

// Plot is the plot definition in the harvester protocol
// https://github.com/Chia-Network/chia-blockchain/blob/main/chia/protocols/harvester_protocol.py#L114
type Plot struct {
Expand All @@ -46,3 +126,21 @@ type Plot struct {
TimeModified types.Timestamp `json:"time_modified"`
CompressionLevel mo.Option[uint8] `json:"compression_level"`
}

// PlotSyncStart is the plot sync start message
// https://github.com/Chia-Network/chia-blockchain/blob/main/chia/protocols/harvester_protocol.py#L150
type PlotSyncStart struct {
Identifier PlotSyncIdentifier `streamable:""`
Initial bool `streamable:""`
LastSyncID uint64 `streamable:""`
PlotFileCount uint32 `streamable:""`
HarvestingMode uint8 `streamable:""`
}

// PlotSyncIdentifier is the plot sync identifier
// https://github.com/Chia-Network/chia-blockchain/blob/main/chia/protocols/harvester_protocol.py#L142
type PlotSyncIdentifier struct {
Timestamp uint64 `streamable:""`
SyncID uint64 `streamable:""`
MessageID uint64 `streamable:""`
}
Loading

0 comments on commit cb08424

Please sign in to comment.