ElementsProject / elements

Open Source implementation of advanced blockchain features extending the Bitcoin protocol
MIT License
1.06k stars 381 forks source link

RPC: Does blindrawtransaction with auxiliary_generators actually work? #783

Closed kallewoof closed 4 years ago

kallewoof commented 4 years ago

When doing blindrawtransaction and providing asset commitments for inputs not owned by the node, first the input_assets array is filled with null assets, because auxiliary_generators is non-zero size:

https://github.com/ElementsProject/elements/blob/a15622ad6cb0ba910459c301345568fb854bb851/src/wallet/rpcwallet.cpp#L5929-L5935

In BlindTransaction, called at the end, the surjection_targets array is then filled with these, empty, input_assets values:

https://github.com/ElementsProject/elements/blob/a15622ad6cb0ba910459c301345568fb854bb851/src/blind.cpp#L292

Additional 32*0x00 entries are added further down:

https://github.com/ElementsProject/elements/blob/a15622ad6cb0ba910459c301345568fb854bb851/src/blind.cpp#L335-L347

Later (with no further interaction with surjection_targets), we initialize an asset variable

https://github.com/ElementsProject/elements/blob/a15622ad6cb0ba910459c301345568fb854bb851/src/blind.cpp#L498-L501

which, I assume, is never null. We then make a call to SurjectOutput() passing our array of 32*0x00 surjection targets, and our non-null asset.

https://github.com/ElementsProject/elements/blob/a15622ad6cb0ba910459c301345568fb854bb851/src/blind.cpp#L562

which requires initialization of surjection proof to succeed for the first surjection target (which we have concluded is 32*0x00):

https://github.com/ElementsProject/elements/blob/a15622ad6cb0ba910459c301345568fb854bb851/src/blind.cpp#L135-L139

but this will never happen because in

https://github.com/ElementsProject/elements/blob/a15622ad6cb0ba910459c301345568fb854bb851/src/secp256k1/src/modules/surjection/main_impl.h#L246

fixed_input_tags is filled with zeroes, and fixed_output_tag == asset, thus non-null.

Am I missing something?

instagibbs commented 4 years ago

(contacted on IRC)

I believe this is intended behavior, and for the cases where each blinding agent doesn't own that particular input they're receiving, they'll need to instead use rawblindrawtransaction

kallewoof commented 4 years ago

As mentioned elsewhere, I am leaving this open as it is inconclusive at the moment whether this is dead code or not. If someone can provide an example using asset commitments that actually works, that would be glorious.

instagibbs commented 4 years ago

renamed and leaving open

dgpv commented 4 years ago

This example https://github.com/Simplexum/python-elementstx/blob/master/examples/asset-atomic-swap.py uses assetcommitments, Alice and Bob use them for blinding a swap tx where they don't own the inputs of each other (see https://github.com/Simplexum/python-elementstx/blob/master/examples/README.md for instructions how to run the example)

kallewoof commented 4 years ago

@dgpv Interesting. Where does it call blindrawtransaction though?

dgpv commented 4 years ago

It does not use RPC for blinding. transactions are blinded at https://github.com/Simplexum/python-elementstx/blob/b4c28475a10dbaf0f1a20abe49fed4c80406bf91/examples/asset-atomic-swap.py#L466 and https://github.com/Simplexum/python-elementstx/blob/b4c28475a10dbaf0f1a20abe49fed4c80406bf91/examples/asset-atomic-swap.py#L309

kallewoof commented 4 years ago

It does not use RPC for blinding.

In that case it's not really relevant, since this issue is about the RPC command.

Your code looks cool though. :)

dgpv commented 4 years ago

It is used though RPC in functional tests https://github.com/ElementsProject/elements/blob/95602ec21f0b3ec4a58f36bbc1c53bb757c97701/test/functional/feature_confidential_transactions.py#L529

dgpv commented 4 years ago

It is also used through RPC in this old asset demo: https://github.com/ElementsProject/confidential-assets-demo/blob/dc2f5e86fa2c7bf126f9767620de16c597a9dce0/src/charlie/main.go#L144

kallewoof commented 4 years ago

That assets demo was made by my colleagues, and no, it doesn't work anymore. I'm checking your first example, though, but need to compile elements and stuff.

kallewoof commented 4 years ago

The first example doesn't seem to be functional at the moment (master):

$ ./feature_confidential_transactions.py
2020-03-23T11:38:51.351000Z TestFramework (INFO): Initializing test directory /var/folders/w0/9h99kj194nb5zmkgl_nj6n600000gp/T/bitcoin_func_test_53gwdqbo
Testing wallet secret recovery
Test blech32 python roundtrip
General Confidential tests
2020-03-23T11:38:54.127000Z TestFramework (ERROR): Unexpected exception caught during testing
Traceback (most recent call last):
  File "/Users/you/workspace/elements/test/functional/test_framework/authproxy.py", line 105, in _request
    return self._get_response()
  File "/Users/you/workspace/elements/test/functional/test_framework/authproxy.py", line 151, in _get_response
    http_response = self.__conn.getresponse()
  File "/usr/local/Cellar/python/3.7.7/Frameworks/Python.framework/Versions/3.7/lib/python3.7/http/client.py", line 1344, in getresponse
    response.begin()
  File "/usr/local/Cellar/python/3.7.7/Frameworks/Python.framework/Versions/3.7/lib/python3.7/http/client.py", line 306, in begin
    version, status, reason = self._read_status()
  File "/usr/local/Cellar/python/3.7.7/Frameworks/Python.framework/Versions/3.7/lib/python3.7/http/client.py", line 275, in _read_status
    raise RemoteDisconnected("Remote end closed connection without"
http.client.RemoteDisconnected: Remote end closed connection without response

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/you/workspace/elements/test/functional/test_framework/test_framework.py", line 177, in main
    self.run_test()
  File "./feature_confidential_transactions.py", line 135, in run_test
    txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), node0, "", "", True)
  File "/Users/you/workspace/elements/test/functional/test_framework/coverage.py", line 47, in __call__
    return_val = self.auth_service_proxy_instance.__call__(*args, **kwargs)
  File "/Users/you/workspace/elements/test/functional/test_framework/authproxy.py", line 134, in __call__
    response = self._request('POST', self.__url.path, postdata.encode('utf-8'))
  File "/Users/you/workspace/elements/test/functional/test_framework/authproxy.py", line 109, in _request
    self.__conn.request(method, path, postdata, headers)
  File "/usr/local/Cellar/python/3.7.7/Frameworks/Python.framework/Versions/3.7/lib/python3.7/http/client.py", line 1252, in request
    self._send_request(method, url, body, headers, encode_chunked)
  File "/usr/local/Cellar/python/3.7.7/Frameworks/Python.framework/Versions/3.7/lib/python3.7/http/client.py", line 1298, in _send_request
    self.endheaders(body, encode_chunked=encode_chunked)
  File "/usr/local/Cellar/python/3.7.7/Frameworks/Python.framework/Versions/3.7/lib/python3.7/http/client.py", line 1247, in endheaders
    self._send_output(message_body, encode_chunked=encode_chunked)
  File "/usr/local/Cellar/python/3.7.7/Frameworks/Python.framework/Versions/3.7/lib/python3.7/http/client.py", line 1026, in _send_output
    self.send(msg)
  File "/usr/local/Cellar/python/3.7.7/Frameworks/Python.framework/Versions/3.7/lib/python3.7/http/client.py", line 966, in send
    self.connect()
  File "/usr/local/Cellar/python/3.7.7/Frameworks/Python.framework/Versions/3.7/lib/python3.7/http/client.py", line 938, in connect
    (self.host,self.port), self.timeout, self.source_address)
  File "/usr/local/Cellar/python/3.7.7/Frameworks/Python.framework/Versions/3.7/lib/python3.7/socket.py", line 728, in create_connection
    raise err
  File "/usr/local/Cellar/python/3.7.7/Frameworks/Python.framework/Versions/3.7/lib/python3.7/socket.py", line 716, in create_connection
    sock.connect(sa)
ConnectionRefusedError: [Errno 61] Connection refused
2020-03-23T11:38:54.183000Z TestFramework (INFO): Stopping nodes
2020-03-23T11:38:54.184000Z TestFramework.node0 (ERROR): Unable to stop node.
Traceback (most recent call last):
  File "/Users/you/workspace/elements/test/functional/test_framework/test_node.py", line 275, in stop_node
    self.stop(wait=wait)
  File "/Users/you/workspace/elements/test/functional/test_framework/coverage.py", line 47, in __call__
    return_val = self.auth_service_proxy_instance.__call__(*args, **kwargs)
  File "/Users/you/workspace/elements/test/functional/test_framework/authproxy.py", line 134, in __call__
    response = self._request('POST', self.__url.path, postdata.encode('utf-8'))
  File "/Users/you/workspace/elements/test/functional/test_framework/authproxy.py", line 104, in _request
    self.__conn.request(method, path, postdata, headers)
  File "/usr/local/Cellar/python/3.7.7/Frameworks/Python.framework/Versions/3.7/lib/python3.7/http/client.py", line 1252, in request
    self._send_request(method, url, body, headers, encode_chunked)
  File "/usr/local/Cellar/python/3.7.7/Frameworks/Python.framework/Versions/3.7/lib/python3.7/http/client.py", line 1263, in _send_request
    self.putrequest(method, url, **skips)
  File "/usr/local/Cellar/python/3.7.7/Frameworks/Python.framework/Versions/3.7/lib/python3.7/http/client.py", line 1108, in putrequest
    raise CannotSendRequest(self.__state)
http.client.CannotSendRequest: Request-sent
Traceback (most recent call last):
  File "./feature_confidential_transactions.py", line 710, in <module>
    CTTest ().main ()
  File "/Users/you/workspace/elements/test/functional/test_framework/test_framework.py", line 202, in main
    self.stop_nodes()
  File "/Users/you/workspace/elements/test/functional/test_framework/test_framework.py", line 381, in stop_nodes
    node.wait_until_stopped()
  File "/Users/you/workspace/elements/test/functional/test_framework/test_node.py", line 316, in wait_until_stopped
    wait_until(self.is_node_stopped, timeout=timeout)
  File "/Users/you/workspace/elements/test/functional/test_framework/util.py", line 229, in wait_until
    if predicate():
  File "/Users/you/workspace/elements/test/functional/test_framework/test_node.py", line 307, in is_node_stopped
    "Node returned non-zero exit code (%d) when stopping" % return_code)
AssertionError: [node 0] Node returned non-zero exit code (-10) when stopping
[node 2] Cleaning up leftover process
[node 1] Cleaning up leftover process
[node 0] Cleaning up leftover process
dgpv commented 4 years ago

I've just updated the asset-atomic-swap.py example so it supports p2wpkh addresses, and checked that it works on current Elements

dgpv commented 4 years ago

feature_confidential_transactions.py runs fine for me (head commit is 95602ec21f0b3ec4a58f36bbc1c53bb757c97701)

$ test/functional/feature_confidential_transactions.py 
2020-03-23T12:32:05.903000Z TestFramework (INFO): Initializing test directory /tmp/bitcoin_func_test_qtinc8ab
Testing wallet secret recovery
Test blech32 python roundtrip
General Confidential tests
Assets tests...
2020-03-23T12:32:31.631000Z TestFramework (INFO): Stopping nodes
2020-03-23T12:32:31.937000Z TestFramework (INFO): Cleaning up /tmp/bitcoin_func_test_qtinc8ab on exit
2020-03-23T12:32:31.937000Z TestFramework (INFO): Tests successful
kallewoof commented 4 years ago

Weird. But if it works for someone that's enough for me. Closing.

stevenroose commented 4 years ago

All the functional tests are ran on Travis CI and they also all work for me locally. Like Dmitry, I also run them from the root dit with ./tests/functional/feature_....py instead of form the functional dir. That might be the reason for your failure.