比特幣交易所 比特幣交易所
Ctrl+D 比特幣交易所
ads
首頁 > FIL > Info

科普 | 從零開始學習比特幣開發:生成地址_RPC

Author:

Time:1900/1/1 0:00:00

生成地址

如果有人想發送比特幣給你,或者你從別人那里買幾個比特幣,就要把地址給對方,對方才能把幣打到你指定的地址上。那么,如何才能擁有一個地址呢,下面我們就來講講這個問題。

比特幣核心提供了很多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這個方法,讀者可以自行閱讀,這里不再展開。

動態 | 報告:區塊鏈等熱點詞促使童書科普百科類成交額同比增速最高:近日,京東圖書與艾瑞咨詢聯合發布了《2019中國圖書市場報告》。報告指出,AI、5G、區塊鏈、機器人、VR、智能家居、AR這些熱點詞,不斷點燃科技熱潮,科技在改變大眾生活的同時,也吸引了越來越多家長的關注,從小培養孩子對科技的興趣和熱愛。因此童書中科普百科類成交額同比增速最高,占比將近40%。[2020/1/8]

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從密鑰池中取出對應的公鑰。

聲音 | 上海股交所總經理:區塊鏈想要大規模發展要做好社會科普工作:金色財經報道,上海股交所總經理張云峰表示,區塊鏈當前還處于一個“概念”的階段,距離成熟應用,影響到百姓的日常生活還有很長的路要走。對于“區塊鏈”和其會帶來的社會和經濟效果,沈陽應當持審慎的態度。區塊鏈想要大規模發展,一方面要做好這項復雜技術的社會科普工作,加快社會大眾對區塊鏈的了解。另一方面,要充分發揮市場的作用,讓企業用實實在在的技術創新,賦能實體經濟的發展。[2019/11/17]

作者:區小白

來源:巴比特

整理發出:贏和財經

以上內容采編自互聯網,如內容侵犯您的版權,請聯系郵箱:law@allwin.world,我們會在24小時內刪除相關內容。

動態 | 人民日報官方微博科普區塊鏈 強調區塊鏈不等于比特幣:人民日報官方微博今早發表9圖科普區塊鏈。其中涉及區塊鏈的特點有:1、安全;2、不可篡改;3、可訪問;4、無第三方。區塊鏈對未來的影響:1、不需繁瑣個人證明;2、看病避免反復檢查;3、旅行消費更加便捷;4、交易無需第三方。同時強調,區塊鏈不等于比特幣。比特幣只是區塊鏈技術的一種應用,區塊鏈還有醫療衛生、食品安全、版權保護等諸多應用領域。[2019/10/28]

動態 | 幣安科普MimbleWimble算法:幣安官方推特今日發布隱私算法Mimblewimble的科普貼,在下方留言區大量網友留言猜測是否是基于 Mimblewimble算法的隱私幣Grin或者Beam即將登陸幣安交易所,其中猜測Grin的呼聲更高。[2019/9/2]

財政部副部長朱光耀:數字經濟還處在發展的過程中,要以科普、推動的態度來推進數字經濟發展:今日,在中國發展高層論壇2018年會上,財政部副部長朱光耀表示:“數字經濟還處在發展的過程中,要以科普、推動的態度來推進數字經濟發展。也要關注數字經濟的其他影響,包括稅收征管、反洗錢監管措施等要跟上。”[2018/3/25]

Tags:RPCatcNRPRPC幣RPC價格atc幣是什么幣NRP價格NRP幣

FIL
“妖幣”GTC 游戲幣中的戰斗機還是韭菜收割機?_區塊鏈游戲

數字貨幣這股巨浪營造了一個巨大的人性舞臺:貪婪、恐懼、患得患失、信仰崩塌時刻上演,幣圈一日,人間一年.

1900/1/1 0:00:00
【百科】淺談通證經濟與貨幣理論_區塊鏈

眾所周知,在區塊鏈經濟中,通證承擔了非常重要的角色,不過很多人對通證誤解很大,將其僅僅作為貨幣數字化的一種形式.

1900/1/1 0:00:00
李笑來變臉:從人生導師到韭菜之王_EOS

分叉、空投、交易對,是李笑來持續不斷在邊緣游走的慣用玩法。本文共計5884字,閱讀時間6分鐘。本文首發自微信公眾號區塊鏈真相 作者/劉景豐趙蘭濤 編輯/賀樹龍 李笑來摘掉面具,人生導師驟變韭菜之.

1900/1/1 0:00:00
晚上翻來覆去睡不著?送給晚睡強迫癥患者的自救手冊_IDA

連續11天不睡覺,身體會怎樣?1965年,一名17歲的美國高中生親身試驗,結果令人大吃一驚。試驗第二天,他的眼睛停止了聚焦,隨后喪失了通過觸摸鑒別事物的能力;第三天,他變得焦躁、笨拙;實驗結束時.

1900/1/1 0:00:00
今日超過比特幣光環的IOTA-埃歐塔,為何暴漲數倍_IOT

今天幣圈內最耀眼的不是比特幣,也不是比特幣的各種分叉幣,而是IOTA-埃歐塔。昨天IOTA的價格還在2.2美元左右,今天竟然最高達到了18美元!為何會如此?此事,還要從IOTA上線火幣說起.

1900/1/1 0:00:00
大佬站臺的幣靠譜嗎?看看這些數據就懂了,李笑來半年虧了1000億_EOS

眾所周知,名人效應是營銷中不可缺失的一部分,古時候開個小賣部還要請嚴嵩提個詞呢,大佬們的站臺和背書確實給用戶們很大信心,而這個在咱們幣圈就更為重要,有的韭菜買幣,甚至就是沖著給它站臺的大佬去的.

1900/1/1 0:00:00
ads