如何在 Ruby 中使用 secp256k1 簽署消息?
我正在嘗試使用 secp256k1 在 Ruby 中為乙太坊編寫一個符號函式。
我可以訪問這個似乎適用於比特幣的 sign_compact 函式:
https://github.com/lian/bitcoin-ruby/blob/master/lib/bitcoin/ffi/secp256k1.rb#L207-L232
我正在嘗試使用它來簽署乙太坊,但似乎缺少一些步驟。具體來說,與頭部、r 值和 s 值有關。有人似乎在這裡使用 OpenSSL 而不是 secp256k1 正確地執行此操作:
https://github.com/se3000/ruby-eth/blob/master/lib/eth/open_ssl.rb#L58-L97
似乎不同的行是:
if signature.get_array_of_pointer(0, 2).all?{|i| BN_num_bits(i) <= 256 } 4.times{|i| head = [ Eth.v_base + i ].pack("C") if public_key_hex == recover_public_key_from_signature(hash, [head, r, s].join, i, pubkey_compressed) rec_id = i; break end } end
我將如何使用 secp256k1 sign_compact 函式對乙太坊消息進行簽名?我需要為簽名更改什麼嗎?或者有什麼地方可以找到這個協議?
我從未編寫過 Ruby,但我對比特幣和乙太坊簽名略知一二。我希望我能幫助你。
比特幣簽名由兩部分組成:
(r,s)
. 乙太坊簽名由三部分組成:(v,r,s)
.只有一個字節的額外值
v
允許從簽名中導出公鑰。只有四個可能的值,v
因此您指向的程式碼會遍歷所有四個可能的值來檢查哪個是正確的。如果恢復的公鑰與提供的公鑰匹配(sign_compact函式的輸入),我們就找到了正確的rec_id
值,然後將這個公鑰與簽名一起返回。所以函式最終返回(v,r,s)
而不是僅僅返回(r,s)
,在我看來,比特幣中的
header
值sign_compact
實際上設置為這個恢復位(假設函式呼叫secp256k1_ecdsa_recoverable_signature_serialize_compact
設置了這個值)。所以我相信這個函式sign_compact
實際上會返回你需要的東西。
查看 Ruby gem
rbsecp256k1
/examples(第 40-51 行):# Signs a personal message with the given private key. # # @param private_key [Secp256k1::PrivateKey] key to use for signing. # @param chain_id [Integer] unique identifier for chain. # @return [String] binary signature data including recovery id v at end. def sign(private_key, chain_id) ctx = Secp256k1::Context.new signature, recovery_id = ctx.sign_recoverable(private_key, hash).compact result = signature.bytes result = result.append(Chains.to_v(recovery_id, chain_id)) result.pack('c*') end
這是一個相當複雜的範例,明確解決了您的問題。
由於您特別詢問了
eth
gem,我最近完全刪除了 OpenSSL 程式碼並專門在 Secp256k1 中實現了簽名和恢復(第 65-83 行):# Signs arbitrary data without validation. Should not be used unless really # desired. See also: {Key.personal_sign}, {Key.sign_typed_data}, and # {Signature.recover}. # # @param blob [Object] that arbitrary data to be signed. # @param chain_id [Integer] the chain id the signature should be generated on. # @return [String] a hexa-decimal signature. def sign(blob, chain_id = nil) context = Secp256k1::Context.new compact, recovery_id = context.sign_recoverable(@private_key, blob).compact signature = compact.bytes v = Chain.to_v recovery_id, chain_id is_leading_zero = true [v].pack("N").unpack("C*").each do |byte| is_leading_zero = false if byte > 0 and is_leading_zero signature.append byte unless is_leading_zero and byte === 0 end Util.bin_to_hex signature.pack "c*" end
它在
Eth::Key
類中,還包含一個personal_sign
andsign_typed_data
。