pm

package
v0.7.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 2, 2024 License: MIT Imports: 23 Imported by: 8

README

Probabilistic Micropayments (PM) Client

The pm package contains a client implementation of the Livepeer probabilistic micropayment protocol. The main areas of interest are:

  • A Ticket type which defines the ticket format that both senders and recipients accept.
  • A Sender type which is responsible for creating tickets.
  • A Recipient type which is responsible for validating/receiving tickets and redeeming winning tickets using the TicketBroker interface (implemented by an Ethereum smart contract). This type will be able to translate a Ticket type into the correct format expected by the TicketBroker

In practice, the package user will need to define a communication channel (i.e. HTTP connection) and a network message format (i.e. protocol buffer) that peers will use to send and receive tickets. The sending peer will use the Sender type and the receiving peer will use the Recipient type.

High Level Flow

flow diagram

RNG

A Sender and a Recipient execute a collaborative commit-reveal protocol in order to generate the random number used to determine if a ticket won or not. The random number construction is:

keccak256(abi.encodePacked(senderSig, recipientRand))

The abi.encodePacked() Solidity built-in is used to match the behavior of the contract implementing the TicketBroker interface. senderSig is a signature produced by Sender over the hash of the ticket and recipientRand is the pre-image of the recipientRandHash that is included in the ticket.

Before a Sender can start creating and sending tickets, it must first fetch ticket parameters from a Recipient. When generating ticket parameters, a Recipient will generate a random number recipientRand, compute the hash of recipientRand as recipientRandHash and include the hash with the ticket parameters. Once a Sender has a set of ticket parameters with a recipientRandHash, the Sender will use a monontonically increasing senderNonce with each new ticket that it creates.

The following conditions are met in order to ensure that the random number generation for winning ticket selection is fair:

  • Once a Sender has a set of ticket parameters with a recipientRandHash, the Sender will use a montonically increasing senderNonce with each new ticket that it creates. The montonically increasing senderNonce will ensure that senderSig is unpredictable to the Recipient.
  • Whenever a Recipient receives a ticket that uses a particular recipientRandHash, it will update an in-memory map of already received senderNonce values for the recipientRandHash. If the Recipient ever receives a ticket with a senderNonce that is already tracked by the map OR the maximum number of senderNonce values have been received for the recipientRandHash (a Sender is expected to regularly request for a new set of ticket parameters with a new recipientRandHash which means the maximum number of senderNonce values for a recipientRandHash would typically never be reached) it will consider the ticket a replay attack where the Sender is reusing an old senderNonce
  • Whenever a Recipient redeems a winning ticket, it will wait until the ticket parameters expire (based on the ParamsExpirationBlock of the ticket parameters) to redeem the ticket. This behavior allows the Recipient to avoid tracking recipientRandHash values to prevent replays because a Sender that attempts to replay a recipientRandHash value that already is associated with a redeemed winning ticket (and thus the recipientRand pre-image was revealed on-chain) would fail the ticket parameters expiration check

Refer to the spec for more information on the specifics of the random number generation for winning ticket selection.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrTicketParamsExpired = errors.New("TicketParams expired")

ErrTicketParamsExpired is returned when ticket params have expired

Functions

func RandAddress

func RandAddress() ethcommon.Address

RandAddress returns a random ETH address

func RandBytes

func RandBytes(size uint) []byte

RandBytes returns a slice of random bytes with the size specified by the caller

func RandHash

func RandHash() ethcommon.Hash

RandHash returns a random keccak256 hash

Types

type Broker

type Broker interface {
	// FundDepositAndReserve funds a sender's deposit and reserve
	FundDepositAndReserve(depositAmount, reserveAmount *big.Int) (*types.Transaction, error)

	// FundDeposit funds a sender's deposit
	FundDeposit(amount *big.Int) (*types.Transaction, error)

	// FundReserve funds a sender's reserve
	FundReserve(amount *big.Int) (*types.Transaction, error)

	// Unlock initiates the unlock period for a sender after which a sender can withdraw its
	// deposit and penalty escrow
	Unlock() (*types.Transaction, error)

	// CancelUnlock stops a sender's active unlock period
	CancelUnlock() (*types.Transaction, error)

	// Withdraw credits a sender with its deposit and penalty escrow after the sender
	// waits through the unlock period
	Withdraw() (*types.Transaction, error)

	// RedeemWinningTicket submits a ticket to be validated by the broker and if a valid winning ticket
	// the broker pays the ticket's face value to the ticket's recipient
	RedeemWinningTicket(ticket *Ticket, sig []byte, recipientRand *big.Int) (*types.Transaction, error)

	// IsUsedTicket checks if a ticket has been used
	IsUsedTicket(ticket *Ticket) (bool, error)

	// CheckTx waits for a transaction to confirm on-chain and returns an error
	// if the transaction failed
	CheckTx(tx *types.Transaction) error
}

Broker is an interface which serves as an abstraction over an on-chain smart contract that handles the administrative tasks in a probabilistic micropayment protocol including processing deposits and pay outs

type DefaultSigVerifier

type DefaultSigVerifier struct {
}

DefaultSigVerifier is client-side-only implementation of sig verification, i.e. not relying on any smart contract inputs.

func (*DefaultSigVerifier) Verify

func (sv *DefaultSigVerifier) Verify(addr ethcommon.Address, msg, sig []byte) bool

Verify checks if a provided signature over a message is valid for a given ETH address

type ErrSenderValidation added in v0.5.4

type ErrSenderValidation struct {
	// contains filtered or unexported fields
}

ErrSenderValidation is returned when the sender cannot send tickets

type FatalReceiveErr added in v0.5.5

type FatalReceiveErr struct {
	// contains filtered or unexported fields
}

func NewFatalReceiveErr added in v0.5.5

func NewFatalReceiveErr(err error) *FatalReceiveErr

type GasPriceMonitor

type GasPriceMonitor interface {
	GasPrice() *big.Int
}

GasPriceMonitor defines methods for monitoring gas prices

type LocalSenderMonitor added in v0.5.9

type LocalSenderMonitor struct {
	// contains filtered or unexported fields
}

func NewSenderMonitor

func NewSenderMonitor(cfg *LocalSenderMonitorConfig, broker Broker, smgr SenderManager, tm TimeManager, store TicketStore) *LocalSenderMonitor

NewSenderMonitor returns a new SenderMonitor

func (*LocalSenderMonitor) MaxFloat added in v0.5.9

func (sm *LocalSenderMonitor) MaxFloat(addr ethcommon.Address) (*big.Int, error)

MaxFloat returns a remote sender's max float

func (*LocalSenderMonitor) QueueTicket added in v0.5.9

func (sm *LocalSenderMonitor) QueueTicket(ticket *SignedTicket) error

QueueTicket adds a ticket to the queue for a remote sender

func (*LocalSenderMonitor) Start added in v0.5.9

func (sm *LocalSenderMonitor) Start()

Start initiates the helper goroutines for the monitor

func (*LocalSenderMonitor) Stop added in v0.5.9

func (sm *LocalSenderMonitor) Stop()

Stop signals the monitor to exit gracefully

func (*LocalSenderMonitor) SubscribeMaxFloatChange added in v0.5.9

func (sm *LocalSenderMonitor) SubscribeMaxFloatChange(sender ethcommon.Address, sink chan<- struct{}) event.Subscription

SubscribeMaxFloatChange notifies subcribers when the max float for a sender has changed and that it should call LocalSenderMonitor.MaxFloat() to get the latest value

func (*LocalSenderMonitor) ValidateSender added in v0.5.9

func (sm *LocalSenderMonitor) ValidateSender(addr ethcommon.Address) error

ValidateSender checks whether a sender's unlock period ends the round after the next round

type LocalSenderMonitorConfig added in v0.5.10

type LocalSenderMonitorConfig struct {
	// The address that will be claiming from senders' reserves
	Claimant ethcommon.Address
	// The interval at which to cleanup inactive remote senders from the cache
	CleanupInterval time.Duration
	// The interval at which to run the cleanup process
	TTL int

	// Gas cost estimate for ticket redemption
	RedeemGas       int
	SuggestGasPrice func(context.Context) (*big.Int, error)
	RPCTimeout      time.Duration
}

type MockRecipient

type MockRecipient struct {
	mock.Mock
}

MockRecipient is useful for testing components that depend on pm.Recipient

func (*MockRecipient) EV

func (m *MockRecipient) EV() *big.Rat

EV Returns the recipient's request ticket EV

func (*MockRecipient) ReceiveTicket

func (m *MockRecipient) ReceiveTicket(ticket *Ticket, sig []byte, seed *big.Int) (sessionID string, won bool, err error)

ReceiveTicket validates and processes a received ticket

func (*MockRecipient) RedeemWinningTicket

func (m *MockRecipient) RedeemWinningTicket(ticket *Ticket, sig []byte, seed *big.Int) error

RedeemWinningTicket redeems a single winning ticket

func (*MockRecipient) RedeemWinningTickets

func (m *MockRecipient) RedeemWinningTickets(sessionIDs []string) error

RedeemWinningTickets redeems all winning tickets with the broker for a all sessionIDs

func (*MockRecipient) SetMaxFaceValue added in v0.5.32

func (m *MockRecipient) SetMaxFaceValue(maxfacevalue *big.Int)

Sets the max ticket facevalue for the orchestrator

func (*MockRecipient) Start

func (m *MockRecipient) Start()

Start initiates the helper goroutines for the recipient

func (*MockRecipient) Stop

func (m *MockRecipient) Stop()

Stop signals the recipient to exit gracefully

func (*MockRecipient) TicketParams

func (m *MockRecipient) TicketParams(sender ethcommon.Address, price *big.Rat) (*TicketParams, error)

TicketParams returns the recipient's currently accepted ticket parameters for a provided sender ETH adddress

func (*MockRecipient) TxCostMultiplier

func (m *MockRecipient) TxCostMultiplier(sender ethcommon.Address) (*big.Rat, error)

TxCostMultiplier returns the transaction cost multiplier for a sender based on sender's MaxFloat

type MockSender

type MockSender struct {
	mock.Mock
}

MockSender is useful for testing components that depend on pm.Sender

func (*MockSender) CreateTicketBatch

func (m *MockSender) CreateTicketBatch(sessionID string, size int) (*TicketBatch, error)

CreateTicketBatch returns a ticket batch of the specified size

func (*MockSender) EV

func (m *MockSender) EV(sessionID string) (*big.Rat, error)

EV returns the ticket EV for a session

func (*MockSender) StartSession

func (m *MockSender) StartSession(ticketParams TicketParams) string

StartSession creates a session for a given set of ticket params which tracks information for creating new tickets

func (*MockSender) ValidateTicketParams

func (m *MockSender) ValidateTicketParams(ticketParams *TicketParams) error

ValidateTicketParams checks if ticket params are acceptable

type Recipient

type Recipient interface {
	// ReceiveTicket validates and processes a received ticket
	ReceiveTicket(ticket *Ticket, sig []byte, seed *big.Int) (sessionID string, won bool, err error)

	// RedeemWinningTicket redeems a single winning ticket
	RedeemWinningTicket(ticket *Ticket, sig []byte, seed *big.Int) error

	// TicketParams returns the recipient's currently accepted ticket parameters
	// for a provided sender ETH adddress
	TicketParams(sender ethcommon.Address, price *big.Rat) (*TicketParams, error)

	// TxCostMultiplier returns the tx cost multiplier for an address
	TxCostMultiplier(sender ethcommon.Address) (*big.Rat, error)

	// EV returns the recipients EV requirement for a ticket as configured on startup
	EV() *big.Rat

	//Set ticket faceValue upper limit
	SetMaxFaceValue(maxfacevalue *big.Int)
}

Recipient is an interface which describes an object capable of receiving tickets

func NewRecipient

func NewRecipient(addr ethcommon.Address, broker Broker, val Validator, gpm GasPriceMonitor, sm SenderMonitor, tm TimeManager, cfg TicketParamsConfig) (Recipient, error)

NewRecipient creates an instance of a recipient with an automatically generated random secret

func NewRecipientWithSecret

func NewRecipientWithSecret(addr ethcommon.Address, broker Broker, val Validator, gpm GasPriceMonitor, sm SenderMonitor, tm TimeManager, secret [32]byte, cfg TicketParamsConfig) Recipient

NewRecipientWithSecret creates an instance of a recipient with a user provided secret. In most cases, NewRecipient should be used instead which will automatically generate a random secret

type RedeemableEmitter

type RedeemableEmitter interface {
	// Redeemable returns a channel that a consumer can use to receive tickets that
	// should be redeemed
	Redeemable() chan *redemption
}

RedeemableEmitter is an interface that describes methods for emitting redeemable tickets

type ReserveInfo

type ReserveInfo struct {
	// FundsRemaining is the amount of funds the sender has left in its reserve
	FundsRemaining *big.Int

	// ClaimedInCurrentRound is the total amount of funds claimed from the sender's reserve in the current round
	ClaimedInCurrentRound *big.Int
}

ReserveInfo holds information about a sender's reserve

type Sender

type Sender interface {
	// StartSession creates a session for a given set of ticket params which tracks information
	// for creating new tickets
	StartSession(ticketParams TicketParams) string

	// CreateTicketBatch returns a ticket batch of the specified size
	CreateTicketBatch(sessionID string, size int) (*TicketBatch, error)

	// ValidateTicketParams checks if ticket params are acceptable
	ValidateTicketParams(ticketParams *TicketParams) error

	// EV returns the ticket EV for a session
	EV(sessionID string) (*big.Rat, error)
}

Sender enables starting multiple probabilistic micropayment sessions with multiple recipients and create tickets that adhere to each session's params and unique nonce requirements.

func NewSender

func NewSender(signer Signer, timeManager TimeManager, senderManager SenderManager, maxEV *big.Rat, maxTotalEV *big.Rat, depositMultiplier int) Sender

NewSender creates a new Sender instance.

type SenderInfo

type SenderInfo struct {
	// Deposit is the amount of funds the sender has in its deposit
	Deposit *big.Int

	// WithdrawRound is the round that the sender can withdraw its deposit and reserve
	WithdrawRound *big.Int

	// ReserveInfo is a struct containing details about a sender's reserve
	Reserve *ReserveInfo
}

SenderInfo contains information about a sender tracked by a Broker

type SenderManager

type SenderManager interface {
	// GetSenderInfo returns a sender's information
	GetSenderInfo(addr ethcommon.Address) (*SenderInfo, error)
	// ClaimedReserve returns the amount claimed from a sender's reserve
	ClaimedReserve(reserveHolder ethcommon.Address, claimant ethcommon.Address) (*big.Int, error)
	// Clear clears the cached values for a sender
	Clear(addr ethcommon.Address)
	// SubscribeReserveChange notifies a subcriber when the senderInfo for a sender changes
	SubscribeReserveChange(sink chan<- ethcommon.Address) event.Subscription
}

SenderManager defines the methods for fetching sender information

type SenderMonitor

type SenderMonitor interface {
	// Start initiates the helper goroutines for the monitor
	Start()

	// Stop signals the monitor to exit gracefully
	Stop()

	// QueueTicket adds a ticket to the queue for a remote sender
	QueueTicket(ticket *SignedTicket) error

	// MaxFloat returns a remote sender's max float
	MaxFloat(addr ethcommon.Address) (*big.Int, error)

	// ValidateSender checks whether a sender's unlock period ends the round after the next round
	ValidateSender(addr ethcommon.Address) error
}

SenderMonitor is an interface that describes methods used to monitor remote senders

type SigVerifier

type SigVerifier interface {
	// Verify checks if a provided signature over a message
	// is valid for a given ETH address
	Verify(addr ethcommon.Address, msg, sig []byte) bool
}

SigVerifier is an interface which describes an object capable of verification of ECDSA signatures produced by ETH addresses

type SignedTicket

type SignedTicket struct {
	// Ticket contains ticket fields that are directly
	// accessible on SignedTicket since it is embedded
	*Ticket

	// Sig is the sender's signature over the ticket
	Sig []byte

	// RecipientRand is the recipient's random value that should be
	// the preimage for the ticket's recipientRandHash
	RecipientRand *big.Int
}

SignedTicket is a wrapper around a Ticket with the sender's signature over the ticket and the recipient recipientRand

type Signer

type Signer interface {
	Sign(msg []byte) ([]byte, error)
	Account() accounts.Account
}

Signer supports identifying as an Ethereum account owner, by providing the Account and enabling message signing.

type Ticket

type Ticket struct {
	// Recipient is the ETH address of recipient
	Recipient ethcommon.Address

	// Sender is the ETH address of sender
	Sender ethcommon.Address

	// FaceValue represents the pay out to
	// the recipient if the ticket wins
	FaceValue *big.Int

	// WinProb represents how likely a ticket will win
	WinProb *big.Int

	// SenderNonce is the monotonically increasing counter that makes
	// each ticket unique given a particular recipientRand value
	SenderNonce uint32

	// RecipientRandHash is the 32 byte keccak-256 hash commitment to a random number
	// provided by the recipient. In order for the recipient to redeem
	// a winning ticket, it must reveal the preimage to this hash
	RecipientRandHash ethcommon.Hash

	// CreationRound is the round during which the ticket is created
	CreationRound int64

	// CreationRoundBlockHash is the block hash associated with CreationRound
	CreationRoundBlockHash ethcommon.Hash

	// ParamsExpirationBlock is the block number at which the ticket parameters used
	// to create the ticket will no longer be valid
	ParamsExpirationBlock *big.Int

	PricePerPixel *big.Rat
}

Ticket is lottery ticket payment in a probabilistic micropayment protocol The expected value of the ticket constitutes the payment and can be calculated using the ticket's face value and winning probability

func NewTicket

func NewTicket(params *TicketParams, expirationParams *TicketExpirationParams, sender ethcommon.Address, senderNonce uint32) *Ticket

NewTicket creates a Ticket instance

func (*Ticket) AuxData

func (t *Ticket) AuxData() []byte

AuxData returns the ticket's CreationRound and CreationRoundBlockHash encoded into a byte array: [0:31] = CreationRound (left padded with zero bytes) [32..63] = CreationRoundBlockHash See: https://github.com/livepeer/protocol/blob/pm/contracts/pm/mixins/MixinTicketProcessor.sol#L94

func (*Ticket) EV

func (t *Ticket) EV() *big.Rat

EV returns the expected value of a ticket

func (*Ticket) Hash

func (t *Ticket) Hash() ethcommon.Hash

Hash returns the keccak-256 hash of the ticket's fields as tightly packed arguments as described in the Solidity documentation See: https://solidity.readthedocs.io/en/v0.4.25/units-and-global-variables.html#mathematical-and-cryptographic-functions

func (*Ticket) WinProbRat

func (t *Ticket) WinProbRat() *big.Rat

WinProbRat returns the ticket WinProb as a percentage represented as a big.Rat

type TicketBatch

type TicketBatch struct {
	*TicketParams
	*TicketExpirationParams

	Sender ethcommon.Address

	SenderParams []*TicketSenderParams
}

TicketBatch is a group of tickets that share the same TicketParams, TicketExpirationParams and Sender Each ticket in a batch is identified by a unique TicketSenderParams

func (*TicketBatch) Tickets

func (b *TicketBatch) Tickets() []*Ticket

Tickets returns the tickets in the batch

type TicketExpirationParams

type TicketExpirationParams struct {
	CreationRound int64

	CreationRoundBlockHash ethcommon.Hash
}

TicketExpirationParams indicates when/how a ticket expires

func (*TicketExpirationParams) AuxData added in v0.5.6

func (e *TicketExpirationParams) AuxData() []byte

type TicketParams

type TicketParams struct {
	Recipient ethcommon.Address

	FaceValue *big.Int

	WinProb *big.Int

	RecipientRandHash ethcommon.Hash

	Seed *big.Int

	ExpirationBlock *big.Int

	PricePerPixel *big.Rat

	ExpirationParams *TicketExpirationParams
}

TicketParams represents the parameters defined by a receiver that a sender must adhere to when sending tickets to receiver.

func (*TicketParams) WinProbRat added in v0.5.4

func (p *TicketParams) WinProbRat() *big.Rat

WinProbRat returns the ticket WinProb as a percentage represented as a big.Rat

type TicketParamsConfig

type TicketParamsConfig struct {
	// EV is the desired expected value of tickets
	EV *big.Int

	// RedeemGas is the expected gas required to redeem a ticket
	RedeemGas int

	// TxCostMultiplier is the desired multiplier of the transaction
	// cost for redemption
	TxCostMultiplier int
}

TicketParamsConfig contains config information for a recipient to determine the parameters to use for tickets

type TicketSenderParams

type TicketSenderParams struct {
	SenderNonce uint32

	Sig []byte
}

TicketSenderParams identifies a unique ticket based on a sender's nonce and signature over a ticket hash

type TicketStore

type TicketStore interface {
	// SelectEarliestWinningTicket selects the earliest stored winning ticket for a 'sender'
	// which is not yet redeemed
	SelectEarliestWinningTicket(sender ethcommon.Address, minCreationRound int64) (*SignedTicket, error)

	// RemoveWinningTicket removes a ticket
	RemoveWinningTicket(ticket *SignedTicket) error

	// StoreWinningTicket stores a signed ticket
	StoreWinningTicket(ticket *SignedTicket) error

	// MarkWinningTicketRedeemed stores the on-chain transaction hash and timestamp of redemption
	// This marks the ticket as being 'redeemed'
	MarkWinningTicketRedeemed(ticket *SignedTicket, txHash ethcommon.Hash) error

	// WinningTicketCount returns the amount of non-redeemed winning tickets for a sender in the TicketStore
	WinningTicketCount(sender ethcommon.Address, minCreationRound int64) (int, error)

	// IsOrchActive returns true if the given orchestrator addr is active in the given round
	IsOrchActive(addr ethcommon.Address, round *big.Int) (bool, error)
}

TicketStore is an interface which describes an object capable of persisting tickets

type TimeManager added in v0.5.5

type TimeManager interface {
	// LastInitializedRound returns the last initialized round of the Livepeer protocol
	LastInitializedRound() *big.Int
	// LastInitializedL1BlockHash returns the blockhash of the L1 block the last round was initiated in
	LastInitializedL1BlockHash() [32]byte
	// PreLastInitializedL1BlockHash returns the blockhash of the L1 block for the round proceeding the last initialized round
	PreLastInitializedL1BlockHash() [32]byte
	// GetTranscoderPoolSize returns the size of the active transcoder set for a round
	GetTranscoderPoolSize() *big.Int
	// LastSeenBlock returns the last seen block number
	LastSeenL1Block() *big.Int
	// SubscribeRounds allows one to subscribe to new round events
	SubscribeRounds(sink chan<- types.Log) event.Subscription
	// SubscribeBlocks allows one to subscribe to newly seen block numbers
	SubscribeL1Blocks(sink chan<- *big.Int) event.Subscription
}

TimeManager defines the methods for fetching the last initialized round and associated block hash of the Livepeer protocol

type Validator

type Validator interface {
	// ValidateTicket checks if a ticket is valid
	ValidateTicket(recipient ethcommon.Address, ticket *Ticket, sig []byte, recipientRand *big.Int) error

	// IsWinningTicket checks if a ticket won
	// Note: This method does not check if a ticket is valid which is done using ValidateTicket
	IsWinningTicket(ticket *Ticket, sig []byte, recipientRand *big.Int) bool
}

Validator is an interface which describes an object capable of validating tickets

func NewValidator

func NewValidator(sigVerifier SigVerifier, tm TimeManager) Validator

NewValidator returns an instance of a validator

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL