github.com/murrekatt/go-ethereum@v1.5.8-0.20170123175102-fc52f2c007fb/swarm/services/swap/swap.go (about) 1 // Copyright 2016 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package swap 18 19 import ( 20 "crypto/ecdsa" 21 "fmt" 22 "math/big" 23 "os" 24 "path/filepath" 25 "sync" 26 "time" 27 28 "github.com/ethereum/go-ethereum/accounts/abi/bind" 29 "github.com/ethereum/go-ethereum/common" 30 "github.com/ethereum/go-ethereum/contracts/chequebook" 31 "github.com/ethereum/go-ethereum/contracts/chequebook/contract" 32 "github.com/ethereum/go-ethereum/core/types" 33 "github.com/ethereum/go-ethereum/crypto" 34 "github.com/ethereum/go-ethereum/logger" 35 "github.com/ethereum/go-ethereum/logger/glog" 36 "github.com/ethereum/go-ethereum/swarm/services/swap/swap" 37 "golang.org/x/net/context" 38 ) 39 40 // SwAP Swarm Accounting Protocol with 41 // SWAP^2 Strategies of Withholding Automatic Payments 42 // SWAP^3 Accreditation: payment via credit SWAP 43 // using chequebook pkg for delayed payments 44 // default parameters 45 46 var ( 47 autoCashInterval = 300 * time.Second // default interval for autocash 48 autoCashThreshold = big.NewInt(50000000000000) // threshold that triggers autocash (wei) 49 autoDepositInterval = 300 * time.Second // default interval for autocash 50 autoDepositThreshold = big.NewInt(50000000000000) // threshold that triggers autodeposit (wei) 51 autoDepositBuffer = big.NewInt(100000000000000) // buffer that is surplus for fork protection etc (wei) 52 buyAt = big.NewInt(20000000000) // maximum chunk price host is willing to pay (wei) 53 sellAt = big.NewInt(20000000000) // minimum chunk price host requires (wei) 54 payAt = 100 // threshold that triggers payment {request} (units) 55 dropAt = 10000 // threshold that triggers disconnect (units) 56 ) 57 58 const ( 59 chequebookDeployRetries = 5 60 chequebookDeployDelay = 1 * time.Second // delay between retries 61 ) 62 63 type SwapParams struct { 64 *swap.Params 65 *PayProfile 66 } 67 68 type SwapProfile struct { 69 *swap.Profile 70 *PayProfile 71 } 72 73 type PayProfile struct { 74 PublicKey string // check against signature of promise 75 Contract common.Address // address of chequebook contract 76 Beneficiary common.Address // recipient address for swarm sales revenue 77 privateKey *ecdsa.PrivateKey 78 publicKey *ecdsa.PublicKey 79 owner common.Address 80 chbook *chequebook.Chequebook 81 lock sync.RWMutex 82 } 83 84 func DefaultSwapParams(contract common.Address, prvkey *ecdsa.PrivateKey) *SwapParams { 85 pubkey := &prvkey.PublicKey 86 return &SwapParams{ 87 PayProfile: &PayProfile{ 88 PublicKey: common.ToHex(crypto.FromECDSAPub(pubkey)), 89 Contract: contract, 90 Beneficiary: crypto.PubkeyToAddress(*pubkey), 91 privateKey: prvkey, 92 publicKey: pubkey, 93 owner: crypto.PubkeyToAddress(*pubkey), 94 }, 95 Params: &swap.Params{ 96 Profile: &swap.Profile{ 97 BuyAt: buyAt, 98 SellAt: sellAt, 99 PayAt: uint(payAt), 100 DropAt: uint(dropAt), 101 }, 102 Strategy: &swap.Strategy{ 103 AutoCashInterval: autoCashInterval, 104 AutoCashThreshold: autoCashThreshold, 105 AutoDepositInterval: autoDepositInterval, 106 AutoDepositThreshold: autoDepositThreshold, 107 AutoDepositBuffer: autoDepositBuffer, 108 }, 109 }, 110 } 111 } 112 113 // swap constructor, parameters 114 // * global chequebook, assume deployed service and 115 // * the balance is at buffer. 116 // swap.Add(n) called in netstore 117 // n > 0 called when sending chunks = receiving retrieve requests 118 // OR sending cheques. 119 // n < 0 called when receiving chunks = receiving delivery responses 120 // OR receiving cheques. 121 122 func NewSwap(local *SwapParams, remote *SwapProfile, backend chequebook.Backend, proto swap.Protocol) (self *swap.Swap, err error) { 123 var ( 124 ctx = context.TODO() 125 ok bool 126 in *chequebook.Inbox 127 out *chequebook.Outbox 128 ) 129 130 // check if remote chequebook is valid 131 // insolvent chequebooks suicide so will signal as invalid 132 // TODO: monitoring a chequebooks events 133 ok, err = chequebook.ValidateCode(ctx, backend, remote.Contract) 134 if !ok { 135 glog.V(logger.Info).Infof("invalid contract %v for peer %v: %v)", remote.Contract.Hex()[:8], proto, err) 136 } else { 137 // remote contract valid, create inbox 138 in, err = chequebook.NewInbox(local.privateKey, remote.Contract, local.Beneficiary, crypto.ToECDSAPub(common.FromHex(remote.PublicKey)), backend) 139 if err != nil { 140 glog.V(logger.Warn).Infof("unable to set up inbox for chequebook contract %v for peer %v: %v)", remote.Contract.Hex()[:8], proto, err) 141 } 142 } 143 144 // check if local chequebook contract is valid 145 ok, err = chequebook.ValidateCode(ctx, backend, local.Contract) 146 if !ok { 147 glog.V(logger.Warn).Infof("unable to set up outbox for peer %v: chequebook contract (owner: %v): %v)", proto, local.owner.Hex(), err) 148 } else { 149 out = chequebook.NewOutbox(local.Chequebook(), remote.Beneficiary) 150 } 151 152 pm := swap.Payment{ 153 In: in, 154 Out: out, 155 Buys: out != nil, 156 Sells: in != nil, 157 } 158 self, err = swap.New(local.Params, pm, proto) 159 if err != nil { 160 return 161 } 162 // remote profile given (first) in handshake 163 self.SetRemote(remote.Profile) 164 var buy, sell string 165 if self.Buys { 166 buy = "purchase from peer enabled at " + remote.SellAt.String() + " wei/chunk" 167 } else { 168 buy = "purchase from peer disabled" 169 } 170 if self.Sells { 171 sell = "selling to peer enabled at " + local.SellAt.String() + " wei/chunk" 172 } else { 173 sell = "selling to peer disabled" 174 } 175 glog.V(logger.Warn).Infof("SWAP arrangement with <%v>: %v; %v)", proto, buy, sell) 176 177 return 178 } 179 180 func (self *SwapParams) Chequebook() *chequebook.Chequebook { 181 defer self.lock.Unlock() 182 self.lock.Lock() 183 return self.chbook 184 } 185 186 func (self *SwapParams) PrivateKey() *ecdsa.PrivateKey { 187 return self.privateKey 188 } 189 190 // func (self *SwapParams) PublicKey() *ecdsa.PublicKey { 191 // return self.publicKey 192 // } 193 194 func (self *SwapParams) SetKey(prvkey *ecdsa.PrivateKey) { 195 self.privateKey = prvkey 196 self.publicKey = &prvkey.PublicKey 197 } 198 199 // setChequebook(path, backend) wraps the 200 // chequebook initialiser and sets up autoDeposit to cover spending. 201 func (self *SwapParams) SetChequebook(ctx context.Context, backend chequebook.Backend, path string) error { 202 self.lock.Lock() 203 contract := self.Contract 204 self.lock.Unlock() 205 206 valid, err := chequebook.ValidateCode(ctx, backend, contract) 207 if err != nil { 208 return err 209 } else if valid { 210 return self.newChequebookFromContract(path, backend) 211 } 212 return self.deployChequebook(ctx, backend, path) 213 } 214 215 func (self *SwapParams) deployChequebook(ctx context.Context, backend chequebook.Backend, path string) error { 216 opts := bind.NewKeyedTransactor(self.privateKey) 217 opts.Value = self.AutoDepositBuffer 218 opts.Context = ctx 219 220 glog.V(logger.Info).Infof("Deploying new chequebook (owner: %v)", opts.From.Hex()) 221 contract, err := deployChequebookLoop(opts, backend) 222 if err != nil { 223 glog.V(logger.Error).Infof("unable to deploy new chequebook: %v", err) 224 return err 225 } 226 glog.V(logger.Info).Infof("new chequebook deployed at %v (owner: %v)", contract.Hex(), opts.From.Hex()) 227 228 // need to save config at this point 229 self.lock.Lock() 230 self.Contract = contract 231 err = self.newChequebookFromContract(path, backend) 232 self.lock.Unlock() 233 if err != nil { 234 glog.V(logger.Warn).Infof("error initialising cheque book (owner: %v): %v", opts.From.Hex(), err) 235 } 236 return err 237 } 238 239 // repeatedly tries to deploy a chequebook. 240 func deployChequebookLoop(opts *bind.TransactOpts, backend chequebook.Backend) (addr common.Address, err error) { 241 var tx *types.Transaction 242 for try := 0; try < chequebookDeployRetries; try++ { 243 if try > 0 { 244 time.Sleep(chequebookDeployDelay) 245 } 246 if _, tx, _, err = contract.DeployChequebook(opts, backend); err != nil { 247 glog.V(logger.Warn).Infof("can't send chequebook deploy tx (try %d): %v", try, err) 248 continue 249 } 250 if addr, err = bind.WaitDeployed(opts.Context, backend, tx); err != nil { 251 glog.V(logger.Warn).Infof("chequebook deploy error (try %d): %v", try, err) 252 continue 253 } 254 return addr, nil 255 } 256 return addr, err 257 } 258 259 // initialise the chequebook from a persisted json file or create a new one 260 // caller holds the lock 261 func (self *SwapParams) newChequebookFromContract(path string, backend chequebook.Backend) error { 262 hexkey := common.Bytes2Hex(self.Contract.Bytes()) 263 err := os.MkdirAll(filepath.Join(path, "chequebooks"), os.ModePerm) 264 if err != nil { 265 return fmt.Errorf("unable to create directory for chequebooks: %v", err) 266 } 267 268 chbookpath := filepath.Join(path, "chequebooks", hexkey+".json") 269 self.chbook, err = chequebook.LoadChequebook(chbookpath, self.privateKey, backend, true) 270 271 if err != nil { 272 self.chbook, err = chequebook.NewChequebook(chbookpath, self.Contract, self.privateKey, backend) 273 if err != nil { 274 glog.V(logger.Warn).Infof("unable to initialise chequebook (owner: %v): %v", self.owner.Hex(), err) 275 return fmt.Errorf("unable to initialise chequebook (owner: %v): %v", self.owner.Hex(), err) 276 } 277 } 278 279 self.chbook.AutoDeposit(self.AutoDepositInterval, self.AutoDepositThreshold, self.AutoDepositBuffer) 280 glog.V(logger.Info).Infof("auto deposit ON for %v -> %v: interval = %v, threshold = %v, buffer = %v)", crypto.PubkeyToAddress(*(self.publicKey)).Hex()[:8], self.Contract.Hex()[:8], self.AutoDepositInterval, self.AutoDepositThreshold, self.AutoDepositBuffer) 281 282 return nil 283 }