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