liqui提取比特币提币时间多少时间

比特币遭休克式监管 倒闭潮将至_新浪科技_新浪网
比特币遭休克式监管 倒闭潮将至
没有监管机构的沟通,没有看到红头文件下发,银行的一个业务员通知销户,银行充值渠道停止,这就是目前中国比特币交易平台面临的监管窘境。曾经火爆异常的比特币仿佛正在一步步迈向灭亡,而最明显的现象莫过于平台的纷纷倒闭。[][]
  “我们希望被监管,但是没有任何监管部门联系我们。我们4月28日接到了招商银行客户经理的电话通知,要求我们前往招行营业网点销户,就是这样。”国内最大的比特币交易平台比特币中国CEO李启元说。
  从4月2日开始,国内几乎所有的比特币交易平台都像比特币中国一样接到了银行的销户通知。根据财新网报道,央行于3月中旬向央行各分支机构下发了一份名为《关于进一步加强比特币风险防范工作的通知》,该文件要求各银行和第三方支付机构,关闭十多家境内的比特币平台的所有交易账户。
  上述通知要求,各商业银行和第三方支付机构关闭这些平台的所有账户,截止期限是4月15日,这一日期也被业内称为“415大限”。
  李启元称,比特币中国从未接到财新网报道中的通知,也没有任何监管部门与他们沟通。据他了解,目前唯一一个关于比特币监管的红头文件还是央行去年12月发出的《关于防范比特币风险的通知》。
  一位比特币行业资深人士告诉新浪科技,截至5月1日,国内几乎所有比特币平台都接到银行通知,停止了银行卡渠道充值。“这相当于一个婴儿被断了奶,一下子进入休克状态。要么就是探索其他非正常渠道充值,要么就是等着平台的钱被慢慢提光,然后死掉。”
比特币平台倒闭潮将至
  伴随着监管加剧,比特币平台已经开始出现个别的倒闭。5月2日,比特币交易所FXBTC()突然宣布关闭,所有的用户QQ群也都被解散。在此之前的几天里,所有客户现金和比特币均无法提现,客服回复是系统原因。
  事实上,FXBTC也是最早宣布接到银行销户通知的比特币平台。4月2日,FXBTC就发布公告,宣布央行下发通知要求任何商业银行和第三方支付机构不得为比特币等业务提供清算服务,关闭所有比特币相关网站的资金来往账户。
  当时有用户质疑,FXBTC发布的紧急通知可能是在撒谎,故意夸大的影响程度,关闭银行账户可能是因为FXBTC的个人原因。FXBTC迅速回击否认撒谎,称银行收到一行三会的行政文件,是按照行政命令要求关闭账户。
  新浪科技从一个FXBTC维权群中了解到,FXBTC在关闭前曾发出通知,称网站将开放到5月10日,要求用户在此时间之前,将平台内的现金或者比特币提出,但是5月9日,FXBTC就已经无法访问,很多用户来没有来得及将钱和币取出,涉及金额数十万元。
  新浪科技辗转联系上一名自称是FXBTC的工作人员,他给新浪科技一篇声明称,“由于近期央行的政策,我们受到前所未有的压力,包括无法充值提现,导致无法正常运营,决策困难等方面的问题。这次面临央行的长期封锁,目前来看我们无能为力,历经长期亏损,我们最终决定停止运营FXBTC。”
  不过,对于用户现金提取的问题,上述人员不置可否。截止发稿时,新浪科技也无法联络上FXBTC的官方客服和法定代表人蔡敦煌。
  一位比特币行业资深人士透露,除了FXBTC,近期还有一家名为人盟比特币的交易所也宣布关闭,随着银行渠道的停止,很多小的交易平台都是苦苦挣扎,名存实亡。他预计,比特币交易平台将遭遇倒闭潮,由于用户担忧资金安全,小平台的用户将快速流失。
绕过监管充值
  根据新浪科技对国内十余家比特币交易平台的调查,银行渠道停止之后,他们的充值方式只剩下三种:充值码充值、现金充值或者是海外充值。
  现金充值最为麻烦,要用户到比特币交易网站的办公地点缴纳现金,而充值码充值是被采用最多。火币网相关负责人透露,他们在接到银行通知之前就已经准备好了充值码充值准备,银行卡渠道一停充值码马上就启用了。
  不过,充值码渠道与银行充值渠道存在同样的风险,随时可能被叫停。根据央行去年12月发出的《关于防范比特币风险的通知》,各金融机构和支付机构不得买卖或作为中央对手买卖比特币,不得直接或间接为客户提供其他与比特币相关的服务。
  新浪科技从比特币中国官方充值页面看到,他们为用户提供了10种购买充值码的方式,但是其中6种都是通过淘宝网进行交易。
  支付宝早在4月25日就发出公告,要求任何机构和个人不得将支付宝用于比特币的交易资金充值及提现、购买及销售相关交易充值码等活动,不得通过支付宝服务划转相关交易资金。一经发现,支付宝有权立即停止有关服务。
  支付宝方面表示,正在清查销售比特币交易平台充值码的网店,查实后将予以封禁。新浪科技使用“比特币 充值码”等关键词从淘宝网搜索得到结果为0。但是比特币中国的充值渠道都使用了“充值平台 安全便捷 到账1000元”等隐晦的说法,逃避了淘宝平台的关键词监管。
  火币网更是干脆将充值转移到了QQ上,在官方页面,有两个代理商,新浪科技联系上一个名为“币信”的“金牌认证”代理商,他告诉新浪科技充值的方式是“向指定账号汇款,转账附言填写uid,收到钱后,给用户卡号和密码,用户点击充值卡充值即可”。
  不过,“币信”给新浪科技的却是一个私人银行卡号,而非公司账号。一位资深比特币投资者告诉新浪科技,现在各大平台上采用这种给私人银行卡汇款充值的方式比比皆是,这种方式让钱游离于公司之外,不但可以避税,还彻底了逃过了监管。
融资融币加剧风险
  唐图(化名)是一个有2年多比特币交易经验的资深玩家,他从投资比特币到挖矿再到建比特币论坛,甚至有一段时间还和好友建了一个小的交易平台,在了解了比特币平台众多内幕之后,他的感觉是“像被骗了一样”。
  他告诉新浪科技,比特币交易平台最初是靠收取交易手续费盈利,例如比特币中国最初买入和卖出的交易手续费费率为0.6%。不过,随着比特币交易平台越来越多,竞争越发激烈,交易费率不断下降,后来各大平台干脆免收手续费。
  在比特币中国,只有在提现时才会收取0.5%的手续费,而在火币网上,如果用户等级高,这一费率甚至可以降低到0.3%。
  不过火币网有新的赚钱方式:融资融币。用户如果判断比特币价格将会上涨时,可以使用借款功能借入人民币购买比特币,待比特币价格上涨后售出比特币归还系统,从而赚取差价获利。一个VIP初级用户就可以借到10万人民币,而如果达到VIP5借款额度可以高达80万。
  如果用户判断比特币价格将会下跌时,可以使用借币功能借入比特币出售,待比特币价格下跌后购入比特币归还系统,从而在比特币下跌时盈利,用户最多可以借到500枚比特币,按照今天的交易价格价值142万。
  “融资融币的诞生,让比特币交易风险成倍上升,然而这些借贷当然不是免费的。”唐图透露,火币网向用户放贷的总量是有限的,这导致很多用户借不到钱,之后火币网又推出了预约借贷功能,谁预约的利息高就借给谁,年化利率一度达到100%甚至更高。
  比特币中国CEO李启元告诉新浪科技,为了应对不断加剧的监管,比特币中国、BtcTrade、OKCoin、火币网、CHBTC等五家国内最大的比特币交易平台与5月6日联合发表自律声明,决定在5月10号之前停止新的融资融币;在全部融资或融币还清后停止杆杠交易业务。
  这五家平台坦承,比特币在中国的发展中出现了一些问题,比如价格上涨过快、庄家操盘、对散户的风险提示不足等等,导致了一些中小投资者出现了损失。“作为比特币交易平台,对目前出现的各种问题,我们的确负有不可推卸的责任。”
比特币交易平台的生财之道
  唐图表示,比特币交易平台的生财之道还远远不只如此。例如利用消息制造利空或者利多,一次就可以获利上百万。
  他举例说,比如下来了一个监管通知,交易平台会先“捂消息”,自己先做空,然后安抚投资者说这个监管命令影响不大,自己做空后,再放消息说监管很严厉,导致比特币价格下跌,自己从中牟利。
  “比特币平台其实远远不止是交易平台,还是最大的玩家。他们可以通过大额资金的买卖,精确的控制平台的价格。”唐图表示,中外比特币交易所在交易时会产生瞬间的价格差异,交易平台往往利用高频交易,国内价格低就从国内买到国外卖,国外价格低就国外买国内卖,通过瞬间的买卖,从中赚取小额差价。
  唐图表示,“大的比特币平台都会开放API接口,利用这些API可以每秒完成数百次的交易。不过比特币平台会把责任推给用户,说是某些用户利用API进行高频交易,其实他们自己才最大的高频交易者。”
  “更为恶劣的是部分平台帮人进行海外资产转移或者洗钱,业内叫做‘搬砖’。他们先把钱兑换成比特币,再转移到海外比特币交易所卖出,提款。”
  “由于中外比特币交易所价格不同,他们可以在国内寻找时机低价购币,再在国外寻找以高价卖出。整个过程可以赚两次钱,一次是比特币的差价,一次是转移的手续费,稳赚不赔。”唐图说。比特币四年疯狂涨百万倍 交易手续费多难回本_网易财经
比特币四年疯狂涨百万倍 交易手续费多难回本
用微信扫码二维码
分享至好友和朋友圈
http://img4./photo/-21/600x450_9E6KS6EN.jpg
http://img3./photo/-21/t_9E6KS6EN.jpg
http://img4./photo/-21/600x450_9E6KHLGG.jpg
http://img3./photo/-21/t_9E6KHLGG.jpg
http://img3./photo/-21/600x450_9E6KHMPH.jpg
http://img3./photo/-21/t_9E6KHMPH.jpg
http://img4./photo/-21/600x450_9E6KHO0E.jpg
http://img3./photo/-21/t_9E6KHO0E.jpg
http://img4./photo/-21/600x450_9E6KHPDU.jpg
http://img3./photo/-21/t_9E6KHPDU.jpg
http://img4./photo/-21/600x450_9E6KHQTQ.jpg
http://img3./photo/-21/t_9E6KHQTQ.jpg
http://img4./photo/-21/600x450_9E6KHSSA.jpg
http://img3./photo/-21/t_9E6KHSSA.jpg
http://img3./photo/-21/600x450_9E6KHUG.jpg
http://img4./photo/-21/t_9E6KHUG.jpg
http://img4./photo/-21/600x450_9E6KHVVE.jpg
http://img3./photo/-21/t_9E6KHVVE.jpg
http://img3./photo/-21/600x450_9E6KI1LN.jpg
http://img3./photo/-21/t_9E6KI1LN.jpg
http://img4./photo/-21/600x450_9E6KI2P.jpg
http://img3./photo/-21/t_9E6KI2P.jpg
http://img3./photo/-21/600x450_9E6KI3QG.jpg
http://img3./photo/-21/t_9E6KI3QG.jpg
http://img3./photo/-21/600x450_9E6KI54D.jpg
http://img4./photo/-21/t_9E6KI54D.jpg
http://img4./photo/-21/600x450_9E6KI6BV.jpg
http://img4./photo/-21/t_9E6KI6BV.jpg
http://img4./photo/-21/600x450_9E6KI7FG.jpg
http://img4./photo/-21/t_9E6KI7FG.jpg
http://img4./photo/-21/600x450_9E6KI8T.jpg
http://img3./photo/-21/t_9E6KI8T.jpg
http://img3./photo/-22/600x450_9E9OVKDS.jpg
http://img4./photo/-22/t_9E9OVKDS.jpg
http://img4./photo/-21/600x450_9E6KIAO.jpg
http://img4./photo/-21/t_9E6KIAO.jpg
http://img3./photo/-21/600x450_9E6KIC8U.jpg
http://img4./photo/-21/t_9E6KIC8U.jpg
http://img4./photo/-21/600x450_9E6KIDDJ.jpg
http://img4./photo/-21/t_9E6KIDDJ.jpg
http://img4./photo/-21/600x450_9E6KIEM.jpg
http://img4./photo/-21/t_9E6KIEM.jpg
http://img4./photo/-21/600x450_9E6KHIQ.jpg
http://img4./photo/-21/t_9E6KHIQ.jpg
http://img4./photo/-22/600x450_9EA4OARN.jpg
http://img4./photo/-22/t_9EA4OARN.jpg
图集已浏览完毕重新浏览
热门产品:   
:        
:         
热门影院:
阅读下一篇
用微信扫描二维码
分享至好友和朋友圈比特币源码学习笔记(一) - 推酷
比特币源码学习笔记(一)
从事区块链的开发,不了解其底层核心技术是不够的。许多人在看了比特币白皮书之后仍然不清楚比特币是怎样实现的,因为比特币的源码设计精巧,有许多设计白皮书未曾提及,加上本身比特币的文档稀少,加大了新手理解的困难程度。尽管现在已经有许多介绍区块链的书和文章,却很少是从源码着手分析的。我通过半年时间对于区块链的学习,开始撰写一份比特币源码的教程。本教程深入浅出,通过分析最经典的区块链——比特币的C++客户端源码,让开发者用最短的时间上手区块链技术。了解比特币源码可帮助开发者更好了解区块链的工作原理并在应用当中根据实际情况做出修改和调整。
本文所引用的源码均来自原始版比特币客户端,即由中本聪发布的第一版源码。该客户端包括大约16000行代码。尽管经过数年的发展,比特币客户端经过了几次较大更新,其数据结构和原理从诞生之日起一直延续至今。本文会尽可能保证文字的严谨准确,表达当中难免会产生疏漏,欢迎指正。
本章节讲述比特币客户端是怎样生成比特币地址,并创建新的交易。
我们来看一下GenerateNewKey()方法,该方法位于main.cpp。
bool AddKey(const CKey& key)
CRITICAL_BLOCK(cs_mapKeys)
mapKeys[key.GetPubKey()] = key.GetPrivKey();
mapPubKeys[Hash160(key.GetPubKey())] = key.GetPubKey();
return CWalletDB().WriteKey(key.GetPubKey(), key.GetPrivKey());
vector&unsigned char& GenerateNewKey()
key.MakeNewKey();
if (!AddKey(key))
throw runtime_error(&GenerateNewKey() : AddKey failed\n&);
return key.GetPubKey();
该方法通过以下步骤生成一个新的公钥对:
首先建立一个新的CKey类型对象(第13行)。
调用addKey()方法将新建的key添加至1)全局映射 mapKeys (第5行)2)全局 map mapPubKeys (第6行)和钱包数据库wallet.dat(第8行)。
mapKeys建立公钥与私钥的一一对应关系。
mapPubKeys建立公钥的hash和公钥本身的对应关系。
返回公钥(第16行)。
该公钥为未压缩的格式,属于OpenSSL标准格式之一。在得到公钥之后,比特币客户端会将该公钥传递至 PubKeyToAddress() 并调用 Hash160ToAddress() 方法生成地址。最后返回的Base58编码字符串值便是一个新生成的比特币地址。Base58由1-9和除i,l,0,o之外的英文字符组成。
CTransaction类
CTransaction的定义位于main.h。在比特币当中,所谓币的概念其实是一系列交易Tx的组合。这种方式虽然实现起来更为复杂,却提高了比特币的安全性。用户可以为每一笔交易创建一个新的地址,地址在使用一次之后可以立即作废。因此,CTransaction是比特币客户端最重要的类之一。
class CTransaction
vector&CTxIn&
vector&CTxOut&
int nLockT
CTransaction包含两个容器类型:输入交易vin和输出交易vout。每个vin由若干CTxIn对象组成,每个vout则由CTxOut组成。
每笔交易Tx的输入交易(CTxIn类)包含一个COutPoint对象prevout,该对象引用另外一笔交易Tx的输出交易作为来源交易。来源交易使当前交易Tx从另一笔交易当中得到可花费的比特币。一笔交易Tx可以拥有任意笔输入交易。
任何交易均由一个256位uint256哈希作为其唯一识别。若要引用某一笔来源交易TxSource当中某个特定的输出交易,我们需要两种信息:TxSource的哈希,和该输出交易在输出交易当中的位置n。这两种信息构成COutPoint类。一个COutPoint对象指向来源交易的某一笔输出交易 TxSource.vout[n] 。如果该笔输出交易被另外一笔交易Tx的位置i的输入交易所引用,例如 Tx.vin[i].prevout ,我们将其称为Tx的第i笔输入交易花费了TxSource中的第n笔输出交易。
uint256和uint160类
这两种类型的定义位于uint.h。一个uint256类包含有一个256位的哈希。它由一个长度为256/32=8的unsigned int数组构成。一个相似的数据结构是uint160,该结构的定义可在同一个文件当中找到。既然SHA-256的长度为256bit,读者不难推断出uint160的作用是存放RIPEMD-160哈希。uint256和uint160均由base_uint类继承而来。
class base_uint {
protected:
enum { WIDTH = BITS / 32 };
unsigned int pn[WIDTH];
bool operator!() const
for (int i = 0; i & WIDTH; i++)
if (pn[i] != 0)
unsigned int GetSerializeSize(int nType = 0, int nVersion = VERSION) const
return sizeof(pn);
template &typename Stream&
void Serialize(Stream& s, int nType = 0, int nVersion = VERSION) const
s.write((char*)pn, sizeof(pn));
template &typename Stream&
void Unserialize(Stream& s, int nType = 0, int nVersion = VERSION)
s.read((char*)pn, sizeof(pn));
该类重载了若干运算符。此外该类拥有3个序列化成员函数, GetSerializeSize() 、 Serialize() 和 Unserialize() 。我们会在后面讲到这三种方法是如何工作的。
SendMoney()
该方法位于main.cpp。以下是该方法的源码:
bool SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew)
CRITICAL_BLOCK(cs_main)
int64 nFeeR
if (!CreateTransaction(scriptPubKey, nValue, wtxNew, nFeeRequired))
string strE
if (nValue + nFeeRequired & GetBalance())
strError = strprintf(&Error: This is an oversized transaction that requires a transaction fee of %s &, FormatMoney(nFeeRequired).c_str());
strError = &Error: Transaction creation failed &;
wxMessageBox(strError, &Sending...&);
return error(&SendMoney() : %s\n&, strError.c_str());
if (!CommitTransactionSpent(wtxNew))
wxMessageBox(&Error finalizing transaction&, &Sending...&);
return error(&SendMoney() : Error finalizing transaction&);
printf(&SendMoney: %s\n&, wtxNew.GetHash().ToString().substr(0,6).c_str());
// Broadcast
if (!wtxNew.AcceptTransaction())
// This must not fail. The transaction has already been signed and recorded.
throw runtime_error(&SendMoney() : wtxNew.AcceptTransaction() failed\n&);
wxMessageBox(&Error: Transaction not valid&, &Sending...&);
return error(&SendMoney() : Error: Transaction not valid&);
wtxNew.RelayWalletTransaction();
MainFrameRepaint();
当用户发送比特币到某一个地址时,比特币客户端会调用 SendMoney() 方法。该方法包含三个参数:
scriptPubKey包含脚本代码OP_DUP OP_HASH160 &收款人地址160位哈希& OP_EQUALVERIFY OP_CHECKSIG。
nValue表示将要转账的金额。该金额并未包含交易费nTrasactionFee。
wtxNew是一个CWalletTx类的本地变量。该变量目前的值为空,之后会包含若干CMerkleTX类对象。该类由CTransaction衍生而来,并且添加了若干方法。我们暂时先不管具体细节,仅将其看作CTransaction类。
该方法的流程显而易见:
首先建立一笔新的交易(CreateTransaction(scriptPubKey, nValue, wtxNet, nFeeRequired),第6行)。
尝试将这笔交易提交至数据库(CommitTransactionSpent(wtxNet),第16行)。
如果该笔交易提交成功(wtxNew.AcceptTransaction(),第23行),将其广播至其他peer节点(wtxNew.RelayWalletTransaction(),第30行)。
这四个方法都与wtxNew相关。我们在本章介绍了第一个,其余三个将会在后续文章中介绍。
CreateTransaction()
该方法位于main.cpp。以下是该方法的源码:
bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, int64& nFeeRequiredRet)
nFeeRequiredRet = 0;
CRITICAL_BLOCK(cs_main)
// txdb must be opened before the mapWallet lock
CTxDB txdb(&r&);
CRITICAL_BLOCK(cs_mapWallet)
int64 nFee = nTransactionF
wtxNew.vin.clear();
wtxNew.vout.clear();
if (nValue & 0)
int64 nValueOut = nV
nValue += nF
// Choose coins to use
set&CWalletTx*& setC
if (!SelectCoins(nValue, setCoins))
int64 nValueIn = 0;
foreach(CWalletTx* pcoin, setCoins)
nValueIn += pcoin-&GetCredit();
// Fill vout[0] to the payee
wtxNew.vout.push_back(CTxOut(nValueOut, scriptPubKey));
// Fill vout[1] back to self with any change
if (nValueIn & nValue)
// Use the same key as one of the coins
vector&unsigned char& vchPubK
CTransaction& txFirst = *(*setCoins.begin());
foreach(const CTxOut& txout, txFirst.vout)
if (txout.IsMine())
if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
if (vchPubKey.empty())
// Fill vout[1] to ourself
CScript scriptPubK
scriptPubKey && vchPubKey && OP_CHECKSIG;
wtxNew.vout.push_back(CTxOut(nValueIn - nValue, scriptPubKey));
// Fill vin
foreach(CWalletTx* pcoin, setCoins)
for (int nOut = 0; nOut & pcoin-&vout.size(); nOut++)
if (pcoin-&vout[nOut].IsMine())
wtxNew.vin.push_back(CTxIn(pcoin-&GetHash(), nOut));
int nIn = 0;
foreach(CWalletTx* pcoin, setCoins)
for (int nOut = 0; nOut & pcoin-&vout.size(); nOut++)
if (pcoin-&vout[nOut].IsMine())
SignSignature(*pcoin, wtxNew, nIn++);
// Check that enough fee is included
if (nFee & wtxNew.GetMinFee(true))
nFee = nFeeRequiredRet = wtxNew.GetMinFee(true);
// Fill vtxPrev by copying from previous transactions vtxPrev
wtxNew.AddSupportingTransactions(txdb);
wtxNew.fTimeReceivedIsTxTime =
调用该方法时,它所需要的四个参数如下:
scriptPubKey包含脚本代码OP_DUP OP_HASH160 &收款人地址160位哈希& OP_EQUALVERIFY OP_CHECKSIG。
nValue是将要转账的数额,交易费nTransactionFee并未包括在内。
wtxNew是一个新的Tx实例。
nFeeRequiredRet是一笔用来支付交易费的输出交易,在该方法执行完成之后获得。
该方法的流程如下:
定义一个本地变量nValueOut = nValue来保存将转账的金额(第17行)。将nValue与交易费nFee相加得到新的包含转账费的nValue。
执行位于第21行的SelectCoins(nValue, setCoins)得到一系列币,并放入setCoins。setCoins包含支付给你本人地址的交易,即你所拥有的币。这些交易将成为wtxNew的来源交易。
执行位于第27行的wtxNew.vout.push_back(CTxOut (nValueOut,sciptPubKey))并添加一笔输出交易至wtxNew。该笔输出将支付给&收款人地址160位哈希&(包含在scriptPubKey里面)数量为的币。
如果需要找零(nValueIn & nValue),添加另一笔输出交易至wtxNew并将零钱发回本人。该过程包含以下步骤:
从setCoin当中获取第一笔交易txFirst,依次检查txFirst.vout中的交易是否属于本人。如果是则从该笔输出交易当中提取出公钥,并放入本地变量vchPubKey
将vchPubKey放入脚本vchPubKey OP_CHECKSIG,并使用这段脚本代码为wtxNew添加一个支付给本人的输出交易(第45行)。
因为setCoins包含支付给本人的交易,所以每笔交易一定包括至少一笔支付给本人的交易。从第一笔交易txFirst中即可找到。
至此,wtxNew的输出交易容器vout已准备就绪。现在,该设置输入交易容器vin。记住每一个输入交易列表vin均引用一笔来源交易,而且wtxNew的每笔来源交易均可在setCoins中被找到。对于每一笔setCoins中的交易pcoin,逐个遍历其输出交易pcoin-&vout[nOut]。如果第nOut笔输出支付给本人(意味着wtxNew从该笔输出交易中获得币),则向wtxNew添加一笔新的输入交易(wtxNew.vin(wtxNew.vin.push_back(CTxIn(pcoin-&GetHash(), nOut)),第51行)。该输入交易指向pcoin中的第nOut笔输出交易,由此将wtxNew.vin与pcoin的第nOut笔输出相连接。
对于setCoins当中的每笔交易pcoin,逐个遍历其所有输出交易pcoin-&vout[nOut]。如果该笔交易属于本人,调用SignSignature(*pcoin,wtxNew, nIn++)为第nIn笔输入交易添加签名。注意nIn为wtxNew的输入交易位置。
如果交易费nFee小于wtxNet.GetMinFee(true),将nFee设为后者,清空wtxNew中的所有数据并重新开始整个过程。在位于第11行的第一次迭代当中,nFee是全局变量nTransactionFee = 0的本地复制。
如果你不明白为什么要如此费力地重新添满wtxNew,源码中的GetMinFee()提供了答案:交易的最低费用与交易的数据大小有关。wtxNew的大小只有在完整构建之后才可得知。如果wtxNew.GetMinFee(true)计算得到的最小交易费用大于之前创造wtxNew时假设的交易费nFee,则除了重新构建wtxNew之外别无他法。
这里遇到了一个先有鸡还是先有蛋的局面:若想创建一笔新的交易,则必须知道交易费用是多少。而交易费只有在整个交易被创建以后才可得知。为了打破这个循环,本地变量nFee被用来放置预计的交易费用,并且新的交易构建在此基础上。在构建完成之后,得到真实的交易费并与预估的交易费作比较。如果预估的交易费小于真实的交易费,则替换成真实交易费并重新构造整个交易。
这里是GetMinFee()的源码:
int64 GetMinFee(bool fDiscount=false) const
unsigned int nBytes = ::GetSerializeSize(*this, SER_NETWORK);
if (fDiscount && nBytes & 10000)
return (1 + (int64)nBytes / 1000) * CENT;
如果计算得到的交易费比之前预计的交易费更高,则跳出第11行开始的循环并返回整个函数(第67行)。在此之前,需要进行以下两个步骤:
执行wtxNew.AddSupportingTransactions(txdb)。这一部分以后会进行更详细介绍。
设置wtxNet.fTimeReceivedIsTxTime=true(第66行)。
现在来看一下如何通过SignSignature()签署新生成的交易wtxNew。
SignSignature()
该方法位于script.cpp。以下是该方法的源码:
bool SignSignature(const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType, CScript scriptPrereq)
assert(nIn & txTo.vin.size());
CTxIn& txin = txTo.vin[nIn];
assert(txin.prevout.n & txFrom.vout.size());
const CTxOut& txout = txFrom.vout[txin.prevout.n];
// Leave out the signature from the hash, since a signature can't sign itself.
// The checksig op will also drop the signatures from its hash.
uint256 hash = SignatureHash(scriptPrereq + txout.scriptPubKey, txTo, nIn, nHashType);
if (!Solver(txout.scriptPubKey, hash, nHashType, txin.scriptSig))
txin.scriptSig = scriptPrereq + txin.scriptS
// Test solution
if (scriptPrereq.empty())
if (!EvalScript(txin.scriptSig + CScript(OP_CODESEPARATOR) + txout.scriptPubKey, txTo, nIn))
首先需要注意的是,该函数有5个参数,而CreateTransaction()只有3个。这是因为在script.h文件里,后两个参数已默认给出。
以下是传递给CreateTransaction()中的3个参数:
txFrom是一个*pcoin对象。它是CreateTransaction()里setCoins中的所有币中的某一个。它同时也是一笔来源交易。它的若干输出交易当中包含了新交易将要花费的币。
txTo是CreateTransaction()里的wtxNew对象。它是将要花费来源交易txFrom的新交易。新交易需要被签署方可生效。
nIn是指向txTo中输入交易列表的索引位置。该输入交易列表包含一个对txFrom的输出交易列表的引用。更准确地讲,txin=txTo.vin[nIn](第4行)是txTo中的输入交易;txout=txFrom.vout[txin.prev.out.n](第6行)是txin所指向的txFrom中的输出交易。
以下是SignSignature()所做的工作:
调用SignatureHash()方法生成txTo的哈希值。
调用Solver()函数签署刚才生成的哈希。
调用EvalScript()来运行一小段脚本并检查签名是否合法。
我们一起看一下这三个函数。
SignatureHash()
该方法位于script.cpp。以下是SignatureHash()的源码。
uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType)
if (nIn &= txTo.vin.size())
printf(&ERROR: SignatureHash() : nIn=%d out of range\n&, nIn);
CTransaction txTmp(txTo);
// In case concatenating two scripts ends up with two codeseparators,
// or an extra one at the end, this prevents all those possible incompatibilities.
scriptCode.FindAndDelete(CScript(OP_CODESEPARATOR));
// Blank out other inputs' signatures
for (int i = 0; i & txTmp.vin.size(); i++)
txTmp.vin[i].scriptSig = CScript();
txTmp.vin[nIn].scriptSig = scriptC
// Blank out some of the outputs
if ((nHashType & 0x1f) == SIGHASH_NONE)
// Wildcard payee
txTmp.vout.clear();
// Let the others update at will
for (int i = 0; i & txTmp.vin.size(); i++)
if (i != nIn)
txTmp.vin[i].nSequence = 0;
else if ((nHashType & 0x1f) == SIGHASH_SINGLE)
// Only lockin the txout payee at same index as txin
unsigned int nOut = nIn;
if (nOut &= txTmp.vout.size())
printf(&ERROR: SignatureHash() : nOut=%d out of range\n&, nOut);
txTmp.vout.resize(nOut+1);
for (int i = 0; i & nO i++)
txTmp.vout[i].SetNull();
// Let the others update at will
for (int i = 0; i & txTmp.vin.size(); i++)
if (i != nIn)
txTmp.vin[i].nSequence = 0;
// Blank out other inputs completely, not recommended for open transactions
if (nHashType & SIGHASH_ANYONECANPAY)
txTmp.vin[0] = txTmp.vin[nIn];
txTmp.vin.resize(1);
// Serialize and hash
CDataStream ss(SER_GETHASH);
ss.reserve(10000);
ss && txTmp && nHashT
return Hash(ss.begin(), ss.end());
以下是该函数所需要的参数:
txTo是将要被签署的交易。它同时也是CreateTransaction()中的wtxNew对象。它的输入交易列表中的第nIn项,txTo.vin[nIn],是该函数将要起作用的目标。
scriptCode是scriptPrereq + txout.scriptPubKey,其中txout是SignSignature()中定义的来源交易txFrom()的输出交易。由于此时scriptPrereq为空,scriptCode事实上是来源交易txFrom中的输出交易列表当中被txTo作为输入交易引用的那笔的脚本代码。txout.scriptPubKey有可能包含两类脚本:
脚本A:OP_DUP OP_HASH160 &你地址的160位哈希& OP_EQUALVERIFY OP_CECKSIG。该脚本将来源交易txFrom中的币发送给你,其中&你地址的160位哈希&是你的比特币地址。
脚本B:&你的公钥& OP_CHECKSIG。该脚本将剩余的币退还至来源交易txFrom的发起人。由于你创建的新交易txTo/wtxNew将会花费来自txFrom的币,你必须同时也是txFrom的创建者。换句话讲,当你在创建txFrom的时候,你其实是在花费之前别人发送给你的币。因此,&你的公钥&即是txFrom创建者的公钥,也是你自己的公钥。
我们在此停留片刻,来思考一下脚本A和脚本B。你有可能会问,这些脚本是从哪来的。中本聪在创造比特币的时候为比特币添加了一套脚本语言系统,所以比特币中的交易都是由脚本代码完成的。该脚本系统其实也是后来智能合约的雏形。脚本A来自第29行,位于方法CSendDialog::OnButtonSend(),脚本B则来自第44行,位于方法CreateTransaction()。
当用户发起一笔交易时,比特币客户端会调用CSendDialog::OnButtonSend()方法并将脚本A添加至txFrom中的一笔输出交易中。由于该输出交易的收款方为你本人,从而脚本中的&收款人地址160位哈希&,就是&你的地址160位哈希&。
如果txFrom是你本人创建的,则脚本B会被添加至CreateTransaction()中txFrom的某一笔输出交易。在这里,第44行位于CreateTransaction()中的公钥vchPubKey是你本人的公钥。
在了解了输入交易之后,我们来一起了解SignatureHash()是怎样工作的。
SignatureHash()首先将txTO拷贝至txTmp,接着清空txTmp.vin中每一笔输入交易的scriptSig,除了txTmp.vin[nIn]之外,该输入交易的scriptSig被设为scriptCode(第14、15行)。
接着,该函数检验nHashType的值。该函数的调用者将一个枚举值传递至该函数nHashType = SIGHASH_ALL。
SIGHASH_ALL = 1,
SIGHASH_NONE = 2,
SIGHASH_SINGLE = 3,
SIGHASH_ANYONECANPAY = 0x80,
由于nHashType = SIGHASH_ALL,所有的if-else条件均不成立,该函数将直接执行最后4行代码。
在最后4行代码中,txTmp和nHashType变成序列化后的类型CDataStream对象。该类型包括一个装有数据的字符容器类型。所返回的哈希值是Hash()方法在计算序列化后的数据所得到的。
一笔交易可以包含多笔输入交易。SignatureHash()取其中一笔作为目标。它通过以下步骤生成哈希:
清空除了目标交易之外的所有输入交易。
复制来源交易中被目标交易作为输入交易引用的那笔输出交易的脚本至目标交易的输入交易列表中。
为修改后的交易生成哈希值。
该方法位于util.h。以下是生成哈希值的方法Hash()的源码:
template&typename T1&
inline uint256 Hash(const T1 pbegin, const T1 pend)
uint256 hash1;
SHA256((unsigned char*)&pbegin[0], (pend - pbegin) * sizeof(pbegin[0]), (unsigned char*)&hash1);
uint256 hash2;
SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2);
return hash2;
该函数对目标数据执行两次SHA256()方法并返回结果。SHA256()的声明可在openssl/sha.h中找到。
该方法位于script.cpp。Solver()在SignSignature()中紧接着SignatureHash()被执行。它是真正用来为SignatureHash()返回的哈希值生成签名的函数。
bool Solver(const CScript& scriptPubKey, uint256 hash, int nHashType, CScript& scriptSigRet)
scriptSigRet.clear();
vector&pair&opcodetype, valtype& & vS
if (!Solver(scriptPubKey, vSolution))
// Compile solution
CRITICAL_BLOCK(cs_mapKeys)
foreach(PAIRTYPE(opcodetype, valtype)& item, vSolution)
if (item.first == OP_PUBKEY)
const valtype& vchPubKey = item.
if (!mapKeys.count(vchPubKey))
if (hash != 0)
vector&unsigned char& vchS
if (!CKey::Sign(mapKeys[vchPubKey], hash, vchSig))
vchSig.push_back((unsigned char)nHashType);
scriptSigRet && vchS
else if (item.first == OP_PUBKEYHASH)
// Sign and give pubkey
map&uint160, valtype&::iterator mi = mapPubKeys.find(uint160(item.second));
if (mi == mapPubKeys.end())
const vector&unsigned char&& vchPubKey = (*mi).
if (!mapKeys.count(vchPubKey))
if (hash != 0)
vector&unsigned char& vchS
if (!CKey::Sign(mapKeys[vchPubKey], hash, vchSig))
vchSig.push_back((unsigned char)nHashType);
scriptSigRet && vchSig && vchPubK
以下是该方法所需要的4个参数:
位于第10行的调用函数SignSignature()将txOut.scriptPubKey,来源交易txFrom的输出脚本,作为输入值传入第一个参数scriptPubKey。记住它可能包含脚本A或者脚本B。
第二个参数hash是由SignatureHash()生成的哈希值。
第三个参数nHashType的值为SIGHASH_ALL。
第四个参数是该函数的返回值,即调用函数SignSIgnature()中位于第12行的txin.scriptSig。记住txin是新生成的交易wtxNew(在调用函数SignSignature()中作为txTo引用)位于第nIn的输入交易。因此,wtxNew第nIn笔输入交易的scriptSig将存放该函数返回的签名。
该函数首先会调用另一个有2个参数的Solver()。我们来研究一下。
带有2个参数的Solver()
该方法位于script.cpp。以下是带有2个参数的Solver()的源码:
bool Solver(const CScript& scriptPubKey, vector&pair&opcodetype, valtype& && vSolutionRet)
// Templates
static vector&CScript& vT
if (vTemplates.empty())
// Standard tx, sender provides pubkey, receiver adds signature
vTemplates.push_back(CScript() && OP_PUBKEY && OP_CHECKSIG);
// Short account number tx, sender provides hash of pubkey, receiver provides signature and pubkey
vTemplates.push_back(CScript() && OP_DUP && OP_HASH160 && OP_PUBKEYHASH && OP_EQUALVERIFY && OP_CHECKSIG);
// Scan templates
const CScript& script1 = scriptPubK
foreach(const CScript& script2, vTemplates)
vSolutionRet.clear();
opcodetype opcode1, opcode2;
vector&unsigned char& vch1, vch2;
// Compare
CScript::const_iterator pc1 = script1.begin();
CScript::const_iterator pc2 = script2.begin();
bool f1 = script1.GetOp(pc1, opcode1, vch1);
bool f2 = script2.GetOp(pc2, opcode2, vch2);
if (!f1 && !f2)
// Success
reverse(vSolutionRet.begin(), vSolutionRet.end());
else if (f1 != f2)
else if (opcode2 == OP_PUBKEY)
if (vch1.size() &= sizeof(uint256))
vSolutionRet.push_back(make_pair(opcode2, vch1));
else if (opcode2 == OP_PUBKEYHASH)
if (vch1.size() != sizeof(uint160))
vSolutionRet.push_back(make_pair(opcode2, vch1));
else if (opcode1 != opcode2)
vSolutionRet.clear();
第一个参数scriptPubKey可能包含脚本A也可能是脚本B。再一次说明,它是SignSignature()中来源交易txFrom的输出脚本。
第二个参数用来存放输出交易。它是一个容器对,每个对由一个脚本运算符(opcodetype类型)和脚本操作元(valtype类型)构成。
该函数第8-10行首先定义两个模板:
模板A:OP_DUP OP_HASH160 OP_PUBKEYHASH OP_EQUALVERIFY OP_CHECKSIG。
模板B:OP_PUBKEY OP_CHECKSIG。
很明显,模板A、模板B与脚本A、脚本B相对应。为了便于对比,以下是脚本A和B的内容:
脚本A:OP_DUP OP_HASH160 &你的地址160位哈希& OP_EQUALVERIFY OP_CHECKSIG。
脚本B:&你的公钥& OP_CHECKSIG。
该函数的作用是将scriptPubKey与两个模板相比较:
如果输入脚本为脚本A,则将模板A中的OP_PUBKEYHASH与脚本A中的&你的地址160位哈希&配对,并将该对放入vSolutionRet。
如果输入脚本为脚本B,则从模板B中提取运算符OP_PUBKEY,和从脚本B中提取运算元&你的公钥&,将二者配对并放入vSolutionRet。
如果输入脚本与两个模板均不匹配,则返回false。
回到Solver()
我们回到有4个参数的Solver()并继续对该函数的分析。现在我们清楚了该函数的工作原理。它会在两个分支中选择一个执行,取决于从vSolutionRet得到的对来自脚本A还是脚本B。如果来自脚本A,item.first& == OP_PUBKEYHASH;如果来自脚本B,item.first& == OP_PUBKEY。
item.first& == OP_PUBKEY(脚本B)。在该情形下,item.second包含&你的公钥&。全局变量mapKeys将你的全部公钥映射至与之对应的私钥。如果mapKeys当中没有该公钥,则报错(第16行)。否则,用从mapKeys中提取出的私钥签署新生成的交易wtxNew的哈希值,其中哈希值作为第2个被传入的参数(CKey::Sign(mapKeys[vchPubKey], hash, vchSig),第23行),再将结果放入vchSig,接着将其序列化成scriptSigRet(scriptSigRet && vchSig,第24行)并返回。
item.first& == OP_PUBKEYHASH(脚本A)。在该情形下,item.second包含&你的地址160位哈希&。该比特币地址将被用于从位于第23行的全局映射mapPubKeys中找到其所对应的公钥。全局映射mapPubKeys将你的地址与生成它们的公钥建立一一对应关系(查看函数AddKey())。接着,通过该公钥从mapKeys中找到所对应的私钥,并用该私钥签署第二个参数hash。签名和公钥将一同被序列化至scriptSigRet并返回(scriptSig && vchSig && vchPubkey,第24行)。
EvalScript()
该方法位于script.cpp。现在我们回到SignSignature()。在该函数的第12行之后,txin.scriptsig,即wtxNew的第nIn笔输入交易中的scriptSig部分,将插入一个签名。该签名可能是以下其中之一:
vchSig vchPubKey(脚本A的签名A)
vchSig(脚本B的签名B)
在下文当中,vchSig将被引用为&你的签名_vchSig&,vchPubKey则为&你的公钥_vchPubKey&,以强调它们分别是你本人的签名和公钥。
我们现在开始调查EvalScript(),该函数是SignSignature()调用的最后一个函数,位于第15行。EvalScript()带有3个参数,分别为:
第一个参数为txin.scriptSig + CScript(OP_CODESEPARATOR) + txout.scriptPubKey。它有可能是:
验证情形A:&你的签名_vchSig& &你的公钥_vchPubKey& OP_CODESEPARATOR OP_DUP OP_HASH160 &你的地址160位哈希& OP_EQUALVERIFY OP_CHECKSIG,即签名A + OP_CODESEPARATOR + 脚本A。
验证情形B:&你的签名_vchSig& OP_CODESEPARATOR &你的公钥_vchPubKey& OP_CHECKSIG,即签名B + OP_CODESEPARATOR + 脚本B。
第二个参数为新创建的交易txTo,即CreateTransaction()中的wtxNew。
第三个参数为nIn,即将被验证的交易在txTo输入交易列表中的位置。
验证过程我们会在后面详细讲述。简单地说,EvalScript()验证新创建交易wtxNew的第nIn笔输入交易是否包含有效的签名。至此,一笔新的比特币交易便创建完成。
发文时比特币标准价格 买价: ¥7040.00 卖价: ¥6800.00
作者:g2com
版权声明:
作者保留权利。文章为作者独立观点,不代表巴比特立场。
已发表评论数()
请填写推刊名
描述不能大于100个字符!
权限设置: 公开
仅自己可见
正文不准确
标题不准确
排版有问题
主题不准确
没有分页内容
图片无法显示
视频无法显示
与原文不一致

我要回帖

更多关于 比特币etf获批时间 的文章

 

随机推荐