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
ZIP 143 test vectors #5
Conversation
Here is the patch required to run these test vectors in place of diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp
index a73bd6f..ee71af6 100644
--- a/src/test/sighash_tests.cpp
+++ b/src/test/sighash_tests.cpp
@@ -281,6 +281,7 @@ BOOST_AUTO_TEST_CASE(sighash_from_data)
std::string raw_tx, raw_script, sigHashHex;
int nIn, nHashType;
+ CAmount amount;
uint32_t consensusBranchId;
uint256 sh;
CTransaction tx;
@@ -292,13 +293,15 @@ BOOST_AUTO_TEST_CASE(sighash_from_data)
raw_script = test[1].get_str();
nIn = test[2].get_int();
nHashType = test[3].get_int();
- consensusBranchId = test[4].get_int();
- sigHashHex = test[5].get_str();
+ assert(-1 == NOT_AN_INPUT);
+ amount = test[4].get_int64();
+ consensusBranchId = test[5].get_int();
+ sigHashHex = test[6].get_str();
uint256 sh;
CDataStream stream(ParseHex(raw_tx), SER_NETWORK, PROTOCOL_VERSION);
stream >> tx;
-
+/*
CValidationState state;
if (tx.fOverwintered) {
// Note that OVERWINTER_MIN_CURRENT_VERSION and OVERWINTER_MAX_CURRENT_VERSION
@@ -321,7 +324,7 @@ BOOST_AUTO_TEST_CASE(sighash_from_data)
BOOST_CHECK_MESSAGE(CheckTransactionWithoutProofVerification(tx, state), strTest);
BOOST_CHECK(state.IsValid());
}
-
+*/
std::vector<unsigned char> raw = ParseHex(raw_script);
scriptCode.insert(scriptCode.end(), raw.begin(), raw.end());
} catch (...) {
@@ -329,7 +332,7 @@ BOOST_AUTO_TEST_CASE(sighash_from_data)
continue;
}
- sh = SignatureHash(scriptCode, tx, nIn, nHashType, 0, consensusBranchId);
+ sh = SignatureHash(scriptCode, tx, nIn, nHashType, amount, consensusBranchId);
BOOST_CHECK_MESSAGE(sh.GetHex() == sigHashHex, strTest);
}
} |
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.
The comment about a length field before a scriptPubKey
is blocking.
transaction.py
Outdated
|
||
|
||
def pack_g1(p): | ||
return struct.pack('b', 0x02 | (1 if p[0] else 0)) + p[1] |
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 should say that it's the BN-254 encoding, not the BLS12-381 encoding. Also document that p[1]
is big-endian (I know it doesn't make a difference for random proofs, but still).
Format character b
means signed char
; I guess it doesn't make a difference here because the high bit of the byte is not set, but I would use B
.
self.g_C = (rand.bool(), rand.b(32)) | ||
self.g_C_prime = (rand.bool(), rand.b(32)) | ||
self.g_K = (rand.bool(), rand.b(32)) | ||
self.g_H = (rand.bool(), rand.b(32)) |
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.
I'm not sure it matters much yet, but these field element encodings might be out of range.
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 is fine, it's not what we're testing.
transaction.py
Outdated
self.scriptPubKey = Script(rand) | ||
|
||
def __bytes__(self): | ||
return struct.pack('<Q', self.nValue) + bytes(self.scriptPubKey) |
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.
The scriptPubKey is preceded by a CompactSize length: https://en.bitcoin.it/wiki/Transaction#General_format_.28inside_a_block.29_of_each_output_of_a_transaction_-_Txout
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.
Yep, that's already being added by Script.__bytes__()
.
transaction.py
Outdated
for i in range(rand.u8() % 3): | ||
self.vJoinSplit.append(JoinSplit(rand)) | ||
if len(self.vJoinSplit): | ||
self.joinSplitPubKey = rand.b(32) |
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 might also be an invalid key encoding (not a problem, but add comment).
transaction.py
Outdated
self.nVersionGroupId == OVERWINTER_VERSION_GROUP_ID and \ | ||
self.nVersion == OVERWINTER_TX_VERSION | ||
|
||
ret += struct.pack('b', len(self.vin)) |
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 depends on self.vin
being short. I suggest writing a general CompactSize encoding function; it'll be clearer in intent as well as more general.
zip_0143.py
Outdated
|
||
|
||
# Currently assumes the nHashType is SIGHASHALL | ||
# and that there are no joinSplits |
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.
Is this comment correct? It does seem to implement hashing for JoinSplits.
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.
Oops, forgot to update it
zip_0143.py
Outdated
if not nHashType & SIGHASH_ANYONECANPAY: | ||
hashPrevouts = getHashPrevouts(tx) | ||
|
||
if (not nHashType & SIGHASH_ANYONECANPAY) and \ |
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 is correct, but write (not (nHashType & SIGHASH_ANYONECANPAY))
so that readers don't have to look up the precedence :-)
transaction.py
Outdated
self.randomSeed + \ | ||
b''.join(self.macs) + \ | ||
bytes(self.proof) + \ | ||
b''.join(self.ciphertexts) |
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.
Style nit: prefer parens to line continuations, i.e.
return (struct.pack(...) +
struct.pack(...) +
... +
b''.join(self.ciphertexts))
or
return (
struct.pack(...) +
struct.pack(...) +
... +
b''.join(self.ciphertexts)
)
zip_0143.py
Outdated
SIGHASH_ALL, | ||
SIGHASH_NONE, | ||
SIGHASH_SINGLE, | ||
]) |
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 doesn't test SIGHASH_ANYONECANPAY
.
zip_0143.py
Outdated
SIGHASH_NONE, | ||
SIGHASH_SINGLE, | ||
]) | ||
amount = rand.u64() % MAX_MONEY |
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 is very pedantic: rand.u64() % (MAX_MONEY+1)
.
(This would mainly act as documentation that the range is 0..MAX_MONEY inclusive.)
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.
The error in write_compact_size
is blocking. Everything else looks good.
zc_utils.py
Outdated
def write_compact_size(n): | ||
if n < 253: | ||
return struct.pack('B', n) | ||
elif n < 0xFFFF: |
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 is not correct; it is <= 0xFFFF
(see https://bitcoin.org/en/developer-reference#compactsize-unsigned-integers ).
zc_utils.py
Outdated
return struct.pack('B', n) | ||
elif n < 0xFFFF: | ||
return struct.pack('B', 253) + struct.pack('<H', n) | ||
elif n < 0xFFFFFFFF: |
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.
Similarly, <= 0xFFFFFFFF
.
zc_utils.py
Outdated
assert write_compact_size(252) == b'\xFC' | ||
assert write_compact_size(253) == b'\xFD\xFD\x00' | ||
assert write_compact_size(0xFFFE) == b'\xFD\xFE\xFF' | ||
assert write_compact_size(0xFFFF) == b'\xFE\xFF\xFF\x00\x00' |
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.
b'\FD\xFF\xFF'
zc_utils.py
Outdated
assert write_compact_size(0xFFFE) == b'\xFD\xFE\xFF' | ||
assert write_compact_size(0xFFFF) == b'\xFE\xFF\xFF\x00\x00' | ||
assert write_compact_size(0xFFFFFFFE) == b'\xFE\xFE\xFF\xFF\xFF' | ||
assert write_compact_size(0xFFFFFFFF) == b'\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00' |
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.
`b'\xFE\xFF\xFF\xFF\xFF'
Also add tests for 0x10000, 0x100000000 (8 '0's), and 0xFFFFFFFFFFFFFFFF (16 'F's).
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.
Flushing comments.
No support for JoinSplits yet. Co-authored-by: Jack Grigg <jack@z.cash>
Rebased on master to fix merge conflicts and address @daira's comments. I also squashed the commits from the second-to-last review round. |
Used to convert a -1 for JSON to Option::None in Rust
Fixed some issues I had when trying to use the test vectors in Rust. I have also manually confirmed that none of the previous test vectors' Rust output is altered by this PR. |
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.
Premature ACK, ignore.
transaction.py
Outdated
return self._script | ||
|
||
def __bytes__(self): | ||
return struct.pack('b', len(self._script)) + self._script |
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.
Note to self: needs CompactSize.
zip_0143.py
Outdated
SIGHASH_SINGLE = 3 | ||
SIGHASH_ANYONECANPAY = 0x80 | ||
|
||
NOT_AN_INPUT = -1 # For portability of the test vectors |
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.
I originally thought this should be NOT_AN_INPUT = 0xFFFFFFFF
, but in fact the C++ code defines it as UINT_MAX
regardless of the size of unsigned int
, so there is no correct cross-platform value to use other than -1. As long as the JSON test vectors use -1, this will work.
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.
utACK
self.g_C = (rand.bool(), rand.b(32)) | ||
self.g_C_prime = (rand.bool(), rand.b(32)) | ||
self.g_K = (rand.bool(), rand.b(32)) | ||
self.g_H = (rand.bool(), rand.b(32)) |
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 is fine, it's not what we're testing.
The Zcash output is almost identical to the existing
sighash.json
output, except that amounts are provided.