github.com/linapex/ethereum-dpos-chinese@v0.0.0-20190316121959-b78b3a4a1ece/swarm/services/swap/swap.go (about) 1 2 //<developer> 3 // <name>linapex 曹一峰</name> 4 // <email>linapex@163.com</email> 5 // <wx>superexc</wx> 6 // <qqgroup>128148617</qqgroup> 7 // <url>https://jsq.ink</url> 8 // <role>pku engineer</role> 9 // <date>2019-03-16 12:09:49</date> 10 //</624342679378989056> 11 12 // 13 // 14 // 15 // 16 // 17 // 18 // 19 // 20 // 21 // 22 // 23 // 24 // 25 // 26 // 27 28 package swap 29 30 import ( 31 "context" 32 "crypto/ecdsa" 33 "errors" 34 "fmt" 35 "math/big" 36 "os" 37 "path/filepath" 38 "sync" 39 "time" 40 41 "github.com/ethereum/go-ethereum/accounts/abi/bind" 42 "github.com/ethereum/go-ethereum/common" 43 "github.com/ethereum/go-ethereum/contracts/chequebook" 44 "github.com/ethereum/go-ethereum/contracts/chequebook/contract" 45 "github.com/ethereum/go-ethereum/core/types" 46 "github.com/ethereum/go-ethereum/crypto" 47 "github.com/ethereum/go-ethereum/swarm/log" 48 "github.com/ethereum/go-ethereum/swarm/services/swap/swap" 49 ) 50 51 // 52 // 53 // 54 // 55 // 56 57 var ( 58 autoCashInterval = 300 * time.Second // 59 autoCashThreshold = big.NewInt(50000000000000) // 60 autoDepositInterval = 300 * time.Second // 61 autoDepositThreshold = big.NewInt(50000000000000) // 62 autoDepositBuffer = big.NewInt(100000000000000) // 63 buyAt = big.NewInt(20000000000) // 64 sellAt = big.NewInt(20000000000) // 65 payAt = 100 // 66 dropAt = 10000 // 67 ) 68 69 const ( 70 chequebookDeployRetries = 5 71 chequebookDeployDelay = 1 * time.Second // 72 ) 73 74 // 75 type LocalProfile struct { 76 *swap.Params 77 *PayProfile 78 } 79 80 // 81 type RemoteProfile struct { 82 *swap.Profile 83 *PayProfile 84 } 85 86 // 87 type PayProfile struct { 88 PublicKey string // 89 Contract common.Address // 90 Beneficiary common.Address // 91 privateKey *ecdsa.PrivateKey 92 publicKey *ecdsa.PublicKey 93 owner common.Address 94 chbook *chequebook.Chequebook 95 lock sync.RWMutex 96 } 97 98 // 99 func NewDefaultSwapParams() *LocalProfile { 100 return &LocalProfile{ 101 PayProfile: &PayProfile{}, 102 Params: &swap.Params{ 103 Profile: &swap.Profile{ 104 BuyAt: buyAt, 105 SellAt: sellAt, 106 PayAt: uint(payAt), 107 DropAt: uint(dropAt), 108 }, 109 Strategy: &swap.Strategy{ 110 AutoCashInterval: autoCashInterval, 111 AutoCashThreshold: autoCashThreshold, 112 AutoDepositInterval: autoDepositInterval, 113 AutoDepositThreshold: autoDepositThreshold, 114 AutoDepositBuffer: autoDepositBuffer, 115 }, 116 }, 117 } 118 } 119 120 // 121 // 122 func (lp *LocalProfile) Init(contract common.Address, prvkey *ecdsa.PrivateKey) { 123 pubkey := &prvkey.PublicKey 124 125 lp.PayProfile = &PayProfile{ 126 PublicKey: common.ToHex(crypto.FromECDSAPub(pubkey)), 127 Contract: contract, 128 Beneficiary: crypto.PubkeyToAddress(*pubkey), 129 privateKey: prvkey, 130 publicKey: pubkey, 131 owner: crypto.PubkeyToAddress(*pubkey), 132 } 133 } 134 135 // 136 // 137 // 138 // 139 // 140 // 141 // 142 // 143 func NewSwap(localProfile *LocalProfile, remoteProfile *RemoteProfile, backend chequebook.Backend, proto swap.Protocol) (swapInstance *swap.Swap, err error) { 144 var ( 145 ctx = context.TODO() 146 ok bool 147 in *chequebook.Inbox 148 out *chequebook.Outbox 149 ) 150 151 remotekey, err := crypto.UnmarshalPubkey(common.FromHex(remoteProfile.PublicKey)) 152 if err != nil { 153 return nil, errors.New("invalid remote public key") 154 } 155 156 // 157 // 158 // 159 ok, err = chequebook.ValidateCode(ctx, backend, remoteProfile.Contract) 160 if !ok { 161 log.Info(fmt.Sprintf("invalid contract %v for peer %v: %v)", remoteProfile.Contract.Hex()[:8], proto, err)) 162 } else { 163 // 164 in, err = chequebook.NewInbox(localProfile.privateKey, remoteProfile.Contract, localProfile.Beneficiary, remotekey, backend) 165 if err != nil { 166 log.Warn(fmt.Sprintf("unable to set up inbox for chequebook contract %v for peer %v: %v)", remoteProfile.Contract.Hex()[:8], proto, err)) 167 } 168 } 169 170 // 171 ok, err = chequebook.ValidateCode(ctx, backend, localProfile.Contract) 172 if !ok { 173 log.Warn(fmt.Sprintf("unable to set up outbox for peer %v: chequebook contract (owner: %v): %v)", proto, localProfile.owner.Hex(), err)) 174 } else { 175 out = chequebook.NewOutbox(localProfile.Chequebook(), remoteProfile.Beneficiary) 176 } 177 178 pm := swap.Payment{ 179 In: in, 180 Out: out, 181 Buys: out != nil, 182 Sells: in != nil, 183 } 184 swapInstance, err = swap.New(localProfile.Params, pm, proto) 185 if err != nil { 186 return 187 } 188 // 189 swapInstance.SetRemote(remoteProfile.Profile) 190 var buy, sell string 191 if swapInstance.Buys { 192 buy = "purchase from peer enabled at " + remoteProfile.SellAt.String() + " wei/chunk" 193 } else { 194 buy = "purchase from peer disabled" 195 } 196 if swapInstance.Sells { 197 sell = "selling to peer enabled at " + localProfile.SellAt.String() + " wei/chunk" 198 } else { 199 sell = "selling to peer disabled" 200 } 201 log.Warn(fmt.Sprintf("SWAP arrangement with <%v>: %v; %v)", proto, buy, sell)) 202 203 return 204 } 205 206 // 207 func (lp *LocalProfile) Chequebook() *chequebook.Chequebook { 208 defer lp.lock.Unlock() 209 lp.lock.Lock() 210 return lp.chbook 211 } 212 213 // 214 func (lp *LocalProfile) PrivateKey() *ecdsa.PrivateKey { 215 return lp.privateKey 216 } 217 218 // 219 // 220 // 221 222 // 223 func (lp *LocalProfile) SetKey(prvkey *ecdsa.PrivateKey) { 224 lp.privateKey = prvkey 225 lp.publicKey = &prvkey.PublicKey 226 } 227 228 // 229 func (lp *LocalProfile) SetChequebook(ctx context.Context, backend chequebook.Backend, path string) error { 230 lp.lock.Lock() 231 swapContract := lp.Contract 232 lp.lock.Unlock() 233 234 valid, err := chequebook.ValidateCode(ctx, backend, swapContract) 235 if err != nil { 236 return err 237 } else if valid { 238 return lp.newChequebookFromContract(path, backend) 239 } 240 return lp.deployChequebook(ctx, backend, path) 241 } 242 243 // 244 func (lp *LocalProfile) deployChequebook(ctx context.Context, backend chequebook.Backend, path string) error { 245 opts := bind.NewKeyedTransactor(lp.privateKey) 246 opts.Value = lp.AutoDepositBuffer 247 opts.Context = ctx 248 249 log.Info(fmt.Sprintf("Deploying new chequebook (owner: %v)", opts.From.Hex())) 250 address, err := deployChequebookLoop(opts, backend) 251 if err != nil { 252 log.Error(fmt.Sprintf("unable to deploy new chequebook: %v", err)) 253 return err 254 } 255 log.Info(fmt.Sprintf("new chequebook deployed at %v (owner: %v)", address.Hex(), opts.From.Hex())) 256 257 // 258 lp.lock.Lock() 259 lp.Contract = address 260 err = lp.newChequebookFromContract(path, backend) 261 lp.lock.Unlock() 262 if err != nil { 263 log.Warn(fmt.Sprintf("error initialising cheque book (owner: %v): %v", opts.From.Hex(), err)) 264 } 265 return err 266 } 267 268 // 269 func deployChequebookLoop(opts *bind.TransactOpts, backend chequebook.Backend) (addr common.Address, err error) { 270 var tx *types.Transaction 271 for try := 0; try < chequebookDeployRetries; try++ { 272 if try > 0 { 273 time.Sleep(chequebookDeployDelay) 274 } 275 if _, tx, _, err = contract.DeployChequebook(opts, backend); err != nil { 276 log.Warn(fmt.Sprintf("can't send chequebook deploy tx (try %d): %v", try, err)) 277 continue 278 } 279 if addr, err = bind.WaitDeployed(opts.Context, backend, tx); err != nil { 280 log.Warn(fmt.Sprintf("chequebook deploy error (try %d): %v", try, err)) 281 continue 282 } 283 return addr, nil 284 } 285 return addr, err 286 } 287 288 // 289 // 290 func (lp *LocalProfile) newChequebookFromContract(path string, backend chequebook.Backend) error { 291 hexkey := common.Bytes2Hex(lp.Contract.Bytes()) 292 err := os.MkdirAll(filepath.Join(path, "chequebooks"), os.ModePerm) 293 if err != nil { 294 return fmt.Errorf("unable to create directory for chequebooks: %v", err) 295 } 296 297 chbookpath := filepath.Join(path, "chequebooks", hexkey+".json") 298 lp.chbook, err = chequebook.LoadChequebook(chbookpath, lp.privateKey, backend, true) 299 300 if err != nil { 301 lp.chbook, err = chequebook.NewChequebook(chbookpath, lp.Contract, lp.privateKey, backend) 302 if err != nil { 303 log.Warn(fmt.Sprintf("unable to initialise chequebook (owner: %v): %v", lp.owner.Hex(), err)) 304 return fmt.Errorf("unable to initialise chequebook (owner: %v): %v", lp.owner.Hex(), err) 305 } 306 } 307 308 lp.chbook.AutoDeposit(lp.AutoDepositInterval, lp.AutoDepositThreshold, lp.AutoDepositBuffer) 309 log.Info(fmt.Sprintf("auto deposit ON for %v -> %v: interval = %v, threshold = %v, buffer = %v)", crypto.PubkeyToAddress(*(lp.publicKey)).Hex()[:8], lp.Contract.Hex()[:8], lp.AutoDepositInterval, lp.AutoDepositThreshold, lp.AutoDepositBuffer)) 310 311 return nil 312 } 313