生成地址
如果有人想發送比特幣給你,或者你從別人那里買幾個比特幣,就要把地址給對方,對方才能把幣打到你指定的地址上。那么,如何才能擁有一個地址呢,下面我們就來講講這個問題。
比特幣核心提供了很多RPC來供客戶端調用,其中一個就是我們這里要講的
getnewaddress
生成一個新的地址,通過這個RPC,我們就可以生成一個新的地址,有了這個地址,別人就可以給我們轉賬了。
getnewaddress
RPC可以接收兩個參數,第一個地址的標簽,第二個是地址的類型。如果沒有提供標簽,那么默認的標簽就是空,地址的類型當前支持:legacy、p2sh-segwit、bech32,默認類型由
-addresstype
參數指定,當前為p2sh-segwit。
如果我們想看下這個RPC的幫助文檔,可以執行如下的命令:
./src/bitcoin-cli-regtesthelpgetnewaddress
就會顯示幫助信息
這個 RPC對應的方法實現位于
src/wallet/rpcwallet.cpp
文件,方法名稱就是RPC名稱,下面我們來看這個方法。
生成地址流程
根據請求參數獲得對應的錢包。std::shared_ptr<CWallet>constwallet=GetWalletForJSONRPCRequest(request);CWallet*constpwallet=wallet.get();GetWalletForJSONRPCRequest方法內部實現如下:調用GetWalletNameFromJSONRPCRequest方法,從請求對象中取得錢包的名字,如果用戶指定了錢包名字,那么把錢包名字保存在參數wallet_name上,并返回真,否則返回假。如果可以獲得用戶指定的錢包名稱,則調用GetWallet方法,從錢包集合vpwallets中取得指定的錢包,然后返回錢包。如果用戶沒有指定錢包或指定的錢包不存在,那么調用GetWallets方法,返回錢包集合vpwallets。如果錢包集合中只有一個錢包,或者在用戶指定了幫助的情況下,至少有一個以上的錢包,那么返回第一個錢包,即默認的錢包。默認錢包在系統啟動時候創建的。接下來,要確保錢包可用。如果錢包不可用,則直接NullUniValue對象。if(!EnsureWalletIsAvailable(pwallet,request.fHelp)){returnNullUniValue;}如果指定了help參數或請求參數數量多于2個,則顯示錢包的幫助信息。檢查錢包是否設置了禁止私鑰,即錢包是只讀的watch-only/pubkeys。如果是,則拋出異常。if(pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)){throwJSONRPCError(RPC_WALLET_ERROR,"Error:Privatekeysaredisabledforthiswallet");}如果指定了標簽,則調用LabelFromValue方法,檢查標簽,確保其不是*。如果是,則拋出異常。std::stringlabel;if(!request.params.isNull())label=LabelFromValue(request.params);如果指定了地址類型,則調用ParseOutputType方法,檢查地址類型,確保其是legacy、p2sh-segwit、bech32之一,如果不指定則默認是p2sh-segwit,并把地址類型保存在output_type變量中。OutputTypeoutput_type=pwallet->m_default_address_type;if(!request.params.isNull()){if(!ParseOutputType(request.params.get_str(),output_type)){throwJSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,strprintf("Unknownaddresstype'%s'",request.params.get_str()));}}如果錢包沒有被鎖定,則調用TopUpKeyPool方法填充密鑰池。if(!pwallet->IsLocked()){pwallet->TopUpKeyPool();}TopUpKeyPool填充密鑰這個方法,我們前面已經講過,這里只簡單解釋下,不做詳細分析。因為在衍生子鑰的過程中,setExternalKeyPool、setInternalKeyPool已經完全填充完了,所以導致missingExternal、missingInternal兩個變量都為0,從而不會重新再次衍生子密鑰,所以實際上本方法在這里基本沒有執行,而直接返回真。調用錢包的GetKeyFromPool方法,從密鑰池中生成一個公鑰。如果不能生成,則拋出異常。CPubKeynewKey;if(!pwallet->GetKeyFromPool(newKey)){throwJSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT,"Error:Keypoolranout,pleasecallkeypoolrefillfirst");}GetKeyFromPool方法,我們在下面詳細講解,此處略過。調用錢包對象的LearnRelatedScripts方法,對公鑰的腳本進行處理。方法內部執行如下:如果公鑰是壓縮的,并且地址類型是p2sh-segwit,或者bech32,那么:如果目標參數類型是CNoDestination,則調用腳本對象的script方法,清除腳本內容。如果目標參數類型是CKeyID,則:首先調用腳本對象的script方法,清除腳本內容;然后,初始化腳本*script<<OP_DUP<<OP_HASH160<<ToByteVector(keyID)<<OP_EQUALVERIFY<<OP_CHECKSIG。如果目標參數類型是CScriptID,則:首先調用腳本對象的script方法,清除腳本內容;然后,初始化腳本*script<<OP_HASH160<<ToByteVector(scriptID)<<OP_EQUAL。如果目標參數類型是WitnessV0KeyHash,則:首先調用腳本對象的script方法,清除腳本內容;然后,初始化腳本*script<<OP_0<<ToByteVector(id)。如果目標參數類型是WitnessV0ScriptHash,則:首先調用腳本對象的script方法,清除腳本內容;然后,初始化腳本*script<<OP_0<<ToByteVector(id)。如果目標參數類型是WitnessUnknown,則:首先調用腳本對象的script方法,清除腳本內容;然后,初始化腳本*script<<CScript::EncodeOP_N(id.version)<<std::vector(id.program,id.program+id.length)。調用WitnessV0KeyHash方法,生成WitnessV0KeyHash對象。CTxDestinationwitdest=WitnessV0KeyHash(key.GetID());調用GetScriptForDestination方法,獲取對應的腳本。CScriptwitprog=GetScriptForDestination(witdest);GetScriptForDestination方法內部調用boost::apply_visitor(CScriptVisitor(&script),dest),以訪問者模式來根據不同的id,獲取其對應的腳本對象。CScriptVisitor對象繼承自boost::static_visitor對象,實現了訪問者模式,并通過重載()操作符來定義不同類型的id。調用AddCScript方法,保存腳本對象。AddCScript方法,首先調用CCryptoKeyStore::AddCScript方法,把腳本保存到keystore的mapScripts集合中;然后,調用數據庫訪問對象的WriteCScript方法,以cscript為鍵把腳本保存到數據庫中。boolCWallet::AddCScript(constCScript&redeemScript){if(!CCryptoKeyStore::AddCScript(redeemScript))returnfalse;returnWalletBatch(*database).WriteCScript(Hash160(redeemScript),redeemScript);}調用GetDestinationForKey方法,獲取目的地CTxDestination對象。CTxDestination是一個具有特定目標的交易輸出腳本模板。定義如下:typedefboost::variant<CNoDestination,CKeyID,CScriptID,WitnessV0ScriptHash,WitnessV0KeyHash,WitnessUnknown>CTxDestination,可能是以下幾種類型之一:GetDestinationForKey方法,使用case表達式來根據不同的地址類型,生成不同的目的CTxDestination。如果公鑰不是壓縮的,處理方法legacy。if(!key.IsCompressed())returnkey.GetID();否則,生成WitnessV0KeyHash對象,然后調用GetScriptForDestination方法,獲取對應的腳本,最后根據不同的地址類型生成的目的。CTxDestinationwitdest=WitnessV0KeyHash(key.GetID());CScriptwitprog=GetScriptForDestination(witdest);if(type==OutputType::P2SH_SEGWIT){returnCScriptID(witprog);}else{returnwitdest;}對于默認的、不傳地址類型的情況,就會返回CScriptID類型的CTxDestination,這個返回值在下面兩步中都會用到。如果地址類型是legacy,則直接返回公鑰的KeyID。內部把公鑰的數據通過SHA256和RIPEMD160雙重哈希之后,構造一個CKeyID對象。如果地址類型是p2sh-segwit,或bech32,則處理如下:CNoDestination沒有目的地設置CKeyIDP2PKH目的CScriptIDP2SH目的WitnessV0ScriptHashP2WSH目的WitnessV0KeyHashP2WPKH目的WitnessUnknown未知目的P2W???調用錢包對象的SetAddressBook方法,來保存公鑰地址。pwallet->SetAddressBook(dest,label,"receive");SetAddressBook方法執行如下:從mapAddressBook集合中,取得對應的目的數據。std::map<CTxDestination,CAddressBookData>::iteratormi=mapAddressBook.find(address);根據集合中是否有對應的數據設置變量是否為更新。fUpdated=mi!=mapAddressBook.end();把標簽保存為地址對應的CAddressBookData的name屬性。mapAddressBook.name=strName;如果參數strPurpose不空,則更新地址對應的CAddressBookData的purpose屬性。if(!strPurpose.empty())mapAddressBook.purpose=strPurpose;調用數據庫訪問對象的WritePurpose方法,保存參數strPurpose到數據庫中。if(!strPurpose.empty()&&!WalletBatch(*database).WritePurpose(EncodeDestination(address),strPurpose))returnfalse;調用數據庫訪問對象的WritePurpose方法,保存地址到數據庫中。WalletBatch(*database).WriteName(EncodeDestination(address),strName);strName為用戶提供的標簽。EncodeDestination方法,我們在下一步講解。調用EncodeDestination方法,解碼目的地址,并返回其結果。EncodeDestination方法同樣采用了訪問者模式returnboost::apply_visitor(DestinationEncoder(Params()),dest)。DestinationEncoder類繼承了boost::static_visitor,實現了訪問者模式,通過重載()操作符來定義不同類型的id。與前面相對應,這個方法會處理CKeyID、CScriptID、WitnessV0KeyHash、WitnessV0ScriptHash、WitnessUnknown這幾種不同情況。對于我們的默認情況來說,目的地址類型為CScriptID,下面我們就看下這種情況的處理,其他情況可自行閱讀。調用當前網絡參數的Base58Prefix方法,返回腳本前綴。std::vector<unsignedchar>data=m_params.Base58Prefix(CChainParams::SCRIPT_ADDRESS);對于主網絡前綴是5,測試網絡是196,回歸測試網絡是196。把當前20個字節的數據加在前綴后面形成21個字節的字符串。data.insert(data.end(),id.begin(),id.end());調用EncodeBase58Check方法,編碼成Base58Check格式,并返回其值。returnEncodeBase58Check(data);下面,我們來看下EncodeBase58Check這個方法的處理。它的內部執行流程如下:用21個字節的字符串生成一個向量,同時調用Hash方法,生成一個32字節長的哈希字符串;然后把其最前面的4個字節作為校驗各加在21個字節的向量尾部,從而生成一個長度為25個字節的字符串;最后,調用EncodeBase58方法,進行Base58編碼。std::vector<unsignedchar>vch(vchIn);uint256hash=Hash(vch.begin(),vch.end());vch.insert(vch.end(),(unsignedchar*)&hash,(unsignedchar*)&hash+4);returnEncodeBase58(vch);在Hash這個方法中,使用了雙重SHA256哈希算法。EncodeBase58這個方法,讀者可以自行閱讀,這里不再展開。
民盟中央建議加速元宇宙科普和立法:3月4日消息,民盟中央已起草了《關于“元宇宙”技術發展的提案》,并將提交全國政協十三屆五次會議。在提案中,民盟中央建議,在科普層面需加速知識傳播,法律層面則需加快立法步伐。民盟中央擬提交的提案指出,目前,在新興網絡層面,相關政策法規相對缺失。“元宇宙”在未來將會帶動形成全新的網絡形態,當遇到突發輿情,全虛擬的環境、場景將更難進行源頭追蹤、問題疏導。因此建議應盡早加快立法研究,盡快形成與技術、市場發展相適應的治理模式和法律基礎,全面提升我國社會治理的水平。建議組織相關部門,針對“元宇宙”相關需求、風險進行立法研究,并盡快發布。此前消息,民進中央擬向全國政協十三屆五次會議提交《關于積極穩妥推進元宇宙技術和產業發展的提案》。建議推進元宇宙技術產業發展,建立相關監管治理體系。(華夏時報)[2022/3/4 13:37:12]
GetKeyFromPool從密鑰池中獲取公鑰
本方法從密鑰池中生成一個公鑰。第一個參數為公鑰的引用,第二個參數
internal
,默認為假。
內部邏輯如下:
如果錢包禁止私鑰,則返回假。if(IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)){returnfalse;}調用ReserveKeyFromKeyPool方法,從密鑰池中取出一個密鑰并獲取其公鑰。如果不成功,則生成數據庫訪問對象,然后調用GenerateNewKey方法,生成一個公鑰。if(!ReserveKeyFromKeyPool(nIndex,keypool,internal)){if(IsLocked())returnfalse;WalletBatchbatch(*database);result=GenerateNewKey(batch,internal);returntrue;}GenerateNewKey這個方法,在創建錢包過程中,我們已經重點分析,這里不浪費口舌,我們重點看下ReserveKeyFromKeyPool方法。這個方法的執行流程如下:生成一個公鑰,并設置為密鑰池的vchPubKey屬性。nIndex=-1;keypool.vchPubKey=CPubKey();如果錢包沒有被鎖,則填充密鑰池。if(!IsLocked())TopUpKeyPool();TopUpKeyPool這個方法,我們也講過,這里直接略過。如果錢包啟用了HD,并且可以支持HD分割,并且參數fRequestedInternal為真,則設置變量fReturningInternal為真。在調用本方法時,這個參數沒有指定,而默認為假,所以變量fRequestedInternal設置假。boolfReturningInternal=IsHDEnabled()&&CanSupportFeature(FEATURE_HD_SPLIT)&&fRequestedInternal;根據集合set_pre_split_keypool是否為空,設置變量use_split_keypool的值。因為這里use_split_keypool集合為空,所以變量use_split_keypool為真。booluse_split_keypool=set_pre_split_keypool.empty();根據變量use_split_keypool、fReturningInternal確定從哪個集合中獲取密鑰池對象。根據上面分析,我們最終會從setExternalKeyPool集合中取數據。std::set<int64_t>&setKeyPool=use_split_keypool?(fReturningInternal?setInternalKeyPool:setExternalKeyPool):set_pre_split_keypool;如果要數據的集合為為空,則返回假。if(setKeyPool.empty()){returnfalse;}生成數據庫訪問對象。WalletBatchbatch(*database);從setKeyPool取得其第一個元素,并從集合中刪除它。autoit=setKeyPool.begin();nIndex=*it;setKeyPool.erase(it);從數據庫取得索引對應的密鑰池。如果失敗,則拋出異常。if(!batch.ReadPool(nIndex,keypool)){throwstd::runtime_error(std::string(__func__)+":readfailed");}從密鑰池中取得公鑰對應的ID,并且檢測其是否在mapKeys、或mapCryptedKeys集合之一,如果不在,則拋出異常。我們在創建錢包過程時候講過,生成的私鑰根據是否加密會保存在這兩個集合之一。if(!HaveKey(keypool.vchPubKey.GetID())){throwstd::runtime_error(std::string(__func__)+":unknownkeyinkeypool");}如果變量use_split_keypool為真,并且密鑰池的fInternal屬性不等于變量fReturningInternal,那么拋出異常。if(use_split_keypool&&keypool.fInternal!=fReturningInternal){throwstd::runtime_error(std::string(__func__)+":keypoolentrymisclassified");}如果密鑰池中保存的公鑰是無效的,那么拋出異常。if(!keypool.vchPubKey.IsValid()){throwstd::runtime_error(std::string(__func__)+":keypoolentryinvalid");}從m_pool_key_to_index集合中消除對應的索引。m_pool_key_to_index.erase(keypool.vchPubKey.GetID());返回真。調用KeepKey從密鑰池中取出對應的公鑰。
火幣推出《一分鐘讀懂DeFi》系列科普視頻:據官方消息,8月24日,火幣推出《一分鐘讀懂DeFi》系列科普視頻,并與微博財經合作冠名播出,布道DeFi認知,助力行業發展《一分鐘讀懂DeFi》是由火幣成長學院打造的業內首個系統全面講解DeFi的系列科普動畫,繼推出《區塊鏈100問》后的再續佳作。《一分鐘讀懂DeFi》系列動畫對DeFi的發展進行系統梳理,適合想要由淺入深、全面系統了解區塊鏈DeFi的人們輕松了解DeFi。目前視頻已由火幣網官方微博發布。[2020/8/24]
作者:區小白
來源:巴比特
整理發出:贏和財經
以上內容采編自互聯網,如內容侵犯您的版權,請聯系郵箱:law@allwin.world,我們會在24小時內刪除相關內容。
動態 | 鏈客社區聯合北京交通廣播推出區塊鏈技術科普節目:12月11日15:15—16:00,區塊鏈技術社區——鏈客區塊鏈技術社區將聯合北京交通廣播FM103.9從零開始為大眾科普解碼區塊鏈技術,蜻蜓FM及北京廣播網同期進行全球直播。首期做客嘉賓為鏈客區塊鏈技術社區創始人郄建軍和百度區塊鏈產品負責人于雅楠。[2019/12/11]
動態 | 區塊鏈技術入選科普雜志《科學美國人》2019十大突破性技術榜單:據新浪網今日新聞報道,美國科普雜志《科學美國人》公布 2019 十大突破性技術榜單。區塊鏈技術因在保障食品安全中的作用而上榜。 入選榜單具體原因:區塊鏈技術的發展應用將顯著改善食品污染源數據追蹤的困境。利用區塊鏈云端系統,食品制造商可以依次在計算機儲存各類過程的信息。[2019/9/29]
中科院自動化研究所將面向大中小學生開展區塊鏈等主題的科普講座:5月21日,新華網訊,今年,中國科學院自動化研究所將舉辦第十四屆“自動化之光”公眾科學開放日活動。屆時,自動化所將面向大中小學生分別開展《腦與智能》、《區塊鏈技術與平行智能》、《大數據時代的視覺智能》、《動畫真奇妙》等4個主題報告,用實例和生動的演示深入淺出地為大家揭示智能技術的原理和奧妙。[2018/5/21]
8月21日報道 為努力應對惡性通貨膨脹以及其他經濟問題,委內瑞拉近期采取了極端措施來穩定其貨幣.
1900/1/1 0:00:00隨著社會的進步,一些傳統風俗習慣正在慢慢消失。就拿婚姻大事中的婚禮來說,也變得越來越簡單了。在城市中,現在的婚禮都是在酒店舉行,其過程完全交給了司儀,新郎新娘在司儀的安排下完成一些步驟即可.
1900/1/1 0:00:008.天地萬物 1976年,新中國的締造者們相繼去世。地震災害又使國家和人民雪上加霜。我15歲這一年,中國人口為9.3億。全年糧食產量2.86億噸。雖然沒有饑荒,但計劃供應無所不包.
1900/1/1 0:00:00一直以來,成都美視國際學校有校級的家長代表會,分年段的家長學校,還有各年級、各班級的家長課堂,形式多樣,內容豐富,每一步都踐行著“家校攜手、合力育人”.
1900/1/1 0:00:00多個知名投資人現身鏈圈;部分資本既助推項目方、又是平臺投資方,還投資了幣圈垂直媒體在經歷了整治、出海之后,虛擬貨幣交易所下一步是什么?近期市場消息傳出火幣或將借殼港股上市的消息,不過截至29日.
1900/1/1 0:00:00今天,猴哥給大家帶來一個全新專題的文章,在這個專題文章中猴哥會將全球歷史上發生的一些貨幣奇聞為大家分享。一方面讓大家了解那些曾經發生在世界各國的貨幣引發的奇妙之事,一方面給大家普及金融知識.
1900/1/1 0:00:00