github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/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 19:16:44</date> 10 //</624450117402890240> 11 12 13 package swap 14 15 import ( 16 "context" 17 "crypto/ecdsa" 18 "errors" 19 "fmt" 20 "math/big" 21 "os" 22 "path/filepath" 23 "sync" 24 "time" 25 26 "github.com/ethereum/go-ethereum/accounts/abi/bind" 27 "github.com/ethereum/go-ethereum/common" 28 "github.com/ethereum/go-ethereum/contracts/chequebook" 29 "github.com/ethereum/go-ethereum/contracts/chequebook/contract" 30 "github.com/ethereum/go-ethereum/core/types" 31 "github.com/ethereum/go-ethereum/crypto" 32 "github.com/ethereum/go-ethereum/swarm/log" 33 "github.com/ethereum/go-ethereum/swarm/services/swap/swap" 34 ) 35 36 //交换Swarm会计协议 37 //交换^2预扣自动付款策略 38 //交换^3认证:通过信用交换付款 39 //使用支票簿包延迟付款 40 //默认参数 41 42 var ( 43 autoCashInterval = 300 * time.Second //自动清除的默认间隔 44 autoCashThreshold = big.NewInt(50000000000000) //触发自动清除的阈值(wei) 45 autoDepositInterval = 300 * time.Second //自动清除的默认间隔 46 autoDepositThreshold = big.NewInt(50000000000000) //触发自动报告的阈值(wei) 47 autoDepositBuffer = big.NewInt(100000000000000) //剩余用于叉保护等的缓冲器(WEI) 48 buyAt = big.NewInt(20000000000) //主机愿意支付的最高价(WEI) 49 sellAt = big.NewInt(20000000000) //主机要求的最小批量价格(WEI) 50 payAt = 100 //触发付款的阈值请求(单位) 51 dropAt = 10000 //触发断开连接的阈值(单位) 52 ) 53 54 const ( 55 chequebookDeployRetries = 5 56 chequebookDeployDelay = 1 * time.Second //重试之间的延迟 57 ) 58 59 //localprofile将payprofile与*swap.params组合在一起 60 type LocalProfile struct { 61 *swap.Params 62 *PayProfile 63 } 64 65 //RemoteProfile将PayProfile与*swap.profile结合在一起。 66 type RemoteProfile struct { 67 *swap.Profile 68 *PayProfile 69 } 70 71 //PayProfile是相关支票簿和受益人选项的容器。 72 type PayProfile struct { 73 PublicKey string //与承诺的签署核对 74 Contract common.Address //支票簿合同地址 75 Beneficiary common.Address //Swarm销售收入的收件人地址 76 privateKey *ecdsa.PrivateKey 77 publicKey *ecdsa.PublicKey 78 owner common.Address 79 chbook *chequebook.Chequebook 80 lock sync.RWMutex 81 } 82 83 //newdefaultswapparams使用默认值创建参数 84 func NewDefaultSwapParams() *LocalProfile { 85 return &LocalProfile{ 86 PayProfile: &PayProfile{}, 87 Params: &swap.Params{ 88 Profile: &swap.Profile{ 89 BuyAt: buyAt, 90 SellAt: sellAt, 91 PayAt: uint(payAt), 92 DropAt: uint(dropAt), 93 }, 94 Strategy: &swap.Strategy{ 95 AutoCashInterval: autoCashInterval, 96 AutoCashThreshold: autoCashThreshold, 97 AutoDepositInterval: autoDepositInterval, 98 AutoDepositThreshold: autoDepositThreshold, 99 AutoDepositBuffer: autoDepositBuffer, 100 }, 101 }, 102 } 103 } 104 105 //init这只能在所有配置选项(file、cmd line、env vars)之后设置。 106 //已经过评估 107 func (lp *LocalProfile) Init(contract common.Address, prvkey *ecdsa.PrivateKey) { 108 pubkey := &prvkey.PublicKey 109 110 lp.PayProfile = &PayProfile{ 111 PublicKey: common.ToHex(crypto.FromECDSAPub(pubkey)), 112 Contract: contract, 113 Beneficiary: crypto.PubkeyToAddress(*pubkey), 114 privateKey: prvkey, 115 publicKey: pubkey, 116 owner: crypto.PubkeyToAddress(*pubkey), 117 } 118 } 119 120 //Newswp构造函数,参数 121 //*全球支票簿,承担部署服务和 122 //*余额处于缓冲状态。 123 //在netstore中调用swap.add(n) 124 //n>0发送块时调用=接收检索请求 125 //或者寄支票。 126 //n<0在接收数据块时调用=接收传递响应 127 //或者收到支票。 128 func NewSwap(localProfile *LocalProfile, remoteProfile *RemoteProfile, backend chequebook.Backend, proto swap.Protocol) (swapInstance *swap.Swap, err error) { 129 var ( 130 ctx = context.TODO() 131 ok bool 132 in *chequebook.Inbox 133 out *chequebook.Outbox 134 ) 135 136 remotekey, err := crypto.UnmarshalPubkey(common.FromHex(remoteProfile.PublicKey)) 137 if err != nil { 138 return nil, errors.New("invalid remote public key") 139 } 140 141 //检查远程配置文件支票簿是否有效 142 //资不抵债支票簿自杀,因此将显示无效 143 //TODO:监视支票簿事件 144 ok, err = chequebook.ValidateCode(ctx, backend, remoteProfile.Contract) 145 if !ok { 146 log.Info(fmt.Sprintf("invalid contract %v for peer %v: %v)", remoteProfile.Contract.Hex()[:8], proto, err)) 147 } else { 148 //远程配置文件合同有效,创建收件箱 149 in, err = chequebook.NewInbox(localProfile.privateKey, remoteProfile.Contract, localProfile.Beneficiary, remotekey, backend) 150 if err != nil { 151 log.Warn(fmt.Sprintf("unable to set up inbox for chequebook contract %v for peer %v: %v)", remoteProfile.Contract.Hex()[:8], proto, err)) 152 } 153 } 154 155 //检查LocalProfile支票簿合同是否有效 156 ok, err = chequebook.ValidateCode(ctx, backend, localProfile.Contract) 157 if !ok { 158 log.Warn(fmt.Sprintf("unable to set up outbox for peer %v: chequebook contract (owner: %v): %v)", proto, localProfile.owner.Hex(), err)) 159 } else { 160 out = chequebook.NewOutbox(localProfile.Chequebook(), remoteProfile.Beneficiary) 161 } 162 163 pm := swap.Payment{ 164 In: in, 165 Out: out, 166 Buys: out != nil, 167 Sells: in != nil, 168 } 169 swapInstance, err = swap.New(localProfile.Params, pm, proto) 170 if err != nil { 171 return 172 } 173 //握手中给定的远程配置文件(第一个) 174 swapInstance.SetRemote(remoteProfile.Profile) 175 var buy, sell string 176 if swapInstance.Buys { 177 buy = "purchase from peer enabled at " + remoteProfile.SellAt.String() + " wei/chunk" 178 } else { 179 buy = "purchase from peer disabled" 180 } 181 if swapInstance.Sells { 182 sell = "selling to peer enabled at " + localProfile.SellAt.String() + " wei/chunk" 183 } else { 184 sell = "selling to peer disabled" 185 } 186 log.Warn(fmt.Sprintf("SWAP arrangement with <%v>: %v; %v)", proto, buy, sell)) 187 188 return 189 } 190 191 //从本地配置文件获取支票簿 192 func (lp *LocalProfile) Chequebook() *chequebook.Chequebook { 193 defer lp.lock.Unlock() 194 lp.lock.Lock() 195 return lp.chbook 196 } 197 198 //私钥访问器 199 func (lp *LocalProfile) PrivateKey() *ecdsa.PrivateKey { 200 return lp.privateKey 201 } 202 203 //func(self*localprofile)publickey()*ecdsa.publickey_ 204 //返回self.publickey 205 //} 206 207 //本地配置文件上的set key集的私钥和公钥 208 func (lp *LocalProfile) SetKey(prvkey *ecdsa.PrivateKey) { 209 lp.privateKey = prvkey 210 lp.publicKey = &prvkey.PublicKey 211 } 212 213 //setcheckbook包装支票簿初始化器并设置自动报告以覆盖支出。 214 func (lp *LocalProfile) SetChequebook(ctx context.Context, backend chequebook.Backend, path string) error { 215 lp.lock.Lock() 216 swapContract := lp.Contract 217 lp.lock.Unlock() 218 219 valid, err := chequebook.ValidateCode(ctx, backend, swapContract) 220 if err != nil { 221 return err 222 } else if valid { 223 return lp.newChequebookFromContract(path, backend) 224 } 225 return lp.deployChequebook(ctx, backend, path) 226 } 227 228 //deploychequebook部署本地配置文件支票簿 229 func (lp *LocalProfile) deployChequebook(ctx context.Context, backend chequebook.Backend, path string) error { 230 opts := bind.NewKeyedTransactor(lp.privateKey) 231 opts.Value = lp.AutoDepositBuffer 232 opts.Context = ctx 233 234 log.Info(fmt.Sprintf("Deploying new chequebook (owner: %v)", opts.From.Hex())) 235 address, err := deployChequebookLoop(opts, backend) 236 if err != nil { 237 log.Error(fmt.Sprintf("unable to deploy new chequebook: %v", err)) 238 return err 239 } 240 log.Info(fmt.Sprintf("new chequebook deployed at %v (owner: %v)", address.Hex(), opts.From.Hex())) 241 242 //此时需要保存配置 243 lp.lock.Lock() 244 lp.Contract = address 245 err = lp.newChequebookFromContract(path, backend) 246 lp.lock.Unlock() 247 if err != nil { 248 log.Warn(fmt.Sprintf("error initialising cheque book (owner: %v): %v", opts.From.Hex(), err)) 249 } 250 return err 251 } 252 253 //deploychequebookloop反复尝试部署支票簿。 254 func deployChequebookLoop(opts *bind.TransactOpts, backend chequebook.Backend) (addr common.Address, err error) { 255 var tx *types.Transaction 256 for try := 0; try < chequebookDeployRetries; try++ { 257 if try > 0 { 258 time.Sleep(chequebookDeployDelay) 259 } 260 if _, tx, _, err = contract.DeployChequebook(opts, backend); err != nil { 261 log.Warn(fmt.Sprintf("can't send chequebook deploy tx (try %d): %v", try, err)) 262 continue 263 } 264 if addr, err = bind.WaitDeployed(opts.Context, backend, tx); err != nil { 265 log.Warn(fmt.Sprintf("chequebook deploy error (try %d): %v", try, err)) 266 continue 267 } 268 return addr, nil 269 } 270 return addr, err 271 } 272 273 //newcheckbookfromcontract-从持久的JSON文件初始化支票簿或创建新的支票簿 274 //呼叫方持有锁 275 func (lp *LocalProfile) newChequebookFromContract(path string, backend chequebook.Backend) error { 276 hexkey := common.Bytes2Hex(lp.Contract.Bytes()) 277 err := os.MkdirAll(filepath.Join(path, "chequebooks"), os.ModePerm) 278 if err != nil { 279 return fmt.Errorf("unable to create directory for chequebooks: %v", err) 280 } 281 282 chbookpath := filepath.Join(path, "chequebooks", hexkey+".json") 283 lp.chbook, err = chequebook.LoadChequebook(chbookpath, lp.privateKey, backend, true) 284 285 if err != nil { 286 lp.chbook, err = chequebook.NewChequebook(chbookpath, lp.Contract, lp.privateKey, backend) 287 if err != nil { 288 log.Warn(fmt.Sprintf("unable to initialise chequebook (owner: %v): %v", lp.owner.Hex(), err)) 289 return fmt.Errorf("unable to initialise chequebook (owner: %v): %v", lp.owner.Hex(), err) 290 } 291 } 292 293 lp.chbook.AutoDeposit(lp.AutoDepositInterval, lp.AutoDepositThreshold, lp.AutoDepositBuffer) 294 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)) 295 296 return nil 297 } 298