Skip to content
This repository has been archived by the owner on Aug 2, 2021. It is now read-only.

Commit

Permalink
swap: Incentives prices (#1587)
Browse files Browse the repository at this point in the history
* swap: correct price handling and reset balances

* swap: conversion to wei on cheque creation; cleanup

* swap: bug fixes and TestEmitCheque

* swap: addressed PR comments

* swap: minor fixes due to PR comments
  • Loading branch information
holisticode authored Jul 24, 2019
1 parent bb6566f commit 579cebb
Show file tree
Hide file tree
Showing 14 changed files with 583 additions and 256 deletions.
12 changes: 2 additions & 10 deletions network/discovery_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import (
p2ptest "github.com/ethereum/go-ethereum/p2p/testing"
"github.com/ethersphere/swarm/p2p/protocols"
"github.com/ethersphere/swarm/pot"
"github.com/ethersphere/swarm/testutil"
)

/***
Expand Down Expand Up @@ -249,16 +250,7 @@ func newDiscPeer(addr pot.Address) (*Peer, error) {
id := nod.ID()
p2pPeer := p2p.NewPeer(id, id.String(), nil)
return NewPeer(&BzzPeer{
Peer: protocols.NewPeer(p2pPeer, &dummyMsgRW{}, DiscoverySpec),
Peer: protocols.NewPeer(p2pPeer, &testutil.DummyMsgRW{}, DiscoverySpec),
BzzAddr: bzzAddr,
}, nil), nil
}

type dummyMsgRW struct{}

func (d *dummyMsgRW) ReadMsg() (p2p.Msg, error) {
return p2p.Msg{}, nil
}
func (d *dummyMsgRW) WriteMsg(msg p2p.Msg) error {
return nil
}
7 changes: 0 additions & 7 deletions network/stream/constants.go

This file was deleted.

5 changes: 3 additions & 2 deletions network/stream/stream.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (
"github.com/ethersphere/swarm/p2p/protocols"
"github.com/ethersphere/swarm/state"
"github.com/ethersphere/swarm/storage"
"github.com/ethersphere/swarm/swap"
)

const (
Expand Down Expand Up @@ -658,13 +659,13 @@ func (sp *StreamerPrices) Price(msg interface{}) *protocols.Price {
// Instead of hardcoding the price, get it
// through a function - it could be quite complex in the future
func (sp *StreamerPrices) getRetrieveRequestMsgPrice() uint64 {
return RetrieveRequestMsgPrice
return swap.RetrieveRequestMsgPrice
}

// Instead of hardcoding the price, get it
// through a function - it could be quite complex in the future
func (sp *StreamerPrices) getChunkDeliveryMsgRetrievalPrice() uint64 {
return ChunkDeliveryMsgRetrievalPrice
return swap.ChunkDeliveryMsgRetrievalPrice
}

// createPriceOracle sets up a matrix which can be queried to get
Expand Down
8 changes: 6 additions & 2 deletions p2p/protocols/accounting.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2018 The go-ethereum Authors
// Copyright 2019 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
Expand Down Expand Up @@ -53,10 +53,13 @@ type Prices interface {
Price(interface{}) *Price
}

// Payer is the base type to define who pays in an exchange between peers
type Payer bool

const (
Sender = Payer(true)
// Sender declares that a message needs to be payed by the sender of the message
Sender = Payer(true)
// Receiver declares that a message needs to be payed by the receiver of the message
Receiver = Payer(false)
)

Expand Down Expand Up @@ -103,6 +106,7 @@ type Accounting struct {
Prices // interface to prices logic
}

// NewAccounting creates a new instance of Accounting
func NewAccounting(balance Balance, po Prices) *Accounting {
ah := &Accounting{
Prices: po,
Expand Down
35 changes: 34 additions & 1 deletion swap/defaults.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,40 @@
// Copyright 2019 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

package swap

// Uknown for now, placeholder values
import "time"

// These are currently arbitrary values which have not been verified nor tested
// Need experimentation to arrive to values which make sense
const (
// Thresholds which trigger payment or disconnection. The unit is in honey (internal accounting unit)
DefaultPaymentThreshold = 1000000
DefaultDisconnectThreshold = 1500000
// DefaultInitialDepositAmount is the default amount to send to the contract when initially deploying
// TODO: deliberate value for now; needs experimentation
DefaultInitialDepositAmount = 0

deployRetries = 5
// delay between retries
deployDelay = 1 * time.Second
// Default timeout until cashing in cheques is possible - TODO: deliberate value, experiment
// Should be non-zero once we implement waivers
defaultCashInDelay = uint64(0)
// This is the amount of time in seconds which an issuer has to wait to decrease the harddeposit of a beneficiary.
// The smart-contract allows for setting this variable differently per beneficiary
defaultHarddepositTimeoutDuration = 24 * time.Hour
)
45 changes: 45 additions & 0 deletions swap/oracle.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright 2019 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

package swap

// PriceOracle is the interface through which Oracles will deliver prices
type PriceOracle interface {
GetPrice(honey uint64) (uint64, error)
}

// NewPriceOracle returns the actual oracle to be used for discovering the price
// TODO: Add a config flag so that this can be configured via command line
// For now it will return a default one
func NewPriceOracle() PriceOracle {
cpo := &FixedPriceOracle{
honeyPrice: defaultHoneyPrice,
}
return cpo
}

// FixedPriceOracle is a price oracle which which returns a fixed price.
// It is the default price oracle used as a placeholder for this iteration of the implementation.
// In production this should probably be some on-chain oracle called remotely
type FixedPriceOracle struct {
honeyPrice uint64
}

// GetPrice returns the actual price for honey
func (cpo *FixedPriceOracle) GetPrice(honey uint64) (uint64, error) {
// otherwise don't refresh the rate and return the latest price
return honey * cpo.honeyPrice, nil
}
35 changes: 10 additions & 25 deletions swap/peer.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,26 +63,19 @@ func (sp *Peer) handleMsg(ctx context.Context, msg interface{}) error {
case *ErrorMsg:
return sp.handleErrorMsg(ctx, msg)

case *ConfirmMsg:
return sp.handleConfirmMsg(ctx, msg)
default:
return fmt.Errorf("unknown message type: %T", msg)
}

return nil
}

// handleEmitChequeMsg should be handled by the creditor when it receives
// a cheque from a creditor
// a cheque from a debitor
// TODO: validate the contract address in the cheque to match the address given at handshake
// TODO: this should not be blocking
func (sp *Peer) handleEmitChequeMsg(ctx context.Context, msg interface{}) error {
func (sp *Peer) handleEmitChequeMsg(ctx context.Context, msg *EmitChequeMsg) error {
log.Info("received emit cheque message")

chequeMsg, ok := msg.(*EmitChequeMsg)
if !ok {
return fmt.Errorf("Invalid message type, %v", msg)
}

cheque := chequeMsg.Cheque
cheque := msg.Cheque
if cheque.Contract != sp.contractAddress {
return fmt.Errorf("wrong cheque parameters: expected contract: %s, was: %s", sp.contractAddress, cheque.Contract)
}
Expand All @@ -103,13 +96,11 @@ func (sp *Peer) handleEmitChequeMsg(ctx context.Context, msg interface{}) error

// TODO: check serial and balance are higher

// reset balance to zero, TODO: fix
sp.swap.resetBalance(sp.ID())
// send confirmation
if err := sp.Send(ctx, &ConfirmMsg{}); err != nil {
log.Error("error while sending confirm msg", "peer", sp.ID().String(), "err", err.Error())
return err
}
// reset balance by amount
// as this is done by the creditor, receiving the cheque, the amount should be negative,
// so that updateBalance will calculate balance + amount which result in reducing the peer's balance
sp.swap.resetBalance(sp.ID(), 0-int64(cheque.Honey))

// cash in cheque
//TODO: input parameter checks?
opts := bind.NewKeyedTransactor(sp.swap.owner.privateKey)
Expand Down Expand Up @@ -146,9 +137,3 @@ func (sp *Peer) handleErrorMsg(ctx context.Context, msg interface{}) error {
// maybe balance disagreement
return nil
}

// handleConfirmMsg is called when a ConfirmMsg is received
func (sp *Peer) handleConfirmMsg(ctx context.Context, msg interface{}) error {
log.Info("received confirm msg")
return nil
}
42 changes: 42 additions & 0 deletions swap/prices.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright 2019 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

package swap

/*
This module contains the pricing for message types as constants.
Pricing in Swarm is defined in an internal unit.
The name of this internal unit is honey.
The honey unit allows to set prices of messages relative to each other
without any dependancy to any currency.
The expectation is then that an external, probably on-chain, **oracle**
would be queried with the total amount of honey for a message,
for which the oracle would return the price in a given currency.
Currently the expected currency from the oracle would be wei,
but it could potentially be any currency the oracle and Swarm support,
allowing for a multi-currency design.
*/

// Placeholder prices
const (
RetrieveRequestMsgPrice = uint64(1)
ChunkDeliveryMsgRetrievalPrice = uint64(1)
// default convertion of honey into output currency - currently ETH
defaultHoneyPrice = uint64(1)
)
43 changes: 33 additions & 10 deletions swap/protocol.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"errors"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/p2p/enode"

"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/rpc"
Expand All @@ -29,18 +30,19 @@ import (
// ErrEmptyAddressInSignature is used when the empty address is used for the chequebook in the handshake
var ErrEmptyAddressInSignature = errors.New("empty address in handshake")

// Spec is the swap protocol specification
var Spec = &protocols.Spec{
Name: "swap",
Version: 1,
MaxMsgSize: 10 * 1024 * 1024,
Messages: []interface{}{
SwapHandshakeMsg{},
HandshakeMsg{},
EmitChequeMsg{},
ErrorMsg{},
ConfirmMsg{},
},
}

// Protocols is a node.Service interface method
func (s *Swap) Protocols() []p2p.Protocol {
return []p2p.Protocol{
{
Expand All @@ -52,6 +54,7 @@ func (s *Swap) Protocols() []p2p.Protocol {
}
}

// APIs is a node.Service interface method
func (s *Swap) APIs() []rpc.API {
return []rpc.API{
{
Expand All @@ -63,50 +66,70 @@ func (s *Swap) APIs() []rpc.API {
}
}

// Start is a node.Service interface method
func (s *Swap) Start(server *p2p.Server) error {
log.Info("Swap service started")
return nil
}

// Stop is a node.Service interface method
func (s *Swap) Stop() error {
return nil
}

// verifyHandshake verifies the chequebook address transmitted in the swap handshake
func (s *Swap) verifyHandshake(msg interface{}) error {
handshake, ok := msg.(*SwapHandshakeMsg)
handshake, ok := msg.(*HandshakeMsg)
if !ok || (handshake.ContractAddress == common.Address{}) {
return ErrEmptyAddressInSignature
}

return s.verifyContract(context.TODO(), handshake.ContractAddress)
}

// run is the actual swap protocol run method
func (s *Swap) run(p *p2p.Peer, rw p2p.MsgReadWriter) error {
protoPeer := protocols.NewPeer(p, rw, Spec)

answer, err := protoPeer.Handshake(context.TODO(), &SwapHandshakeMsg{
answer, err := protoPeer.Handshake(context.TODO(), &HandshakeMsg{
ContractAddress: s.owner.Contract,
}, s.verifyHandshake)
if err != nil {
return err
}

beneficiary, err := s.getContractOwner(context.TODO(), answer.(*SwapHandshakeMsg).ContractAddress)
beneficiary, err := s.getContractOwner(context.TODO(), answer.(*HandshakeMsg).ContractAddress)
if err != nil {
return err
}

swapPeer := NewPeer(protoPeer, s, s.backend, beneficiary, answer.(*SwapHandshakeMsg).ContractAddress)

s.lock.Lock()
s.peers[p.ID()] = swapPeer
s.lock.Unlock()
swapPeer := NewPeer(protoPeer, s, s.backend, beneficiary, answer.(*HandshakeMsg).ContractAddress)
s.addPeer(swapPeer)
defer s.removePeer(swapPeer)

s.logBalance(protoPeer)

return swapPeer.Run(swapPeer.handleMsg)
}

func (s *Swap) removePeer(p *Peer) {
s.lock.Lock()
defer s.lock.Unlock()
delete(s.peers, p.ID())
}

func (s *Swap) addPeer(p *Peer) {
s.lock.Lock()
defer s.lock.Unlock()
s.peers[p.ID()] = p
}

func (s *Swap) getPeer(id enode.ID) *Peer {
s.lock.Lock()
defer s.lock.Unlock()
return s.peers[id]
}

// PublicAPI would be the public API accessor for protocol methods
type PublicAPI struct {
}
Loading

0 comments on commit 579cebb

Please sign in to comment.