證與公鑰恢復(fù)實(shí)戰(zhàn))
區(qū)塊鏈【免費(fèi)下載鏈接】eosAn open source smart contract platform項(xiàng)目地址https://gitcode.com/gh_mirrors/eo/eos點(diǎn)擊查看免費(fèi)下載本指南完整講解 EOS 節(jié)點(diǎn)工具cleos中validate signatures子命令的用法、參數(shù)與底層實(shí)現(xiàn)。該命令不依賴錢包、不上鏈僅憑交易 JSON 與鏈 ID 即可驗(yàn)簽并恢復(fù)出簽名所用公鑰適用于交易簽名自檢、離線驗(yàn)簽、多簽權(quán)限核查等場景。閱讀本文后你將掌握該命令的完整語法、如何構(gòu)造合法交易 JSON、--chain-id參數(shù)的作用以及命令背后的簽名恢復(fù)原理與源碼級(jí)調(diào)用鏈。命令概述驗(yàn)證簽名并恢復(fù)公鑰cleos validate signatures是cleos validate命名空間下的唯一子命令其功能正如命令本身的描述所示Validate signatures and recover public keys驗(yàn)證簽名并恢復(fù)公鑰它接收一份已簽名交易的 JSON 表示在本地對(duì)交易摘要進(jìn)行簽名校驗(yàn)并從簽名中恢復(fù)出對(duì)應(yīng)的公鑰最終以 JSON 數(shù)組的形式打印出來。該命令的定位是純離線校驗(yàn)工具它不會(huì)把交易廣播到鏈上也不會(huì)調(diào)用錢包簽名因此在排查某筆簽名是否有效這筆交易到底由哪些公鑰簽署之類的問題時(shí)非常實(shí)用。在cleos的命令樹中該命令位于validate分支之下相關(guān)定義可見 programs/cleos/main.cpp#L2652-L2686// validate subcommand auto validate app.add_subcommand(validate, localized(Validate transactions)); validate-require_subcommand(); // validate signatures string trx_json_to_validate; string str_chain_id; auto validate_signatures validate-add_subcommand(signatures, localized(Validate signatures and recover public keys)); validate_signatures-add_option(transaction, trx_json_to_validate, localized(The JSON string or filename defining the transaction to validate), true)-required(); validate_signatures-add_option(-c,--chain-id, str_chain_id, localized(The chain id that will be used in signature verification));提示關(guān)于 JSON 輸入格式的穩(wěn)定性官方文檔對(duì)此命令有一則重要提示該命令的 JSON 輸入依賴于底層 C 類定義即transaction、signed_transaction、action等數(shù)據(jù)結(jié)構(gòu)的序列化布局因此這類 JSON 輸入在 EOSIO 未來版本中可能發(fā)生變化。在編寫自動(dòng)化腳本時(shí)應(yīng)留意版本差異并盡量基于當(dāng)前版本導(dǎo)出/生成 JSON避免硬編碼長期依賴。語法與參數(shù)基本用法cleos validate signatures [OPTIONS] transaction位置參數(shù)參數(shù)類型說明transactionTEXT定義待驗(yàn)證已簽名交易的 JSON 字符串或指向該 JSON 的文件名必填從源碼可見programs/cleos/main.cpp#L2660-L2661該參數(shù)通過add_option(...)-required()聲明為必填項(xiàng)同時(shí)命令內(nèi)部使用json_from_file_or_string()解析輸入programs/cleos/main.cpp#L541因此既可以直接傳入 JSON 字符串也可以傳入包含 JSON 的文件路徑兩者等價(jià)。選項(xiàng)選項(xiàng)類型說明-c, --chain-idTEXT簽名驗(yàn)證所使用的鏈 IDchain id--chain-id的行為在源碼中有明確分支programs/cleos/main.cpp#L2672-L2680未指定--chain-id時(shí)cleos 會(huì)調(diào)用 RPC 接口get_info()從當(dāng)前連接的節(jié)點(diǎn)默認(rèn)http://127.0.0.1:8888可用-u/--url覆蓋拉取鏈 ID并輸出日志grabbing chain_id from nodeos指定--chain-id時(shí)則直接使用該值構(gòu)造chain_id_type完全不需要與任何節(jié)點(diǎn)通信實(shí)現(xiàn)離線驗(yàn)簽。if( str_chain_id.size() 0 ) { ilog( grabbing chain_id from ${n}, (n, node_executable_name) ); auto info get_info(); chain_id info.chain_id; } else { chain_id chain_id_type(str_chain_id); }為何鏈 ID 如此關(guān)鍵EOS 交易的簽名摘要由sig_digest(chain_id, context_free_data)計(jì)算得出鏈 ID 參與哈希拼接詳見 libraries/chain/transaction.cpp#L59-L78 對(duì)get_signature_keys的實(shí)現(xiàn)。鏈 ID 不匹配驗(yàn)簽必然失敗這也正是 EOS 簽名天然綁定特定鏈、無法跨鏈重放的根本原因。主網(wǎng)與各測試網(wǎng)的鏈 ID 各不相同例如文檔示例中使用的cf057bbfb72640471fd910bcb67639c22df9f92470936cddc1ade0e2f2e7dc4f即為某一測試網(wǎng)的鏈 ID實(shí)際使用時(shí)應(yīng)以目標(biāo)鏈cleos get info輸出的chain_id為準(zhǔn)。底層原理簽名恢復(fù)的完整調(diào)用鏈validate signatures雖然只是 cleos 的一個(gè)子命令但其核心邏輯直接復(fù)用鏈內(nèi)核libraries/chain的簽名恢復(fù)實(shí)現(xiàn)與節(jié)點(diǎn)在交易執(zhí)行前的驗(yàn)簽路徑完全一致因此結(jié)果具有權(quán)威性。第一步解析 JSON 為 signed_transaction命令回調(diào)首先將輸入字符串或文件解析為fc::variant再通過abi_serializer::from_variant反序列化為signed_transaction對(duì)象programs/cleos/main.cpp#L2664-L2670fc::variant trx_var json_from_file_or_string(trx_json_to_validate); signed_transaction trx; try { abi_serializer::from_variant( trx_var, trx, abi_serializer_resolver_empty, abi_serializer::create_yield_function( abi_serializer_max_time ) ); } EOS_RETHROW_EXCEPTIONS(transaction_type_exception, Invalid transaction format: ${data}, ...)注意此處使用的是abi_serializer_resolver_emptyprograms/cleos/main.cpp#L371-L373即不依賴任何合約 ABI 解析 action data因?yàn)轵?yàn)簽只關(guān)心交易的簽名摘要data字段以原始 hex 形式參與計(jì)算即可無需解碼。signed_transaction在鏈內(nèi)核中的定義libraries/chain/include/eosio/chain/transaction.hpp#L104-L125表明它繼承自transaction并額外攜帶兩類字段signatures交易簽名列表context_free_data與各 context-free action 一一對(duì)應(yīng)的上下文無關(guān)數(shù)據(jù)。第二步get_signature_keys 恢復(fù)公鑰隨后命令調(diào)用交易對(duì)象的簽名恢復(fù)方法programs/cleos/main.cpp#L2682-L2685flat_setpublic_key_type recovered_pub_keys; trx.get_signature_keys( *chain_id, fc::time_point::maximum(), recovered_pub_keys, false ); std::cout fc::json::to_pretty_string(recovered_pub_keys) std::endl;其底層實(shí)現(xiàn)位于 libraries/chain/transaction.cpp#L59-L78fc::microseconds transaction::get_signature_keys( const vectorsignature_type signatures, const chain_id_type chain_id, fc::time_point deadline, const vectorbytes cfd, flat_setpublic_key_type recovered_pub_keys, bool allow_duplicate_keys)const { try { auto start fc::time_point::now(); recovered_pub_keys.clear(); const digest_type digest sig_digest(chain_id, cfd); for(const signature_type sig : signatures) { auto now fc::time_point::now(); EOS_ASSERT( now deadline, tx_cpu_usage_exceeded, ... ); auto[ itr, successful_insertion ] recovered_pub_keys.emplace( sig, digest ); EOS_ASSERT( allow_duplicate_keys || successful_insertion, tx_duplicate_sig, transaction includes more than one signature signed using the same key ... ); } return fc::time_point::now() - start; } FC_CAPTURE_AND_RETHROW() }從中可以提煉出幾個(gè)關(guān)鍵行為先算摘要再逐簽恢復(fù)sig_digest(chain_id, cfd)根據(jù)鏈 ID 與上下文無關(guān)數(shù)據(jù)生成交易摘要然后對(duì)signatures列表中的每個(gè)簽名執(zhí)行橢圓曲線簽名恢復(fù)ECDSA recover得到對(duì)應(yīng)公鑰并放入flat_set自動(dòng)去重、排序默認(rèn)禁止重復(fù)密鑰簽名命令調(diào)用時(shí)傳入allow_duplicate_keys false若同一密鑰對(duì)同一交易簽署多份簽名將拋出tx_duplicate_sig異常這符合鏈上對(duì)交易簽名的約束存在 CPU 時(shí)間上限檢查deadline參數(shù)用于防止驗(yàn)簽耗時(shí)過長cleos 此處傳入fc::time_point::maximum()表示不限時(shí)而在節(jié)點(diǎn)側(cè)同樣的方法會(huì)以區(qū)塊上下文限定的 deadline 執(zhí)行并計(jì)入tx_cpu_usage計(jì)量。值得一提的是節(jié)點(diǎn)在打包、驗(yàn)證交易時(shí)也走同一路徑transaction_metadata.cpp中同樣調(diào)用trx-get_transaction().get_signature_keys(...)完成簽名恢復(fù)libraries/chain/transaction_metadata.cpp#L19-L22。因此cleos validate signatures的輸出等價(jià)于節(jié)點(diǎn)視角下的這筆交易由哪些公鑰授權(quán)。實(shí)戰(zhàn)示例待驗(yàn)證的已簽名交易 JSON官方文檔給出了一個(gè)完整的示例一筆eosio::voteproducer投票交易由initbactive權(quán)限發(fā)起攜帶一個(gè) K1 曲線簽名。該 JSON 的關(guān)鍵結(jié)構(gòu)如下{ expiration: 2020-04-23T04:47:23, ref_block_num: 20, ref_block_prefix: 3872940040, max_net_usage_words: 0, max_cpu_usage_ms: 0, delay_sec: 0, context_free_actions: [], actions: [ { account: eosio, name: voteproducer, authorization: [ { actor: initb, permission: active } ], data: 000000008093dd74000000000000000001000000008093dd74 } ], transaction_extensions: [], signatures: [ SIG_K1_Jy81u5yWSE4vGET1cm9TChKrzhAz4QE2hB2pWnUsHQExGafqhVwXtg7a7mbLZwXcon8bVQJ3J5jtZuecJQADTiz2kwcm7c ], context_free_data: [] }字段含義一覽字段說明expiration交易過期時(shí)間UTC用于限制交易的生命周期ref_block_num/ref_block_prefix引用的最近區(qū)塊號(hào)與區(qū)塊前綴用于防止重放max_net_usage_words/max_cpu_usage_ms資源使用上限0 表示不限制delay_sec延遲交易秒數(shù)0 表示立即生效context_free_actions上下文無關(guān)動(dòng)作列表actions常規(guī)動(dòng)作列表含account、name、authorizationactorpermission與 hex 編碼的datatransaction_extensions交易擴(kuò)展signatures已簽名列表此處為一條SIG_K1_前綴的簽名K1 曲線context_free_data與 context-free action 對(duì)應(yīng)的數(shù)據(jù)方式一直接傳入 JSON 字符串cleos validate signatures --chain-id cf057bbfb72640471fd910bcb67639c22df9f92470936cddc1ade0e2f2e7dc4f { expiration: 2020-04-23T04:47:23, ref_block_num: 20, ref_block_prefix: 3872940040, max_net_usage_words: 0, max_cpu_usage_ms: 0, delay_sec: 0, context_free_actions: [], actions: [ { account: eosio, name: voteproducer, authorization: [ { actor: initb, permission: active } ], data: 000000008093dd74000000000000000001000000008093dd74 } ], transaction_extensions: [], signatures: [ SIG_K1_Jy81u5yWSE4vGET1cm9TChKrzhAz4QE2hB2pWnUsHQExGafqhVwXtg7a7mbLZwXcon8bVQJ3J5jtZuecJQADTiz2kwcm7c ], context_free_data: [] }方式二連接節(jié)點(diǎn)自動(dòng)獲取鏈 ID若不顯式指定--chain-id則需保證能訪問到目標(biāo)鏈的節(jié)點(diǎn)例如通過-u指定 API 地址cleos 會(huì)調(diào)用get info自動(dòng)獲取鏈 IDcleos -u https://api.testnet.eos.io validate signatures { expiration: 2020-04-23T04:47:23, ref_block_num: 20, ref_block_prefix: 3872940040, max_net_usage_words: 0, max_cpu_usage_ms: 0, delay_sec: 0, context_free_actions: [], actions: [ { account: eosio, name: voteproducer, authorization: [ { actor: initb, permission: active } ], data: 000000008093dd74000000000000000001000000008093dd74 } ], transaction_extensions: [], signatures: [ SIG_K1_Jy81u5yWSE4vGET1cm9TChKrzhAz4QE2hB2pWnUsHQExGafqhVwXtg7a7mbLZwXcon8bVQJ3J5jtZuecJQADTiz2kwcm7c ], context_free_data: [] }輸出解讀命令成功執(zhí)行后輸出恢復(fù)出的公鑰數(shù)組每行一個(gè)[ EOS7pCywBCz5zw2bc7teCVcT7MEWUr9s749qnYDNPEsBoH32vGqqN ]該公鑰即簽署這筆交易所用私鑰對(duì)應(yīng)的 EOS 格式公鑰。開發(fā)者可將其與賬戶initb的active權(quán)限中聲明的公鑰進(jìn)行比對(duì)例如cleos get account initb從而確認(rèn)這筆交易確實(shí)由該賬戶的授權(quán)密鑰簽署。多條簽名時(shí)輸出數(shù)組中會(huì)包含多個(gè)公鑰flat_set自動(dòng)去重排序可用于核對(duì)多簽交易的簽名方集合。如何構(gòu)造待驗(yàn)證的交易 JSONvalidate signatures只負(fù)責(zé)驗(yàn)簽不負(fù)責(zé)生成交易。構(gòu)造一份合法的已簽名交易 JSON 通常有三種途徑cleos push transaction配合-ddry-run與-jjson選項(xiàng)先不廣播僅輸出交易 JSON再交由錢包簽名后保存cleos sign或cleos multisig propose等命令的輸出簽名或提案相關(guān)命令可生成帶signatures字段的 JSON自行構(gòu)造參照上文 JSON 結(jié)構(gòu)手工組裝signatures字段留空或填入既有簽名。注意data字段為 action 數(shù)據(jù)的 hex 編碼簽名計(jì)算不依賴 ABI 解碼因此手寫 JSON 也能驗(yàn)簽。完整的提交流程可參考 如何提交一筆交易其中詳細(xì)介紹了如何用cleos push transaction -d -j生成交易 JSON再按需簽名并廣播配合validate signatures可在廣播前完成一次獨(dú)立的簽名正確性檢查。注意事項(xiàng)與常見問題鏈 ID 必須與簽名時(shí)一致簽名摘要包含鏈 ID使用錯(cuò)誤的--chain-id或連接了錯(cuò)誤的節(jié)點(diǎn)將導(dǎo)致驗(yàn)簽失敗或恢復(fù)出錯(cuò)誤的公鑰JSON 輸入格式依賴版本如文檔提示該命令的 JSON 輸入受底層類定義影響EOSIO 后續(xù)版本可能調(diào)整結(jié)構(gòu)請(qǐng)勿將 JSON 結(jié)構(gòu)視為長期穩(wěn)定的 API默認(rèn)拒絕重復(fù)簽名同一密鑰對(duì)同一交易的多份簽名會(huì)被判定為非法對(duì)應(yīng)tx_duplicate_sig多簽場景應(yīng)確保每個(gè)簽名來自不同密鑰純本地校驗(yàn)時(shí)不需節(jié)點(diǎn)只要顯式提供--chain-id該命令即可在完全離線的環(huán)境下完成驗(yàn)簽與公鑰恢復(fù)expiration / ref_block 字段不影響驗(yàn)簽驗(yàn)簽只針對(duì)交易摘要本身交易是否過期、引用區(qū)塊是否有效屬于節(jié)點(diǎn)側(cè)入塊校驗(yàn)的范疇離線驗(yàn)簽不會(huì)對(duì)這些做判斷。相關(guān)文檔cleos validate 命令索引validate父命令及子命令總覽如何提交一筆交易交易 JSON 的生成與廣播全流程簽名恢復(fù)核心實(shí)現(xiàn)transaction::get_signature_keys源碼signed_transaction 結(jié)構(gòu)定義簽名與上下文無關(guān)數(shù)據(jù)的承載結(jié)構(gòu)cleos validate signatures 命令實(shí)現(xiàn)命令行解析與回調(diào)邏輯贊分享區(qū)塊鏈【免費(fèi)下載鏈接】eosAn open source smart contract platform項(xiàng)目地址https://gitcode.com/gh_mirrors/eo/eos點(diǎn)擊查看免費(fèi)下載相關(guān)推薦Rust 交叉編譯到 PlayStation 1mipsel-sony-psx 目標(biāo)定義與實(shí)戰(zhàn)構(gòu)建指南Rust 交叉編譯到 PlayStation 1mipsel sony psx 目標(biāo)定義與實(shí)戰(zhàn)構(gòu)建指南 本文基于 Rust 編譯器官方文檔 mipsel so區(qū)塊鏈cleos convert unpack_transaction 命令詳解將打包交易還原為明文簽名 JSONcleos convert unpack_transaction 命令詳解將打包交易還原為明文簽名 JSON cleos convert unpack_tra區(qū)塊鏈Wasp 安裝方式遷移實(shí)戰(zhàn)從腳本安裝器到 npm 全局包migrate-to-npmWasp 安裝方式遷移實(shí)戰(zhàn)從腳本安裝器到 npm 全局包migrate to npm Wasp 自 0.21 版本起將安裝方式從傳統(tǒng)的 curl 腳本安裝區(qū)塊鏈上一篇Rust pcap性能優(yōu)化指南提升數(shù)據(jù)包捕獲效率的10個(gè)方法下一篇100 個(gè) Rust 練習(xí)從零入門每道練習(xí)都有 cargo test 驗(yàn)證創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考