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