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

Commit

Permalink
swap: use the SimpleSwapFactory for deployment and verification
Browse files Browse the repository at this point in the history
  • Loading branch information
ralph-pichler committed Sep 24, 2019
1 parent 5f00a9c commit b066a40
Show file tree
Hide file tree
Showing 17 changed files with 2,599 additions and 78 deletions.
13 changes: 7 additions & 6 deletions api/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,13 @@ type Config struct {
BaseKey []byte

// Swap configs
SwapBackendURL string // Ethereum API endpoint
SwapEnabled bool // whether SWAP incentives are enabled
SwapPaymentThreshold uint64 // honey amount at which a payment is triggered
SwapDisconnectThreshold uint64 // honey amount at which a peer disconnects
SwapLogPath string // dir to swap related audit logs
Contract common.Address // address of the chequebook contract
SwapBackendURL string // Ethereum API endpoint
SwapEnabled bool // whether SWAP incentives are enabled
SwapPaymentThreshold uint64 // honey amount at which a payment is triggered
SwapDisconnectThreshold uint64 // honey amount at which a peer disconnects
SwapLogPath string // dir to swap related audit logs
Contract common.Address // address of the chequebook contract
ChequebookFactoryContract common.Address // address of the chequebook factory contract
// end of Swap configs

*network.HiveParams
Expand Down
5 changes: 4 additions & 1 deletion cmd/swarm/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ const (
SwarmEnvSwapPaymentThreshold = "SWARM_SWAP_PAYMENT_THRESHOLD"
SwarmEnvSwapDisconnectThreshold = "SWARM_SWAP_DISCONNECT_THRESHOLD"
SwarmEnvSwapLogPath = "SWARM_SWAP_LOG_PATH"
SwarmEnvChequebookFactory = "SWARM_SWAP_CHEQUEBOOK_FACTORY"
SwarmEnvSyncDisable = "SWARM_SYNC_DISABLE"
SwarmEnvSyncUpdateDelay = "SWARM_ENV_SYNC_UPDATE_DELAY"
SwarmEnvMaxStreamPeerServers = "SWARM_ENV_MAX_STREAM_PEER_SERVERS"
Expand Down Expand Up @@ -182,7 +183,9 @@ func flagsOverride(currentConfig *bzzapi.Config, ctx *cli.Context) *bzzapi.Confi
if chbookaddr := ctx.GlobalString(SwarmSwapChequebookAddrFlag.Name); chbookaddr != "" {
currentConfig.Contract = common.HexToAddress(chbookaddr)
}

if chbookfactoryaddr := ctx.GlobalString(SwarmSwapChequebookFactoryFlag.Name); chbookfactoryaddr != "" {
currentConfig.ChequebookFactoryContract = common.HexToAddress(chbookfactoryaddr)
}
networkid := ctx.GlobalUint64(SwarmNetworkIdFlag.Name)
if networkid != 0 && networkid != network.DefaultNetworkID {
currentConfig.NetworkID = networkid
Expand Down
5 changes: 5 additions & 0 deletions cmd/swarm/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ var (
Usage: "Write execution logs of swap audit to the given directory",
EnvVar: SwarmEnvSwapLogPath,
}
SwarmSwapChequebookFactoryFlag = cli.StringFlag{
Name: "swap-chequebook-factory",
Usage: "address for the chequebook factory",
EnvVar: SwarmEnvChequebookFactory,
}
SwarmSyncDisabledFlag = cli.BoolTFlag{
Name: "nosync",
Usage: "Disable swarm syncing",
Expand Down
1 change: 1 addition & 0 deletions cmd/swarm/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ func init() {
SwarmSwapPaymentThresholdFlag,
SwarmSwapLogPathFlag,
SwarmSwapChequebookAddrFlag,
SwarmSwapChequebookFactoryFlag,
// end of swap flags
SwarmSyncDisabledFlag,
SwarmSyncUpdateDelay,
Expand Down
100 changes: 100 additions & 0 deletions contracts/swap/factory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package swap

import (
"bytes"
"context"
"errors"
"math/big"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
contractFactory "github.com/ethersphere/go-sw3/contracts-v0-1-1/simpleswapfactory"
)

var (
// ErrNotDeployedByFactory is given when a contract was not deployed by the factory
ErrNotDeployedByFactory = errors.New("not deployed by factory")
)

type simpleSwapFactory struct {
instance *contractFactory.SimpleSwapFactory
address common.Address
backend Backend
}

type SimpleSwapFactory interface {
DeploySimpleSwap(auth *bind.TransactOpts, issuer common.Address, defaultHardDepositTimeoutDuration *big.Int) (Contract, error)
VerifyContract(address common.Address) error
VerifySelf() error
}

func FactoryAt(address common.Address, backend Backend) (SimpleSwapFactory, error) {
simple, err := contractFactory.NewSimpleSwapFactory(address, backend)
if err != nil {
return nil, err
}
c := simpleSwapFactory{instance: simple, address: address, backend: backend}
return c, err
}

func (sf simpleSwapFactory) VerifySelf() error {
code, err := sf.backend.CodeAt(context.Background(), sf.address, nil)
if err != nil {
return err
}

referenceCode := common.FromHex(contractFactory.SimpleSwapFactoryDeployedCode)
if !bytes.Equal(code, referenceCode) {
return errors.New("not a valid factory contract")
}

return nil
}

func (sf simpleSwapFactory) DeploySimpleSwap(auth *bind.TransactOpts, issuer common.Address, defaultHardDepositTimeoutDuration *big.Int) (Contract, error) {
auth.GasLimit = 1700000
tx, err := sf.instance.DeploySimpleSwap(auth, issuer, defaultHardDepositTimeoutDuration)
if err != nil {
return nil, err
}

receipt, err := WaitFunc(auth, sf.backend, tx)
if err != nil {
return nil, err
}

address := common.Address{}

for _, log := range receipt.Logs {
if log.Address != sf.address {
continue
}

if event, err := sf.instance.ParseSimpleSwapDeployed(*log); err == nil {
address = event.ContractAddress
break
}
}

if (address == common.Address{}) {
return nil, errors.New("contract deployment failed")
}

simpleSwap, err := InstanceAt(address, sf.backend)
if err != nil {
return nil, err
}

return simpleSwap, nil
}

func (sf simpleSwapFactory) VerifyContract(address common.Address) error {
isDeployed, err := sf.instance.DeployedContracts(&bind.CallOpts{}, address)
if err != nil {
return err
}
if !isDeployed {
return ErrNotDeployedByFactory
}
return nil
}
21 changes: 5 additions & 16 deletions contracts/swap/swap.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
package swap

import (
"bytes"
"context"
"errors"
"math/big"
Expand All @@ -29,12 +28,10 @@ import (
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
contract "github.com/ethersphere/go-sw3/contracts-v0-1-0/simpleswap"
contract "github.com/ethersphere/go-sw3/contracts-v0-1-1/simpleswap"
)

var (
// ErrNotASwapContract is given when an address is verified not to have a SWAP contract based on its bytecode
ErrNotASwapContract = errors.New("not a swap contract")
// ErrTransactionReverted is given when the transaction that cashes a cheque is reverted
ErrTransactionReverted = errors.New("Transaction reverted")
)
Expand All @@ -55,6 +52,8 @@ type Contract interface {
Issuer(opts *bind.CallOpts) (common.Address, error)
// PaidOut returns the total paid out amount for the given address
PaidOut(opts *bind.CallOpts, addr common.Address) (*big.Int, error)
// Address gets the address of this contract instance
Address() common.Address
}

// CashChequeResult summarizes the result of a CashCheque or CashChequeBeneficiary call
Expand Down Expand Up @@ -151,18 +150,8 @@ func (s simpleContract) PaidOut(opts *bind.CallOpts, addr common.Address) (*big.
return s.instance.PaidOut(opts, addr)
}

// ValidateCode checks that the on-chain code at address matches the expected swap
// contract code.
func ValidateCode(ctx context.Context, b bind.ContractBackend, address common.Address) error {
codeReadFromAddress, err := b.CodeAt(ctx, address, nil)
if err != nil {
return err
}
referenceCode := common.FromHex(contract.SimpleSwapDeployedCode)
if !bytes.Equal(codeReadFromAddress, referenceCode) {
return ErrNotASwapContract
}
return nil
func (s simpleContract) Address() common.Address {
return s.address
}

// WaitFunc is the default function to wait for transactions
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ require (
github.com/edsrzf/mmap-go v0.0.0-20160512033002-935e0e8a636c // indirect
github.com/elastic/gosigar v0.0.0-20180330100440-37f05ff46ffa // indirect
github.com/ethereum/go-ethereum v1.9.2
github.com/ethersphere/go-sw3 v0.1.0
github.com/ethersphere/go-sw3 v0.1.1
github.com/fatih/color v1.7.0 // indirect
github.com/fjl/memsize v0.0.0-20180418122429-ca190fb6ffbc
github.com/gballet/go-libpcsclite v0.0.0-20190528105824-2fd9b619dd3c // indirect
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ github.com/elastic/gosigar v0.0.0-20180330100440-37f05ff46ffa/go.mod h1:cdorVVzy
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/ethereum/go-ethereum v1.9.2 h1:RMIHDO/diqXEgORSVzYx8xW9x2+S32PoAX5lQwya0Lw=
github.com/ethereum/go-ethereum v1.9.2/go.mod h1:PwpWDrCLZrV+tfrhqqF6kPknbISMHaJv9Ln3kPCZLwY=
github.com/ethersphere/go-sw3 v0.1.0 h1:XMaWxiBhFtrxfOel2tXmxuan3eCfFw/NcftV6sUVtIc=
github.com/ethersphere/go-sw3 v0.1.0/go.mod h1:HukT0aZ6QdW/d7zuD/0g5xlw6ewu9QeqHojxLDsaERQ=
github.com/ethersphere/go-sw3 v0.1.1 h1:czLnLSU0/XJLJt/GyPiEAds9YYnIgZZzfy+OQyiYQtk=
github.com/ethersphere/go-sw3 v0.1.1/go.mod h1:HukT0aZ6QdW/d7zuD/0g5xlw6ewu9QeqHojxLDsaERQ=
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
Expand Down
2 changes: 1 addition & 1 deletion swap/protocol.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ func (s *Swap) verifyHandshake(msg interface{}) error {
return ErrEmptyAddressInSignature
}

return contract.ValidateCode(context.Background(), s.backend, handshake.ContractAddress)
return s.chequebookFactory.VerifyContract(handshake.ContractAddress)
}

// run is the actual swap protocol run method
Expand Down
59 changes: 34 additions & 25 deletions swap/swap.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ import (

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
Expand All @@ -54,17 +53,18 @@ const swapLogLevel = 3
// Only messages which have a price will be accounted for
type Swap struct {
api API
store state.Store // store is needed in order to keep balances and cheques across sessions
peers map[enode.ID]*Peer // map of all swap Peers
peersLock sync.RWMutex // lock for peers map
backend contract.Backend // the backend (blockchain) used
owner *Owner // contract access
params *Params // economic and operational parameters
contract swap.Contract // reference to the smart contract
honeyPriceOracle HoneyOracle // oracle which resolves the price of honey (in Wei)
paymentThreshold int64 // honey amount at which a payment is triggered
disconnectThreshold int64 // honey amount at which a peer disconnects
backendNetworkID string // the Ethereum network ID to which the node is connected
store state.Store // store is needed in order to keep balances and cheques across sessions
peers map[enode.ID]*Peer // map of all swap Peers
peersLock sync.RWMutex // lock for peers map
backend contract.Backend // the backend (blockchain) used
owner *Owner // contract access
params *Params // economic and operational parameters
contract swap.Contract // reference to the smart contract
chequebookFactory swap.SimpleSwapFactory // address of the chequebook factory
honeyPriceOracle HoneyOracle // oracle which resolves the price of honey (in Wei)
paymentThreshold int64 // honey amount at which a payment is triggered
disconnectThreshold int64 // honey amount at which a peer disconnects
backendNetworkID string // the Ethereum network ID to which the node is connected
}

// Owner encapsulates information related to accessing the contract
Expand Down Expand Up @@ -120,7 +120,7 @@ func swapRotatingFileHandler(logdir string) (log.Handler, error) {
}

// new - swap constructor without integrity check
func new(logpath string, stateStore state.Store, prvkey *ecdsa.PrivateKey, backend contract.Backend, disconnectThreshold uint64, paymentThreshold uint64, backendNetworkID string) *Swap {
func new(logpath string, stateStore state.Store, prvkey *ecdsa.PrivateKey, backend contract.Backend, disconnectThreshold uint64, paymentThreshold uint64, backendNetworkID string, chequebookFactory contract.SimpleSwapFactory) *Swap {
auditLog = newLogger(logpath)
return &Swap{
store: stateStore,
Expand All @@ -132,11 +132,12 @@ func new(logpath string, stateStore state.Store, prvkey *ecdsa.PrivateKey, backe
paymentThreshold: int64(paymentThreshold),
honeyPriceOracle: NewHoneyPriceOracle(),
backendNetworkID: backendNetworkID,
chequebookFactory: chequebookFactory,
}
}

// New - swap constructor with integrity checks
func New(logpath string, dbPath string, prvkey *ecdsa.PrivateKey, backendURL string, disconnectThreshold uint64, paymentThreshold uint64) (*Swap, error) {
func New(logpath string, dbPath string, prvkey *ecdsa.PrivateKey, backendURL string, disconnectThreshold uint64, paymentThreshold uint64, factoryAddress common.Address) (*Swap, error) {
// we MUST have a backend
if backendURL == "" {
return nil, errors.New("swap init error: no backend URL given")
Expand All @@ -158,6 +159,17 @@ func New(logpath string, dbPath string, prvkey *ecdsa.PrivateKey, backendURL str
if err != nil {
return nil, err
}
if (factoryAddress == common.Address{}) {
return nil, errors.New("not allowed to be nil for now")
}
factory, err := contract.FactoryAt(factoryAddress, backend)
if err != nil {
return nil, err
}
if err := factory.VerifySelf(); err != nil {
return nil, err
}

return new(
logpath,
stateStore,
Expand All @@ -166,6 +178,7 @@ func New(logpath string, dbPath string, prvkey *ecdsa.PrivateKey, backendURL str
disconnectThreshold,
paymentThreshold,
networkID.String(),
factory,
), nil
}

Expand Down Expand Up @@ -483,7 +496,7 @@ func (s *Swap) StartChequebook(chequebookAddrFlag common.Address) error {
// BindToContractAt binds to an instance of an already existing chequebook contract at address
func (s *Swap) bindToContractAt(address common.Address) (err error) {
// validate whether address is a chequebook
if err := contract.ValidateCode(context.Background(), s.backend, address); err != nil {
if err := s.chequebookFactory.VerifyContract(address); err != nil {
return fmt.Errorf("contract validation for %v failed: %v", address.Hex(), err)
}
// get the instance and save it on swap.contract
Expand All @@ -509,23 +522,19 @@ func (s *Swap) Deploy(ctx context.Context) (common.Address, error) {

// deployLoop repeatedly tries to deploy the swap contract .
func (s *Swap) deployLoop(opts *bind.TransactOpts, owner common.Address, defaultHarddepositTimeoutDuration time.Duration) (addr common.Address, err error) {
var tx *types.Transaction
for try := 0; try < deployRetries; try++ {
if try > 0 {
time.Sleep(deployDelay)
}

if addr, tx, _, err = contract.Deploy(opts, s.backend, owner, defaultHarddepositTimeoutDuration); err != nil {
log.Warn("can't send chequebook deploy tx", "try", try, "error", err)
continue
}
if addr, err = bind.WaitDeployed(opts.Context, s.backend, tx); err != nil {
auditLog.Warn("chequebook deploy error", "try", try, "error", err)
continue
chequebook, err := s.chequebookFactory.DeploySimpleSwap(opts, owner, big.NewInt(int64(defaultHarddepositTimeoutDuration)))
if err != nil {
return common.Address{}, err
}
return addr, nil

return chequebook.Address(), nil
}
return addr, err
return common.Address{}, errors.New("failed to deploy chequebook")
}

func (s *Swap) loadChequebook() (common.Address, error) {
Expand Down
Loading

0 comments on commit b066a40

Please sign in to comment.