diff --git a/include/wally_script.h b/include/wally_script.h index a51761ac9..c9dfb4442 100644 --- a/include/wally_script.h +++ b/include/wally_script.h @@ -483,6 +483,22 @@ WALLY_CORE_API int wally_witness_program_from_bytes( size_t *written); #ifdef BUILD_ELEMENTS +/** + * Get the pegout script size. + * + * :param parent_genesis_blockhash_len: Length of ``parent_genesis_blockhash`` in bytes. Must be 32. + * :param mainchain_script_len: Length of ``mainchain_script`` in bytes. + * :param sub_pubkey_len: Length of ``sub_pubkey`` in bytes. Must be ``EC_PUBLIC_KEY_LEN``. + * :param whitelist_proof_len: The length of ``whitelist_proof`` in bytes. + * :param written: Destination for the number of bytes required to hold the pegout script. + */ +WALLY_CORE_API int wally_elements_pegout_script_size( + size_t parent_genesis_blockhash_len, + size_t mainchain_script_len, + size_t sub_pubkey_len, + size_t whitelist_proof_len, + size_t *written); + /** * Create a pegout script. * @@ -541,4 +557,3 @@ WALLY_CORE_API int wally_elements_pegin_contract_script_from_bytes( #endif #endif /* LIBWALLY_CORE_SCRIPT_H */ - diff --git a/src/Makefile.am b/src/Makefile.am index befabce5a..2f5d04a55 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -151,7 +151,8 @@ SWIG_JAVA_TEST_DEPS = \ $(sjs)/$(cbt)/test_mnemonic.class if BUILD_ELEMENTS -SWIG_JAVA_TEST_DEPS += $(sjs)/$(cbt)/test_assets.class +SWIG_JAVA_TEST_DEPS += $(sjs)/$(cbt)/test_assets.class \ + $(sjs)/$(cbt)/test_pegs.class endif all: $(SWIG_JAVA_TEST_DEPS) @@ -291,6 +292,7 @@ endif # RUN_PYTHON_TESTS if RUN_JAVA_TESTS if BUILD_ELEMENTS $(AM_V_at)$(JAVA_TEST)test_assets + $(AM_V_at)$(JAVA_TEST)test_pegs endif $(AM_V_at)$(JAVA_TEST)test_bip32 $(AM_V_at)$(JAVA_TEST)test_mnemonic diff --git a/src/script.c b/src/script.c index 28766eaf4..666e563ef 100644 --- a/src/script.c +++ b/src/script.c @@ -873,6 +873,20 @@ int wally_witness_program_from_bytes(const unsigned char *bytes, size_t bytes_le return ret; } +int wally_elements_pegout_script_size(size_t parent_genesis_blockhash_len, + size_t mainchain_script_len, + size_t sub_pubkey_len, + size_t whitelist_proof_len, + size_t *written) +{ + *written = 1 + + parent_genesis_blockhash_len + calc_push_opcode_size(parent_genesis_blockhash_len) + + mainchain_script_len + calc_push_opcode_size(mainchain_script_len) + + sub_pubkey_len + calc_push_opcode_size(sub_pubkey_len) + + whitelist_proof_len + calc_push_opcode_size(whitelist_proof_len); + return WALLY_OK; +} + int wally_elements_pegout_script_from_bytes(const unsigned char *parent_genesis_blockhash, size_t parent_genesis_blockhash_len, const unsigned char *mainchain_script, diff --git a/src/swig_java/jni_elements_extra.java_in b/src/swig_java/jni_elements_extra.java_in index 7751df3ef..462d8e16c 100644 --- a/src/swig_java/jni_elements_extra.java_in +++ b/src/swig_java/jni_elements_extra.java_in @@ -125,6 +125,26 @@ return tx_get_elements_signature_hash(tx, index, script, value, sighash, flags, null); } + public final static byte[] asset_pak_whitelistproof(byte[] on_keys, byte[] off_keys, long idx, byte[] sub_pubkey, byte[] priv_key, byte[] summed_key) { + final int required_len = asset_pak_whitelistproof_size(off_keys.length / Wally.EC_PUBLIC_KEY_LEN); + final byte[] buf = new byte[required_len]; + asset_pak_whitelistproof(on_keys, off_keys, idx, sub_pubkey, priv_key, summed_key, buf); + return buf; + } + + public final static byte[] elements_pegout_script_from_bytes(byte[] bh, byte[] mcs, byte[] pk, byte[] whl, long flags) { + final int required_len = elements_pegout_script_size(bh.length, mcs.length, pk.length, whl.length); + final byte[] buf = new byte[required_len]; + final int len = elements_pegout_script_from_bytes(bh, mcs, pk, whl, flags, buf); + return checkBuffer(buf, len); + } + + public final static byte[] elements_pegin_contract_script_from_bytes(byte[] rs, byte[] cs, long flags) { + final byte[] buf = new byte[rs.length]; + final int len = elements_pegin_contract_script_from_bytes(rs, cs, flags, buf); + return checkBuffer(buf, len); + } + public final static byte[] tx_get_input_blinding_nonce(Object tx, final int index) { return tx_get_input_blinding_nonce(tx, index, null); } diff --git a/src/swig_java/src/com/blockstream/test/test_pegs.java b/src/swig_java/src/com/blockstream/test/test_pegs.java new file mode 100644 index 000000000..dc63e237b --- /dev/null +++ b/src/swig_java/src/com/blockstream/test/test_pegs.java @@ -0,0 +1,60 @@ +package com.blockstream.test; + +import com.blockstream.libwally.Wally; + +public class test_pegs { + + // All hexes taken from src/test/test_peg[in|out].py + public void test_pak_whitelistproof() { + final byte[] onkeys = h("031f26676db48716aff0f6ac477db463715d3280e2c706b55556425831620fdcce"); + final byte[] offkeys = h("030781ae6f87c0b3af83b7350eb38bbd22322f525046d0320f1cb45a97c05cbeb7"); + final int index = 0; + final byte[] pub_key = h("03c58ebf2840c9321e42e1859a387d42cc78241048f81ce9c911bd57b240139e97"); + final byte[] master_online_key = h("06def06500e5efae3addf7e0ed1178074405587a95c49a3ef31367eec782319f"); + final byte[] pub_tweak = h("5d0f162bd18a271d8d219efd013f51d6fc8597d035a04b2b1e4686c05e92aaed"); + + final String expected_hex = "013996e9eca65e06b3deda77fdc19b3476cd83af3ae8f543647a52b097558c33878752c52536c493ea00d446159009ce484795287aca1de8aaa52d6064b5960caa"; + + final byte[] wlproof = Wally.asset_pak_whitelistproof(onkeys, offkeys, index, pub_key, master_online_key, pub_tweak); + + if (!h(wlproof).equals(expected_hex)) + throw new RuntimeException("Unexpected whitelistproof result"); + } + + public void test_pegin_contract_script() { + final byte[] federation_script = h("745c87635b21020e0338c96a8870479f2396c373cc7696ba124e8635d41b0ea581112b678172612102675333a4e4b8fb51d9d4e22fa5a8eaced3fdac8a8cbf9be8c030f75712e6af992102896807d54bc55c24981f24a453c60ad3e8993d693732288068a23df3d9f50d4821029e51a5ef5db3137051de8323b001749932f2ff0d34c82e96a2c2461de96ae56c2102a4e1a9638d46923272c266631d94d36bdb03a64ee0e14c7518e49d2f29bc40102102f8a00b269f8c5e59c67d36db3cdc11b11b21f64b4bffb2815e9100d9aa8daf072103079e252e85abffd3c401a69b087e590a9b86f33f574f08129ccbd3521ecf516b2103111cf405b627e22135b3b3733a4a34aa5723fb0f58379a16d32861bf576b0ec2210318f331b3e5d38156da6633b31929c5b220349859cc9ca3d33fb4e68aa08401742103230dae6b4ac93480aeab26d000841298e3b8f6157028e47b0897c1e025165de121035abff4281ff00660f99ab27bb53e6b33689c2cd8dcd364bc3c90ca5aea0d71a62103bd45cddfacf2083b14310ae4a84e25de61e451637346325222747b157446614c2103cc297026b06c71cbfa52089149157b5ff23de027ac5ab781800a578192d175462103d3bde5d63bdb3a6379b461be64dad45eabff42f758543a9645afd42f6d4248282103ed1e8d5109c9ed66f7941bc53cc71137baa76d50d274bda8d5e8ffbd6e61fe9a5f6702c00fb275522103aab896d53a8e7d6433137bbba940f9c521e085dd07e60994579b64a6d992cf79210291b7d0b1b692f8f524516ed950872e5da10fb1b808b5a526dedc6fed1cf29807210386aa9372fbab374593466bc5451dc59954e90787f08060964d95c87ef34ca5bb5368ae"); + + final byte[] script_in = h("0014d712bcaf8f9384fd388efca86d77e033d5cfffd9"); + + final String expected_hex = "745c87635b21039e7dc52351b81d97dde2b369f692d89b5cf938534ad503094bc89f830473796321030508eb92dccb704ac7511eb55369f2259485b50f56671ff5bf1dfd8cdf5c6c662102140052a92c55c5a0c2f960b438bba966974cdfa2c872a4702645ff6028f6b0f2210209aa6d8ab038fd00088355324c9c3fea336b2cf650d951b23c2b20ad47386daa2102328940da1f59bc214757a8fdedd86887fc48a952e0fa19c1f1959ffa826c88b42102188281e1055fb81f642a7f2ca994a8f6abaa8bbcc2720aff35c0fd263188ef7c2103ae97faefcdba436269cc36c31db7956ade6c1977b174b87174173ea92c112d332103f4a2d090f03684a65f74f5ee031d6d9bf5fd02d4e643693701f86d4e4f721ae82102d5ee27530bc9075c310e53b308a127a3ed7a90c6039355d00d2d1ea72874add72103960c1740e6ac39c15fc8fd048789fdd480b68331a49e6e557fcbec192d0b3a252103e33ae6c4b978523ff81e8f2fb2e2c0174f9483de86c186e228753715fa2228392103003d2490f282d7628a2a8efa08366f317efa6473579bb5c34b4c409e36e7b2df2102da66e69bd08a68d4c8fabefd797786bb6de16d553acab4ee85e3aceda8e48d8c2103d46bd2ba127f1666650de1e0d85f438978c28399a9ac866ea84db30cc77446c3210257fdfffaf0a360f7ee1d0c2588d931a5b51302ab5a100cd9fa54ebe1d63adbdb5f6702c00fb275522103aab896d53a8e7d6433137bbba940f9c521e085dd07e60994579b64a6d992cf79210291b7d0b1b692f8f524516ed950872e5da10fb1b808b5a526dedc6fed1cf29807210386aa9372fbab374593466bc5451dc59954e90787f08060964d95c87ef34ca5bb5368ae"; + + final byte[] contract_script = Wally.elements_pegin_contract_script_from_bytes(federation_script, script_in, 0); + + if (!h(contract_script).equals(expected_hex)) + throw new RuntimeException("Unexpected pegin_contract_script result"); + } + + public void test_get_pegout_script() { + final byte[] gbh_reversed = h("06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f"); + final byte[] pubkey = h("03c58ebf2840c9321e42e1859a387d42cc78241048f81ce9c911bd57b240139e97"); + final byte[] mainchain_script = Wally.scriptpubkey_p2pkh_from_bytes(pubkey, Wally.WALLY_SCRIPT_HASH160); + final byte[] wlproof = h("013996e9eca65e06b3deda77fdc19b3476cd83af3ae8f543647a52b097558c33878752c52536c493ea00d446159009ce484795287aca1de8aaa52d6064b5960caa"); + + final String expected_hex = "6a2006226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f1976a91420f2d8c7514c601984fffee90f988f33bd87f96f88ac2103c58ebf2840c9321e42e1859a387d42cc78241048f81ce9c911bd57b240139e9741013996e9eca65e06b3deda77fdc19b3476cd83af3ae8f543647a52b097558c33878752c52536c493ea00d446159009ce484795287aca1de8aaa52d6064b5960caa"; + + final byte[] pegout_script = Wally.elements_pegout_script_from_bytes(gbh_reversed, mainchain_script, pubkey, wlproof, 0); + + if (!h(pegout_script).equals(expected_hex)) + throw new RuntimeException("Unexpected pegout_script result"); + } + + private String h(final byte[] bytes) { return Wally.hex_from_bytes(bytes); } + private byte[] h(final String hex) { return Wally.hex_to_bytes(hex); } + + public static void main(final String[] args) { + final test_pegs t = new test_pegs(); + t.test_pak_whitelistproof(); + t.test_pegin_contract_script(); + t.test_get_pegout_script(); + } +} diff --git a/src/swig_java/src/com/blockstream/test/test_tx.java b/src/swig_java/src/com/blockstream/test/test_tx.java index c623d8925..f8d73a127 100644 --- a/src/swig_java/src/com/blockstream/test/test_tx.java +++ b/src/swig_java/src/com/blockstream/test/test_tx.java @@ -88,7 +88,7 @@ public void test_dersigs() { final String ders[] = { "3044022067efef0d968862524308632be6e724db29bd33e5a373fa98e4c726b753b459c302203fd98e793a7926c0281423c64dd555eb6aa43db8ada5dba86a21c5121298b87e", "3045022100eb6ef01ce422cda7f58e1768ba60192f1714c4af26535657fcfa058359db34460220633eba147b88ba8cb52f6b2ca26f083d6c0f7e744d1f1113eb33b1c3c3c83f60" }; for ( int i = 0 ; i < sigs.length; ++i ) { - //assert_eq(sigs[i], h(Wally.ec_sig_from_der(h(ders[i]))), "Unexpected ec_sig_from_der() result."); + assert_eq(sigs[i], h(Wally.ec_sig_from_der(h(ders[i]))), "Unexpected ec_sig_from_der() result."); assert_eq(ders[i], h(Wally.ec_sig_to_der(h(sigs[i]))), "Unexpected ec_sig_to_der() result."); } } diff --git a/src/swig_java/swig.i b/src/swig_java/swig.i index f079d8a8b..973e6417c 100644 --- a/src/swig_java/swig.i +++ b/src/swig_java/swig.i @@ -472,6 +472,7 @@ static jbyteArray create_array(JNIEnv *jenv, const unsigned char* p, size_t len) %returns_struct(wally_witness_p2wpkh_from_sig, wally_tx_witness_stack); %returns_struct(wally_witness_p2wpkh_from_der, wally_tx_witness_stack); %returns_struct(wally_witness_multisig_from_bytes, wally_tx_witness_stack); +%returns_size_t(wally_elements_pegout_script_size); %returns_size_t(wally_elements_pegout_script_from_bytes); %returns_size_t(wally_elements_pegin_contract_script_from_bytes); %returns_void__(wally_scrypt); @@ -668,7 +669,7 @@ static jbyteArray create_array(JNIEnv *jenv, const unsigned char* p, size_t len) %returns_array_(wally_symmetric_key_from_seed, 3, 4, HMAC_SHA512_LEN); %returns_array_(wally_symmetric_key_from_parent, 6, 7, HMAC_SHA512_LEN); %returns_size_t(wally_asset_pak_whitelistproof_size); -%returns_size_t(wally_asset_pak_whitelistproof); +%returns_void__(wally_asset_pak_whitelistproof); %rename("_cleanup") wally_cleanup; %returns_void__(_cleanup) diff --git a/src/swig_python/python_extra.py_in b/src/swig_python/python_extra.py_in index 43f8374dc..282e65f75 100644 --- a/src/swig_python/python_extra.py_in +++ b/src/swig_python/python_extra.py_in @@ -226,7 +226,7 @@ if is_elements_build(): bip32_key_get_pub_key_tweak_sum = _wrap_bin(bip32_key_get_pub_key_tweak_sum, 32) def _epsfb_len_fn(bh, mcs, pk, whl, flag): - return len(bh) + len(mcs) + len(pk) + len(whl) + 1 + return elements_pegout_script_size(len(bh), len(mcs), len(pk), len(whl)) elements_pegout_script_from_bytes = _wrap_bin(elements_pegout_script_from_bytes, _epsfb_len_fn, resize=True) def _epcsfb_len_fn(rs, cs, flag): diff --git a/src/swig_python/swig.i b/src/swig_python/swig.i index 7ddef3a2e..1c877c357 100644 --- a/src/swig_python/swig.i +++ b/src/swig_python/swig.i @@ -1,8 +1,8 @@ /* - The automatic module importing varies between Swig3 and Swig4. - Make explicit so should work for both versions. - (Basically the swig3 version). -*/ + * The automatic module importing varies between Swig3 and Swig4. + * Make explicit so should work for both versions. + * (Basically the swig3 version). + */ %define MODULEIMPORT " def swig_import_helper(): @@ -110,12 +110,13 @@ static void destroy_words(PyObject *obj) { (void)obj; } %} /* - The behaviour of pybuffer_binary varies wrt a Py_None argument between Swig3 - (raises TypeError) and Swig4 (passes through as NULL) - so make explicit - 'nullable' and 'nonnull' macros for consistent behaviour across versions. - NOTE: the code in the 'else' branch is essentially taken from swig4's - pybuffer_binary macro implementation. -*/ + * The behaviour of pybuffer_binary varies wrt a Py_None argument between Swig3 + * (raises TypeError) and Swig4 (passes through as NULL) - so make explicit + * 'nullable' and 'nonnull' macros for consistent behaviour across versions. + * NOTE: the code in the 'else' branch is essentially taken from swig4's + * pybuffer_binary macro implementation. + * Note local fix for: https://github.com/swig/swig/issues/1640 + */ %define %pybuffer_nullable_binary(TYPEMAP, SIZE) %typemap(in) (TYPEMAP, SIZE) { int res; Py_ssize_t size = 0; const void *buf = 0; @@ -165,6 +166,7 @@ static void destroy_words(PyObject *obj) { (void)obj; } * FIXME: Remove in favour of pybuffer_mutable_binary when: * a) we move to swig4 * b) the call to Release() is fixed upstream + * see: https://github.com/swig/swig/issues/1640 */ %define %pybuffer_output_binary(TYPEMAP, SIZE) %typemap(in) (TYPEMAP, SIZE) {