以太坊微支付通道原理与实现

以太坊微支付通道原理与实现

线上直接转账需要一定的费用,如果存在大量小额交易的情况下,费用会变的难以承受,因而以太坊引入了微交易支付通道来解决这个问题。以太坊提供了一个票据支付方案,主要依赖于智能合约实现的一对多的账单系统。该账单系统大致上的执行流程如下.

  • 1:账单生成,同时提供机制往账单上存入保证金。

  • 2:交易发起人生成交易票据

  • 3:直接将票据发送给接收人

  • 4:接收人兑现票据,从合约转账(尽管某次兑现可能会失败,但是只要票据存在最终还是能够兑现).

这种交易优点在于可以在线下发送任意数量的交易,而只需要两个链上交易(存入保证金,兑现)只要存入保证金,线下通过交换票据进行任意数量的交易,避免了频繁的线上转账,节省了交易费用。

代码结构


     
     
  1. .
  2. ├── api. go //对外接口
  3. ├── cheque. go //账单
  4. ├── cheque_test. go
  5. ├── contract
  6. │   ├── chequebook. go //合约go语言接口
  7. │   ├── chequebook.sol //合约源码
  8. │   ├── code. go //合约byte码
  9. │   ├── mortal.sol //合约销毁
  10. │   └── owned.sol //hebe权限
  11. └── gencode. go //合约byte码生成

合约层

合约自身是接收转账的,用户可以在初始化或者后来增加金额,可以通过cash方法兑现票据,转账金额都会保存在send变量上。


     
     
  1. pragma solidity ^ 0.4 .18;
  2. import "./mortal.sol";
  3. /// @title Chequebook for Ethereum micropayments
  4. /// @author Daniel A. Nagy <daniel@ethereum.org>
  5. contract chequebook is mortal {
  6. // Cumulative paid amount in wei to each beneficiary
  7. //已经支付的 可以控制双花,防止多次兑换票据
  8. mapping ( address => uint256) public sent;
  9. /// @notice Overdraft event
  10. event Overdraft(address deadbeat);
  11. // Allow sending ether to the chequebook.
  12. function() public payable { }
  13. /// @notice Cash cheque
  14. ///
  15. /// @param beneficiary beneficiary address
  16. /// @param amount cumulative amount in wei
  17. /// @param sig_v signature parameter v
  18. /// @param sig_r signature parameter r
  19. /// @param sig_s signature parameter s
  20. /// The digital signature is calculated on the concatenated triplet of contract address, beneficiary address and cumulative amount
  21. function cash(address beneficiary, uint256 amount, uint8 sig_v, bytes32 sig_r, bytes32 sig_s) public {
  22. // Check if the cheque is old.
  23. // Only cheques that are more recent than the last cashed one are considered.
  24. require(amount > sent[beneficiary]);
  25. // Check the digital signature of the cheque.
  26. bytes32 hash = keccak256(address( this), beneficiary, amount);
  27. require(owner == ecrecover(hash, sig_v, sig_r, sig_s));
  28. // Attempt sending the difference between the cumulative amount on the cheque
  29. // and the cumulative amount on the last cashed cheque to beneficiary.
  30. uint256 diff = amount - sent[beneficiary];
  31. if (diff <= this.balance) {
  32. // update the cumulative amount before sending
  33. sent[beneficiary] = amount;
  34. beneficiary.transfer(diff);
  35. } else {
  36. // Upon failure, punish owner for writing a bounced cheque.
  37. // owner.sendToDebtorsPrison();
  38. Overdraft(owner);
  39. // Compensate beneficiary.
  40. selfdestruct(beneficiary);
  41. }
  42. }
  43. }

支付层

账单保存了账本的位置,记账人,所有人等信


     
     
  1. // Chequebook can create and sign cheques from a single contract to multiple beneficiaries.
  2. // It is the outgoing payment handler for peer to peer micropayments.
  3. type Chequebook struct {
  4. path string // path to chequebook file
  5. prvKey *ecdsa.PrivateKey // private key to sign cheque with
  6. lock sync.Mutex //
  7. backend Backend // blockchain API
  8. quit chan bool // when closed causes autodeposit to stop
  9. owner common.Address // owner address (derived from pubkey)
  10. contract *contract.Chequebook // abigen binding
  11. session *contract.ChequebookSession // abigen binding with Tx Opts
  12. // persisted fields
  13. balance *big.Int // not synced with blockchain
  14. contractAddr common.Address // contract address
  15. sent map[common.Address]*big.Int //tallies for beneficiaries
  16. txhash string // tx hash of last deposit tx
  17. threshold *big.Int // threshold that triggers autodeposit if not nil
  18. buffer *big.Int // buffer to keep on top of balance for fork protection
  19. log log.Logger // contextual logger with the contract address embedded
  20. }

票据:合约位置,接收人,金额,签名


     
     
  1. type Cheque struct {
  2. Contract common. Address // address of chequebook, needed to avoid cross-contract submission
  3. Beneficiary common. Address
  4. Amount * big. Int // cumulative amount of all funds sent
  5. Sig [] byte // signature Sign(Keccak256(contract, beneficiary, amount), prvKey)
  6. }

票据生成

生成一条支付记录,返回一份签名后的票据,收费这凭借这张票据从合约里面取钱.


     
     
  1. // Issue creates a cheque signed by the chequebook owner's private key. The
  2. // signer commits to a contract (one that they own), a beneficiary and amount.
  3. func (self *Chequebook) Issue(beneficiary common.Address, amount *big.Int) (ch *Cheque, err error) {
  4. defer self.lock. Unlock()
  5. self.lock. Lock()
  6. if amount. Sign() <= 0 {
  7. return nil, fmt. Errorf( "amount must be greater than zero (%v)", amount)
  8. }
  9. if self.balance. Cmp(amount) < 0 {
  10. err = fmt. Errorf( "insufficient funds to issue cheque for amount: %v. balance: %v", amount, self.balance)
  11. } else {
  12. var sig [] byte
  13. sent, found := self.sent[beneficiary]
  14. if !found {
  15. sent = new(big. Int)
  16. self.sent[beneficiary] = sent
  17. }
  18. sum := new(big. Int). Set(sent)
  19. sum. Add(sum, amount)
  20. sig, err = crypto. Sign(sigHash( self.contractAddr, beneficiary, sum), self.prvKey)
  21. if err == nil {
  22. ch = & Cheque{
  23. Contract: self.contractAddr,
  24. Beneficiary: beneficiary,
  25. Amount: sum,
  26. Sig: sig,
  27. }
  28. sent. Set(sum)
  29. self.balance. Sub( self.balance, amount) // subtract amount from balance
  30. }
  31. }
  32. // 账单余额少于阈值,自动补充.
  33. if self.threshold != nil {
  34. if self.balance. Cmp( self.threshold) < 0 {
  35. send := new(big. Int). Sub( self.buffer, self.balance)
  36. self.deposit(send)
  37. }
  38. }
  39. return
  40. }

存储金额


     
     
  1. func (self *Chequebook) Deposit(amount *big.Int) (string, error) {
  2. defer self.lock. Unlock()
  3. self.lock. Lock()
  4. return self.deposit(amount)
  5. }
  6. func (self *Chequebook) deposit(amount *big.Int) (string, error) {
  7. // since the amount is variable here, we do not use sessions
  8. depositTransactor := bind. NewKeyedTransactor( self.prvKey)
  9. depositTransactor. Value = amount
  10. chbookRaw := &contract. ChequebookRaw{ Contract: self.contract}
  11. //转入金额
  12. tx, err := chbookRaw. Transfer(depositTransactor)
  13. if err != nil {
  14. self.log. Warn( "Failed to fund chequebook", "amount", amount, "balance", self.balance, "target", self.buffer, "err", err)
  15. return "", err
  16. }
  17. // assume that transaction is actually successful, we add the amount to balance right away
  18. self.balance. Add( self.balance, amount)
  19. self.log. Trace( "Deposited funds to chequebook", "amount", amount, "balance", self.balance, "target", self.buffer)
  20. return tx. Hash(). Hex(), nil
  21. }

兑换票据


     
     
  1. // Cash is a convenience method to cash any cheque.
  2. func (self *Chequebook) Cash(ch *Cheque) (txhash string, err error) {
  3. return ch.Cash(self.session)
  4. }
  5. // Cash cashes the cheque by sending an Ethereum transaction.
  6. func (self *Cheque) Cash(session *contract.ChequebookSession) (string, error) {
  7. v, r, s := sig2vrs(self.Sig)
  8. //调用合约的cash方法 提取代币
  9. tx, err := session.Cash(self.Beneficiary, self.Amount, v, r, s)
  10. if err != nil {
  11. return "", err
  12. }
  13. return tx.Hash().Hex(), nil
  14. }

其他接口

OutBox:用于在电对点网络中发行票据,提供了保证金存入,票据发行,自动存入保证金等接口。


     
     
  1. type Outbox struct {
  2. chequeBook * Chequebook
  3. beneficiary common. Address
  4. }
  5. // Issue creates cheque.
  6. func (self *Outbox) Issue(amount *big.Int) (swap.Promise, error) {
  7. return self.chequeBook. Issue( self.beneficiary, amount)
  8. }
  9. // AutoDeposit enables auto-deposits on the underlying chequebook.
  10. func (self *Outbox) AutoDeposit(interval time.Duration, threshold, buffer *big.Int) {
  11. self.chequeBook. AutoDeposit(interval, threshold, buffer)
  12. }

InBox:用于在电对点网络中票据兑换,提供了直接兑换,定时兑换,延迟兑换的接口功能。


     
     
  1. // Inbox can deposit, verify and cash cheques from a single contract to a single
  2. // beneficiary. It is the incoming payment handler for peer to peer micropayments.
  3. type Inbox struct {
  4. lock sync. Mutex
  5. contract common. Address // peer's chequebook contract
  6. beneficiary common. Address // local peer's receiving address
  7. sender common. Address // local peer's address to send cashing tx from
  8. signer *ecdsa. PublicKey // peer's public key
  9. txhash string // tx hash of last cashing tx
  10. session *contract. ChequebookSession // abi contract backend with tx opts
  11. quit chan bool // when closed causes autocash to stop
  12. maxUncashed *big. Int // threshold that triggers autocashing
  13. cashed *big. Int // cumulative amount cashed
  14. cheque * Cheque // last cheque, nil if none yet received
  15. log log. Logger // contextual logger with the contract address embedded
  16. }
  17. // Cash attempts to cash the current cheque.
  18. func (self *Inbox) Cash() (txhash string, err error) {
  19. if self.cheque != nil {
  20. txhash, err = self.cheque. Cash( self.session)
  21. self.log. Trace( "Cashing in chequebook cheque", "amount", self.cheque. Amount, "beneficiary", self.beneficiary)
  22. self.cashed = self.cheque. Amount
  23. }
  24. return
  25. }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值