-
Notifications
You must be signed in to change notification settings - Fork 35.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RFC: libbitcoinconsensus #5235
RFC: libbitcoinconsensus #5235
Conversation
|
b659260
to
08b93ca
Compare
Er, sorry for the busted c/p. Fixed. |
This needs some additions to
|
/// Returns true if the input nIn of the serialized transaction pointed to by | ||
/// txTo correctly spends the scriptPubKey pointed to by scriptPubKey under | ||
/// the additional constraints specified by flags. | ||
EXPORT_SYMBOL bool bitcoinconsensus_verify_script(const unsigned char *scriptPubKey, const unsigned int scriptPubKeyLen, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the reason for having const
here on basic types like unsigned int
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we be using 'bool' here? That is C99. As we know, some platforms cough MSVC cough still don't implement that, so it may be better to just return a 0/1 int.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No reason, will change. ACK on not using bool as well.
Works for me. I patched main.cpp to write out script verification operations diff --git a/src/main.cpp b/src/main.cpp
index 2bff781..80433d8 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1359,8 +1359,16 @@ void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCach
bool CScriptCheck::operator()() const {
const CScript &scriptSig = ptxTo->vin[nIn].scriptSig;
- if (!VerifyScript(scriptSig, scriptPubKey, nFlags, CachingSignatureChecker(*ptxTo, nIn, cacheStore)))
+ bool rv;
+ if (!(rv=VerifyScript(scriptSig, scriptPubKey, nFlags, CachingSignatureChecker(*ptxTo, nIn, cacheStore))))
return error("CScriptCheck() : %s:%d VerifySignature failed", ptxTo->GetHash().ToString(), nIn);
+
+ CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
+ ssTx << *ptxTo;
+ std::string strTxTo = HexStr(ssTx.begin(), ssTx.end());
+ std::string strScriptPubKey = HexStr(scriptPubKey.begin(), scriptPubKey.end());
+ printf("verify_script %s %s %i %i %i\n", strTxTo.c_str(), strScriptPubKey.c_str(), nIn, nFlags, rv);
+
return true;
} Then, I wrote a Python script that uses ctypes to load and call into the library, verifying the operations written from __future__ import print_function, division
import json, binascii, os
import ctypes
from ctypes import c_uint
class BitcoinConsensus(object):
def __init__(self, path):
self.l = ctypes.CDLL(path)
def version(self):
return self.l.bitcoinconsensus_version()
def verify_script(self, scriptPubKey, txTo, nIn, flags):
return self.l.bitcoinconsensus_verify_script(scriptPubKey, c_uint(len(scriptPubKey)),
txTo, c_uint(len(txTo)),
c_uint(nIn), c_uint(flags))
if __name__ == '__main__':
l = BitcoinConsensus('./src/.libs/libbitcoinconsensus.so')
infile = '/tmp/scripttest.txt'
print('API version', l.version())
assert(l.version() == 0)
with open(infile, 'r') as f:
for i,line in enumerate(f):
d = line.split(' ')
txTo = binascii.a2b_hex(d[1])
scriptPubKey = binascii.a2b_hex(d[2])
nIn = int(d[3])
nFlags = int(d[4]) # as CScript bitfield
rv = int(d[5])
# check
r = l.verify_script(scriptPubKey, txTo, nIn, nFlags)
if r != rv:
print('Mismatch at line %i, verify returns %i versus %i (nFlags %i)' % (i+1, r, rv, nFlags))
|
@laanwj Thanks for testing! It sounds like a good idea to fail if the entire input isn't consumed. That can be reflected in a new error (error codes still need to be hooked up here, I'll do that once that PR is merged). |
hah! I suppose I'll do that now :) |
08b93ca
to
1b306c9
Compare
Rebased and updated for the comments above. Fixed the missing copyright, wrong name for include guard, and unnecessary const arguments, and squashed those into the original commits. The last new commit adds error codes for busted txto's, though it's not very graceful. The errors aren't related to the script, but to the tx.. so the codes are shoe-horned into the existing enum. Suggestions for a better approach are welcome. |
@theuni I'm not against a more-detailed failure reason in the API, but the current way is inconsistent. IMO if we're going to do this we need a ScriptError just for external consumption, just like we did for the flags. This keeps |
I prefer have our API, at least at version 0, to be defined by one header. And yes - keeping the internal and external definitions completely separate is better, not least because they have diverged already: the new codes BTW: If |
Currently libconsensus only has script verification, but since we plan to add more things in the future, I don't think it should be in script/, maybe have its own dir? |
@jtimon agree |
1b306c9
to
b494007
Compare
Updated after discussion on IRC. I went ahead and squashed it down since it was impossible to review commit-by-commit with things changing back and forth. Probably easier to review the new files wholly anyway. Sorry if that complicates at all. |
Briefly tested the .dll and .dylib enough to verify that they actually load and bitcoinconsensus_version() works, so presumably the objects themselves are in working order. |
API looks good to me now. @theuni
Only exported symbols are those two that we want:
Edit: test on windows completed succesfully. I executed the same above test in Python on windows - no problems. Tried to just load/unload the library a few times, just to try it out, no problems. |
ACK commithash ae6bb2f7ff016df27456d544842ec6bc5f5c7cfa https://dev.visucore.com/bitcoin/acks/5235 |
|
||
typedef enum bitcoinconsensus_txerror_t | ||
{ | ||
bitcoinconsensus_TX_ERR_OK = 0, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thinking about it IMO this OK should be generic: bitcoinconsensus_ERR_OK = 0
. It doesn't really matter whether this is a transaction OK or another error OK :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was meant to imply that the tx was accepted by the api, whether or not the verification succeeded. bitcoinconsensus_ERR_OK is a bit hazy, as it could imply that bitcoinconsensus_verify_script() itself succeeded.
Ultimately I think you're right, we just need to make it clear in the docs that it means "verification was attempted".
ACK. Compiling with -O2 -flto, and stripping results in an 83 kbyte .so file. Nice. It would be nice to have some proof of concept command-line tool that shows how to use the library. |
@laanwj "Appending arbitrary data to txTo did not cause verification to fail: is this expected? Should it check whether the entire input is consumed?" |
@jtimon That's changed now; a check was added that the passed transaction is deserialized completely. |
Needs rebase. |
…dflags For windows builds, exe's are always static, but libs should still conform to --enabled-shared and --enable-static.
They're not necessary, and not always supported. We only need to know about hidden and default.
ae6bb2f
to
9eb5a5f
Compare
Credit BlueMatt for libbitcoinsonsensus.h/cpp
They should be hooked up in other places as well, but this is a start.
dll's are no longer dynamically linked to libgcc/libstdc++/libssp
This ensures that users of the lib will be able to mangle the paths to work in their bundles.
9eb5a5f build: pad header for osx libs (Cory Fields) 9ed8979 build: fix static dll link for mingw (Cory Fields) 19df238 build: shared lib build should work reasonably well now (Cory Fields) 269efa3 build: add quick consensus lib tests (Cory Fields) cdd36c6 build: add --with-libs so that libs are optional (Cory Fields) 2cf5f16 build: add libbitcoinconsensus files and hook up the lib build (Cory Fields) ee64c53 build: remove internal/protected build attribute checks (Cory Fields) f36a40f build: check visibility attributes (Cory Fields) 811a765 build: mingw needs libssp for hardening with dlls (Cory Fields) e0077de build: make a distinction between static app ldflags and static lib ldflags (Cory Fields)
Some users may have libtool libs (.la) installed in their linker search paths. In this case, using -static-libtool-libs would try to link in .a's instead of shared libs. That would be harmless unless the .a was built in a way that would break linking, like non-fpic. What we really want is "-static" here. Despite its name, it's actually less aggressive than -static-libtool-libs. It causes only internal libs to be linked statically (libbitcoinconsensus is the one were'a after).
Some users may have libtool libs (.la) installed in their linker search paths. In this case, using -static-libtool-libs would try to link in .a's instead of shared libs. That would be harmless unless the .a was built in a way that would break linking, like non-fpic. What we really want is "-static" here. Despite its name, it's actually less aggressive than -static-libtool-libs. It causes only internal libs to be linked statically (libbitcoinconsensus is the one were'a after). (cherry picked from commit f618577) # Conflicts: # src/Makefile.test.include
…or Darwin targets de4238f build: consolidate reduced export checks (fanquake) 012bdec build: add building libconsensus to end-of-configure output (fanquake) 8f360e3 build: remove ax_gcc_func_attribute macro (fanquake) f054a08 build: remove AX_GCC_FUNC_ATTRIBUTE test for dllimport (fanquake) 7cd0a69 build: test for __declspec(dllexport) in configure (fanquake) 1624e17 build: remove duplicate visibility attribute detection (fanquake) Pull request description: Darwin targets do not have a `protected` visibility function attribute, see [LLVM explanation](https://github.com/llvm/llvm-project/blob/8e9a505139fbef7d2e6e9d0adfe1efc87326f9ef/clang/lib/Basic/Targets/OSTargets.h#L131). This means that the `AX_GCC_FUNC_ATTRIBUTE` check for `visibility` fails: ```bash configure:24513: checking for __attribute__((visibility)) configure:24537: g++ -std=c++11 -o conftest -g -O2 -DHAVE_BUILD_INFO -D__STDC_FORMAT_MACROS -DMAC_OSX -DOBJC_OLD_DISPATCH_PROTOTYPES=0 -Wl,-headerpad_max_install_names conftest.cpp >&5 conftest.cpp:35:56: warning: target does not support 'protected' visibility; using 'default' [-Wunsupported-visibility] int foo_pro( void ) __attribute__((visibility("protected"))); ^ 1 warning generated. configure:24537: $? = 0 configure:24550: result: no ``` This leads to `EXPORT_SYMBOL` being [defined to nothing](https://github.com/bitcoin/bitcoin/blob/f4de89edfa8be4501534fec0c662c650a4ce7ef2/src/script/bitcoinconsensus.h#L29), as `HAVE_FUNC_ATTRIBUTE_VISIBILITY` is not defined, and when building with reduced exports, you end up with a libbitcoinconsensus.dylib that doesn't export any `_bitcoinconsensus_*` symbols. ```bash ➜ git:(master) nm -C src/.libs/libbitcoinconsensus.dylib | rg _bitcoinconsensus_ ➜ git:(master) ``` We do have a [second check](https://github.com/bitcoin/bitcoin/blob/f4de89edfa8be4501534fec0c662c650a4ce7ef2/configure.ac#L882) for the `visibility` attribute, which works for Darwin as it's only testing for default visibility, however the result of this check isn't used at all. It was added in #4725, along with the `--enable-reduce-exports` option, however when libbitcoinconsensus was added in #5235, it used the results of the added `AX_GCC_FUNC_ATTRIBUTE` calls. This PR removes our usage of the AX_GCC_FUNC_ATTRIBUTE macro entirely, in favour of our own checks in configure. This meant adding a check for `dllexport`, which I've tested as working with both [GCC](https://gcc.gnu.org/onlinedocs/gcc/Microsoft-Windows-Function-Attributes.html) and [Clang](https://releases.llvm.org/10.0.0/tools/clang/docs/AttributeReference.html#dllexport) when building for Windows. I haven't added an equivalent check for `dllimport`, as we weren't actually using the result of that check, we're just testing that `MSC_VER` was defined before using. With these changes building a libbitcoinconsensus with reduced exports, when targeting Darwin, works as expected: ```bash ./autogen.sh ./configure --disable-tests --disable-bench --with-utils=no --with-daemon=no --with-gui=no --disable-wallet --with-libs=yes --enable-reduce-exports make -j8 ... nm -C src/.libs/libbitcoinconsensus.dylib | rg _bitcoinconsensus_ 000000000000a340 T _bitcoinconsensus_verify_script 00000000000097e0 T _bitcoinconsensus_verify_script_with_amount 000000000000a3c0 T _bitcoinconsensus_version ``` ```python >>> import ctypes >>> consensus = ctypes.CDLL("src/.libs/libbitcoinconsensus.dylib") >>> print(consensus.bitcoinconsensus_version()) 1 >>> exit() ``` TODO: Modify a CI job to compile with --enable-reduce-exports and check for symbols in shared lib? ACKs for top commit: laanwj: Code review ACK de4238f Tree-SHA512: d148f3c55d14dac6e9e5b718cc65bb557bcf6f663218d24bc9044b86281bd5dd3d931ebea79c336a58e8ed50d683218c0a9e75494f2267b91097665043e252ae
…ports for Darwin targets de4238f build: consolidate reduced export checks (fanquake) 012bdec build: add building libconsensus to end-of-configure output (fanquake) 8f360e3 build: remove ax_gcc_func_attribute macro (fanquake) f054a08 build: remove AX_GCC_FUNC_ATTRIBUTE test for dllimport (fanquake) 7cd0a69 build: test for __declspec(dllexport) in configure (fanquake) 1624e17 build: remove duplicate visibility attribute detection (fanquake) Pull request description: Darwin targets do not have a `protected` visibility function attribute, see [LLVM explanation](https://github.com/llvm/llvm-project/blob/8e9a505139fbef7d2e6e9d0adfe1efc87326f9ef/clang/lib/Basic/Targets/OSTargets.h#L131). This means that the `AX_GCC_FUNC_ATTRIBUTE` check for `visibility` fails: ```bash configure:24513: checking for __attribute__((visibility)) configure:24537: g++ -std=c++11 -o conftest -g -O2 -DHAVE_BUILD_INFO -D__STDC_FORMAT_MACROS -DMAC_OSX -DOBJC_OLD_DISPATCH_PROTOTYPES=0 -Wl,-headerpad_max_install_names conftest.cpp >&5 conftest.cpp:35:56: warning: target does not support 'protected' visibility; using 'default' [-Wunsupported-visibility] int foo_pro( void ) __attribute__((visibility("protected"))); ^ 1 warning generated. configure:24537: $? = 0 configure:24550: result: no ``` This leads to `EXPORT_SYMBOL` being [defined to nothing](https://github.com/bitcoin/bitcoin/blob/f4de89edfa8be4501534fec0c662c650a4ce7ef2/src/script/bitcoinconsensus.h#L29), as `HAVE_FUNC_ATTRIBUTE_VISIBILITY` is not defined, and when building with reduced exports, you end up with a libbitcoinconsensus.dylib that doesn't export any `_bitcoinconsensus_*` symbols. ```bash ➜ git:(master) nm -C src/.libs/libbitcoinconsensus.dylib | rg _bitcoinconsensus_ ➜ git:(master) ``` We do have a [second check](https://github.com/bitcoin/bitcoin/blob/f4de89edfa8be4501534fec0c662c650a4ce7ef2/configure.ac#L882) for the `visibility` attribute, which works for Darwin as it's only testing for default visibility, however the result of this check isn't used at all. It was added in bitcoin#4725, along with the `--enable-reduce-exports` option, however when libbitcoinconsensus was added in bitcoin#5235, it used the results of the added `AX_GCC_FUNC_ATTRIBUTE` calls. This PR removes our usage of the AX_GCC_FUNC_ATTRIBUTE macro entirely, in favour of our own checks in configure. This meant adding a check for `dllexport`, which I've tested as working with both [GCC](https://gcc.gnu.org/onlinedocs/gcc/Microsoft-Windows-Function-Attributes.html) and [Clang](https://releases.llvm.org/10.0.0/tools/clang/docs/AttributeReference.html#dllexport) when building for Windows. I haven't added an equivalent check for `dllimport`, as we weren't actually using the result of that check, we're just testing that `MSC_VER` was defined before using. With these changes building a libbitcoinconsensus with reduced exports, when targeting Darwin, works as expected: ```bash ./autogen.sh ./configure --disable-tests --disable-bench --with-utils=no --with-daemon=no --with-gui=no --disable-wallet --with-libs=yes --enable-reduce-exports make -j8 ... nm -C src/.libs/libbitcoinconsensus.dylib | rg _bitcoinconsensus_ 000000000000a340 T _bitcoinconsensus_verify_script 00000000000097e0 T _bitcoinconsensus_verify_script_with_amount 000000000000a3c0 T _bitcoinconsensus_version ``` ```python >>> import ctypes >>> consensus = ctypes.CDLL("src/.libs/libbitcoinconsensus.dylib") >>> print(consensus.bitcoinconsensus_version()) 1 >>> exit() ``` TODO: Modify a CI job to compile with --enable-reduce-exports and check for symbols in shared lib? ACKs for top commit: laanwj: Code review ACK de4238f Tree-SHA512: d148f3c55d14dac6e9e5b718cc65bb557bcf6f663218d24bc9044b86281bd5dd3d931ebea79c336a58e8ed50d683218c0a9e75494f2267b91097665043e252ae
This includes #5212, which will need to be merged before this one.
This adds a consensus lib that offers nothing more than transaction verification. It does not rely on any bitcoind globals or state, and boost is not required.
The external api still needs some attention, it's not intended to be final. What's here is just something to get started, I believe @sipa had some plans for mapping external api flags to internal ones.
Build-wise, I believe it's feature complete other than Windows dll's. They work fine, but libtool refuses to statically-link libc/libgcc. That can be fixed post-merge.