github.com/aaa256/atlantis@v0.0.0-20210707112435-42ee889287a2/swarm/services/swap/swap.go (about) 1 // Copyright 2016 The go-athereum Authors 2 // This file is part of the go-athereum library. 3 // 4 // The go-athereum 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-athereum 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-athereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package swap 18 19 import ( 20 "context" 21 "crypto/ecdsa" 22 "errors" 23 "fmt" 24 "math/big" 25 "os" 26 "path/filepath" 27 "sync" 28 "time" 29 30 "github.com/athereum/go-athereum/accounts/abi/bind" 31 "github.com/athereum/go-athereum/common" 32 "github.com/athereum/go-athereum/contracts/chequebook" 33 "github.com/athereum/go-athereum/contracts/chequebook/contract" 34 "github.com/athereum/go-athereum/core/types" 35 "github.com/athereum/go-athereum/crypto" 36 "github.com/athereum/go-athereum/log" 37 "github.com/athereum/go-athereum/swarm/services/swap/swap" 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 //create params with default values 85 func NewDefaultSwapParams() *SwapParams { 86 return &SwapParams{ 87 PayProfile: &PayProfile{}, 88 Params: &swap.Params{ 89 Profile: &swap.Profile{ 90 BuyAt: buyAt, 91 SellAt: sellAt, 92 PayAt: uint(payAt), 93 DropAt: uint(dropAt), 94 }, 95 Strategy: &swap.Strategy{ 96 AutoCashInterval: autoCashInterval, 97 AutoCashThreshold: autoCashThreshold, 98 AutoDepositInterval: autoDepositInterval, 99 AutoDepositThreshold: autoDepositThreshold, 100 AutoDepositBuffer: autoDepositBuffer, 101 }, 102 }, 103 } 104 } 105 106 //this can only finally be set after all config options (file, cmd line, env vars) 107 //have been evaluated 108 func (self *SwapParams) Init(contract common.Address, prvkey *ecdsa.PrivateKey) { 109 pubkey := &prvkey.PublicKey 110 111 self.PayProfile = &PayProfile{ 112 PublicKey: common.ToHex(crypto.FromECDSAPub(pubkey)), 113 Contract: contract, 114 Beneficiary: crypto.PubkeyToAddress(*pubkey), 115 privateKey: prvkey, 116 publicKey: pubkey, 117 owner: crypto.PubkeyToAddress(*pubkey), 118 } 119 } 120 121 // swap constructor, parameters 122 // * global chequebook, assume deployed service and 123 // * the balance is at buffer. 124 // swap.Add(n) called in netstore 125 // n > 0 called when sending chunks = receiving retrieve requests 126 // OR sending cheques. 127 // n < 0 called when receiving chunks = receiving delivery responses 128 // OR receiving cheques. 129 130 func NewSwap(local *SwapParams, remote *SwapProfile, backend chequebook.Backend, proto swap.Protocol) (self *swap.Swap, err error) { 131 var ( 132 ctx = context.TODO() 133 ok bool 134 in *chequebook.Inbox 135 out *chequebook.Outbox 136 ) 137 138 remotekey, err := crypto.UnmarshalPubkey(common.FromHex(remote.PublicKey)) 139 if err != nil { 140 return nil, errors.New("invalid remote public key") 141 } 142 143 // check if remote chequebook is valid 144 // insolvent chequebooks suicide so will signal as invalid 145 // TODO: monitoring a chequebooks events 146 ok, err = chequebook.ValidateCode(ctx, backend, remote.Contract) 147 if !ok { 148 log.Info(fmt.Sprintf("invalid contract %v for peer %v: %v)", remote.Contract.Hex()[:8], proto, err)) 149 } else { 150 // remote contract valid, create inbox 151 in, err = chequebook.NewInbox(local.privateKey, remote.Contract, local.Beneficiary, remotekey, backend) 152 if err != nil { 153 log.Warn(fmt.Sprintf("unable to set up inbox for chequebook contract %v for peer %v: %v)", remote.Contract.Hex()[:8], proto, err)) 154 } 155 } 156 157 // check if local chequebook contract is valid 158 ok, err = chequebook.ValidateCode(ctx, backend, local.Contract) 159 if !ok { 160 log.Warn(fmt.Sprintf("unable to set up outbox for peer %v: chequebook contract (owner: %v): %v)", proto, local.owner.Hex(), err)) 161 } else { 162 out = chequebook.NewOutbox(local.Chequebook(), remote.Beneficiary) 163 } 164 165 pm := swap.Payment{ 166 In: in, 167 Out: out, 168 Buys: out != nil, 169 Sells: in != nil, 170 } 171 self, err = swap.New(local.Params, pm, proto) 172 if err != nil { 173 return 174 } 175 // remote profile given (first) in handshake 176 self.SetRemote(remote.Profile) 177 var buy, sell string 178 if self.Buys { 179 buy = "purchase from peer enabled at " + remote.SellAt.String() + " wei/chunk" 180 } else { 181 buy = "purchase from peer disabled" 182 } 183 if self.Sells { 184 sell = "selling to peer enabled at " + local.SellAt.String() + " wei/chunk" 185 } else { 186 sell = "selling to peer disabled" 187 } 188 log.Warn(fmt.Sprintf("SWAP arrangement with <%v>: %v; %v)", proto, buy, sell)) 189 190 return 191 } 192 193 func (self *SwapParams) Chequebook() *chequebook.Chequebook { 194 defer self.lock.Unlock() 195 self.lock.Lock() 196 return self.chbook 197 } 198 199 func (self *SwapParams) PrivateKey() *ecdsa.PrivateKey { 200 return self.privateKey 201 } 202 203 // func (self *SwapParams) PublicKey() *ecdsa.PublicKey { 204 // return self.publicKey 205 // } 206 207 func (self *SwapParams) SetKey(prvkey *ecdsa.PrivateKey) { 208 self.privateKey = prvkey 209 self.publicKey = &prvkey.PublicKey 210 } 211 212 // setChequebook(path, backend) wraps the 213 // chequebook initialiser and sets up autoDeposit to cover spending. 214 func (self *SwapParams) SetChequebook(ctx context.Context, backend chequebook.Backend, path string) error { 215 self.lock.Lock() 216 contract := self.Contract 217 self.lock.Unlock() 218 219 valid, err := chequebook.ValidateCode(ctx, backend, contract) 220 if err != nil { 221 return err 222 } else if valid { 223 return self.newChequebookFromContract(path, backend) 224 } 225 return self.deployChequebook(ctx, backend, path) 226 } 227 228 func (self *SwapParams) deployChequebook(ctx context.Context, backend chequebook.Backend, path string) error { 229 opts := bind.NewKeyedTransactor(self.privateKey) 230 opts.Value = self.AutoDepositBuffer 231 opts.Context = ctx 232 233 log.Info(fmt.Sprintf("Deploying new chequebook (owner: %v)", opts.From.Hex())) 234 contract, err := deployChequebookLoop(opts, backend) 235 if err != nil { 236 log.Error(fmt.Sprintf("unable to deploy new chequebook: %v", err)) 237 return err 238 } 239 log.Info(fmt.Sprintf("new chequebook deployed at %v (owner: %v)", contract.Hex(), opts.From.Hex())) 240 241 // need to save config at this point 242 self.lock.Lock() 243 self.Contract = contract 244 err = self.newChequebookFromContract(path, backend) 245 self.lock.Unlock() 246 if err != nil { 247 log.Warn(fmt.Sprintf("error initialising cheque book (owner: %v): %v", opts.From.Hex(), err)) 248 } 249 return err 250 } 251 252 // repeatedly tries to deploy a chequebook. 253 func deployChequebookLoop(opts *bind.TransactOpts, backend chequebook.Backend) (addr common.Address, err error) { 254 var tx *types.Transaction 255 for try := 0; try < chequebookDeployRetries; try++ { 256 if try > 0 { 257 time.Sleep(chequebookDeployDelay) 258 } 259 if _, tx, _, err = contract.DeployChequebook(opts, backend); err != nil { 260 log.Warn(fmt.Sprintf("can't send chequebook deploy tx (try %d): %v", try, err)) 261 continue 262 } 263 if addr, err = bind.WaitDeployed(opts.Context, backend, tx); err != nil { 264 log.Warn(fmt.Sprintf("chequebook deploy error (try %d): %v", try, err)) 265 continue 266 } 267 return addr, nil 268 } 269 return addr, err 270 } 271 272 // initialise the chequebook from a persisted json file or create a new one 273 // caller holds the lock 274 func (self *SwapParams) newChequebookFromContract(path string, backend chequebook.Backend) error { 275 hexkey := common.Bytes2Hex(self.Contract.Bytes()) 276 err := os.MkdirAll(filepath.Join(path, "chequebooks"), os.ModePerm) 277 if err != nil { 278 return fmt.Errorf("unable to create directory for chequebooks: %v", err) 279 } 280 281 chbookpath := filepath.Join(path, "chequebooks", hexkey+".json") 282 self.chbook, err = chequebook.LoadChequebook(chbookpath, self.privateKey, backend, true) 283 284 if err != nil { 285 self.chbook, err = chequebook.NewChequebook(chbookpath, self.Contract, self.privateKey, backend) 286 if err != nil { 287 log.Warn(fmt.Sprintf("unable to initialise chequebook (owner: %v): %v", self.owner.Hex(), err)) 288 return fmt.Errorf("unable to initialise chequebook (owner: %v): %v", self.owner.Hex(), err) 289 } 290 } 291 292 self.chbook.AutoDeposit(self.AutoDepositInterval, self.AutoDepositThreshold, self.AutoDepositBuffer) 293 log.Info(fmt.Sprintf("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)) 294 295 return nil 296 }