以太坊微支付通道原理与实现
以太坊微支付通道原理与实现
线上直接转账需要一定的费用,如果存在大量小额交易的情况下,费用会变的难以承受,因而以太坊引入了微交易支付通道来解决这个问题。以太坊提供了一个票据支付方案,主要依赖于智能合约实现的一对多的账单系统。该账单系统大致上的执行流程如下.
1:账单生成,同时提供机制往账单上存入保证金。
2:交易发起人生成交易票据
3:直接将票据发送给接收人
4:接收人兑现票据,从合约转账(尽管某次兑现可能会失败,但是只要票据存在最终还是能够兑现).
这种交易优点在于可以在线下发送任意数量的交易,而只需要两个链上交易(存入保证金,兑现)只要存入保证金,线下通过交换票据进行任意数量的交易,避免了频繁的线上转账,节省了交易费用。
代码结构
-
.
-
├── api.go //对外接口
-
├── cheque.go //账单
-
├── cheque_test.go
-
├── contract
-
│ ├── chequebook.go //合约go语言接口
-
│ ├── chequebook.sol //合约源码
-
│ ├── code.go //合约byte码
-
│ ├── mortal.sol //合约销毁
-
│ └── owned.sol //hebe权限
-
└── gencode.go //合约byte码生成
合约层
合约自身是接收转账的,用户可以在初始化或者后来增加金额,可以通过cash方法兑现票据,转账金额都会保存在send变量上。
-
pragma solidity ^0.4.18;
-
-
/// @title Chequebook for Ethereum micropayments
-
/// @author Daniel A. Nagy <daniel@ethereum.org>
-
contract chequebook is mortal {
-
// Cumulative paid amount in wei to each beneficiary
-
//已经支付的 可以控制双花,防止多次兑换票据
-
mapping (address => uint256) public sent;
-
/// @notice Overdraft event
-
event Overdraft(address deadbeat);
-
// Allow sending ether to the chequebook.
-
function() public payable{ }
-
/// @notice Cash cheque
-
///
-
/// @param beneficiary beneficiary address
-
/// @param amount cumulative amount in wei
-
/// @param sig_v signature parameter v
-
/// @param sig_r signature parameter r
-
/// @param sig_s signature parameter s
-
/// The digital signature is calculated on the concatenated triplet of contract address, beneficiary address and cumulative amount
-
function cash(address beneficiary, uint256 amount, uint8 sig_v, bytes32 sig_r, bytes32 sig_s) public{
-
// Check if the cheque is old.
-
// Only cheques that are more recent than the last cashed one are considered.
-
require(amount > sent[beneficiary]);
-
// Check the digital signature of the cheque.
-
bytes32 hash = keccak256(address(this), beneficiary, amount);
-
require(owner == ecrecover(hash, sig_v, sig_r, sig_s));
-
// Attempt sending the difference between the cumulative amount on the cheque
-
// and the cumulative amount on the last cashed cheque to beneficiary.
-
uint256 diff = amount - sent[beneficiary];
-
if (diff <= this.balance) {
-
// update the cumulative amount before sending
-
sent[beneficiary] = amount;
-
beneficiary.transfer(diff);
-
} else {
-
// Upon failure, punish owner for writing a bounced cheque.
-
// owner.sendToDebtorsPrison();
-
Overdraft(owner);
-
// Compensate beneficiary.
-
selfdestruct(beneficiary);
-
}
-
}
-
}
支付层
账单保存了账本的位置,记账人,所有人等信
-
// Chequebook can create and sign cheques from a single contract to multiple beneficiaries.
-
// It is the outgoing payment handler for peer to peer micropayments.
-
type Chequebook struct {
-
path string // path to chequebook file
-
prvKey *ecdsa.PrivateKey // private key to sign cheque with
-
lock sync.Mutex //
-
backend Backend // blockchain API
-
quit chan bool // when closed causes autodeposit to stop
-
owner common.Address // owner address (derived from pubkey)
-
contract *contract.Chequebook // abigen binding
-
session *contract.ChequebookSession // abigen binding with Tx Opts
-
// persisted fields
-
balance *big.Int // not synced with blockchain
-
contractAddr common.Address // contract address
-
sent map[common.Address]*big.Int //tallies for beneficiaries
-
txhash string // tx hash of last deposit tx
-
threshold *big.Int // threshold that triggers autodeposit if not nil
-
buffer *big.Int // buffer to keep on top of balance for fork protection
-
log log.Logger // contextual logger with the contract address embedded
-
}
票据:合约位置,接收人,金额,签名
-
type Cheque struct {
-
Contract common.Address // address of chequebook, needed to avoid cross-contract submission
-
Beneficiary common.Address
-
Amount *big.Int // cumulative amount of all funds sent
-
Sig []byte // signature Sign(Keccak256(contract, beneficiary, amount), prvKey)
-
}
票据生成
生成一条支付记录,返回一份签名后的票据,收费这凭借这张票据从合约里面取钱.
-
// Issue creates a cheque signed by the chequebook owner's private key. The
-
// signer commits to a contract (one that they own), a beneficiary and amount.
-
func (self *Chequebook) Issue(beneficiary common.Address, amount *big.Int) (ch *Cheque, err error) {
-
defer self.lock.Unlock()
-
self.lock.Lock()
-
if amount.Sign() <= 0 {
-
return nil, fmt.Errorf("amount must be greater than zero (%v)", amount)
-
}
-
if self.balance.Cmp(amount) < 0 {
-
err = fmt.Errorf("insufficient funds to issue cheque for amount: %v. balance: %v", amount, self.balance)
-
} else {
-
var sig []byte
-
sent, found := self.sent[beneficiary]
-
if !found {
-
sent = new(big.Int)
-
self.sent[beneficiary] = sent
-
}
-
sum := new(big.Int).Set(sent)
-
sum.Add(sum, amount)
-
sig, err = crypto.Sign(sigHash(self.contractAddr, beneficiary, sum), self.prvKey)
-
if err == nil {
-
ch = &Cheque{
-
Contract: self.contractAddr,
-
Beneficiary: beneficiary,
-
Amount: sum,
-
Sig: sig,
-
}
-
sent.Set(sum)
-
self.balance.Sub(self.balance, amount) // subtract amount from balance
-
}
-
}
-
// 账单余额少于阈值,自动补充.
-
if self.threshold != nil {
-
if self.balance.Cmp(self.threshold) < 0 {
-
send := new(big.Int).Sub(self.buffer, self.balance)
-
self.deposit(send)
-
}
-
}
-
return
-
}
存储金额
-
func (self *Chequebook) Deposit(amount *big.Int) (string, error) {
-
defer self.lock.Unlock()
-
self.lock.Lock()
-
return self.deposit(amount)
-
}
-
func (self *Chequebook) deposit(amount *big.Int) (string, error) {
-
// since the amount is variable here, we do not use sessions
-
depositTransactor := bind.NewKeyedTransactor(self.prvKey)
-
depositTransactor.Value = amount
-
chbookRaw := &contract.ChequebookRaw{Contract: self.contract}
-
//转入金额
-
tx, err := chbookRaw.Transfer(depositTransactor)
-
if err != nil {
-
self.log.Warn("Failed to fund chequebook", "amount", amount, "balance", self.balance, "target", self.buffer, "err", err)
-
return "", err
-
}
-
// assume that transaction is actually successful, we add the amount to balance right away
-
self.balance.Add(self.balance, amount)
-
self.log.Trace("Deposited funds to chequebook", "amount", amount, "balance", self.balance, "target", self.buffer)
-
return tx.Hash().Hex(), nil
-
}
兑换票据
-
// Cash is a convenience method to cash any cheque.
-
func (self *Chequebook) Cash(ch *Cheque) (txhash string, err error) {
-
return ch.Cash(self.session)
-
}
-
// Cash cashes the cheque by sending an Ethereum transaction.
-
func (self *Cheque) Cash(session *contract.ChequebookSession) (string, error) {
-
v, r, s := sig2vrs(self.Sig)
-
//调用合约的cash方法 提取代币
-
tx, err := session.Cash(self.Beneficiary, self.Amount, v, r, s)
-
if err != nil {
-
return "", err
-
}
-
return tx.Hash().Hex(), nil
-
}
其他接口
OutBox:用于在电对点网络中发行票据,提供了保证金存入,票据发行,自动存入保证金等接口。
-
type Outbox struct {
-
chequeBook *Chequebook
-
beneficiary common.Address
-
}
-
// Issue creates cheque.
-
func (self *Outbox) Issue(amount *big.Int) (swap.Promise, error) {
-
return self.chequeBook.Issue(self.beneficiary, amount)
-
}
-
// AutoDeposit enables auto-deposits on the underlying chequebook.
-
func (self *Outbox) AutoDeposit(interval time.Duration, threshold, buffer *big.Int) {
-
self.chequeBook.AutoDeposit(interval, threshold, buffer)
-
}
InBox:用于在电对点网络中票据兑换,提供了直接兑换,定时兑换,延迟兑换的接口功能。
-
// Inbox can deposit, verify and cash cheques from a single contract to a single
-
// beneficiary. It is the incoming payment handler for peer to peer micropayments.
-
type Inbox struct {
-
lock sync.Mutex
-
contract common.Address // peer's chequebook contract
-
beneficiary common.Address // local peer's receiving address
-
sender common.Address // local peer's address to send cashing tx from
-
signer *ecdsa.PublicKey // peer's public key
-
txhash string // tx hash of last cashing tx
-
session *contract.ChequebookSession // abi contract backend with tx opts
-
quit chan bool // when closed causes autocash to stop
-
maxUncashed *big.Int // threshold that triggers autocashing
-
cashed *big.Int // cumulative amount cashed
-
cheque *Cheque // last cheque, nil if none yet received
-
log log.Logger // contextual logger with the contract address embedded
-
}
-
// Cash attempts to cash the current cheque.
-
func (self *Inbox) Cash() (txhash string, err error) {
-
if self.cheque != nil {
-
txhash, err = self.cheque.Cash(self.session)
-
self.log.Trace("Cashing in chequebook cheque", "amount", self.cheque.Amount, "beneficiary", self.beneficiary)
-
self.cashed = self.cheque.Amount
-
}
-
return
-
}
以太坊微支付通道原理与实现相关推荐
- 闪电网络RSMC协议技术原理----从微支付通道到RSMC(序列到期可撤销合约)
闪电网络的RSMC协议技术原理 "if all tree falls in the forest and no one is around to hear it,does it make a ...
- 雷电网络 微支付通道
1 微支付通道搭建: http://blog.hubwiz.com/2018/08/18/ethereum-payment-channel/ 简易微支付通道搭建 https://ethfans.o ...
- 以太坊闪电网络实现原理
2019独角兽企业重金招聘Python工程师标准>>> 以太坊闪电网络实现原理 线上直接转账需要一定的费用,如果存在大量小额交易的情况下,费用会变的难以承受,因而以太坊引入了微交易支 ...
- 从技术人视角看闪电网络之微支付通道
(要看懂这篇文章,读者需掌握BTC基本原理,包括转账原理和多签地址.地址锁定时间.隔离见证) (感谢闪电网络很难懂?你需要看看这篇文章 | 硬核科普这个教程提供的图片) 比特币的价值传输相当安全,但是 ...
- 闪电网络预备知识二:微支付通道
微支付通道 首先在介绍微支付之前,说说我对于区块链世界的理解.我们可以这样认为:首先区块链世界是去中心化的,也就是说没有一个中心机构.在现实世界中,中心机构可以作为一种权威,比如银行,你转账通过银行, ...
- 第13课 微支付通道(MicroPayment Channel) -- 迄今为止最透彻的讲解了
有兴趣朋友也可以进一步关注公众号"架构之道与术", 获取原文. 或扫描如下二维码: 这1课很关键,微支付通道将是后面闪电网络.隔离见证的基础,微支付通道不能搞透彻,后面的闪电网络. ...
- 微支付通道与闪电网络
参考链接: 闪电网络学习资源目录 一.微支付通道 微支付通道举例与理解 "如果森林里的一棵树倒了,而周围没有人听到,它会发出声音吗?" 应用场景: 大量的小额交易,因为手续费的 ...
- 以太坊Bloom过滤器实现原理及应用场景分析
一,Bloom过滤器的数据结构和reciept创建Bloom的过程 type Bloom [BloomByteLength]byte BloomByteLength = 256 Bloom 就是一个2 ...
- Ethereum 以太坊 交易数据 构建原理
当我们需要将某些数据写入区块链,这个写入的过程叫交易.从区块链中取数据叫调用Call.交易有别与传统数据库数据的写入,以太坊区块链需要先将写入的数据编码成16进制的字节码,区块链块中存储的基础类型如: ...
最新文章
- 如何运用NLP向个性类型客户介绍产品
- [转载] Win7下MATLAB 7.0下载地址和详细安装
- jQuery EasyUI使用教程之创建标签页
- 开发者基础知识游戏,共10关,欢迎挑战
- python计算今年第几天_Python三种方法计算指定日期是今年的第几天
- LINUX之samba服务器的安装与配置(基于redhat 6.3发行版)
- 阿里巴巴发布第四财季财报 菜鸟驿站包裹量增长100%
- 16.Nginx 服务器的 gzip 压缩
- shell编程之函数简单使用
- ASP.NET文件的上传与下载
- Win7安装虚拟光驱蓝屏解决办法
- 视频教程-2020年软考网络规划设计师论文写作历年真题详解软考视频教程-软考
- 成绩造假!你看到400+大佬,有可能是P图!
- html5 pc端 客户端 web端的区别,wap版、手机版以及web的区别
- android carlife 源码,CarLife开发总结
- 雷神加速器无限更新失败️️️
- 微信营销系统(第三方微信平台)之微分销模块拓展
- 安装vs2015后C#注释变为英文
- 数据仓库DW、ODS、DM及其区别
- Makefile新手?千万别错过了《驾驭Makefile》