Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(): add farmer protocol #120

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 41 additions & 9 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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just curious when the case is we already have a websocket connection vs an IP to connect to?

Is there a reason we can't just use the peerPort for this? I don't really see a difference between serverPort and peerPort (and they default to the same config value unless overridden with config option)

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 @@ -171,14 +198,14 @@ func (c *Connection) PeerID() ([32]byte, error) {
}

// Handshake performs the RPC handshake. This should be called before any other method
func (c *Connection) Handshake() error {
func (c *Connection) handshake(nodeType protocols.NodeType) error {
// Handshake
handshake := &protocols.Handshake{
NetworkID: c.networkID,
ProtocolVersion: protocols.ProtocolVersion,
SoftwareVersion: "2.0.0",
ServerPort: c.peerPort,
NodeType: protocols.NodeTypeFullNode, // I guess we're a full node
SoftwareVersion: "2.2.1",
ServerPort: c.serverPort,
NodeType: nodeType,
Capabilities: []protocols.Capability{
{
Capability: protocols.CapabilityTypeBase,
Expand All @@ -190,6 +217,11 @@ func (c *Connection) Handshake() error {
return c.Do(protocols.ProtocolMessageTypeHandshake, handshake)
}

// Connect connects to websocket
func (c *Connection) Connect() error {
return c.ensureConnection()
}

// Do send a request over the websocket
func (c *Connection) Do(messageType protocols.ProtocolMessageType, data interface{}) error {
err := c.ensureConnection()
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
30 changes: 30 additions & 0 deletions pkg/peerprotocol/farmer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package peerprotocol

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

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

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

// Handshake performs the handshake with the peer
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)

}
13 changes: 8 additions & 5 deletions pkg/peerprotocol/fullnode.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,25 @@ import (

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

// NewFullNodeProtocol returns a new instance of the full node protocol
func NewFullNodeProtocol(connection *Connection) (*FullNodeProtocol, error) {
fnp := &FullNodeProtocol{connection: connection}
return &FullNodeProtocol{connection}, nil
}

return fnp, nil
// Handshake performs the handshake with the peer
func (c *FullNodeProtocol) Handshake() error {
return c.handshake(protocols.NodeTypeFullNode)
}

// RequestPeers asks the current peer to respond with their current peer list
func (c *FullNodeProtocol) RequestPeers() error {
return c.connection.Do(protocols.ProtocolMessageTypeRequestPeers, &protocols.RequestPeers{})
return c.Do(protocols.ProtocolMessageTypeRequestPeers, &protocols.RequestPeers{})
}

// RequestBlock asks the current peer to respond with a block
func (c *FullNodeProtocol) RequestBlock(data *protocols.RequestBlock) error {
return c.connection.Do(protocols.ProtocolMessageTypeRequestBlock, data)
return c.Do(protocols.ProtocolMessageTypeRequestBlock, data)
}
56 changes: 56 additions & 0 deletions pkg/peerprotocol/harvester.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package peerprotocol

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 {
conn *Connection
}

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

// Handshake performs the handshake with the peer
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.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)
}
87 changes: 87 additions & 0 deletions pkg/protocols/farmer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package protocols

import (
"github.com/chia-network/go-chia-libs/pkg/types"
"github.com/samber/mo"
)

// SPSubSlotSourceData is the format for the sp_sub_slot_source_data response
// https://github.com/Chia-Network/chia-blockchain/blob/main/chia/protocols/farmer_protocol.py#L26
type SPSubSlotSourceData struct {
CCSubSlot types.ChallengeChainSubSlot `streamable:""`
RCSubSlot types.RewardChainSubSlot `streamable:""`
}

// SPVDFSourceData is the format for the sp_vdf_source_data response
// https://github.com/Chia-Network/chia-blockchain/blob/main/chia/protocols/farmer_protocol.py#L33
type SPVDFSourceData struct {
CCVDF types.ClassgroupElement `streamable:""`
RCVDF types.ClassgroupElement `streamable:""`
}

// NewSignagePoint is the format for the new_signage_point response
// https://github.com/Chia-Network/chia-blockchain/blob/main/chia/protocols/farmer_protocol.py#L47
type NewSignagePoint struct {
ChallengeHash types.Bytes32 `streamable:""`
ChallengeChainSP types.Bytes32 `streamable:""`
RewardChainSP types.Bytes32 `streamable:""`
Difficulty uint64 `streamable:""`
SubSlotIters uint64 `streamable:""`
SignagePointIndex uint8 `streamable:""`
PeakHeight uint32 `streamable:""`
SPSourceData mo.Option[SignagePointSourceData] `streamable:""`
}

// SignagePointSourceData is the format for the signage_point_source_data response
// https://github.com/Chia-Network/chia-blockchain/blob/main/chia/protocols/farmer_protocol.py#L40
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:""`
}
Loading
Loading