From 760907dcf5d09ecc5b2b3ef63cf177b0ad6c7d7c Mon Sep 17 00:00:00 2001 From: Nicholas Evans Date: Sat, 15 Jun 2019 03:51:02 -0500 Subject: [PATCH 01/27] Update provideBTCFundingProof and provideFraudBTCFundingProof Update to allow variable inputs --- implementation/contracts/deposit/Deposit.sol | 30 ++-- .../contracts/deposit/DepositFunding.sol | 170 +++++++----------- .../contracts/deposit/DepositUtils.sol | 102 ++++++++++- 3 files changed, 181 insertions(+), 121 deletions(-) diff --git a/implementation/contracts/deposit/Deposit.sol b/implementation/contracts/deposit/Deposit.sol index 0e2e8814c..4d204a236 100644 --- a/implementation/contracts/deposit/Deposit.sol +++ b/implementation/contracts/deposit/Deposit.sol @@ -177,37 +177,37 @@ contract Deposit { /// @notice Anyone may notify the deposit of a funding proof during funding fraud /// @dev We reward the funder the entire bond if this occurs - /// @param _bitcoinTx The bitcoin tx that purportedly contains the funding output /// @param _merkleProof The merkle proof of inclusion of the tx in the bitcoin block /// @param _index The index of the tx in the Bitcoin block (1-indexed) /// @param _bitcoinHeaders An array of tightly-packed bitcoin headers /// @return True if successful, False if prevented by timeout, otherwise revert - function provideFraudBTCFundingProof( - bytes _bitcoinTx, + function provideFraudBTCFundingProof( + bytes _version, + bytes _vin, + bytes _vout, + bytes _locktime, bytes _merkleProof, uint256 _index, + uint8 _outputIndex, bytes _bitcoinHeaders ) public returns (bool) { - self.provideFraudBTCFundingProof(_bitcoinTx, _merkleProof, _index, _bitcoinHeaders); + self.provideFraudBTCFundingProof(_version, _vin, _vout, _locktime, _merkleProof, _index, _outputIndex, _bitcoinHeaders); return true; - } + } - /// @notice Anyone may notify the deposit of a funding proof to activate the deposit - /// @dev This is the happy-path of the funding flow. It means that we have suecceeded - /// @param _bitcoinTx The bitcoin tx that purportedly contains the funding output - /// @param _merkleProof The merkle proof of inclusion of the tx in the bitcoin block - /// @param _index The index of the tx in the Bitcoin block (1-indexed) - /// @param _bitcoinHeaders An array of tightly-packed bitcoin headers - /// @return True if successful, False if prevented by timeout, otherwise revert function provideBTCFundingProof( - bytes _bitcoinTx, + bytes _version, + bytes _vin, + bytes _vout, + bytes _locktime, bytes _merkleProof, uint256 _index, + uint8 _outputIndex, bytes _bitcoinHeaders ) public returns (bool) { - self.provideBTCFundingProof(_bitcoinTx, _merkleProof, _index, _bitcoinHeaders); + self.provideBTCFundingProof(_version, _vin, _vout, _locktime, _merkleProof, _index, _outputIndex, _bitcoinHeaders); return true; - } + } // // FRAUD diff --git a/implementation/contracts/deposit/DepositFunding.sol b/implementation/contracts/deposit/DepositFunding.sol index e34865359..be5804eba 100644 --- a/implementation/contracts/deposit/DepositFunding.sol +++ b/implementation/contracts/deposit/DepositFunding.sol @@ -111,59 +111,6 @@ library DepositFunding { _d.depositBeneficiary().transfer(_seized); // Transfer whole amount } - /// @notice Parses a bitcoin tx to find an output paying the signing group PKH - /// @dev Reverts if no funding output found - /// @param _d deposit storage pointer - /// @param _bitcoinTx The bitcoin tx that should contain the funding output - /// @return The 8-byte LE encoded value, and the index of the output - function findAndParseFundingOutput( - DepositUtils.Deposit storage _d, - bytes _bitcoinTx - ) public view returns (bytes8, uint8) { - bytes8 _valueBytes; - bytes memory _output; - - // Find the output paying the signer PKH - // This will fail if there are more than 256 outputs - for (uint8 i = 0; i < _bitcoinTx.extractNumOutputs(); i++) { - _output = _bitcoinTx.extractOutputAtIndex(i); - if (keccak256(_output.extractHash()) == keccak256(abi.encodePacked(_d.signerPKH()))) { - _valueBytes = bytes8(_output.slice(0, 8).toBytes32()); - return (_valueBytes, i); - } - } - // If we don't return from inside the loop, we failed. - revert("Did not find output with correct PKH"); - } - - /// @notice Validates the funding tx and parses information from it - /// @dev Stateless SPV Proof & Bitcoin tx format documented elsewhere - /// @param _d deposit storage pointer - /// @param _bitcoinTx The bitcoin tx that purportedly contain the funding output - /// @param _merkleProof The merkle proof of inclusion of the tx in the bitcoin block - /// @param _index The index of the tx in the Bitcoin block (1-indexed) - /// @param _bitcoinHeaders An array of tightly-packed bitcoin headers - /// @return The 8-byte LE UTXO size in satoshi, the 36byte outpoint - function validateAndParseFundingSPVProof( - DepositUtils.Deposit storage _d, - bytes _bitcoinTx, - bytes _merkleProof, - uint256 _index, - bytes _bitcoinHeaders - ) public view returns (bytes8 _valueBytes, bytes _outpoint) { - uint8 _outputIndex; - bytes32 _txid = _d.checkProof(_bitcoinTx, _merkleProof, _index, _bitcoinHeaders); - (_valueBytes, _outputIndex) = findAndParseFundingOutput(_d, _bitcoinTx); - - // Don't validate deposits under the lot size - require(DepositUtils.bytes8LEToUint(_valueBytes) >= TBTCConstants.getLotSize(), "Deposit too small"); - - // The outpoint is the LE TXID plus the index of the output as a 4-byte LE int - // _outputIndex is a uint8, so we know it is only 1 byte - // Therefore, pad with 3 more bytes - _outpoint = abi.encodePacked(_txid, _outputIndex, hex"000000"); - } - /// @notice Anyone may notify the contract that signing group setup has timed out /// @dev We rely on the keep system punishes the signers in this case /// @param _d deposit storage pointer @@ -273,69 +220,41 @@ library DepositFunding { fundingFraudTeardown(_d); } - /// @notice Anyone may notify the deposit of a funding proof during funding fraud - /// @dev We reward the funder the entire bond if this occurs - /// @param _d deposit storage pointer - /// @param _bitcoinTx The bitcoin tx that purportedly contains the funding output - /// @param _merkleProof The merkle proof of inclusion of the tx in the bitcoin block - /// @param _index The index of the tx in the Bitcoin block (1-indexed) - /// @param _bitcoinHeaders An array of tightly-packed bitcoin headers - /// @return True if successful, False if prevented by timeout, otherwise revert - function provideFraudBTCFundingProof( - DepositUtils.Deposit storage _d, - bytes _bitcoinTx, - bytes _merkleProof, - uint256 _index, - bytes _bitcoinHeaders - ) public returns (bool) { - bytes8 _valueBytes; - bytes memory _outpoint; - require(_d.inFraudAwaitingBTCFundingProof(), "Not awaiting a funding proof during setup fraud"); - - (_valueBytes, _outpoint) = validateAndParseFundingSPVProof(_d, _bitcoinTx, _merkleProof, _index, _bitcoinHeaders); - - _d.setFailedSetup(); - _d.logSetupFailed(); - - // If the proof is accepted, update to failed, and distribute signer bonds - distributeSignerBondsToFunder(_d); - fundingFraudTeardown(_d); - - return true; - } - - /// @notice Anyone may notify the deposit of a funding proof to activate the deposit - /// @dev This is the happy-path of the funding flow. It means that we have suecceeded - /// @param _d deposit storage pointer - /// @param _bitcoinTx The bitcoin tx that purportedly contains the funding output - /// @param _merkleProof The merkle proof of inclusion of the tx in the bitcoin block - /// @param _index The index of the tx in the Bitcoin block (1-indexed) - /// @param _bitcoinHeaders An array of tightly-packed bitcoin headers - /// @return True if successful, False if prevented by timeout, otherwise revert + /// @notice Anyone may notify the deposit of a funding proof to activate the deposit + /// @dev This is the happy-path of the funding flow. It means that we have suecceeded + /// @param _version 4-byte version number + /// @param _vin length-prepended inputs + /// @param _vout length-prepended outputs + /// @param _locktime 4-byte locktime + /// @param _index transaction index + /// @param _outputIndex index of funding output in _vout + /// @param _merkleProof the merkle proof of inclusion + /// @param _bitcoinHeaders header chain for work proof + /// @return true if successful function provideBTCFundingProof( DepositUtils.Deposit storage _d, - bytes _bitcoinTx, + bytes _version, + bytes _vin, + bytes _vout, + bytes _locktime, bytes _merkleProof, uint256 _index, + uint8 _outputIndex, bytes _bitcoinHeaders ) public returns (bool) { bytes8 _valueBytes; - bytes memory _outpoint; - - require(_d.inAwaitingBTCFundingProof(), "Not awaiting funding"); + bytes32 txId = abi.encodePacked(_version, _vin, _vout, _locktime).hash256(); - // Design decision: - // We COULD revoke the funder bond here if the funding proof timeout has elapsed - // HOWEVER, that would only create a situation where the funder loses eerything - // It would be a large punishment for a small crime (being slightly late) - // So if the funder manages to call this before anyone notifies of timeout - // We let them have a freebie + require(_d.inAwaitingBTCFundingProof(), 'Not awaiting funding'); - (_valueBytes, _outpoint) = validateAndParseFundingSPVProof(_d, _bitcoinTx, _merkleProof, _index, _bitcoinHeaders); + _valueBytes = _d.findAndParseFundingOutput(_vout, _outputIndex); + + _d.checkFundingProof(txId, _bitcoinHeaders.extractMerkleRootLE().toBytes32(), _merkleProof, _index); + _d.evaluateProofDifficulty(_bitcoinHeaders); // Write down the UTXO info and set to active. Congratulations :) _d.utxoSizeBytes = _valueBytes; - _d.utxoOutpoint = _outpoint; + _d.utxoOutpoint = abi.encodePacked(txId, _outputIndex, hex'000000'); fundingTeardown(_d); _d.setActive(); @@ -350,4 +269,47 @@ library DepositFunding { return true; } + + /// @notice Anyone may notify the deposit of a funding proof to activate the deposit + /// @dev This is the happy-path of the funding flow. It means that we have suecceeded + /// @param _version 4-byte version number + /// @param _vin length-prepended inputs + /// @param _vout length-prepended outputs + /// @param _locktime 4-byte locktime + /// @param _index transaction index + /// @param _outputIndex index of funding output in _vout + /// @param _merkleProof the merkle proof of inclusion + /// @param _bitcoinHeaders header chain for work proof + /// @return true if successful + function provideFraudBTCFundingProof( + DepositUtils.Deposit storage _d, + bytes _version, + bytes _vin, + bytes _vout, + bytes _locktime, + bytes _merkleProof, + uint256 _index, + uint8 _outputIndex, + bytes _bitcoinHeaders + ) public returns (bool) { + bytes8 _valueBytes; + bytes32 txId = abi.encodePacked(_version, _vin, _vout, _locktime).hash256(); + + require(_d.inFraudAwaitingBTCFundingProof(), 'Not awaiting a funding proof during setup fraud'); + + _valueBytes = _d.findAndParseFundingOutput(_vout, _outputIndex); + + _d.checkFundingProof(txId, _bitcoinHeaders.extractMerkleRootLE().toBytes32(), _merkleProof, _index); + _d.evaluateProofDifficulty(_bitcoinHeaders); + + _d.setFailedSetup(); + _d.logSetupFailed(); + + // If the proof is accepted, update to failed, and distribute signer bonds + distributeSignerBondsToFunder(_d); + fundingFraudTeardown(_d); + + return true; + } + } diff --git a/implementation/contracts/deposit/DepositUtils.sol b/implementation/contracts/deposit/DepositUtils.sol index 2d8bf81cd..d202b32d5 100644 --- a/implementation/contracts/deposit/DepositUtils.sol +++ b/implementation/contracts/deposit/DepositUtils.sol @@ -118,19 +118,117 @@ library DepositUtils { bytes memory _locktime; bytes32 _txid; (_nIns, _ins, _nOuts, _outs, _locktime, _txid) = _bitcoinTx.parseTransaction(); - require(_txid != bytes32(0), "Failed tx parsing"); + require(_txid != bytes32(0), 'Failed tx parsing'); require( _txid.prove( _bitcoinHeaders.extractMerkleRootLE().toBytes32(), _merkleProof, _index), - "Tx merkle proof is not valid for provided header and tx"); + 'Tx merkle proof is not valid for provided header and tx'); evaluateProofDifficulty(_d, _bitcoinHeaders); return _txid; } + /// @notice Syntactically check an SPV proof for a bitcoin tx + /// @dev Stateless SPV Proof verification documented elsewhere + /// @param _d deposit storage pointer + /// @param _merkleRoot The Root of the merkle path + /// @param _merkleProof The merkle proof of inclusion of the tx in the bitcoin block + /// @param _index The index of the tx in the Bitcoin block (1-indexed) + function checkFundingProof( + Deposit storage _d, + bytes32 _txId, + bytes32 _merkleRoot, + bytes _merkleProof, + uint256 _index + ) public view { + require( + _txId.prove( + _merkleRoot, + _merkleProof, + _index), + "Tx merkle proof is not valid for provided header and tx"); + } + + /// @dev find funding ourput using provided index + /// @param _vout length-prepended outputs + /// @param _index index of funding output + /// @return funding value + function findAndParseFundingOutput( + DepositUtils.Deposit storage _d, + bytes _vout, + uint8 _index + ) public view returns (bytes8) { + bytes8 _valueBytes; + bytes memory _output; + uint8 _numOutputs; + + uint256 _n = (_vout.slice(0, 1)).bytesToUint(); + require(_n < 0xfd, "VarInts not supported"); + _numOutputs = uint8(_n); + + // Find the output paying the signer PKH + // This will fail if there are more than 256 outputs + _output = extractOutputAtIndex(_vout, _index); + if (keccak256(_output.extractHash()) == keccak256(abi.encodePacked(signerPKH(_d)))) { + _valueBytes = bytes8(_output.slice(0, 8).toBytes32()); + return _valueBytes; + } + // If we don't return from inside the loop, we failed. + revert('Did not find output with correct PKH'); + } + + /// @notice Extracts the output at a given index in the TxIns vector + /// @param _b The tx to evaluate + /// @param _index The 0-indexed location of the output to extract + /// @return The specified output + function extractOutputAtIndex(bytes _b, uint8 _index) public pure returns (bytes) { + + // Determine length of first ouput + uint _offset = 1; + uint _len = determineOutputLength(_b.slice(8 + _offset, 2)); + + // This loop moves forward, and then gets the len of the next one + for (uint i = 0; i < _index; i++) { + _offset = _offset + _len; + _len = determineOutputLength(_b.slice(8, 2)); + } + + // We now have the length and offset of the one we want + return _b.slice(_offset, _len); + } + + /// @notice Determines the length of an output + /// @dev 5 types: WPKH, WSH, PKH, SH, and OP_RETURN + /// @param _b 2 bytes from the start of the output script + /// @return The length indicated by the prefix, error if invalid length + function determineOutputLength(bytes _b) public pure returns (uint256) { + // P2WSH + if (keccak256(_b) == keccak256(hex"2200")) { return 43; } + + // P2WPKH + if (keccak256(_b) == keccak256(hex"1600")) { return 31; } + + // Legacy P2PKH + if (keccak256(_b) == keccak256(hex'1976')) { return 34; } + + // legacy P2SH + if (keccak256(_b) == keccak256(hex'17a9')) { return 32; } + + // OP_RETURN + if (keccak256(_b.slice(1, 1)) == keccak256(hex"6a")) { + uint _pushLen = (_b.slice(0, 1)).bytesToUint(); + require(_pushLen < 76, "Multi-byte pushes not supported"); + // 8 byte value + 1 byte len + len bytes data + return 9 + _pushLen; + } + // Error if we fall through the if statements + require(false, "Unable to determine output length"); + } + + /// @notice Calculates the amount of value at auction right now /// @dev We calculate the % of the auction that has elapsed, then scale the value up /// @param _d deposit storage pointer From 01b35ae047703c92a668db2ad2864fc899a8e388 Mon Sep 17 00:00:00 2001 From: Nicholas Evans Date: Sat, 15 Jun 2019 03:57:32 -0500 Subject: [PATCH 02/27] Make findAndParseFundingOutput helper functions internal --- implementation/contracts/deposit/DepositUtils.sol | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/implementation/contracts/deposit/DepositUtils.sol b/implementation/contracts/deposit/DepositUtils.sol index d202b32d5..64ba7480e 100644 --- a/implementation/contracts/deposit/DepositUtils.sol +++ b/implementation/contracts/deposit/DepositUtils.sol @@ -171,7 +171,7 @@ library DepositUtils { // Find the output paying the signer PKH // This will fail if there are more than 256 outputs - _output = extractOutputAtIndex(_vout, _index); + _output = _extractOutputAtIndex(_vout, _index); if (keccak256(_output.extractHash()) == keccak256(abi.encodePacked(signerPKH(_d)))) { _valueBytes = bytes8(_output.slice(0, 8).toBytes32()); return _valueBytes; @@ -184,16 +184,16 @@ library DepositUtils { /// @param _b The tx to evaluate /// @param _index The 0-indexed location of the output to extract /// @return The specified output - function extractOutputAtIndex(bytes _b, uint8 _index) public pure returns (bytes) { + function _extractOutputAtIndex(bytes _b, uint8 _index) internal pure returns (bytes) { // Determine length of first ouput uint _offset = 1; - uint _len = determineOutputLength(_b.slice(8 + _offset, 2)); + uint _len = _determineOutputLength(_b.slice(8 + _offset, 2)); // This loop moves forward, and then gets the len of the next one for (uint i = 0; i < _index; i++) { _offset = _offset + _len; - _len = determineOutputLength(_b.slice(8, 2)); + _len = _determineOutputLength(_b.slice(8, 2)); } // We now have the length and offset of the one we want @@ -204,7 +204,7 @@ library DepositUtils { /// @dev 5 types: WPKH, WSH, PKH, SH, and OP_RETURN /// @param _b 2 bytes from the start of the output script /// @return The length indicated by the prefix, error if invalid length - function determineOutputLength(bytes _b) public pure returns (uint256) { + function _determineOutputLength(bytes _b) internal pure returns (uint256) { // P2WSH if (keccak256(_b) == keccak256(hex"2200")) { return 43; } @@ -216,7 +216,7 @@ library DepositUtils { // legacy P2SH if (keccak256(_b) == keccak256(hex'17a9')) { return 32; } - + // OP_RETURN if (keccak256(_b.slice(1, 1)) == keccak256(hex"6a")) { uint _pushLen = (_b.slice(0, 1)).bytesToUint(); From 88035bcd969f114a99d0f90289a907dd29f71ce3 Mon Sep 17 00:00:00 2001 From: Nicholas Evans Date: Sat, 15 Jun 2019 04:27:33 -0500 Subject: [PATCH 03/27] Update tests for new funding proof --- implementation/test/DepositTest.js | 84 +++++++++++++++++------------- 1 file changed, 48 insertions(+), 36 deletions(-) diff --git a/implementation/test/DepositTest.js b/implementation/test/DepositTest.js index 328eaadb6..9f830dcf8 100644 --- a/implementation/test/DepositTest.js +++ b/implementation/test/DepositTest.js @@ -858,21 +858,24 @@ contract('Deposit', accounts => { }) describe('provideFraudBTCFundingProof', async () => { - // real tx from mainnet bitcoin, interpreted as funding tx - const currentDiff = 6353030562983 - const txid = '0x7c48181cb5c030655eea651c5e9aa808983f646465cbe9d01c227d99cfbc405f' - const txid_le = '0x5f40bccf997d221cd0e9cb6564643f9808a89a5e1c65ea5e6530c0b51c18487c' - const tx = '0x01000000000101913e39197867de39bff2c93c75173e086388ee7e8707c90ce4a02dd23f7d2c0d0000000000ffffffff012040351d0000000016001486e7303082a6a21d5837176bc808bf4828371ab602473044022046c3c852a2042ee01ffd7d8d252f297ccc67ae2aa1fac4170f50e8a90af5398802201585ffbbed6e812fb60c025d2f82ae115774279369610b0c76165b6c7132f2810121020c67643b5c862a1aa1afe0a77a28e51a21b08396a0acae69965b22d2a403fd1c4ec10800' - const proof = '0x5f40bccf997d221cd0e9cb6564643f9808a89a5e1c65ea5e6530c0b51c18487c886f7da48f4ccfe49283c678dedb376c89853ba46d9a297fe39e8dd557d1f8deb0fb1a28c03f71b267f3a33459b2566975b1653a1238947ed05edca17ef64181b1f09d858a6e25bae4b0e245993d4ea77facba8ed0371bb9b8a6724475bcdc9edf9ead30b61cf6714758b7c93d1b725f86c2a66a07dd291ef566eaa5a59516823d57fd50557f1d938cc2fb61fe0e1acee6f9cb618a9210688a2965c52feabee66d660a5e7f158e363dc464fca2bb1cc856173366d5d20b5cd513a3aab8ebc5be2bd196b783b8773af2472abcea3e32e97938283f7b454769aa1c064c311c3342a755029ee338664999bd8d432080eafae3ca86b52ad2e321e9e634a46c1bd0d174e38bcd4c59a0f0a78c5906c015ef4daf6beb0500a59f4cae00cd46069ce60db2182e74561028e4462f59f639c89b8e254602d6ad9c212b7c2af5db9275e48c467539c6af678d6f09214182df848bd79a06df706f7c3fddfdd95e6f27326c6217ee446543a443f82b711f48c173a769ae8d1e92a986bc76fca732f088bbe04995ba61df5961d7fa0a45cd7467e11f20932c7a0b74c59318e86581c6b5095548' - const index = 130 - const headerChain = '0x00e0ff3fd877ad23af1d0d3e0eb6a700d85b692975dacd36e47b1b00000000000000000095ba61df5961d7fa0a45cd7467e11f20932c7a0b74c59318e86581c6b509554876f6c65c114e2c17e42524d300000020994d3802da5adf80345261bcff2eb87ab7b70db786cb0000000000000000000003169efc259f6e4b5e1bfa469f06792d6f07976a098bff2940c8e7ed3105fdc5eff7c65c114e2c170c4dffc30000c020f898b7ea6a405728055b0627f53f42c57290fe78e0b91900000000000000000075472c91a94fa2aab73369c0686a58796949cf60976e530f6eb295320fa15a1b77f8c65c114e2c17387f1df00000002069137421fc274aa2c907dbf0ec4754285897e8aa36332b0000000000000000004308f2494b702c40e9d61991feb7a15b3be1d73ce988e354e52e7a4e611bd9c2a2f8c65c114e2c1740287df200000020ab63607b09395f856adaa69d553755d9ba5bd8d15da20a000000000000000000090ea7559cda848d97575cb9696c8e33ba7f38d18d5e2f8422837c354aec147839fbc65c114e2c175cf077d6000000200ab3612eac08a31a8fb1d9b5397f897db8d26f6cd83a230000000000000000006f4888720ecbf980ff9c983a8e2e60ad329cc7b130916c2bf2300ea54e412a9ed6fcc65c114e2c17d4fbb88500000020d3e51560f77628a26a8fad01c88f98bd6c9e4bc8703b180000000000000000008e2c6e62a1f4d45dd03be1e6692df89a4e3b1223a4dbdfa94cca94c04c22049992fdc65c114e2c17463edb5e' - const outputValue = 490029088 - const signerPubkeyX = '0xd4aee75e57179f7cd18adcbaa7e2fca4ff7b1b446df88bf0b4398e4a26965a6e' - const signerPubkeyY = '0xe8bfb23428a4efecb3ebdc636139de9a568ed427fff20d28baa33ed48e9c44e1' + // real tx from testnet bitcoin, interpreted as funding tx + const _bitcoinTx = '0x01000000000101179137fe810d6712b6b00c196c1b76a93240f396c53d15f4cd523995dad6f6880000000000ffffffff02581b000000000000160014d849b1e1cede2ac7d7188cf8700e97d6975c91c4e8030000000000001976a914d849b1e1cede2ac7d7188cf8700e97d6975c91c488ac0247304402204edb2a92223cd854755ec3e34ce9a0cbeb48ad88a603d4d268203a81245258440220465a1412b8676f1504fae0026c89df342b04b741fcbb55604930339242a5a1630121028896955d043b5a43957b21901f2cce9f0bfb484531b03ad6cd3153e45e73ee2e00000000'; + const _version = '0x01000000' + const _vin = `0x01179137fe810d6712b6b00c196c1b76a93240f396c53d15f4cd523995dad6f6880000000000ffffffff` + const _vout = '0x02581b000000000000160014d849b1e1cede2ac7d7188cf8700e97d6975c91c4e8030000000000001976a914d849b1e1cede2ac7d7188cf8700e97d6975c91c488ac' + const _fundingOutputIndex = 0; + const _locktime = '0x00000000' + const _merkleProof = '0xcb68238689609f8a5c3f159628d54f2de03aec2e36920ff633a7cd05043d97d8529898acab187401f4d1eed73d60cf2efb15361b48c82d84451980c3e645a8bbbc5d20f5aaed58e535d68761a166437bb92cc0c2fff5f9e7e5381e945c95f49cf971736c3bf71a38eaa2022d84f00a3492375bfc68e538ee292ae89e873eac62157d5ca1290a3b412e2adcdadf746c18952be2ddaa66bad24467be0f212b9c45a31bd04725f962b4b18f619e22cc1ab7e906c2cdcaa205d8e51cf1cf199fcf1de2da374c31f1fecb0f9ae6d8366df16da24a63c7c6504713f34bcac7fab3b12227415c10b23066c79980d7081cc31cd38009fa7b14fa807d862d81f9e6989ba6eb018246bbea71dabe571c4fb88b13bcc046569dfad297f406beceaa42ee2766ec5cdfbf35ff7ab6cd6c16744269966a2042dc6ce0b81ecf0a5f51ecd3655ed7'; + const _index = 71; + const _bitcoinHeaders = '0x00000020aaefe576c20c7680e8da9baf4db2fac1f59043b9afaf87361701000000000000ec5cdfbf35ff7ab6cd6c16744269966a2042dc6ce0b81ecf0a5f51ecd3655ed7bd81ee5c453e011a26018de900000020c3188a1be4ff654d8ff147e6f264200ecaeaef5bfe12d3e97000000000000000b3063a20a0567d88dfba3cd6ca4cf0b3bb6d03da5e2a3335a7f0495ce17004cc8086ee5cffff001d37da48a90000002092a7a8318c5e84369c7391b80ab80de961ad30e995244110b73f1600000000000b1d95fd860b17fb38e3c3ce63b623212f12b353dcfd892cfbc92ec9c9ed2684f189ee5c453e011ac750f246000000203991cb8866955373cbab078f25bf38d50a02587a2b342f1a7c00000000000000e865281766dee7ef0ed5786b56e9c05e57f41a50f95c08530e62ffe2f64fc65b598aee5c453e011aa90ed39000000020cc08a7635123b495dee29b1b795bdc9d08ac555cc194519f2f01000000000000b298a563189c2139e0d7e8cb9aa49b681d85b3dd8b930580c33a4af8e0a65b651d8fee5cffff001defe70bcd000000206f04e29a46f3bb4bd3d92f299f5c4fe1bfd4c7f6b6f72dc1b80c180000000000a1ab92186e8980b5a47446578647acbaa54030db04dc4d9d0e5672b9747ae98ecf90ee5c453e011a51e3a3b200000020dd9f42060a303fffd81b3c2cb9ae45897782ccf4e44fc7dfd700000000000000f67fe1436f88023fe2102f3a850c197715d8f394d6887350d95e08f223081911b591ee5c453e011a23a70f1400000020f8d36c426b6c9ddb8b902a33e62a08ba876988ca95ee89ec530000000000000044c9ac396e66df9dfe42f4f85acd871a1e82000f4e3025c20973fc29656851b03792ee5c453e011ad8c1e70d'; + const _signerPubkeyX = '0x8896955d043b5a43957b21901f2cce9f0bfb484531b03ad6cd3153e45e73ee2e' + const _signerPubkeyY = '0xf687f923b5896a409cb7e2b5ae456f61ac61862305c6ec86bd7421b5bce115e0' beforeEach(async () => { - await testInstance.setKeepInfo(0, 0, 0, signerPubkeyX, signerPubkeyY) - await deployed.SystemStub.setCurrentDiff(currentDiff) + const target = await deployed.BTCUtils.extractTarget(_bitcoinHeaders) + const difficulty = await deployed.BTCUtils.calculateDifficulty(target); + await testInstance.setKeepInfo(0, 0, 0, _signerPubkeyX, _signerPubkeyY) + await deployed.SystemStub.setCurrentDiff(difficulty) await testInstance.setState(utils.states.FRAUD_AWAITING_BTC_FUNDING_PROOF) await deployed.KeepStub.send(1000000, {from: accounts[0]}) }) @@ -880,7 +883,7 @@ contract('Deposit', accounts => { it('updates to setup failed, logs SetupFailed, ', async () => { const blockNumber = await web3.eth.getBlock('latest').number - await testInstance.provideFraudBTCFundingProof(tx, proof, index, headerChain) + await testInstance.provideFraudBTCFundingProof(_version, _vin, _vout, _locktime, _merkleProof, _index, _fundingOutputIndex, _bitcoinHeaders) const keepState = await testInstance.getKeepInfo.call() assert(keepState[0].eqn(0), 'Keep id not deconsted') @@ -899,7 +902,7 @@ contract('Deposit', accounts => { it('reverts if not awaiting a funding proof during setup fraud', async () => { try { await testInstance.setState(utils.states.START) - await testInstance.provideFraudBTCFundingProof(tx, proof, index, headerChain) + await testInstance.provideFraudBTCFundingProof(_version, _vin, _vout, _locktime, _merkleProof, _index, _fundingOutputIndex, _bitcoinHeaders) } catch (e) { assert.include(e.message, 'Not awaiting a funding proof during setup fraud') } @@ -912,7 +915,7 @@ contract('Deposit', accounts => { const initialBalance = await web3.eth.getBalance(beneficiary) const signerBond = await web3.eth.getBalance(deployed.KeepStub.address) - await testInstance.provideFraudBTCFundingProof(tx, proof, index, headerChain) + await testInstance.provideFraudBTCFundingProof(_version, _vin, _vout, _locktime, _merkleProof, _index, _fundingOutputIndex, _bitcoinHeaders) const balanceAfter = await web3.eth.getBalance(beneficiary) const balanceCheck = new BN(initialBalance).add(new BN(signerBond)) @@ -922,22 +925,28 @@ contract('Deposit', accounts => { }) describe('provideBTCFundingProof', async () => { - // real tx from mainnet bitcoin, interpreted as funding tx - const currentDiff = 6353030562983 - const txid = '0x7c48181cb5c030655eea651c5e9aa808983f646465cbe9d01c227d99cfbc405f' - const txid_le = '0x5f40bccf997d221cd0e9cb6564643f9808a89a5e1c65ea5e6530c0b51c18487c' - const tx = '0x01000000000101913e39197867de39bff2c93c75173e086388ee7e8707c90ce4a02dd23f7d2c0d0000000000ffffffff012040351d0000000016001486e7303082a6a21d5837176bc808bf4828371ab602473044022046c3c852a2042ee01ffd7d8d252f297ccc67ae2aa1fac4170f50e8a90af5398802201585ffbbed6e812fb60c025d2f82ae115774279369610b0c76165b6c7132f2810121020c67643b5c862a1aa1afe0a77a28e51a21b08396a0acae69965b22d2a403fd1c4ec10800' - const proof = '0x5f40bccf997d221cd0e9cb6564643f9808a89a5e1c65ea5e6530c0b51c18487c886f7da48f4ccfe49283c678dedb376c89853ba46d9a297fe39e8dd557d1f8deb0fb1a28c03f71b267f3a33459b2566975b1653a1238947ed05edca17ef64181b1f09d858a6e25bae4b0e245993d4ea77facba8ed0371bb9b8a6724475bcdc9edf9ead30b61cf6714758b7c93d1b725f86c2a66a07dd291ef566eaa5a59516823d57fd50557f1d938cc2fb61fe0e1acee6f9cb618a9210688a2965c52feabee66d660a5e7f158e363dc464fca2bb1cc856173366d5d20b5cd513a3aab8ebc5be2bd196b783b8773af2472abcea3e32e97938283f7b454769aa1c064c311c3342a755029ee338664999bd8d432080eafae3ca86b52ad2e321e9e634a46c1bd0d174e38bcd4c59a0f0a78c5906c015ef4daf6beb0500a59f4cae00cd46069ce60db2182e74561028e4462f59f639c89b8e254602d6ad9c212b7c2af5db9275e48c467539c6af678d6f09214182df848bd79a06df706f7c3fddfdd95e6f27326c6217ee446543a443f82b711f48c173a769ae8d1e92a986bc76fca732f088bbe04995ba61df5961d7fa0a45cd7467e11f20932c7a0b74c59318e86581c6b5095548' - const index = 130 - const headerChain = '0x00e0ff3fd877ad23af1d0d3e0eb6a700d85b692975dacd36e47b1b00000000000000000095ba61df5961d7fa0a45cd7467e11f20932c7a0b74c59318e86581c6b509554876f6c65c114e2c17e42524d300000020994d3802da5adf80345261bcff2eb87ab7b70db786cb0000000000000000000003169efc259f6e4b5e1bfa469f06792d6f07976a098bff2940c8e7ed3105fdc5eff7c65c114e2c170c4dffc30000c020f898b7ea6a405728055b0627f53f42c57290fe78e0b91900000000000000000075472c91a94fa2aab73369c0686a58796949cf60976e530f6eb295320fa15a1b77f8c65c114e2c17387f1df00000002069137421fc274aa2c907dbf0ec4754285897e8aa36332b0000000000000000004308f2494b702c40e9d61991feb7a15b3be1d73ce988e354e52e7a4e611bd9c2a2f8c65c114e2c1740287df200000020ab63607b09395f856adaa69d553755d9ba5bd8d15da20a000000000000000000090ea7559cda848d97575cb9696c8e33ba7f38d18d5e2f8422837c354aec147839fbc65c114e2c175cf077d6000000200ab3612eac08a31a8fb1d9b5397f897db8d26f6cd83a230000000000000000006f4888720ecbf980ff9c983a8e2e60ad329cc7b130916c2bf2300ea54e412a9ed6fcc65c114e2c17d4fbb88500000020d3e51560f77628a26a8fad01c88f98bd6c9e4bc8703b180000000000000000008e2c6e62a1f4d45dd03be1e6692df89a4e3b1223a4dbdfa94cca94c04c22049992fdc65c114e2c17463edb5e' - const outputValue = 490029088 - const outputValueBytes = '0x2040351d00000000' - const signerPubkeyX = '0xd4aee75e57179f7cd18adcbaa7e2fca4ff7b1b446df88bf0b4398e4a26965a6e' - const signerPubkeyY = '0xe8bfb23428a4efecb3ebdc636139de9a568ed427fff20d28baa33ed48e9c44e1' + // real tx from testnet bitcoin, interpreted as funding tx + const _bitcoinTx = '0x01000000000101179137fe810d6712b6b00c196c1b76a93240f396c53d15f4cd523995dad6f6880000000000ffffffff02581b000000000000160014d849b1e1cede2ac7d7188cf8700e97d6975c91c4e8030000000000001976a914d849b1e1cede2ac7d7188cf8700e97d6975c91c488ac0247304402204edb2a92223cd854755ec3e34ce9a0cbeb48ad88a603d4d268203a81245258440220465a1412b8676f1504fae0026c89df342b04b741fcbb55604930339242a5a1630121028896955d043b5a43957b21901f2cce9f0bfb484531b03ad6cd3153e45e73ee2e00000000'; + const txID = '0xd8973d0405cda733f60f92362eec3ae02d4fd52896153f5c8a9f6089862368cb' + const _version = '0x01000000' + const _vin = `0x01179137fe810d6712b6b00c196c1b76a93240f396c53d15f4cd523995dad6f6880000000000ffffffff` + const _vout = '0x02581b000000000000160014d849b1e1cede2ac7d7188cf8700e97d6975c91c4e8030000000000001976a914d849b1e1cede2ac7d7188cf8700e97d6975c91c488ac' + const _fundingOutputIndex = 0; + const _locktime = '0x00000000' + const _merkleProof = '0xcb68238689609f8a5c3f159628d54f2de03aec2e36920ff633a7cd05043d97d8529898acab187401f4d1eed73d60cf2efb15361b48c82d84451980c3e645a8bbbc5d20f5aaed58e535d68761a166437bb92cc0c2fff5f9e7e5381e945c95f49cf971736c3bf71a38eaa2022d84f00a3492375bfc68e538ee292ae89e873eac62157d5ca1290a3b412e2adcdadf746c18952be2ddaa66bad24467be0f212b9c45a31bd04725f962b4b18f619e22cc1ab7e906c2cdcaa205d8e51cf1cf199fcf1de2da374c31f1fecb0f9ae6d8366df16da24a63c7c6504713f34bcac7fab3b12227415c10b23066c79980d7081cc31cd38009fa7b14fa807d862d81f9e6989ba6eb018246bbea71dabe571c4fb88b13bcc046569dfad297f406beceaa42ee2766ec5cdfbf35ff7ab6cd6c16744269966a2042dc6ce0b81ecf0a5f51ecd3655ed7'; + const _merkleValid = '0xcb68238689609f8a5c3f159628d54f2de03aec2e36920ff633a7cd05043d97d800000000' + const _index = 71; + const _bitcoinHeaders = '0x00000020aaefe576c20c7680e8da9baf4db2fac1f59043b9afaf87361701000000000000ec5cdfbf35ff7ab6cd6c16744269966a2042dc6ce0b81ecf0a5f51ecd3655ed7bd81ee5c453e011a26018de900000020c3188a1be4ff654d8ff147e6f264200ecaeaef5bfe12d3e97000000000000000b3063a20a0567d88dfba3cd6ca4cf0b3bb6d03da5e2a3335a7f0495ce17004cc8086ee5cffff001d37da48a90000002092a7a8318c5e84369c7391b80ab80de961ad30e995244110b73f1600000000000b1d95fd860b17fb38e3c3ce63b623212f12b353dcfd892cfbc92ec9c9ed2684f189ee5c453e011ac750f246000000203991cb8866955373cbab078f25bf38d50a02587a2b342f1a7c00000000000000e865281766dee7ef0ed5786b56e9c05e57f41a50f95c08530e62ffe2f64fc65b598aee5c453e011aa90ed39000000020cc08a7635123b495dee29b1b795bdc9d08ac555cc194519f2f01000000000000b298a563189c2139e0d7e8cb9aa49b681d85b3dd8b930580c33a4af8e0a65b651d8fee5cffff001defe70bcd000000206f04e29a46f3bb4bd3d92f299f5c4fe1bfd4c7f6b6f72dc1b80c180000000000a1ab92186e8980b5a47446578647acbaa54030db04dc4d9d0e5672b9747ae98ecf90ee5c453e011a51e3a3b200000020dd9f42060a303fffd81b3c2cb9ae45897782ccf4e44fc7dfd700000000000000f67fe1436f88023fe2102f3a850c197715d8f394d6887350d95e08f223081911b591ee5c453e011a23a70f1400000020f8d36c426b6c9ddb8b902a33e62a08ba876988ca95ee89ec530000000000000044c9ac396e66df9dfe42f4f85acd871a1e82000f4e3025c20973fc29656851b03792ee5c453e011ad8c1e70d'; + const _signerPubkeyX = '0x8896955d043b5a43957b21901f2cce9f0bfb484531b03ad6cd3153e45e73ee2e' + const _signerPubkeyY = '0xf687f923b5896a409cb7e2b5ae456f61ac61862305c6ec86bd7421b5bce115e0' + const _value = 7000; + const _outValueBytes = '0x581b000000000000' beforeEach(async () => { - await testInstance.setKeepInfo(0, 0, 0, signerPubkeyX, signerPubkeyY) - await deployed.SystemStub.setCurrentDiff(currentDiff) + const target = await deployed.BTCUtils.extractTarget(_bitcoinHeaders) + const difficulty = await deployed.BTCUtils.calculateDifficulty(target); + await testInstance.setKeepInfo(0, 0, 0, _signerPubkeyX, _signerPubkeyY) + await deployed.SystemStub.setCurrentDiff(difficulty) await testInstance.setState(utils.states.AWAITING_BTC_FUNDING_PROOF) await deployed.KeepStub.send(1000000, {from: accounts[0]}) }) @@ -945,11 +954,11 @@ contract('Deposit', accounts => { it('updates to active, stores UTXO info, deconstes funding info, logs Funded', async () => { const blockNumber = await web3.eth.getBlock('latest').number - await testInstance.provideBTCFundingProof(tx, proof, index, headerChain) + await testInstance.provideBTCFundingProof(_version, _vin, _vout, _locktime, _merkleProof, _index, _fundingOutputIndex, _bitcoinHeaders) const UTXOInfo = await testInstance.getUTXOInfo.call() - assert.equal(UTXOInfo[0], outputValueBytes) - assert.equal(UTXOInfo[2], '0x5f40bccf997d221cd0e9cb6564643f9808a89a5e1c65ea5e6530c0b51c18487c00000000') + assert.equal(UTXOInfo[0], _outValueBytes) + assert.equal(UTXOInfo[2], _merkleValid) const keepState = await testInstance.getKeepInfo.call() assert(keepState[1].eqn(0), 'signingGroupRequestedAt not deconsted') @@ -965,7 +974,7 @@ contract('Deposit', accounts => { it('reverts if not awaiting funding proof', async () => { try { await testInstance.setState(utils.states.START) - await testInstance.provideBTCFundingProof(tx, proof, index, headerChain) + await testInstance.provideBTCFundingProof(_version, _vin, _vout, _locktime, _merkleProof, _index, _fundingOutputIndex, _bitcoinHeaders) } catch (e) { assert.include(e.message, 'Not awaiting funding') } @@ -975,17 +984,20 @@ contract('Deposit', accounts => { const beneficiary = accounts[4] const signerBond = 10000000000 const initialTokenBalance = await deployed.TBTCStub.getBalance(beneficiary) + await testInstance.send(signerBond, {from: beneficiary}) await deployed.SystemStub.setDepositOwner(0, beneficiary) + const initialBalance = await web3.eth.getBalance(beneficiary) - await testInstance.provideBTCFundingProof(tx, proof, index, headerChain) + await testInstance.provideBTCFundingProof(_version, _vin, _vout, _locktime, _merkleProof, _index, _fundingOutputIndex, _bitcoinHeaders) const balanceAfter = await web3.eth.getBalance(beneficiary) const balanceCheck = new BN(initialBalance).add(new BN(signerBond)) + assert.equal(balanceCheck, balanceAfter, 'funder bond not currectly returned') + const endingTokenBalancce = await deployed.TBTCStub.getBalance(beneficiary) - const lotSize = await deployed.TBTCConstants.getLotSize.call() const toMint = lotSize.mul(new BN(95)).div(new BN(100)); const tokenCheck = initialTokenBalance.add(new BN(toMint)) From aa7972427722d9babaeca8f3927cc8faefbd5dcd Mon Sep 17 00:00:00 2001 From: Nicholas Evans Date: Sun, 16 Jun 2019 03:41:11 -0500 Subject: [PATCH 04/27] Update NatSpec and parameters for solidity functions --- implementation/contracts/deposit/Deposit.sol | 35 +++++++++++++------ .../contracts/deposit/DepositFunding.sol | 14 ++++---- .../contracts/deposit/DepositUtils.sol | 8 +++-- implementation/test/DepositTest.js | 12 +++---- 4 files changed, 44 insertions(+), 25 deletions(-) diff --git a/implementation/contracts/deposit/Deposit.sol b/implementation/contracts/deposit/Deposit.sol index 4d204a236..a93f1960b 100644 --- a/implementation/contracts/deposit/Deposit.sol +++ b/implementation/contracts/deposit/Deposit.sol @@ -175,37 +175,52 @@ contract Deposit { return true; } - /// @notice Anyone may notify the deposit of a funding proof during funding fraud - /// @dev We reward the funder the entire bond if this occurs - /// @param _merkleProof The merkle proof of inclusion of the tx in the bitcoin block - /// @param _index The index of the tx in the Bitcoin block (1-indexed) - /// @param _bitcoinHeaders An array of tightly-packed bitcoin headers + /// @notice Anyone may notify the deposit of a funding proof to activate the deposit + /// @dev This is the happy-path of the funding flow. It means that we have succeeded + /// @param _version 4-byte version number + /// @param _vin length-prepended inputs + /// @param _vout length-prepended outputs + /// @param _locktime 4-byte locktime + /// @param _index transaction index + /// @param _merkleProof the merkle proof of inclusion + /// @param _outputIndex index of funding output in _vout + /// @param _bitcoinHeaders header chain for work proof /// @return True if successful, False if prevented by timeout, otherwise revert function provideFraudBTCFundingProof( bytes _version, bytes _vin, bytes _vout, bytes _locktime, - bytes _merkleProof, uint256 _index, + bytes _merkleProof, uint8 _outputIndex, bytes _bitcoinHeaders ) public returns (bool) { - self.provideFraudBTCFundingProof(_version, _vin, _vout, _locktime, _merkleProof, _index, _outputIndex, _bitcoinHeaders); + self.provideFraudBTCFundingProof(_version, _vin, _vout, _locktime, _index, _merkleProof, _outputIndex, _bitcoinHeaders); return true; } - + /// @notice Anyone may notify the deposit of a funding proof to activate the deposit + /// @dev This is the happy-path of the funding flow. It means that we have succeeded + /// @param _version 4-byte version number + /// @param _vin length-prepended inputs + /// @param _vout length-prepended outputs + /// @param _locktime 4-byte locktime + /// @param _index transaction index + /// @param _merkleProof the merkle proof of inclusion + /// @param _outputIndex index of funding output in _vout + /// @param _bitcoinHeaders header chain for work proof + /// @return true if successful function provideBTCFundingProof( bytes _version, bytes _vin, bytes _vout, bytes _locktime, - bytes _merkleProof, uint256 _index, + bytes _merkleProof, uint8 _outputIndex, bytes _bitcoinHeaders ) public returns (bool) { - self.provideBTCFundingProof(_version, _vin, _vout, _locktime, _merkleProof, _index, _outputIndex, _bitcoinHeaders); + self.provideBTCFundingProof(_version, _vin, _vout, _locktime, _index, _merkleProof, _outputIndex, _bitcoinHeaders); return true; } diff --git a/implementation/contracts/deposit/DepositFunding.sol b/implementation/contracts/deposit/DepositFunding.sol index be5804eba..97a9bc970 100644 --- a/implementation/contracts/deposit/DepositFunding.sol +++ b/implementation/contracts/deposit/DepositFunding.sol @@ -221,14 +221,15 @@ library DepositFunding { } /// @notice Anyone may notify the deposit of a funding proof to activate the deposit - /// @dev This is the happy-path of the funding flow. It means that we have suecceeded + /// @dev This is the happy-path of the funding flow. It means that we have succeeded + /// @param _d deposit storage pointer /// @param _version 4-byte version number /// @param _vin length-prepended inputs /// @param _vout length-prepended outputs /// @param _locktime 4-byte locktime /// @param _index transaction index - /// @param _outputIndex index of funding output in _vout /// @param _merkleProof the merkle proof of inclusion + /// @param _outputIndex index of funding output in _vout /// @param _bitcoinHeaders header chain for work proof /// @return true if successful function provideBTCFundingProof( @@ -237,8 +238,8 @@ library DepositFunding { bytes _vin, bytes _vout, bytes _locktime, - bytes _merkleProof, uint256 _index, + bytes _merkleProof, uint8 _outputIndex, bytes _bitcoinHeaders ) public returns (bool) { @@ -271,14 +272,15 @@ library DepositFunding { } /// @notice Anyone may notify the deposit of a funding proof to activate the deposit - /// @dev This is the happy-path of the funding flow. It means that we have suecceeded + /// @dev This is the happy-path of the funding flow. It means that we have succeeded + /// @param _d deposit storage pointer /// @param _version 4-byte version number /// @param _vin length-prepended inputs /// @param _vout length-prepended outputs /// @param _locktime 4-byte locktime /// @param _index transaction index - /// @param _outputIndex index of funding output in _vout /// @param _merkleProof the merkle proof of inclusion + /// @param _outputIndex index of funding output in _vout /// @param _bitcoinHeaders header chain for work proof /// @return true if successful function provideFraudBTCFundingProof( @@ -287,8 +289,8 @@ library DepositFunding { bytes _vin, bytes _vout, bytes _locktime, - bytes _merkleProof, uint256 _index, + bytes _merkleProof, uint8 _outputIndex, bytes _bitcoinHeaders ) public returns (bool) { diff --git a/implementation/contracts/deposit/DepositUtils.sol b/implementation/contracts/deposit/DepositUtils.sol index 64ba7480e..04f703a6c 100644 --- a/implementation/contracts/deposit/DepositUtils.sol +++ b/implementation/contracts/deposit/DepositUtils.sol @@ -98,7 +98,7 @@ library DepositUtils { /// @notice Syntactically check an SPV proof for a bitcoin tx /// @dev Stateless SPV Proof verification documented elsewhere - /// @param _d deposit storage pointer + /// @param _d deposit storage pointer /// @param _bitcoinTx The bitcoin tx that is purportedly included in the header chain /// @param _merkleProof The merkle proof of inclusion of the tx in the bitcoin block /// @param _index The index of the tx in the Bitcoin block (1-indexed) @@ -133,7 +133,8 @@ library DepositUtils { /// @notice Syntactically check an SPV proof for a bitcoin tx /// @dev Stateless SPV Proof verification documented elsewhere - /// @param _d deposit storage pointer + /// @param _d deposit storage pointer + /// @param _merkleRoot The ID of the Bitcoin transaction to check /// @param _merkleRoot The Root of the merkle path /// @param _merkleProof The merkle proof of inclusion of the tx in the bitcoin block /// @param _index The index of the tx in the Bitcoin block (1-indexed) @@ -153,9 +154,10 @@ library DepositUtils { } /// @dev find funding ourput using provided index + /// @param _d deposit storage pointer /// @param _vout length-prepended outputs /// @param _index index of funding output - /// @return funding value + /// @return funding value (bytes8) function findAndParseFundingOutput( DepositUtils.Deposit storage _d, bytes _vout, diff --git a/implementation/test/DepositTest.js b/implementation/test/DepositTest.js index 9f830dcf8..5cfa7e5a6 100644 --- a/implementation/test/DepositTest.js +++ b/implementation/test/DepositTest.js @@ -883,7 +883,7 @@ contract('Deposit', accounts => { it('updates to setup failed, logs SetupFailed, ', async () => { const blockNumber = await web3.eth.getBlock('latest').number - await testInstance.provideFraudBTCFundingProof(_version, _vin, _vout, _locktime, _merkleProof, _index, _fundingOutputIndex, _bitcoinHeaders) + await testInstance.provideFraudBTCFundingProof(_version, _vin, _vout, _locktime, _index, _merkleProof, _fundingOutputIndex, _bitcoinHeaders) const keepState = await testInstance.getKeepInfo.call() assert(keepState[0].eqn(0), 'Keep id not deconsted') @@ -902,7 +902,7 @@ contract('Deposit', accounts => { it('reverts if not awaiting a funding proof during setup fraud', async () => { try { await testInstance.setState(utils.states.START) - await testInstance.provideFraudBTCFundingProof(_version, _vin, _vout, _locktime, _merkleProof, _index, _fundingOutputIndex, _bitcoinHeaders) + await testInstance.provideFraudBTCFundingProof(_version, _vin, _vout, _locktime, _index, _merkleProof, _fundingOutputIndex, _bitcoinHeaders) } catch (e) { assert.include(e.message, 'Not awaiting a funding proof during setup fraud') } @@ -915,7 +915,7 @@ contract('Deposit', accounts => { const initialBalance = await web3.eth.getBalance(beneficiary) const signerBond = await web3.eth.getBalance(deployed.KeepStub.address) - await testInstance.provideFraudBTCFundingProof(_version, _vin, _vout, _locktime, _merkleProof, _index, _fundingOutputIndex, _bitcoinHeaders) + await testInstance.provideFraudBTCFundingProof(_version, _vin, _vout, _locktime, _index, _merkleProof, _fundingOutputIndex, _bitcoinHeaders) const balanceAfter = await web3.eth.getBalance(beneficiary) const balanceCheck = new BN(initialBalance).add(new BN(signerBond)) @@ -954,7 +954,7 @@ contract('Deposit', accounts => { it('updates to active, stores UTXO info, deconstes funding info, logs Funded', async () => { const blockNumber = await web3.eth.getBlock('latest').number - await testInstance.provideBTCFundingProof(_version, _vin, _vout, _locktime, _merkleProof, _index, _fundingOutputIndex, _bitcoinHeaders) + await testInstance.provideBTCFundingProof(_version, _vin, _vout, _locktime, _index, _merkleProof, _fundingOutputIndex, _bitcoinHeaders) const UTXOInfo = await testInstance.getUTXOInfo.call() assert.equal(UTXOInfo[0], _outValueBytes) @@ -974,7 +974,7 @@ contract('Deposit', accounts => { it('reverts if not awaiting funding proof', async () => { try { await testInstance.setState(utils.states.START) - await testInstance.provideBTCFundingProof(_version, _vin, _vout, _locktime, _merkleProof, _index, _fundingOutputIndex, _bitcoinHeaders) + await testInstance.provideBTCFundingProof(_version, _vin, _vout, _locktime, _index, _merkleProof, _fundingOutputIndex, _bitcoinHeaders) } catch (e) { assert.include(e.message, 'Not awaiting funding') } @@ -990,7 +990,7 @@ contract('Deposit', accounts => { const initialBalance = await web3.eth.getBalance(beneficiary) - await testInstance.provideBTCFundingProof(_version, _vin, _vout, _locktime, _merkleProof, _index, _fundingOutputIndex, _bitcoinHeaders) + await testInstance.provideBTCFundingProof(_version, _vin, _vout, _locktime, _index, _merkleProof, _fundingOutputIndex, _bitcoinHeaders) const balanceAfter = await web3.eth.getBalance(beneficiary) const balanceCheck = new BN(initialBalance).add(new BN(signerBond)) From c8645b2d57442b4a896a69480c1afe4b0727419e Mon Sep 17 00:00:00 2001 From: Nicholas Evans Date: Sun, 16 Jun 2019 14:22:15 -0500 Subject: [PATCH 05/27] Remove unneeded bitcoin-spv function from DepositUtils --- .../contracts/deposit/DepositUtils.sol | 33 ++----------------- 1 file changed, 2 insertions(+), 31 deletions(-) diff --git a/implementation/contracts/deposit/DepositUtils.sol b/implementation/contracts/deposit/DepositUtils.sol index 04f703a6c..b959e069e 100644 --- a/implementation/contracts/deposit/DepositUtils.sol +++ b/implementation/contracts/deposit/DepositUtils.sol @@ -190,47 +190,18 @@ library DepositUtils { // Determine length of first ouput uint _offset = 1; - uint _len = _determineOutputLength(_b.slice(8 + _offset, 2)); + uint _len = (_b.slice(8 + _offset, 2)).determineOutputLength(); // This loop moves forward, and then gets the len of the next one for (uint i = 0; i < _index; i++) { _offset = _offset + _len; - _len = _determineOutputLength(_b.slice(8, 2)); + _len = (_b.slice(8, 2)).determineOutputLength(); } // We now have the length and offset of the one we want return _b.slice(_offset, _len); } - /// @notice Determines the length of an output - /// @dev 5 types: WPKH, WSH, PKH, SH, and OP_RETURN - /// @param _b 2 bytes from the start of the output script - /// @return The length indicated by the prefix, error if invalid length - function _determineOutputLength(bytes _b) internal pure returns (uint256) { - // P2WSH - if (keccak256(_b) == keccak256(hex"2200")) { return 43; } - - // P2WPKH - if (keccak256(_b) == keccak256(hex"1600")) { return 31; } - - // Legacy P2PKH - if (keccak256(_b) == keccak256(hex'1976')) { return 34; } - - // legacy P2SH - if (keccak256(_b) == keccak256(hex'17a9')) { return 32; } - - // OP_RETURN - if (keccak256(_b.slice(1, 1)) == keccak256(hex"6a")) { - uint _pushLen = (_b.slice(0, 1)).bytesToUint(); - require(_pushLen < 76, "Multi-byte pushes not supported"); - // 8 byte value + 1 byte len + len bytes data - return 9 + _pushLen; - } - // Error if we fall through the if statements - require(false, "Unable to determine output length"); - } - - /// @notice Calculates the amount of value at auction right now /// @dev We calculate the % of the auction that has elapsed, then scale the value up /// @param _d deposit storage pointer From d48dbfa64757e7f33023ce7756680663aa7beeac Mon Sep 17 00:00:00 2001 From: Nicholas Evans Date: Sun, 16 Jun 2019 14:27:57 -0500 Subject: [PATCH 06/27] Fix solidity linting errors/warnings --- implementation/contracts/deposit/Deposit.sol | 6 +++--- .../contracts/deposit/DepositFunding.sol | 10 +++++----- .../contracts/deposit/DepositUtils.sol | 18 +++++++++--------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/implementation/contracts/deposit/Deposit.sol b/implementation/contracts/deposit/Deposit.sol index a93f1960b..4c527d8e4 100644 --- a/implementation/contracts/deposit/Deposit.sol +++ b/implementation/contracts/deposit/Deposit.sol @@ -186,7 +186,7 @@ contract Deposit { /// @param _outputIndex index of funding output in _vout /// @param _bitcoinHeaders header chain for work proof /// @return True if successful, False if prevented by timeout, otherwise revert - function provideFraudBTCFundingProof( + function provideFraudBTCFundingProof( bytes _version, bytes _vin, bytes _vout, @@ -198,7 +198,7 @@ contract Deposit { ) public returns (bool) { self.provideFraudBTCFundingProof(_version, _vin, _vout, _locktime, _index, _merkleProof, _outputIndex, _bitcoinHeaders); return true; - } + } /// @notice Anyone may notify the deposit of a funding proof to activate the deposit /// @dev This is the happy-path of the funding flow. It means that we have succeeded /// @param _version 4-byte version number @@ -222,7 +222,7 @@ contract Deposit { ) public returns (bool) { self.provideBTCFundingProof(_version, _vin, _vout, _locktime, _index, _merkleProof, _outputIndex, _bitcoinHeaders); return true; - } + } // // FRAUD diff --git a/implementation/contracts/deposit/DepositFunding.sol b/implementation/contracts/deposit/DepositFunding.sol index 97a9bc970..ef68c9367 100644 --- a/implementation/contracts/deposit/DepositFunding.sol +++ b/implementation/contracts/deposit/DepositFunding.sol @@ -246,16 +246,16 @@ library DepositFunding { bytes8 _valueBytes; bytes32 txId = abi.encodePacked(_version, _vin, _vout, _locktime).hash256(); - require(_d.inAwaitingBTCFundingProof(), 'Not awaiting funding'); + require(_d.inAwaitingBTCFundingProof(), "Not awaiting funding"); _valueBytes = _d.findAndParseFundingOutput(_vout, _outputIndex); - + _d.checkFundingProof(txId, _bitcoinHeaders.extractMerkleRootLE().toBytes32(), _merkleProof, _index); _d.evaluateProofDifficulty(_bitcoinHeaders); // Write down the UTXO info and set to active. Congratulations :) _d.utxoSizeBytes = _valueBytes; - _d.utxoOutpoint = abi.encodePacked(txId, _outputIndex, hex'000000'); + _d.utxoOutpoint = abi.encodePacked(txId, _outputIndex, hex"000000"); fundingTeardown(_d); _d.setActive(); @@ -297,10 +297,10 @@ library DepositFunding { bytes8 _valueBytes; bytes32 txId = abi.encodePacked(_version, _vin, _vout, _locktime).hash256(); - require(_d.inFraudAwaitingBTCFundingProof(), 'Not awaiting a funding proof during setup fraud'); + require(_d.inFraudAwaitingBTCFundingProof(), "Not awaiting a funding proof during setup fraud"); _valueBytes = _d.findAndParseFundingOutput(_vout, _outputIndex); - + _d.checkFundingProof(txId, _bitcoinHeaders.extractMerkleRootLE().toBytes32(), _merkleProof, _index); _d.evaluateProofDifficulty(_bitcoinHeaders); diff --git a/implementation/contracts/deposit/DepositUtils.sol b/implementation/contracts/deposit/DepositUtils.sol index b959e069e..8e7e1c227 100644 --- a/implementation/contracts/deposit/DepositUtils.sol +++ b/implementation/contracts/deposit/DepositUtils.sol @@ -118,13 +118,13 @@ library DepositUtils { bytes memory _locktime; bytes32 _txid; (_nIns, _ins, _nOuts, _outs, _locktime, _txid) = _bitcoinTx.parseTransaction(); - require(_txid != bytes32(0), 'Failed tx parsing'); + require(_txid != bytes32(0), "Failed tx parsing"); require( _txid.prove( _bitcoinHeaders.extractMerkleRootLE().toBytes32(), _merkleProof, _index), - 'Tx merkle proof is not valid for provided header and tx'); + "Tx merkle proof is not valid for provided header and tx"); evaluateProofDifficulty(_d, _bitcoinHeaders); @@ -174,12 +174,12 @@ library DepositUtils { // Find the output paying the signer PKH // This will fail if there are more than 256 outputs _output = _extractOutputAtIndex(_vout, _index); - if (keccak256(_output.extractHash()) == keccak256(abi.encodePacked(signerPKH(_d)))) { - _valueBytes = bytes8(_output.slice(0, 8).toBytes32()); - return _valueBytes; - } + if (keccak256(_output.extractHash()) == keccak256(abi.encodePacked(signerPKH(_d)))) { + _valueBytes = bytes8(_output.slice(0, 8).toBytes32()); + return _valueBytes; + } // If we don't return from inside the loop, we failed. - revert('Did not find output with correct PKH'); + revert("Did not find output with correct PKH"); } /// @notice Extracts the output at a given index in the TxIns vector @@ -191,13 +191,13 @@ library DepositUtils { // Determine length of first ouput uint _offset = 1; uint _len = (_b.slice(8 + _offset, 2)).determineOutputLength(); - + // This loop moves forward, and then gets the len of the next one for (uint i = 0; i < _index; i++) { _offset = _offset + _len; _len = (_b.slice(8, 2)).determineOutputLength(); } - + // We now have the length and offset of the one we want return _b.slice(_offset, _len); } From 5dcc57831ecc35409165abc977eb01822a386ea3 Mon Sep 17 00:00:00 2001 From: Nicholas Evans Date: Tue, 18 Jun 2019 01:50:03 -0500 Subject: [PATCH 07/27] Update NatSpec and variable naming --- implementation/contracts/deposit/Deposit.sol | 88 +++++++----- .../contracts/deposit/DepositFunding.sol | 134 +++++++++--------- .../contracts/deposit/DepositUtils.sol | 70 +++++---- implementation/test/DepositTest.js | 26 ++-- implementation/test/DepositUtilsTest.js | 4 +- .../contracts/deposit/TestTBTCConstants.sol | 2 +- 6 files changed, 173 insertions(+), 151 deletions(-) diff --git a/implementation/contracts/deposit/Deposit.sol b/implementation/contracts/deposit/Deposit.sol index 4c527d8e4..a01700b0f 100644 --- a/implementation/contracts/deposit/Deposit.sol +++ b/implementation/contracts/deposit/Deposit.sol @@ -175,53 +175,71 @@ contract Deposit { return true; } - /// @notice Anyone may notify the deposit of a funding proof to activate the deposit - /// @dev This is the happy-path of the funding flow. It means that we have succeeded - /// @param _version 4-byte version number - /// @param _vin length-prepended inputs - /// @param _vout length-prepended outputs - /// @param _locktime 4-byte locktime - /// @param _index transaction index - /// @param _merkleProof the merkle proof of inclusion - /// @param _outputIndex index of funding output in _vout - /// @param _bitcoinHeaders header chain for work proof - /// @return True if successful, False if prevented by timeout, otherwise revert + /// @notice Anyone may notify the deposit of a funding proof to activate the deposit + /// @dev This is the happy-path of the funding flow. It means that we have succeeded + /// @param _txVersion 4-byte LE version number + /// @param _txInputVector All inputs prepended by the number of inputs encoded as a VarInt, max 0xFC inputs + /// @param _txOutputVector All outputs prepended by the number of outputs encoded as a VarInt, max 0xFC outputs + /// @param _locktime Final 4 bytes of the BTC transaction. Needed to calculate txId + /// @param _fundingOutputIndex Index of funding output in _txOutputVector (0-indexed) + /// @param _merkleProof The merkle proof of transaction inclusin in a block + /// @param _txIndexInBlock Transaction index in the block (1-indexed) + /// @param _bitcoinHeaders Single bytestring of 80-byte bitcoin headers, lowest height first + /// @return True if successful function provideFraudBTCFundingProof( - bytes _version, - bytes _vin, - bytes _vout, + bytes _txVersion, + bytes _txInputVector, + bytes _txOutputVector, bytes _locktime, - uint256 _index, + uint8 _fundingOutputIndex, bytes _merkleProof, - uint8 _outputIndex, + uint256 _txIndexInBlock, bytes _bitcoinHeaders ) public returns (bool) { - self.provideFraudBTCFundingProof(_version, _vin, _vout, _locktime, _index, _merkleProof, _outputIndex, _bitcoinHeaders); + self.provideFraudBTCFundingProof( + _txVersion, + _txInputVector, + _txOutputVector, + _locktime, + _fundingOutputIndex, + _merkleProof, + _txIndexInBlock, + _bitcoinHeaders + ); return true; } - /// @notice Anyone may notify the deposit of a funding proof to activate the deposit - /// @dev This is the happy-path of the funding flow. It means that we have succeeded - /// @param _version 4-byte version number - /// @param _vin length-prepended inputs - /// @param _vout length-prepended outputs - /// @param _locktime 4-byte locktime - /// @param _index transaction index - /// @param _merkleProof the merkle proof of inclusion - /// @param _outputIndex index of funding output in _vout - /// @param _bitcoinHeaders header chain for work proof - /// @return true if successful + + /// @notice Anyone may notify the deposit of a funding proof to activate the deposit + /// @dev This is the happy-path of the funding flow. It means that we have succeeded + /// @param _txVersion 4-byte LE version number + /// @param _txInputVector All inputs prepended by the number of inputs encoded as a VarInt, max 0xFC inputs + /// @param _txOutputVector All outputs prepended by the number of outputs encoded as a VarInt, max 0xFC outputs + /// @param _locktime Final 4 bytes of the BTC transaction. Needed to calculate txId + /// @param _fundingOutputIndex Index of funding output in _txOutputVector (0-indexed) + /// @param _merkleProof The merkle proof of transaction inclusin in a block + /// @param _txIndexInBlock Transaction index in the block (1-indexed) + /// @param _bitcoinHeaders Single bytestring of 80-byte bitcoin headers, lowest height first + /// @return True if successful function provideBTCFundingProof( - bytes _version, - bytes _vin, - bytes _vout, + bytes _txVersion, + bytes _txInputVector, + bytes _txOutputVector, bytes _locktime, - uint256 _index, + uint8 _fundingOutputIndex, bytes _merkleProof, - uint8 _outputIndex, + uint256 _txIndexInBlock, bytes _bitcoinHeaders ) public returns (bool) { - self.provideBTCFundingProof(_version, _vin, _vout, _locktime, _index, _merkleProof, _outputIndex, _bitcoinHeaders); - return true; + self.provideBTCFundingProof( + _txVersion, + _txInputVector, + _txOutputVector, + _locktime, + _fundingOutputIndex, + _merkleProof, + _txIndexInBlock, + _bitcoinHeaders + ); return true; } // diff --git a/implementation/contracts/deposit/DepositFunding.sol b/implementation/contracts/deposit/DepositFunding.sol index ef68c9367..8df8964be 100644 --- a/implementation/contracts/deposit/DepositFunding.sol +++ b/implementation/contracts/deposit/DepositFunding.sol @@ -220,96 +220,102 @@ library DepositFunding { fundingFraudTeardown(_d); } - /// @notice Anyone may notify the deposit of a funding proof to activate the deposit - /// @dev This is the happy-path of the funding flow. It means that we have succeeded - /// @param _d deposit storage pointer - /// @param _version 4-byte version number - /// @param _vin length-prepended inputs - /// @param _vout length-prepended outputs - /// @param _locktime 4-byte locktime - /// @param _index transaction index - /// @param _merkleProof the merkle proof of inclusion - /// @param _outputIndex index of funding output in _vout - /// @param _bitcoinHeaders header chain for work proof - /// @return true if successful - function provideBTCFundingProof( + /// @notice Anyone may notify the deposit of a funding proof to activate the deposit + /// @dev This is the happy-path of the funding flow. It means that we have succeeded + /// @param _d Deposit storage pointer + /// @param _txVersion 4-byte LE version number + /// @param _txInputVector All inputs prepended by the number of inputs encoded as a VarInt, max 0xFC inputs + /// @param _txOutputVector All outputs prepended by the number of outputs encoded as a VarInt, max 0xFC outputs + /// @param _locktime Final 4 bytes of the BTC transaction. Needed to calculate txId + /// @param _fundingOutputIndex Index of funding output in _txOutputVector (0-indexed) + /// @param _merkleProof The merkle proof of transaction inclusin in a block + /// @param _txIndexInBlock Transaction index in the block (1-indexed) + /// @param _bitcoinHeaders Single bytestring of 80-byte bitcoin headers, lowest height first + /// @return True if successful + function provideFraudBTCFundingProof( DepositUtils.Deposit storage _d, - bytes _version, - bytes _vin, - bytes _vout, + bytes _txVersion, + bytes _txInputVector, + bytes _txOutputVector, bytes _locktime, - uint256 _index, + uint8 _fundingOutputIndex, bytes _merkleProof, - uint8 _outputIndex, + uint256 _txIndexInBlock, bytes _bitcoinHeaders ) public returns (bool) { - bytes8 _valueBytes; - bytes32 txId = abi.encodePacked(_version, _vin, _vout, _locktime).hash256(); + + require(_d.inFraudAwaitingBTCFundingProof(), "Not awaiting a funding proof during setup fraud"); - require(_d.inAwaitingBTCFundingProof(), "Not awaiting funding"); + bytes8 _valueBytes; + bytes32 txId = abi.encodePacked(_txVersion, _txInputVector, _txOutputVector, _locktime).hash256(); - _valueBytes = _d.findAndParseFundingOutput(_vout, _outputIndex); + _valueBytes = _d.findAndParseFundingOutput(_txOutputVector, _fundingOutputIndex); + require(DepositUtils.bytes8LEToUint(_valueBytes) >= TBTCConstants.getLotSize(), "Deposit too small"); - _d.checkFundingProof(txId, _bitcoinHeaders.extractMerkleRootLE().toBytes32(), _merkleProof, _index); + _d.checkFundingProof(txId, _bitcoinHeaders.extractMerkleRootLE().toBytes32(), _merkleProof, _txIndexInBlock); _d.evaluateProofDifficulty(_bitcoinHeaders); - // Write down the UTXO info and set to active. Congratulations :) - _d.utxoSizeBytes = _valueBytes; - _d.utxoOutpoint = abi.encodePacked(txId, _outputIndex, hex"000000"); - - fundingTeardown(_d); - _d.setActive(); - _d.logFunded(); - - returnFunderBond(_d); + _d.setFailedSetup(); + _d.logSetupFailed(); - // Mint 95% of the deposit size - IBurnableERC20 _tbtc = IBurnableERC20(_d.TBTCToken); - uint256 _value = TBTCConstants.getLotSize(); - _tbtc.mint(_d.depositBeneficiary(), _value.mul(95).div(100)); + // If the proof is accepted, update to failed, and distribute signer bonds + distributeSignerBondsToFunder(_d); + fundingFraudTeardown(_d); return true; } - /// @notice Anyone may notify the deposit of a funding proof to activate the deposit - /// @dev This is the happy-path of the funding flow. It means that we have succeeded - /// @param _d deposit storage pointer - /// @param _version 4-byte version number - /// @param _vin length-prepended inputs - /// @param _vout length-prepended outputs - /// @param _locktime 4-byte locktime - /// @param _index transaction index - /// @param _merkleProof the merkle proof of inclusion - /// @param _outputIndex index of funding output in _vout - /// @param _bitcoinHeaders header chain for work proof - /// @return true if successful - function provideFraudBTCFundingProof( + /// @notice Anyone may notify the deposit of a funding proof to activate the deposit + /// @dev This is the happy-path of the funding flow. It means that we have succeeded + /// @param _d Deposit storage pointer + /// @param _txVersion 4-byte LE version number + /// @param _txInputVector All inputs prepended by the number of inputs encoded as a VarInt, max 0xFC inputs + /// @param _txOutputVector All outputs prepended by the number of outputs encoded as a VarInt, max 0xFC outputs + /// @param _locktime Final 4 bytes of the BTC transaction. Needed to calculate txId + /// @param _fundingOutputIndex Index of funding output in _txOutputVector (0-indexed) + /// @param _merkleProof The merkle proof of transaction inclusin in a block + /// @param _txIndexInBlock Transaction index in the block (1-indexed) + /// @param _bitcoinHeaders Single bytestring of 80-byte bitcoin headers, lowest height first + /// @return True if successful + function provideBTCFundingProof( DepositUtils.Deposit storage _d, - bytes _version, - bytes _vin, - bytes _vout, + bytes _txVersion, + bytes _txInputVector, + bytes _txOutputVector, bytes _locktime, - uint256 _index, + uint8 _fundingOutputIndex, bytes _merkleProof, - uint8 _outputIndex, + uint256 _txIndexInBlock, bytes _bitcoinHeaders ) public returns (bool) { - bytes8 _valueBytes; - bytes32 txId = abi.encodePacked(_version, _vin, _vout, _locktime).hash256(); - require(_d.inFraudAwaitingBTCFundingProof(), "Not awaiting a funding proof during setup fraud"); + require(_d.inAwaitingBTCFundingProof(), "Not awaiting funding"); + + bytes8 _valueBytes; + bytes32 txId = abi.encodePacked(_txVersion, _txInputVector, _txOutputVector, _locktime).hash256(); - _valueBytes = _d.findAndParseFundingOutput(_vout, _outputIndex); + _valueBytes = _d.findAndParseFundingOutput(_txOutputVector, _fundingOutputIndex); - _d.checkFundingProof(txId, _bitcoinHeaders.extractMerkleRootLE().toBytes32(), _merkleProof, _index); + _d.checkFundingProof(txId, _bitcoinHeaders.extractMerkleRootLE().toBytes32(), _merkleProof, _txIndexInBlock); _d.evaluateProofDifficulty(_bitcoinHeaders); - _d.setFailedSetup(); - _d.logSetupFailed(); + // Write down the UTXO info and set to active. Congratulations :) + // The utxoOutpoint is the LE TXID plus the index of the output as a 4-byte LE int + // _fundingOutputIndex is a uint8, so we know it is only 1 byte + // Therefore, pad with 3 more bytes + _d.utxoSizeBytes = _valueBytes; + _d.utxoOutpoint = abi.encodePacked(txId, _fundingOutputIndex, hex"000000"); - // If the proof is accepted, update to failed, and distribute signer bonds - distributeSignerBondsToFunder(_d); - fundingFraudTeardown(_d); + fundingTeardown(_d); + _d.setActive(); + _d.logFunded(); + + returnFunderBond(_d); + + // Mint 95% of the deposit size + IBurnableERC20 _tbtc = IBurnableERC20(_d.TBTCToken); + uint256 _value = TBTCConstants.getLotSize(); + _tbtc.mint(_d.depositBeneficiary(), _value.mul(95).div(100)); return true; } diff --git a/implementation/contracts/deposit/DepositUtils.sol b/implementation/contracts/deposit/DepositUtils.sol index 8e7e1c227..ea45ef29e 100644 --- a/implementation/contracts/deposit/DepositUtils.sol +++ b/implementation/contracts/deposit/DepositUtils.sol @@ -98,11 +98,11 @@ library DepositUtils { /// @notice Syntactically check an SPV proof for a bitcoin tx /// @dev Stateless SPV Proof verification documented elsewhere - /// @param _d deposit storage pointer - /// @param _bitcoinTx The bitcoin tx that is purportedly included in the header chain - /// @param _merkleProof The merkle proof of inclusion of the tx in the bitcoin block - /// @param _index The index of the tx in the Bitcoin block (1-indexed) - /// @param _bitcoinHeaders An array of tightly-packed bitcoin headers + /// @param _d deposit storage pointer + /// @param _bitcoinTx The bitcoin tx that is purportedly included in the header chain + /// @param _merkleProof The merkle proof of inclusion of the tx in the bitcoin block + /// @param _index The index of the tx in the Bitcoin block (1-indexed) + /// @param _bitcoinHeaders An array of tightly-packed bitcoin headers /// @return The 32 byte transaction id (little-endian, not block-explorer) function checkProof( Deposit storage _d, @@ -131,49 +131,47 @@ library DepositUtils { return _txid; } - /// @notice Syntactically check an SPV proof for a bitcoin tx - /// @dev Stateless SPV Proof verification documented elsewhere - /// @param _d deposit storage pointer - /// @param _merkleRoot The ID of the Bitcoin transaction to check - /// @param _merkleRoot The Root of the merkle path - /// @param _merkleProof The merkle proof of inclusion of the tx in the bitcoin block - /// @param _index The index of the tx in the Bitcoin block (1-indexed) + /// @notice Syntactically check an SPV proof for a bitcoin tx + /// @dev Stateless SPV Proof verification documented elsewhere + /// @param _d Deposit storage pointer + /// @param _txId The ID of the Bitcoin transaction to check + /// @param _merkleRoot The Root of the merkle path + /// @param _merkleProof The merkle proof of inclusion of the tx in the bitcoin block + /// @param _txIndexInBlock 1-indexed transaction index in the block function checkFundingProof( Deposit storage _d, bytes32 _txId, bytes32 _merkleRoot, bytes _merkleProof, - uint256 _index + uint256 _txIndexInBlock ) public view { require( _txId.prove( _merkleRoot, _merkleProof, - _index), + _txIndexInBlock), "Tx merkle proof is not valid for provided header and tx"); } - /// @dev find funding ourput using provided index - /// @param _d deposit storage pointer - /// @param _vout length-prepended outputs - /// @param _index index of funding output - /// @return funding value (bytes8) + /// @dev find funding ourput using provided index + /// @param _d deposit storage pointer + /// @param _txOutputVector All outputs prepended by the number of outputs encoded as a VarInt, max 0xFC outputs + /// @param _fundingOutputIndex Index of funding output in _txOutputVector + /// @return funding value (bytes8) function findAndParseFundingOutput( DepositUtils.Deposit storage _d, - bytes _vout, - uint8 _index + bytes _txOutputVector, + uint8 _fundingOutputIndex ) public view returns (bytes8) { bytes8 _valueBytes; bytes memory _output; - uint8 _numOutputs; - - uint256 _n = (_vout.slice(0, 1)).bytesToUint(); + + uint256 _n = (_txOutputVector.slice(0, 1)).bytesToUint(); require(_n < 0xfd, "VarInts not supported"); - _numOutputs = uint8(_n); - + // Find the output paying the signer PKH // This will fail if there are more than 256 outputs - _output = _extractOutputAtIndex(_vout, _index); + _output = _extractOutputAtIndex(_txOutputVector, _fundingOutputIndex); if (keccak256(_output.extractHash()) == keccak256(abi.encodePacked(signerPKH(_d)))) { _valueBytes = bytes8(_output.slice(0, 8).toBytes32()); return _valueBytes; @@ -182,24 +180,24 @@ library DepositUtils { revert("Did not find output with correct PKH"); } - /// @notice Extracts the output at a given index in the TxIns vector - /// @param _b The tx to evaluate - /// @param _index The 0-indexed location of the output to extract - /// @return The specified output - function _extractOutputAtIndex(bytes _b, uint8 _index) internal pure returns (bytes) { + /// @notice Extracts the output at a given index in _txOutputVector + /// @param _txOutputVector All outputs prepended by the number of outputs encoded as a VarInt, max 0xFC outputs + /// @param _fundingOutputIndex Index of funding output in _txOutputVector + /// @return The specified output + function _extractOutputAtIndex(bytes _txOutputVector, uint8 _fundingOutputIndex) internal pure returns (bytes) { // Determine length of first ouput uint _offset = 1; - uint _len = (_b.slice(8 + _offset, 2)).determineOutputLength(); + uint _len = (_txOutputVector.slice(8 + _offset, 2)).determineOutputLength(); // This loop moves forward, and then gets the len of the next one - for (uint i = 0; i < _index; i++) { + for (uint i = 0; i < _fundingOutputIndex; i++) { _offset = _offset + _len; - _len = (_b.slice(8, 2)).determineOutputLength(); + _len = (_txOutputVector.slice(8, 2)).determineOutputLength(); } // We now have the length and offset of the one we want - return _b.slice(_offset, _len); + return _txOutputVector.slice(_offset, _len); } /// @notice Calculates the amount of value at auction right now diff --git a/implementation/test/DepositTest.js b/implementation/test/DepositTest.js index 5cfa7e5a6..8d9b2f44f 100644 --- a/implementation/test/DepositTest.js +++ b/implementation/test/DepositTest.js @@ -856,17 +856,17 @@ contract('Deposit', accounts => { assert.equal(balanceCheck, balanceAfter, 'partial slash not correctly awarded to funder') }) }) - + describe('provideFraudBTCFundingProof', async () => { // real tx from testnet bitcoin, interpreted as funding tx const _bitcoinTx = '0x01000000000101179137fe810d6712b6b00c196c1b76a93240f396c53d15f4cd523995dad6f6880000000000ffffffff02581b000000000000160014d849b1e1cede2ac7d7188cf8700e97d6975c91c4e8030000000000001976a914d849b1e1cede2ac7d7188cf8700e97d6975c91c488ac0247304402204edb2a92223cd854755ec3e34ce9a0cbeb48ad88a603d4d268203a81245258440220465a1412b8676f1504fae0026c89df342b04b741fcbb55604930339242a5a1630121028896955d043b5a43957b21901f2cce9f0bfb484531b03ad6cd3153e45e73ee2e00000000'; const _version = '0x01000000' - const _vin = `0x01179137fe810d6712b6b00c196c1b76a93240f396c53d15f4cd523995dad6f6880000000000ffffffff` - const _vout = '0x02581b000000000000160014d849b1e1cede2ac7d7188cf8700e97d6975c91c4e8030000000000001976a914d849b1e1cede2ac7d7188cf8700e97d6975c91c488ac' + const _txInputVector = `0x01179137fe810d6712b6b00c196c1b76a93240f396c53d15f4cd523995dad6f6880000000000ffffffff` + const _txOutputVector = '0x02581b000000000000160014d849b1e1cede2ac7d7188cf8700e97d6975c91c4e8030000000000001976a914d849b1e1cede2ac7d7188cf8700e97d6975c91c488ac' const _fundingOutputIndex = 0; const _locktime = '0x00000000' const _merkleProof = '0xcb68238689609f8a5c3f159628d54f2de03aec2e36920ff633a7cd05043d97d8529898acab187401f4d1eed73d60cf2efb15361b48c82d84451980c3e645a8bbbc5d20f5aaed58e535d68761a166437bb92cc0c2fff5f9e7e5381e945c95f49cf971736c3bf71a38eaa2022d84f00a3492375bfc68e538ee292ae89e873eac62157d5ca1290a3b412e2adcdadf746c18952be2ddaa66bad24467be0f212b9c45a31bd04725f962b4b18f619e22cc1ab7e906c2cdcaa205d8e51cf1cf199fcf1de2da374c31f1fecb0f9ae6d8366df16da24a63c7c6504713f34bcac7fab3b12227415c10b23066c79980d7081cc31cd38009fa7b14fa807d862d81f9e6989ba6eb018246bbea71dabe571c4fb88b13bcc046569dfad297f406beceaa42ee2766ec5cdfbf35ff7ab6cd6c16744269966a2042dc6ce0b81ecf0a5f51ecd3655ed7'; - const _index = 71; + const _txIndexInBlock = 71; const _bitcoinHeaders = '0x00000020aaefe576c20c7680e8da9baf4db2fac1f59043b9afaf87361701000000000000ec5cdfbf35ff7ab6cd6c16744269966a2042dc6ce0b81ecf0a5f51ecd3655ed7bd81ee5c453e011a26018de900000020c3188a1be4ff654d8ff147e6f264200ecaeaef5bfe12d3e97000000000000000b3063a20a0567d88dfba3cd6ca4cf0b3bb6d03da5e2a3335a7f0495ce17004cc8086ee5cffff001d37da48a90000002092a7a8318c5e84369c7391b80ab80de961ad30e995244110b73f1600000000000b1d95fd860b17fb38e3c3ce63b623212f12b353dcfd892cfbc92ec9c9ed2684f189ee5c453e011ac750f246000000203991cb8866955373cbab078f25bf38d50a02587a2b342f1a7c00000000000000e865281766dee7ef0ed5786b56e9c05e57f41a50f95c08530e62ffe2f64fc65b598aee5c453e011aa90ed39000000020cc08a7635123b495dee29b1b795bdc9d08ac555cc194519f2f01000000000000b298a563189c2139e0d7e8cb9aa49b681d85b3dd8b930580c33a4af8e0a65b651d8fee5cffff001defe70bcd000000206f04e29a46f3bb4bd3d92f299f5c4fe1bfd4c7f6b6f72dc1b80c180000000000a1ab92186e8980b5a47446578647acbaa54030db04dc4d9d0e5672b9747ae98ecf90ee5c453e011a51e3a3b200000020dd9f42060a303fffd81b3c2cb9ae45897782ccf4e44fc7dfd700000000000000f67fe1436f88023fe2102f3a850c197715d8f394d6887350d95e08f223081911b591ee5c453e011a23a70f1400000020f8d36c426b6c9ddb8b902a33e62a08ba876988ca95ee89ec530000000000000044c9ac396e66df9dfe42f4f85acd871a1e82000f4e3025c20973fc29656851b03792ee5c453e011ad8c1e70d'; const _signerPubkeyX = '0x8896955d043b5a43957b21901f2cce9f0bfb484531b03ad6cd3153e45e73ee2e' const _signerPubkeyY = '0xf687f923b5896a409cb7e2b5ae456f61ac61862305c6ec86bd7421b5bce115e0' @@ -883,7 +883,7 @@ contract('Deposit', accounts => { it('updates to setup failed, logs SetupFailed, ', async () => { const blockNumber = await web3.eth.getBlock('latest').number - await testInstance.provideFraudBTCFundingProof(_version, _vin, _vout, _locktime, _index, _merkleProof, _fundingOutputIndex, _bitcoinHeaders) + await testInstance.provideFraudBTCFundingProof(_version, _txInputVector, _txOutputVector, _locktime, _fundingOutputIndex, _merkleProof, _txIndexInBlock, _bitcoinHeaders) const keepState = await testInstance.getKeepInfo.call() assert(keepState[0].eqn(0), 'Keep id not deconsted') @@ -902,7 +902,7 @@ contract('Deposit', accounts => { it('reverts if not awaiting a funding proof during setup fraud', async () => { try { await testInstance.setState(utils.states.START) - await testInstance.provideFraudBTCFundingProof(_version, _vin, _vout, _locktime, _index, _merkleProof, _fundingOutputIndex, _bitcoinHeaders) + await testInstance.provideFraudBTCFundingProof(_version, _txInputVector, _txOutputVector, _locktime, _fundingOutputIndex, _merkleProof, _txIndexInBlock, _bitcoinHeaders) } catch (e) { assert.include(e.message, 'Not awaiting a funding proof during setup fraud') } @@ -915,7 +915,7 @@ contract('Deposit', accounts => { const initialBalance = await web3.eth.getBalance(beneficiary) const signerBond = await web3.eth.getBalance(deployed.KeepStub.address) - await testInstance.provideFraudBTCFundingProof(_version, _vin, _vout, _locktime, _index, _merkleProof, _fundingOutputIndex, _bitcoinHeaders) + await testInstance.provideFraudBTCFundingProof(_version, _txInputVector, _txOutputVector, _locktime, _fundingOutputIndex, _merkleProof, _txIndexInBlock, _bitcoinHeaders) const balanceAfter = await web3.eth.getBalance(beneficiary) const balanceCheck = new BN(initialBalance).add(new BN(signerBond)) @@ -929,13 +929,13 @@ contract('Deposit', accounts => { const _bitcoinTx = '0x01000000000101179137fe810d6712b6b00c196c1b76a93240f396c53d15f4cd523995dad6f6880000000000ffffffff02581b000000000000160014d849b1e1cede2ac7d7188cf8700e97d6975c91c4e8030000000000001976a914d849b1e1cede2ac7d7188cf8700e97d6975c91c488ac0247304402204edb2a92223cd854755ec3e34ce9a0cbeb48ad88a603d4d268203a81245258440220465a1412b8676f1504fae0026c89df342b04b741fcbb55604930339242a5a1630121028896955d043b5a43957b21901f2cce9f0bfb484531b03ad6cd3153e45e73ee2e00000000'; const txID = '0xd8973d0405cda733f60f92362eec3ae02d4fd52896153f5c8a9f6089862368cb' const _version = '0x01000000' - const _vin = `0x01179137fe810d6712b6b00c196c1b76a93240f396c53d15f4cd523995dad6f6880000000000ffffffff` - const _vout = '0x02581b000000000000160014d849b1e1cede2ac7d7188cf8700e97d6975c91c4e8030000000000001976a914d849b1e1cede2ac7d7188cf8700e97d6975c91c488ac' + const _txInputVector = `0x01179137fe810d6712b6b00c196c1b76a93240f396c53d15f4cd523995dad6f6880000000000ffffffff` + const _txOutputVector = '0x02581b000000000000160014d849b1e1cede2ac7d7188cf8700e97d6975c91c4e8030000000000001976a914d849b1e1cede2ac7d7188cf8700e97d6975c91c488ac' const _fundingOutputIndex = 0; const _locktime = '0x00000000' const _merkleProof = '0xcb68238689609f8a5c3f159628d54f2de03aec2e36920ff633a7cd05043d97d8529898acab187401f4d1eed73d60cf2efb15361b48c82d84451980c3e645a8bbbc5d20f5aaed58e535d68761a166437bb92cc0c2fff5f9e7e5381e945c95f49cf971736c3bf71a38eaa2022d84f00a3492375bfc68e538ee292ae89e873eac62157d5ca1290a3b412e2adcdadf746c18952be2ddaa66bad24467be0f212b9c45a31bd04725f962b4b18f619e22cc1ab7e906c2cdcaa205d8e51cf1cf199fcf1de2da374c31f1fecb0f9ae6d8366df16da24a63c7c6504713f34bcac7fab3b12227415c10b23066c79980d7081cc31cd38009fa7b14fa807d862d81f9e6989ba6eb018246bbea71dabe571c4fb88b13bcc046569dfad297f406beceaa42ee2766ec5cdfbf35ff7ab6cd6c16744269966a2042dc6ce0b81ecf0a5f51ecd3655ed7'; const _merkleValid = '0xcb68238689609f8a5c3f159628d54f2de03aec2e36920ff633a7cd05043d97d800000000' - const _index = 71; + const _txIndexInBlock = 71; const _bitcoinHeaders = '0x00000020aaefe576c20c7680e8da9baf4db2fac1f59043b9afaf87361701000000000000ec5cdfbf35ff7ab6cd6c16744269966a2042dc6ce0b81ecf0a5f51ecd3655ed7bd81ee5c453e011a26018de900000020c3188a1be4ff654d8ff147e6f264200ecaeaef5bfe12d3e97000000000000000b3063a20a0567d88dfba3cd6ca4cf0b3bb6d03da5e2a3335a7f0495ce17004cc8086ee5cffff001d37da48a90000002092a7a8318c5e84369c7391b80ab80de961ad30e995244110b73f1600000000000b1d95fd860b17fb38e3c3ce63b623212f12b353dcfd892cfbc92ec9c9ed2684f189ee5c453e011ac750f246000000203991cb8866955373cbab078f25bf38d50a02587a2b342f1a7c00000000000000e865281766dee7ef0ed5786b56e9c05e57f41a50f95c08530e62ffe2f64fc65b598aee5c453e011aa90ed39000000020cc08a7635123b495dee29b1b795bdc9d08ac555cc194519f2f01000000000000b298a563189c2139e0d7e8cb9aa49b681d85b3dd8b930580c33a4af8e0a65b651d8fee5cffff001defe70bcd000000206f04e29a46f3bb4bd3d92f299f5c4fe1bfd4c7f6b6f72dc1b80c180000000000a1ab92186e8980b5a47446578647acbaa54030db04dc4d9d0e5672b9747ae98ecf90ee5c453e011a51e3a3b200000020dd9f42060a303fffd81b3c2cb9ae45897782ccf4e44fc7dfd700000000000000f67fe1436f88023fe2102f3a850c197715d8f394d6887350d95e08f223081911b591ee5c453e011a23a70f1400000020f8d36c426b6c9ddb8b902a33e62a08ba876988ca95ee89ec530000000000000044c9ac396e66df9dfe42f4f85acd871a1e82000f4e3025c20973fc29656851b03792ee5c453e011ad8c1e70d'; const _signerPubkeyX = '0x8896955d043b5a43957b21901f2cce9f0bfb484531b03ad6cd3153e45e73ee2e' const _signerPubkeyY = '0xf687f923b5896a409cb7e2b5ae456f61ac61862305c6ec86bd7421b5bce115e0' @@ -954,7 +954,7 @@ contract('Deposit', accounts => { it('updates to active, stores UTXO info, deconstes funding info, logs Funded', async () => { const blockNumber = await web3.eth.getBlock('latest').number - await testInstance.provideBTCFundingProof(_version, _vin, _vout, _locktime, _index, _merkleProof, _fundingOutputIndex, _bitcoinHeaders) + await testInstance.provideBTCFundingProof(_version, _txInputVector, _txOutputVector, _locktime, _fundingOutputIndex, _merkleProof, _txIndexInBlock, _bitcoinHeaders) const UTXOInfo = await testInstance.getUTXOInfo.call() assert.equal(UTXOInfo[0], _outValueBytes) @@ -974,7 +974,7 @@ contract('Deposit', accounts => { it('reverts if not awaiting funding proof', async () => { try { await testInstance.setState(utils.states.START) - await testInstance.provideBTCFundingProof(_version, _vin, _vout, _locktime, _index, _merkleProof, _fundingOutputIndex, _bitcoinHeaders) + await testInstance.provideBTCFundingProof(_version, _txInputVector, _txOutputVector, _locktime, _fundingOutputIndex, _merkleProof, _txIndexInBlock, _bitcoinHeaders) } catch (e) { assert.include(e.message, 'Not awaiting funding') } @@ -990,7 +990,7 @@ contract('Deposit', accounts => { const initialBalance = await web3.eth.getBalance(beneficiary) - await testInstance.provideBTCFundingProof(_version, _vin, _vout, _locktime, _index, _merkleProof, _fundingOutputIndex, _bitcoinHeaders) + await testInstance.provideBTCFundingProof(_version, _txInputVector, _txOutputVector, _locktime, _fundingOutputIndex, _merkleProof, _txIndexInBlock, _bitcoinHeaders) const balanceAfter = await web3.eth.getBalance(beneficiary) const balanceCheck = new BN(initialBalance).add(new BN(signerBond)) diff --git a/implementation/test/DepositUtilsTest.js b/implementation/test/DepositUtilsTest.js index 7148dc63a..21fa36bc4 100644 --- a/implementation/test/DepositUtilsTest.js +++ b/implementation/test/DepositUtilsTest.js @@ -161,14 +161,14 @@ contract('DepositUtils', accounts => { describe('signerFee()', async () => { it('returns a derived constant', async () => { const signerFee = await testUtilsInstance.signerFee.call() - assert(signerFee.eq(new BN(500000))) + assert(signerFee.eq(new BN(5))) }) }) describe('beneficiaryReward()', async () => { it('returns a derived constant', async () => { const beneficiaryReward = await testUtilsInstance.beneficiaryReward.call() - assert(beneficiaryReward.eq(new BN(10 ** 5))) + assert(beneficiaryReward.eq(new BN(1))) }) }) diff --git a/implementation/test/contracts/deposit/TestTBTCConstants.sol b/implementation/test/contracts/deposit/TestTBTCConstants.sol index 509bd37de..c1444ed91 100644 --- a/implementation/test/contracts/deposit/TestTBTCConstants.sol +++ b/implementation/test/contracts/deposit/TestTBTCConstants.sol @@ -6,7 +6,7 @@ library TestTBTCConstants { // During testing swap this out with another constats contract // System Parameters - uint256 public constant MINIMUM_LOT_SIZE = 10 ** 8; // tsatoshi + uint256 public constant MINIMUM_LOT_SIZE = 10 ** 3; // tsatoshi uint256 public constant SIGNER_FEE_DIVISOR = 200; // 1/200 == 50bps == 0.5% == 0.005 uint256 public constant BENEFICIARY_FEE_DIVISOR = 1000; // 1/1000 = 10 bps = 0.1% = 0.001 uint256 public constant FUNDING_FRAUD_PARTIAL_SLASH_DIVISOR = 2; // 1/2 = 5000bps = 50% = 0.5 From 9b7338bc6253229da86dcb86369b218314401cc2 Mon Sep 17 00:00:00 2001 From: Nicholas Evans Date: Tue, 18 Jun 2019 01:57:25 -0500 Subject: [PATCH 08/27] Fix solidity linter warnings --- implementation/contracts/deposit/Deposit.sol | 3 ++- implementation/contracts/deposit/DepositFunding.sol | 2 +- implementation/contracts/deposit/DepositUtils.sol | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/implementation/contracts/deposit/Deposit.sol b/implementation/contracts/deposit/Deposit.sol index a01700b0f..5a4c04f24 100644 --- a/implementation/contracts/deposit/Deposit.sol +++ b/implementation/contracts/deposit/Deposit.sol @@ -239,7 +239,8 @@ contract Deposit { _merkleProof, _txIndexInBlock, _bitcoinHeaders - ); return true; + ); + return true; } // diff --git a/implementation/contracts/deposit/DepositFunding.sol b/implementation/contracts/deposit/DepositFunding.sol index 8df8964be..1092ebc58 100644 --- a/implementation/contracts/deposit/DepositFunding.sol +++ b/implementation/contracts/deposit/DepositFunding.sol @@ -243,7 +243,7 @@ library DepositFunding { uint256 _txIndexInBlock, bytes _bitcoinHeaders ) public returns (bool) { - + require(_d.inFraudAwaitingBTCFundingProof(), "Not awaiting a funding proof during setup fraud"); bytes8 _valueBytes; diff --git a/implementation/contracts/deposit/DepositUtils.sol b/implementation/contracts/deposit/DepositUtils.sol index ea45ef29e..0e1432175 100644 --- a/implementation/contracts/deposit/DepositUtils.sol +++ b/implementation/contracts/deposit/DepositUtils.sol @@ -165,10 +165,10 @@ library DepositUtils { ) public view returns (bytes8) { bytes8 _valueBytes; bytes memory _output; - + uint256 _n = (_txOutputVector.slice(0, 1)).bytesToUint(); require(_n < 0xfd, "VarInts not supported"); - + // Find the output paying the signer PKH // This will fail if there are more than 256 outputs _output = _extractOutputAtIndex(_txOutputVector, _fundingOutputIndex); From d751c42f66eef7a090e6d8e2d14a81cda31a2f97 Mon Sep 17 00:00:00 2001 From: Nicholas Evans Date: Tue, 18 Jun 2019 03:04:57 -0500 Subject: [PATCH 09/27] Update TX for funding proof tests to mainnet TX --- implementation/test/DepositTest.js | 62 +++++++++++++++--------------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/implementation/test/DepositTest.js b/implementation/test/DepositTest.js index 8d9b2f44f..9cd98fb93 100644 --- a/implementation/test/DepositTest.js +++ b/implementation/test/DepositTest.js @@ -858,24 +858,26 @@ contract('Deposit', accounts => { }) describe('provideFraudBTCFundingProof', async () => { - // real tx from testnet bitcoin, interpreted as funding tx - const _bitcoinTx = '0x01000000000101179137fe810d6712b6b00c196c1b76a93240f396c53d15f4cd523995dad6f6880000000000ffffffff02581b000000000000160014d849b1e1cede2ac7d7188cf8700e97d6975c91c4e8030000000000001976a914d849b1e1cede2ac7d7188cf8700e97d6975c91c488ac0247304402204edb2a92223cd854755ec3e34ce9a0cbeb48ad88a603d4d268203a81245258440220465a1412b8676f1504fae0026c89df342b04b741fcbb55604930339242a5a1630121028896955d043b5a43957b21901f2cce9f0bfb484531b03ad6cd3153e45e73ee2e00000000'; + // real tx from mainnet bitcoin, interpreted as funding tx + //const tx = '0x01000000000101913e39197867de39bff2c93c75173e086388ee7e8707c90ce4a02dd23f7d2c0d0000000000ffffffff012040351d0000000016001486e7303082a6a21d5837176bc808bf4828371ab602473044022046c3c852a2042ee01ffd7d8d252f297ccc67ae2aa1fac4170f50e8a90af5398802201585ffbbed6e812fb60c025d2f82ae115774279369610b0c76165b6c7132f2810121020c67643b5c862a1aa1afe0a77a28e51a21b08396a0acae69965b22d2a403fd1c4ec10800' + //const txid = '0x7c48181cb5c030655eea651c5e9aa808983f646465cbe9d01c227d99cfbc405f'; + //const txid_le = '0x5f40bccf997d221cd0e9cb6564643f9808a89a5e1c65ea5e6530c0b51c18487c'; + const currentDifficulty = 6353030562983 const _version = '0x01000000' - const _txInputVector = `0x01179137fe810d6712b6b00c196c1b76a93240f396c53d15f4cd523995dad6f6880000000000ffffffff` - const _txOutputVector = '0x02581b000000000000160014d849b1e1cede2ac7d7188cf8700e97d6975c91c4e8030000000000001976a914d849b1e1cede2ac7d7188cf8700e97d6975c91c488ac' + const _txInputVector = `0x01913e39197867de39bff2c93c75173e086388ee7e8707c90ce4a02dd23f7d2c0d0000000000ffffffff` + const _txOutputVector = '0x012040351d0000000016001486e7303082a6a21d5837176bc808bf4828371ab6' const _fundingOutputIndex = 0; - const _locktime = '0x00000000' - const _merkleProof = '0xcb68238689609f8a5c3f159628d54f2de03aec2e36920ff633a7cd05043d97d8529898acab187401f4d1eed73d60cf2efb15361b48c82d84451980c3e645a8bbbc5d20f5aaed58e535d68761a166437bb92cc0c2fff5f9e7e5381e945c95f49cf971736c3bf71a38eaa2022d84f00a3492375bfc68e538ee292ae89e873eac62157d5ca1290a3b412e2adcdadf746c18952be2ddaa66bad24467be0f212b9c45a31bd04725f962b4b18f619e22cc1ab7e906c2cdcaa205d8e51cf1cf199fcf1de2da374c31f1fecb0f9ae6d8366df16da24a63c7c6504713f34bcac7fab3b12227415c10b23066c79980d7081cc31cd38009fa7b14fa807d862d81f9e6989ba6eb018246bbea71dabe571c4fb88b13bcc046569dfad297f406beceaa42ee2766ec5cdfbf35ff7ab6cd6c16744269966a2042dc6ce0b81ecf0a5f51ecd3655ed7'; - const _txIndexInBlock = 71; - const _bitcoinHeaders = '0x00000020aaefe576c20c7680e8da9baf4db2fac1f59043b9afaf87361701000000000000ec5cdfbf35ff7ab6cd6c16744269966a2042dc6ce0b81ecf0a5f51ecd3655ed7bd81ee5c453e011a26018de900000020c3188a1be4ff654d8ff147e6f264200ecaeaef5bfe12d3e97000000000000000b3063a20a0567d88dfba3cd6ca4cf0b3bb6d03da5e2a3335a7f0495ce17004cc8086ee5cffff001d37da48a90000002092a7a8318c5e84369c7391b80ab80de961ad30e995244110b73f1600000000000b1d95fd860b17fb38e3c3ce63b623212f12b353dcfd892cfbc92ec9c9ed2684f189ee5c453e011ac750f246000000203991cb8866955373cbab078f25bf38d50a02587a2b342f1a7c00000000000000e865281766dee7ef0ed5786b56e9c05e57f41a50f95c08530e62ffe2f64fc65b598aee5c453e011aa90ed39000000020cc08a7635123b495dee29b1b795bdc9d08ac555cc194519f2f01000000000000b298a563189c2139e0d7e8cb9aa49b681d85b3dd8b930580c33a4af8e0a65b651d8fee5cffff001defe70bcd000000206f04e29a46f3bb4bd3d92f299f5c4fe1bfd4c7f6b6f72dc1b80c180000000000a1ab92186e8980b5a47446578647acbaa54030db04dc4d9d0e5672b9747ae98ecf90ee5c453e011a51e3a3b200000020dd9f42060a303fffd81b3c2cb9ae45897782ccf4e44fc7dfd700000000000000f67fe1436f88023fe2102f3a850c197715d8f394d6887350d95e08f223081911b591ee5c453e011a23a70f1400000020f8d36c426b6c9ddb8b902a33e62a08ba876988ca95ee89ec530000000000000044c9ac396e66df9dfe42f4f85acd871a1e82000f4e3025c20973fc29656851b03792ee5c453e011ad8c1e70d'; - const _signerPubkeyX = '0x8896955d043b5a43957b21901f2cce9f0bfb484531b03ad6cd3153e45e73ee2e' - const _signerPubkeyY = '0xf687f923b5896a409cb7e2b5ae456f61ac61862305c6ec86bd7421b5bce115e0' + const _locktime = '0x4ec10800' + const _txIndexInBlock = 130; + const _bitcoinHeaders = '0x00e0ff3fd877ad23af1d0d3e0eb6a700d85b692975dacd36e47b1b00000000000000000095ba61df5961d7fa0a45cd7467e11f20932c7a0b74c59318e86581c6b509554876f6c65c114e2c17e42524d300000020994d3802da5adf80345261bcff2eb87ab7b70db786cb0000000000000000000003169efc259f6e4b5e1bfa469f06792d6f07976a098bff2940c8e7ed3105fdc5eff7c65c114e2c170c4dffc30000c020f898b7ea6a405728055b0627f53f42c57290fe78e0b91900000000000000000075472c91a94fa2aab73369c0686a58796949cf60976e530f6eb295320fa15a1b77f8c65c114e2c17387f1df00000002069137421fc274aa2c907dbf0ec4754285897e8aa36332b0000000000000000004308f2494b702c40e9d61991feb7a15b3be1d73ce988e354e52e7a4e611bd9c2a2f8c65c114e2c1740287df200000020ab63607b09395f856adaa69d553755d9ba5bd8d15da20a000000000000000000090ea7559cda848d97575cb9696c8e33ba7f38d18d5e2f8422837c354aec147839fbc65c114e2c175cf077d6000000200ab3612eac08a31a8fb1d9b5397f897db8d26f6cd83a230000000000000000006f4888720ecbf980ff9c983a8e2e60ad329cc7b130916c2bf2300ea54e412a9ed6fcc65c114e2c17d4fbb88500000020d3e51560f77628a26a8fad01c88f98bd6c9e4bc8703b180000000000000000008e2c6e62a1f4d45dd03be1e6692df89a4e3b1223a4dbdfa94cca94c04c22049992fdc65c114e2c17463edb5e' + const _signerPubkeyX = '0xd4aee75e57179f7cd18adcbaa7e2fca4ff7b1b446df88bf0b4398e4a26965a6e' + const _signerPubkeyY = '0xe8bfb23428a4efecb3ebdc636139de9a568ed427fff20d28baa33ed48e9c44e1' + const _merkleProof = '0x5f40bccf997d221cd0e9cb6564643f9808a89a5e1c65ea5e6530c0b51c18487c886f7da48f4ccfe49283c678dedb376c89853ba46d9a297fe39e8dd557d1f8deb0fb1a28c03f71b267f3a33459b2566975b1653a1238947ed05edca17ef64181b1f09d858a6e25bae4b0e245993d4ea77facba8ed0371bb9b8a6724475bcdc9edf9ead30b61cf6714758b7c93d1b725f86c2a66a07dd291ef566eaa5a59516823d57fd50557f1d938cc2fb61fe0e1acee6f9cb618a9210688a2965c52feabee66d660a5e7f158e363dc464fca2bb1cc856173366d5d20b5cd513a3aab8ebc5be2bd196b783b8773af2472abcea3e32e97938283f7b454769aa1c064c311c3342a755029ee338664999bd8d432080eafae3ca86b52ad2e321e9e634a46c1bd0d174e38bcd4c59a0f0a78c5906c015ef4daf6beb0500a59f4cae00cd46069ce60db2182e74561028e4462f59f639c89b8e254602d6ad9c212b7c2af5db9275e48c467539c6af678d6f09214182df848bd79a06df706f7c3fddfdd95e6f27326c6217ee446543a443f82b711f48c173a769ae8d1e92a986bc76fca732f088bbe04995ba61df5961d7fa0a45cd7467e11f20932c7a0b74c59318e86581c6b5095548' beforeEach(async () => { - const target = await deployed.BTCUtils.extractTarget(_bitcoinHeaders) - const difficulty = await deployed.BTCUtils.calculateDifficulty(target); + await testInstance.setKeepInfo(0, 0, 0, _signerPubkeyX, _signerPubkeyY) - await deployed.SystemStub.setCurrentDiff(difficulty) + await deployed.SystemStub.setCurrentDiff(currentDifficulty) await testInstance.setState(utils.states.FRAUD_AWAITING_BTC_FUNDING_PROOF) await deployed.KeepStub.send(1000000, {from: accounts[0]}) }) @@ -925,28 +927,28 @@ contract('Deposit', accounts => { }) describe('provideBTCFundingProof', async () => { - // real tx from testnet bitcoin, interpreted as funding tx - const _bitcoinTx = '0x01000000000101179137fe810d6712b6b00c196c1b76a93240f396c53d15f4cd523995dad6f6880000000000ffffffff02581b000000000000160014d849b1e1cede2ac7d7188cf8700e97d6975c91c4e8030000000000001976a914d849b1e1cede2ac7d7188cf8700e97d6975c91c488ac0247304402204edb2a92223cd854755ec3e34ce9a0cbeb48ad88a603d4d268203a81245258440220465a1412b8676f1504fae0026c89df342b04b741fcbb55604930339242a5a1630121028896955d043b5a43957b21901f2cce9f0bfb484531b03ad6cd3153e45e73ee2e00000000'; - const txID = '0xd8973d0405cda733f60f92362eec3ae02d4fd52896153f5c8a9f6089862368cb' + // real tx from mainnet bitcoin, interpreted as funding tx + //const tx = '0x01000000000101913e39197867de39bff2c93c75173e086388ee7e8707c90ce4a02dd23f7d2c0d0000000000ffffffff012040351d0000000016001486e7303082a6a21d5837176bc808bf4828371ab602473044022046c3c852a2042ee01ffd7d8d252f297ccc67ae2aa1fac4170f50e8a90af5398802201585ffbbed6e812fb60c025d2f82ae115774279369610b0c76165b6c7132f2810121020c67643b5c862a1aa1afe0a77a28e51a21b08396a0acae69965b22d2a403fd1c4ec10800' + //const txid = '0x7c48181cb5c030655eea651c5e9aa808983f646465cbe9d01c227d99cfbc405f'; + //const txid_le = '0x5f40bccf997d221cd0e9cb6564643f9808a89a5e1c65ea5e6530c0b51c18487c'; + const currentDifficulty = 6353030562983 const _version = '0x01000000' - const _txInputVector = `0x01179137fe810d6712b6b00c196c1b76a93240f396c53d15f4cd523995dad6f6880000000000ffffffff` - const _txOutputVector = '0x02581b000000000000160014d849b1e1cede2ac7d7188cf8700e97d6975c91c4e8030000000000001976a914d849b1e1cede2ac7d7188cf8700e97d6975c91c488ac' + const _txInputVector = `0x01913e39197867de39bff2c93c75173e086388ee7e8707c90ce4a02dd23f7d2c0d0000000000ffffffff` + const _txOutputVector = '0x012040351d0000000016001486e7303082a6a21d5837176bc808bf4828371ab6' const _fundingOutputIndex = 0; - const _locktime = '0x00000000' - const _merkleProof = '0xcb68238689609f8a5c3f159628d54f2de03aec2e36920ff633a7cd05043d97d8529898acab187401f4d1eed73d60cf2efb15361b48c82d84451980c3e645a8bbbc5d20f5aaed58e535d68761a166437bb92cc0c2fff5f9e7e5381e945c95f49cf971736c3bf71a38eaa2022d84f00a3492375bfc68e538ee292ae89e873eac62157d5ca1290a3b412e2adcdadf746c18952be2ddaa66bad24467be0f212b9c45a31bd04725f962b4b18f619e22cc1ab7e906c2cdcaa205d8e51cf1cf199fcf1de2da374c31f1fecb0f9ae6d8366df16da24a63c7c6504713f34bcac7fab3b12227415c10b23066c79980d7081cc31cd38009fa7b14fa807d862d81f9e6989ba6eb018246bbea71dabe571c4fb88b13bcc046569dfad297f406beceaa42ee2766ec5cdfbf35ff7ab6cd6c16744269966a2042dc6ce0b81ecf0a5f51ecd3655ed7'; - const _merkleValid = '0xcb68238689609f8a5c3f159628d54f2de03aec2e36920ff633a7cd05043d97d800000000' - const _txIndexInBlock = 71; - const _bitcoinHeaders = '0x00000020aaefe576c20c7680e8da9baf4db2fac1f59043b9afaf87361701000000000000ec5cdfbf35ff7ab6cd6c16744269966a2042dc6ce0b81ecf0a5f51ecd3655ed7bd81ee5c453e011a26018de900000020c3188a1be4ff654d8ff147e6f264200ecaeaef5bfe12d3e97000000000000000b3063a20a0567d88dfba3cd6ca4cf0b3bb6d03da5e2a3335a7f0495ce17004cc8086ee5cffff001d37da48a90000002092a7a8318c5e84369c7391b80ab80de961ad30e995244110b73f1600000000000b1d95fd860b17fb38e3c3ce63b623212f12b353dcfd892cfbc92ec9c9ed2684f189ee5c453e011ac750f246000000203991cb8866955373cbab078f25bf38d50a02587a2b342f1a7c00000000000000e865281766dee7ef0ed5786b56e9c05e57f41a50f95c08530e62ffe2f64fc65b598aee5c453e011aa90ed39000000020cc08a7635123b495dee29b1b795bdc9d08ac555cc194519f2f01000000000000b298a563189c2139e0d7e8cb9aa49b681d85b3dd8b930580c33a4af8e0a65b651d8fee5cffff001defe70bcd000000206f04e29a46f3bb4bd3d92f299f5c4fe1bfd4c7f6b6f72dc1b80c180000000000a1ab92186e8980b5a47446578647acbaa54030db04dc4d9d0e5672b9747ae98ecf90ee5c453e011a51e3a3b200000020dd9f42060a303fffd81b3c2cb9ae45897782ccf4e44fc7dfd700000000000000f67fe1436f88023fe2102f3a850c197715d8f394d6887350d95e08f223081911b591ee5c453e011a23a70f1400000020f8d36c426b6c9ddb8b902a33e62a08ba876988ca95ee89ec530000000000000044c9ac396e66df9dfe42f4f85acd871a1e82000f4e3025c20973fc29656851b03792ee5c453e011ad8c1e70d'; - const _signerPubkeyX = '0x8896955d043b5a43957b21901f2cce9f0bfb484531b03ad6cd3153e45e73ee2e' - const _signerPubkeyY = '0xf687f923b5896a409cb7e2b5ae456f61ac61862305c6ec86bd7421b5bce115e0' - const _value = 7000; - const _outValueBytes = '0x581b000000000000' + const _locktime = '0x4ec10800' + const _txIndexInBlock = 130; + const _bitcoinHeaders = '0x00e0ff3fd877ad23af1d0d3e0eb6a700d85b692975dacd36e47b1b00000000000000000095ba61df5961d7fa0a45cd7467e11f20932c7a0b74c59318e86581c6b509554876f6c65c114e2c17e42524d300000020994d3802da5adf80345261bcff2eb87ab7b70db786cb0000000000000000000003169efc259f6e4b5e1bfa469f06792d6f07976a098bff2940c8e7ed3105fdc5eff7c65c114e2c170c4dffc30000c020f898b7ea6a405728055b0627f53f42c57290fe78e0b91900000000000000000075472c91a94fa2aab73369c0686a58796949cf60976e530f6eb295320fa15a1b77f8c65c114e2c17387f1df00000002069137421fc274aa2c907dbf0ec4754285897e8aa36332b0000000000000000004308f2494b702c40e9d61991feb7a15b3be1d73ce988e354e52e7a4e611bd9c2a2f8c65c114e2c1740287df200000020ab63607b09395f856adaa69d553755d9ba5bd8d15da20a000000000000000000090ea7559cda848d97575cb9696c8e33ba7f38d18d5e2f8422837c354aec147839fbc65c114e2c175cf077d6000000200ab3612eac08a31a8fb1d9b5397f897db8d26f6cd83a230000000000000000006f4888720ecbf980ff9c983a8e2e60ad329cc7b130916c2bf2300ea54e412a9ed6fcc65c114e2c17d4fbb88500000020d3e51560f77628a26a8fad01c88f98bd6c9e4bc8703b180000000000000000008e2c6e62a1f4d45dd03be1e6692df89a4e3b1223a4dbdfa94cca94c04c22049992fdc65c114e2c17463edb5e' + const _signerPubkeyX = '0xd4aee75e57179f7cd18adcbaa7e2fca4ff7b1b446df88bf0b4398e4a26965a6e' + const _signerPubkeyY = '0xe8bfb23428a4efecb3ebdc636139de9a568ed427fff20d28baa33ed48e9c44e1' + const _merkleProof = '0x5f40bccf997d221cd0e9cb6564643f9808a89a5e1c65ea5e6530c0b51c18487c886f7da48f4ccfe49283c678dedb376c89853ba46d9a297fe39e8dd557d1f8deb0fb1a28c03f71b267f3a33459b2566975b1653a1238947ed05edca17ef64181b1f09d858a6e25bae4b0e245993d4ea77facba8ed0371bb9b8a6724475bcdc9edf9ead30b61cf6714758b7c93d1b725f86c2a66a07dd291ef566eaa5a59516823d57fd50557f1d938cc2fb61fe0e1acee6f9cb618a9210688a2965c52feabee66d660a5e7f158e363dc464fca2bb1cc856173366d5d20b5cd513a3aab8ebc5be2bd196b783b8773af2472abcea3e32e97938283f7b454769aa1c064c311c3342a755029ee338664999bd8d432080eafae3ca86b52ad2e321e9e634a46c1bd0d174e38bcd4c59a0f0a78c5906c015ef4daf6beb0500a59f4cae00cd46069ce60db2182e74561028e4462f59f639c89b8e254602d6ad9c212b7c2af5db9275e48c467539c6af678d6f09214182df848bd79a06df706f7c3fddfdd95e6f27326c6217ee446543a443f82b711f48c173a769ae8d1e92a986bc76fca732f088bbe04995ba61df5961d7fa0a45cd7467e11f20932c7a0b74c59318e86581c6b5095548' + const _merkleValid = '0x5f40bccf997d221cd0e9cb6564643f9808a89a5e1c65ea5e6530c0b51c18487c00000000' + //const _outputValue = 490029088; + const _outValueBytes = '0x2040351d00000000' beforeEach(async () => { - const target = await deployed.BTCUtils.extractTarget(_bitcoinHeaders) - const difficulty = await deployed.BTCUtils.calculateDifficulty(target); await testInstance.setKeepInfo(0, 0, 0, _signerPubkeyX, _signerPubkeyY) - await deployed.SystemStub.setCurrentDiff(difficulty) + await deployed.SystemStub.setCurrentDiff(currentDifficulty) await testInstance.setState(utils.states.AWAITING_BTC_FUNDING_PROOF) await deployed.KeepStub.send(1000000, {from: accounts[0]}) }) From ce1816f1891634365dc89d103c4489990fbc5623 Mon Sep 17 00:00:00 2001 From: Nicholas Evans Date: Tue, 18 Jun 2019 03:17:09 -0500 Subject: [PATCH 10/27] Revert change to minimum_lot_size back to 10**8 for TestTBTCConstants --- implementation/test/DepositUtilsTest.js | 4 ++-- implementation/test/contracts/deposit/TestTBTCConstants.sol | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/implementation/test/DepositUtilsTest.js b/implementation/test/DepositUtilsTest.js index 45e5a9557..194e7d8ae 100644 --- a/implementation/test/DepositUtilsTest.js +++ b/implementation/test/DepositUtilsTest.js @@ -158,14 +158,14 @@ contract('DepositUtils', (accounts) => { describe('signerFee()', async () => { it('returns a derived constant', async () => { const signerFee = await testUtilsInstance.signerFee.call() - assert(signerFee.eq(new BN(5))) + assert(signerFee.eq(new BN(500000))) }) }) describe('beneficiaryReward()', async () => { it('returns a derived constant', async () => { const beneficiaryReward = await testUtilsInstance.beneficiaryReward.call() - assert(beneficiaryReward.eq(new BN(1))) + assert(beneficiaryReward.eq(new BN(10 ** 5))) }) }) diff --git a/implementation/test/contracts/deposit/TestTBTCConstants.sol b/implementation/test/contracts/deposit/TestTBTCConstants.sol index c1444ed91..509bd37de 100644 --- a/implementation/test/contracts/deposit/TestTBTCConstants.sol +++ b/implementation/test/contracts/deposit/TestTBTCConstants.sol @@ -6,7 +6,7 @@ library TestTBTCConstants { // During testing swap this out with another constats contract // System Parameters - uint256 public constant MINIMUM_LOT_SIZE = 10 ** 3; // tsatoshi + uint256 public constant MINIMUM_LOT_SIZE = 10 ** 8; // tsatoshi uint256 public constant SIGNER_FEE_DIVISOR = 200; // 1/200 == 50bps == 0.5% == 0.005 uint256 public constant BENEFICIARY_FEE_DIVISOR = 1000; // 1/1000 = 10 bps = 0.1% = 0.001 uint256 public constant FUNDING_FRAUD_PARTIAL_SLASH_DIVISOR = 2; // 1/2 = 5000bps = 50% = 0.5 From e643e6824a9a12edc05e7d2e692542756c9237d1 Mon Sep 17 00:00:00 2001 From: Nicholas Evans Date: Tue, 18 Jun 2019 03:32:43 -0500 Subject: [PATCH 11/27] Fix js lint errors --- implementation/test/DepositTest.js | 232 ++++++++++++++--------------- 1 file changed, 109 insertions(+), 123 deletions(-) diff --git a/implementation/test/DepositTest.js b/implementation/test/DepositTest.js index 9cd98fb93..89e56403e 100644 --- a/implementation/test/DepositTest.js +++ b/implementation/test/DepositTest.js @@ -1,4 +1,4 @@ -import expectThrow from './helpers/expectThrow'; +import expectThrow from './helpers/expectThrow' const BytesLib = artifacts.require('BytesLib') const BTCUtils = artifacts.require('BTCUtils') @@ -6,7 +6,6 @@ const ValidateSPV = artifacts.require('ValidateSPV') const CheckBitcoinSigs = artifacts.require('CheckBitcoinSigs') const OutsourceDepositLogging = artifacts.require('OutsourceDepositLogging') -const DepositLog = artifacts.require('DepositLog') const DepositStates = artifacts.require('DepositStates') const DepositUtils = artifacts.require('DepositUtils') const DepositFunding = artifacts.require('DepositFunding') @@ -29,22 +28,22 @@ const bnChai = require('bn-chai') chai.use(bnChai(BN)) const TEST_DEPOSIT_DEPLOY = [ - {name: 'BytesLib', contract: BytesLib}, - {name: 'BTCUtils', contract: BTCUtils}, - {name: 'ValidateSPV', contract: ValidateSPV}, - {name: 'CheckBitcoinSigs', contract: CheckBitcoinSigs}, - {name: 'TBTCConstants', contract: TestTBTCConstants}, // note the name - {name: 'OutsourceDepositLogging', contract: OutsourceDepositLogging}, - {name: 'DepositStates', contract: DepositStates}, - {name: 'DepositUtils', contract: DepositUtils}, - {name: 'DepositFunding', contract: DepositFunding}, - {name: 'DepositRedemption', contract: DepositRedemption}, - {name: 'DepositLiquidation', contract: DepositLiquidation}, - {name: 'TestDeposit', contract: TestDeposit}, - {name: 'TestDepositUtils', contract: TestDepositUtils}, - {name: 'KeepStub', contract: KeepStub}, - {name: 'TBTCStub', contract: TBTCStub}, - {name: 'SystemStub', contract: SystemStub}] + { name: 'BytesLib', contract: BytesLib }, + { name: 'BTCUtils', contract: BTCUtils }, + { name: 'ValidateSPV', contract: ValidateSPV }, + { name: 'CheckBitcoinSigs', contract: CheckBitcoinSigs }, + { name: 'TBTCConstants', contract: TestTBTCConstants }, // note the name + { name: 'OutsourceDepositLogging', contract: OutsourceDepositLogging }, + { name: 'DepositStates', contract: DepositStates }, + { name: 'DepositUtils', contract: DepositUtils }, + { name: 'DepositFunding', contract: DepositFunding }, + { name: 'DepositRedemption', contract: DepositRedemption }, + { name: 'DepositLiquidation', contract: DepositLiquidation }, + { name: 'TestDeposit', contract: TestDeposit }, + { name: 'TestDepositUtils', contract: TestDepositUtils }, + { name: 'KeepStub', contract: KeepStub }, + { name: 'TBTCStub', contract: TBTCStub }, + { name: 'SystemStub', contract: SystemStub }] // spare signature: // signing with privkey '11' * 32 @@ -55,10 +54,11 @@ const TEST_DEPOSIT_DEPLOY = [ // const r = '0x9a40a074721355f427762f5e6d5cb16a0a9ada06011984e49fc81b3ce89cab6d' // const s = '0x234e909713e74a9a49bf9484a69968dabcb1953bf091fa3e31d48531695cf293' -contract('Deposit', accounts => { - - let deployed, keep, testInstance, withdrawalRequestTime, fundingProofTimerStart - +contract('Deposit', (accounts) => { + let deployed + let testInstance + let withdrawalRequestTime + let fundingProofTimerStart before(async () => { deployed = await utils.deploySystem(TEST_DEPOSIT_DEPLOY) @@ -81,7 +81,7 @@ contract('Deposit', accounts => { deployed.SystemStub.address, deployed.TBTCStub.address, deployed.KeepStub.address, - 1, //m + 1, // m 1) // state updates @@ -104,7 +104,7 @@ contract('Deposit', accounts => { deployed.SystemStub.address, deployed.TBTCStub.address, deployed.KeepStub.address, - 1, //m + 1, // m 1) assert(false, 'Test call did not error as expected') } catch (e) { @@ -126,7 +126,7 @@ contract('Deposit', accounts => { const valueBytes = '0x1111111111111111' const keepPubkeyX = '0x' + '33'.repeat(32) const keepPubkeyY = '0x' + '44'.repeat(32) - const requesterPKH = '0x' + '33'.repeat(20) + const requesterPKH = '0x' + '33'.repeat(20) beforeEach(async () => { await testInstance.setState(utils.states.ACTIVE) @@ -182,11 +182,9 @@ contract('Deposit', accounts => { const approved = await deployed.KeepStub.wasDigestApprovedForSigning.call(0, sighash) assert(!approved.eqn(0), 'digest was not approved') }) - }) describe('provideRedemptionSignature', async () => { - // signing the sha 256 of '11' * 32 // signing with privkey '11' * 32 // using RFC 6979 nonce (libsecp256k1) @@ -234,7 +232,6 @@ contract('Deposit', accounts => { assert.include(e.message, 'Invalid signature') } }) - }) describe('increaseRedemptionFee', async () => { @@ -335,8 +332,8 @@ contract('Deposit', accounts => { describe('provideRedemptionProof', async () => { // real tx from mainnet bitcoin const currentDiff = 6353030562983 - const txid = '0x7c48181cb5c030655eea651c5e9aa808983f646465cbe9d01c227d99cfbc405f' - const txid_le = '0x5f40bccf997d221cd0e9cb6564643f9808a89a5e1c65ea5e6530c0b51c18487c' + // const txid = '0x7c48181cb5c030655eea651c5e9aa808983f646465cbe9d01c227d99cfbc405f' + const txidLE = '0x5f40bccf997d221cd0e9cb6564643f9808a89a5e1c65ea5e6530c0b51c18487c' const tx = '0x01000000000101913e39197867de39bff2c93c75173e086388ee7e8707c90ce4a02dd23f7d2c0d0000000000ffffffff012040351d0000000016001486e7303082a6a21d5837176bc808bf4828371ab602473044022046c3c852a2042ee01ffd7d8d252f297ccc67ae2aa1fac4170f50e8a90af5398802201585ffbbed6e812fb60c025d2f82ae115774279369610b0c76165b6c7132f2810121020c67643b5c862a1aa1afe0a77a28e51a21b08396a0acae69965b22d2a403fd1c4ec10800' const proof = '0x5f40bccf997d221cd0e9cb6564643f9808a89a5e1c65ea5e6530c0b51c18487c886f7da48f4ccfe49283c678dedb376c89853ba46d9a297fe39e8dd557d1f8deb0fb1a28c03f71b267f3a33459b2566975b1653a1238947ed05edca17ef64181b1f09d858a6e25bae4b0e245993d4ea77facba8ed0371bb9b8a6724475bcdc9edf9ead30b61cf6714758b7c93d1b725f86c2a66a07dd291ef566eaa5a59516823d57fd50557f1d938cc2fb61fe0e1acee6f9cb618a9210688a2965c52feabee66d660a5e7f158e363dc464fca2bb1cc856173366d5d20b5cd513a3aab8ebc5be2bd196b783b8773af2472abcea3e32e97938283f7b454769aa1c064c311c3342a755029ee338664999bd8d432080eafae3ca86b52ad2e321e9e634a46c1bd0d174e38bcd4c59a0f0a78c5906c015ef4daf6beb0500a59f4cae00cd46069ce60db2182e74561028e4462f59f639c89b8e254602d6ad9c212b7c2af5db9275e48c467539c6af678d6f09214182df848bd79a06df706f7c3fddfdd95e6f27326c6217ee446543a443f82b711f48c173a769ae8d1e92a986bc76fca732f088bbe04995ba61df5961d7fa0a45cd7467e11f20932c7a0b74c59318e86581c6b5095548' const index = 130 @@ -360,12 +357,12 @@ contract('Deposit', accounts => { assert(depositState.eq(utils.states.REDEEMED)) const requestInfo = await testInstance.getRequestInfo.call() - assert.equal(requestInfo[0], '0x' + '11'.repeat(20)) // address is intentionally not cleared + assert.equal(requestInfo[0], '0x' + '11'.repeat(20)) // address is intentionally not cleared assert.equal(requestInfo[1], utils.address0) assert.equal(requestInfo[4], utils.bytes32zero) const eventList = await deployed.SystemStub.getPastEvents('Redeemed', { fromBlock: blockNumber, toBlock: 'latest' }) - assert.equal(eventList[0].returnValues._txid, txid_le) + assert.equal(eventList[0].returnValues._txid, txidLE) }) it('reverts if not in the redemption flow', async () => { @@ -387,7 +384,7 @@ contract('Deposit', accounts => { }) describe('redemptionTransactionChecks', async () => { - const txid_le = '0x5f40bccf997d221cd0e9cb6564643f9808a89a5e1c65ea5e6530c0b51c18487c' + const txidLE = '0x5f40bccf997d221cd0e9cb6564643f9808a89a5e1c65ea5e6530c0b51c18487c' const tx = '0x01000000000101913e39197867de39bff2c93c75173e086388ee7e8707c90ce4a02dd23f7d2c0d0000000000ffffffff012040351d0000000016001486e7303082a6a21d5837176bc808bf4828371ab602473044022046c3c852a2042ee01ffd7d8d252f297ccc67ae2aa1fac4170f50e8a90af5398802201585ffbbed6e812fb60c025d2f82ae115774279369610b0c76165b6c7132f2810121020c67643b5c862a1aa1afe0a77a28e51a21b08396a0acae69965b22d2a403fd1c4ec10800' const badtx = '0x05' + tx.slice(4) const outputValue = 490029088 @@ -402,7 +399,7 @@ contract('Deposit', accounts => { it('returns the little-endian txid and output value', async () => { const redemptionChecks = await testInstance.redemptionTransactionChecks.call(tx) - assert.equal(redemptionChecks[0], txid_le) + assert.equal(redemptionChecks[0], txidLE) expect(redemptionChecks[1]).to.eq.BN(outputValue) }) @@ -434,7 +431,7 @@ contract('Deposit', accounts => { describe('notifySignatureTimeout', async () => { let timer - + before(async () => { timer = await deployed.TBTCConstants.getSignatureTimeout.call() }) @@ -457,12 +454,12 @@ contract('Deposit', accounts => { }) it('reverts if the signature timeout has not elapsed', async () => { - await testInstance.setRequestInfo(utils.address0, utils.address0, 0, withdrawalRequestTime + timer + 1, utils.bytes32zero) - try { - await testInstance.notifySignatureTimeout() - } catch (e) { - assert.include(e.message, 'Signature timer has not elapsed') - } + await testInstance.setRequestInfo(utils.address0, utils.address0, 0, withdrawalRequestTime + timer + 1, utils.bytes32zero) + try { + await testInstance.notifySignatureTimeout() + } catch (e) { + assert.include(e.message, 'Signature timer has not elapsed') + } }) it('reverts if no funds recieved as signer bond', async () => { @@ -470,14 +467,14 @@ contract('Deposit', accounts => { const bond = await web3.eth.getBalance(deployed.KeepStub.address) assert.equal(0, bond, 'no bond should be sent') try { - await expectThrow(testInstance.notifySignatureTimeout()) + await expectThrow(testInstance.notifySignatureTimeout()) } catch (e) { assert.include(e.message, 'No funds should be received as signer bond') } }) it('starts abort liquidation', async () => { - await deployed.KeepStub.send(1000000, {from: accounts[0]}) + await deployed.KeepStub.send(1000000, { from: accounts[0] }) await testInstance.notifySignatureTimeout() const bond = await web3.eth.getBalance(deployed.KeepStub.address) @@ -485,7 +482,7 @@ contract('Deposit', accounts => { const liquidationTime = await testInstance.getLiquidationAndCourtesyInitiated.call() expect(liquidationTime[0], 'liquidation timestamp not recorded').not.to.eq.BN(0) - }) + }) }) describe('notifyRedemptionProofTimeout', async () => { @@ -528,7 +525,7 @@ contract('Deposit', accounts => { }) it('starts abort liquidation', async () => { - await deployed.KeepStub.send(1000000, {from: accounts[0]}) + await deployed.KeepStub.send(1000000, { from: accounts[0] }) await testInstance.notifyRedemptionProofTimeout() const bond = await web3.eth.getBalance(deployed.KeepStub.address) @@ -587,14 +584,14 @@ contract('Deposit', accounts => { it('returns funder bond', async () => { const beneficiary = accounts[4] const bond = 1000000000000 - + await deployed.SystemStub.setDepositOwner(0, beneficiary) - await testInstance.send(bond, {from: beneficiary}) + await testInstance.send(bond, { from: beneficiary }) const initialBalance = await web3.eth.getBalance(beneficiary) - await testInstance.setKeepInfo(0, fundingProofTimerStart, 0, utils.bytes32zero, utils.bytes32zero) - await deployed.KeepStub.setBondAmount(bond) + await testInstance.setKeepInfo(0, fundingProofTimerStart, 0, utils.bytes32zero, utils.bytes32zero) + await deployed.KeepStub.setBondAmount(bond) await testInstance.notifySignerSetupFailure() const balance = await web3.eth.getBalance(beneficiary) @@ -604,7 +601,6 @@ contract('Deposit', accounts => { }) describe('retrieveSignerPubkey', async () => { - const pubkeyX = '0x4f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa' const pubkeyY = '0x385b6b1b8ead809ca67454d9683fcf2ba03456d6fe2c4abe2b07f0fbdbb2f1c1' @@ -674,8 +670,8 @@ contract('Deposit', accounts => { it('reverts if not awaiting a funding proof', async () => { try { - await testInstance.setState(utils.states.START) - await testInstance.notifyFundingTimeout() + await testInstance.setState(utils.states.START) + await testInstance.notifyFundingTimeout() } catch (e) { assert.include(e.message, 'Funding timeout has not started') } @@ -691,13 +687,13 @@ contract('Deposit', accounts => { }) it('distributes the funder bond to the keep group', async () => { - const beneficiary = accounts[4] - const bond = 1000000 - await testInstance.setKeepInfo(0, 0, withdrawalRequestTime, utils.bytes32zero, utils.bytes32zero) - await testInstance.send(bond, {from: beneficiary}) - await testInstance.notifyFundingTimeout() - const keepBalance = await web3.eth.getBalance(deployed.KeepStub.address) - assert.equal(keepBalance, new BN(bond), 'funder bond not distributed properly') + const beneficiary = accounts[4] + const bond = 1000000 + await testInstance.setKeepInfo(0, 0, withdrawalRequestTime, utils.bytes32zero, utils.bytes32zero) + await testInstance.send(bond, { from: beneficiary }) + await testInstance.notifyFundingTimeout() + const keepBalance = await web3.eth.getBalance(deployed.KeepStub.address) + assert.equal(keepBalance, new BN(bond), 'funder bond not distributed properly') }) }) @@ -711,16 +707,16 @@ contract('Deposit', accounts => { beforeEach(async () => { const block = await web3.eth.getBlock('latest') const blockTimestamp = block.timestamp - fundingProofTimerStart = blockTimestamp - timer.toNumber() - 1 // has elapsed + fundingProofTimerStart = blockTimestamp - timer.toNumber() - 1 // has elapsed await deployed.KeepStub.setSuccess(true) await testInstance.setState(utils.states.AWAITING_BTC_FUNDING_PROOF) await testInstance.setKeepInfo(0, 0, fundingProofTimerStart, utils.bytes32zero, utils.bytes32zero) - await deployed.KeepStub.send(1000000, {from: accounts[0]}) + await deployed.KeepStub.send(1000000, { from: accounts[0] }) }) it('updates to awaiting fraud funding proof and logs FraudDuringSetup if the timer has not elapsed', async () => { const blockNumber = await web3.eth.getBlock('latest').number - await testInstance.setKeepInfo(0, 0, fundingProofTimerStart * 5, utils.bytes32zero, utils.bytes32zero) // timer has not elapsed + await testInstance.setKeepInfo(0, 0, fundingProofTimerStart * 5, utils.bytes32zero, utils.bytes32zero) // timer has not elapsed await testInstance.provideFundingECDSAFraudProof(0, utils.bytes32zero, utils.bytes32zero, utils.bytes32zero, '0x00') const depositState = await testInstance.getState.call() @@ -767,11 +763,11 @@ contract('Deposit', accounts => { const beneficiary = accounts[4] const bond = 10000000000 const blockNumber = await web3.eth.getBlock('latest').number - await testInstance.send(bond, {from: beneficiary}) + await testInstance.send(bond, { from: beneficiary }) const initialBalance = await web3.eth.getBalance(beneficiary) const signerBalance = await web3.eth.getBalance(deployed.KeepStub.address) - await deployed.KeepStub.setBondAmount(bond) + await deployed.KeepStub.setBondAmount(bond) await deployed.SystemStub.setDepositOwner(0, beneficiary) await testInstance.setKeepInfo(0, 0, fundingProofTimerStart * 6, utils.bytes32zero, utils.bytes32zero) await testInstance.provideFundingECDSAFraudProof(0, utils.bytes32zero, utils.bytes32zero, utils.bytes32zero, '0x00') @@ -783,7 +779,6 @@ contract('Deposit', accounts => { assert.equal(eventList.length, 1) expect(finalBalance, 'funder and signer bond should be included in final result').to.eq.BN(balanceCheck) }) - }) describe('notifyFraudFundingTimeout', async () => { @@ -796,10 +791,10 @@ contract('Deposit', accounts => { beforeEach(async () => { const block = await web3.eth.getBlock('latest') const blockTimestamp = block.timestamp - fundingProofTimerStart = blockTimestamp - timer.toNumber() - 1 // timer has elapsed + fundingProofTimerStart = blockTimestamp - timer.toNumber() - 1 // timer has elapsed await testInstance.setState(utils.states.FRAUD_AWAITING_BTC_FUNDING_PROOF) await testInstance.setKeepInfo(0, 0, fundingProofTimerStart, utils.bytes32zero, utils.bytes32zero) - await deployed.KeepStub.send(1000000, {from: accounts[0]}) + await deployed.KeepStub.send(1000000, { from: accounts[0] }) }) it('updates state to setup failed, logs SetupFailed, and deconstes state', async () => { @@ -842,7 +837,7 @@ contract('Deposit', accounts => { it('asserts that it partially slashes signers', async () => { const beneficiary = accounts[4] await deployed.SystemStub.setDepositOwner(0, beneficiary) - const initialBalance = await web3.eth.getBalance(beneficiary) + const initialBalance = await web3.eth.getBalance(beneficiary) const toSeize = await web3.eth.getBalance(deployed.KeepStub.address) await testInstance.setKeepInfo(0, 0, fundingProofTimerStart, utils.bytes32zero, utils.bytes32zero) @@ -850,36 +845,35 @@ contract('Deposit', accounts => { const divisor = await deployed.TBTCConstants.getFundingFraudPartialSlashDivisor.call() const slash = new BN(toSeize).div(new BN(divisor)) - const balanceAfter = await web3.eth.getBalance(beneficiary) + const balanceAfter = await web3.eth.getBalance(beneficiary) const balanceCheck = new BN(initialBalance).add(slash) assert.equal(balanceCheck, balanceAfter, 'partial slash not correctly awarded to funder') }) }) - + describe('provideFraudBTCFundingProof', async () => { // real tx from mainnet bitcoin, interpreted as funding tx - //const tx = '0x01000000000101913e39197867de39bff2c93c75173e086388ee7e8707c90ce4a02dd23f7d2c0d0000000000ffffffff012040351d0000000016001486e7303082a6a21d5837176bc808bf4828371ab602473044022046c3c852a2042ee01ffd7d8d252f297ccc67ae2aa1fac4170f50e8a90af5398802201585ffbbed6e812fb60c025d2f82ae115774279369610b0c76165b6c7132f2810121020c67643b5c862a1aa1afe0a77a28e51a21b08396a0acae69965b22d2a403fd1c4ec10800' - //const txid = '0x7c48181cb5c030655eea651c5e9aa808983f646465cbe9d01c227d99cfbc405f'; - //const txid_le = '0x5f40bccf997d221cd0e9cb6564643f9808a89a5e1c65ea5e6530c0b51c18487c'; + // const tx = '0x01000000000101913e39197867de39bff2c93c75173e086388ee7e8707c90ce4a02dd23f7d2c0d0000000000ffffffff012040351d0000000016001486e7303082a6a21d5837176bc808bf4828371ab602473044022046c3c852a2042ee01ffd7d8d252f297ccc67ae2aa1fac4170f50e8a90af5398802201585ffbbed6e812fb60c025d2f82ae115774279369610b0c76165b6c7132f2810121020c67643b5c862a1aa1afe0a77a28e51a21b08396a0acae69965b22d2a403fd1c4ec10800' + // const txid = '0x7c48181cb5c030655eea651c5e9aa808983f646465cbe9d01c227d99cfbc405f'; + // const txidLE = '0x5f40bccf997d221cd0e9cb6564643f9808a89a5e1c65ea5e6530c0b51c18487c'; const currentDifficulty = 6353030562983 const _version = '0x01000000' const _txInputVector = `0x01913e39197867de39bff2c93c75173e086388ee7e8707c90ce4a02dd23f7d2c0d0000000000ffffffff` const _txOutputVector = '0x012040351d0000000016001486e7303082a6a21d5837176bc808bf4828371ab6' - const _fundingOutputIndex = 0; + const _fundingOutputIndex = 0 const _locktime = '0x4ec10800' - const _txIndexInBlock = 130; + const _txIndexInBlock = 130 const _bitcoinHeaders = '0x00e0ff3fd877ad23af1d0d3e0eb6a700d85b692975dacd36e47b1b00000000000000000095ba61df5961d7fa0a45cd7467e11f20932c7a0b74c59318e86581c6b509554876f6c65c114e2c17e42524d300000020994d3802da5adf80345261bcff2eb87ab7b70db786cb0000000000000000000003169efc259f6e4b5e1bfa469f06792d6f07976a098bff2940c8e7ed3105fdc5eff7c65c114e2c170c4dffc30000c020f898b7ea6a405728055b0627f53f42c57290fe78e0b91900000000000000000075472c91a94fa2aab73369c0686a58796949cf60976e530f6eb295320fa15a1b77f8c65c114e2c17387f1df00000002069137421fc274aa2c907dbf0ec4754285897e8aa36332b0000000000000000004308f2494b702c40e9d61991feb7a15b3be1d73ce988e354e52e7a4e611bd9c2a2f8c65c114e2c1740287df200000020ab63607b09395f856adaa69d553755d9ba5bd8d15da20a000000000000000000090ea7559cda848d97575cb9696c8e33ba7f38d18d5e2f8422837c354aec147839fbc65c114e2c175cf077d6000000200ab3612eac08a31a8fb1d9b5397f897db8d26f6cd83a230000000000000000006f4888720ecbf980ff9c983a8e2e60ad329cc7b130916c2bf2300ea54e412a9ed6fcc65c114e2c17d4fbb88500000020d3e51560f77628a26a8fad01c88f98bd6c9e4bc8703b180000000000000000008e2c6e62a1f4d45dd03be1e6692df89a4e3b1223a4dbdfa94cca94c04c22049992fdc65c114e2c17463edb5e' const _signerPubkeyX = '0xd4aee75e57179f7cd18adcbaa7e2fca4ff7b1b446df88bf0b4398e4a26965a6e' const _signerPubkeyY = '0xe8bfb23428a4efecb3ebdc636139de9a568ed427fff20d28baa33ed48e9c44e1' const _merkleProof = '0x5f40bccf997d221cd0e9cb6564643f9808a89a5e1c65ea5e6530c0b51c18487c886f7da48f4ccfe49283c678dedb376c89853ba46d9a297fe39e8dd557d1f8deb0fb1a28c03f71b267f3a33459b2566975b1653a1238947ed05edca17ef64181b1f09d858a6e25bae4b0e245993d4ea77facba8ed0371bb9b8a6724475bcdc9edf9ead30b61cf6714758b7c93d1b725f86c2a66a07dd291ef566eaa5a59516823d57fd50557f1d938cc2fb61fe0e1acee6f9cb618a9210688a2965c52feabee66d660a5e7f158e363dc464fca2bb1cc856173366d5d20b5cd513a3aab8ebc5be2bd196b783b8773af2472abcea3e32e97938283f7b454769aa1c064c311c3342a755029ee338664999bd8d432080eafae3ca86b52ad2e321e9e634a46c1bd0d174e38bcd4c59a0f0a78c5906c015ef4daf6beb0500a59f4cae00cd46069ce60db2182e74561028e4462f59f639c89b8e254602d6ad9c212b7c2af5db9275e48c467539c6af678d6f09214182df848bd79a06df706f7c3fddfdd95e6f27326c6217ee446543a443f82b711f48c173a769ae8d1e92a986bc76fca732f088bbe04995ba61df5961d7fa0a45cd7467e11f20932c7a0b74c59318e86581c6b5095548' beforeEach(async () => { - await testInstance.setKeepInfo(0, 0, 0, _signerPubkeyX, _signerPubkeyY) await deployed.SystemStub.setCurrentDiff(currentDifficulty) await testInstance.setState(utils.states.FRAUD_AWAITING_BTC_FUNDING_PROOF) - await deployed.KeepStub.send(1000000, {from: accounts[0]}) + await deployed.KeepStub.send(1000000, { from: accounts[0] }) }) it('updates to setup failed, logs SetupFailed, ', async () => { @@ -922,35 +916,35 @@ contract('Deposit', accounts => { const balanceAfter = await web3.eth.getBalance(beneficiary) const balanceCheck = new BN(initialBalance).add(new BN(signerBond)) - assert.equal(balanceCheck, balanceAfter, 'partial slash not correctly awarded to funder') + assert.equal(balanceCheck, balanceAfter, 'partial slash not correctly awarded to funder') }) }) describe('provideBTCFundingProof', async () => { // real tx from mainnet bitcoin, interpreted as funding tx - //const tx = '0x01000000000101913e39197867de39bff2c93c75173e086388ee7e8707c90ce4a02dd23f7d2c0d0000000000ffffffff012040351d0000000016001486e7303082a6a21d5837176bc808bf4828371ab602473044022046c3c852a2042ee01ffd7d8d252f297ccc67ae2aa1fac4170f50e8a90af5398802201585ffbbed6e812fb60c025d2f82ae115774279369610b0c76165b6c7132f2810121020c67643b5c862a1aa1afe0a77a28e51a21b08396a0acae69965b22d2a403fd1c4ec10800' - //const txid = '0x7c48181cb5c030655eea651c5e9aa808983f646465cbe9d01c227d99cfbc405f'; - //const txid_le = '0x5f40bccf997d221cd0e9cb6564643f9808a89a5e1c65ea5e6530c0b51c18487c'; + // const tx = '0x01000000000101913e39197867de39bff2c93c75173e086388ee7e8707c90ce4a02dd23f7d2c0d0000000000ffffffff012040351d0000000016001486e7303082a6a21d5837176bc808bf4828371ab602473044022046c3c852a2042ee01ffd7d8d252f297ccc67ae2aa1fac4170f50e8a90af5398802201585ffbbed6e812fb60c025d2f82ae115774279369610b0c76165b6c7132f2810121020c67643b5c862a1aa1afe0a77a28e51a21b08396a0acae69965b22d2a403fd1c4ec10800' + // const txid = '0x7c48181cb5c030655eea651c5e9aa808983f646465cbe9d01c227d99cfbc405f'; + // const txidLE = '0x5f40bccf997d221cd0e9cb6564643f9808a89a5e1c65ea5e6530c0b51c18487c'; const currentDifficulty = 6353030562983 const _version = '0x01000000' const _txInputVector = `0x01913e39197867de39bff2c93c75173e086388ee7e8707c90ce4a02dd23f7d2c0d0000000000ffffffff` const _txOutputVector = '0x012040351d0000000016001486e7303082a6a21d5837176bc808bf4828371ab6' - const _fundingOutputIndex = 0; + const _fundingOutputIndex = 0 const _locktime = '0x4ec10800' - const _txIndexInBlock = 130; + const _txIndexInBlock = 130 const _bitcoinHeaders = '0x00e0ff3fd877ad23af1d0d3e0eb6a700d85b692975dacd36e47b1b00000000000000000095ba61df5961d7fa0a45cd7467e11f20932c7a0b74c59318e86581c6b509554876f6c65c114e2c17e42524d300000020994d3802da5adf80345261bcff2eb87ab7b70db786cb0000000000000000000003169efc259f6e4b5e1bfa469f06792d6f07976a098bff2940c8e7ed3105fdc5eff7c65c114e2c170c4dffc30000c020f898b7ea6a405728055b0627f53f42c57290fe78e0b91900000000000000000075472c91a94fa2aab73369c0686a58796949cf60976e530f6eb295320fa15a1b77f8c65c114e2c17387f1df00000002069137421fc274aa2c907dbf0ec4754285897e8aa36332b0000000000000000004308f2494b702c40e9d61991feb7a15b3be1d73ce988e354e52e7a4e611bd9c2a2f8c65c114e2c1740287df200000020ab63607b09395f856adaa69d553755d9ba5bd8d15da20a000000000000000000090ea7559cda848d97575cb9696c8e33ba7f38d18d5e2f8422837c354aec147839fbc65c114e2c175cf077d6000000200ab3612eac08a31a8fb1d9b5397f897db8d26f6cd83a230000000000000000006f4888720ecbf980ff9c983a8e2e60ad329cc7b130916c2bf2300ea54e412a9ed6fcc65c114e2c17d4fbb88500000020d3e51560f77628a26a8fad01c88f98bd6c9e4bc8703b180000000000000000008e2c6e62a1f4d45dd03be1e6692df89a4e3b1223a4dbdfa94cca94c04c22049992fdc65c114e2c17463edb5e' const _signerPubkeyX = '0xd4aee75e57179f7cd18adcbaa7e2fca4ff7b1b446df88bf0b4398e4a26965a6e' const _signerPubkeyY = '0xe8bfb23428a4efecb3ebdc636139de9a568ed427fff20d28baa33ed48e9c44e1' const _merkleProof = '0x5f40bccf997d221cd0e9cb6564643f9808a89a5e1c65ea5e6530c0b51c18487c886f7da48f4ccfe49283c678dedb376c89853ba46d9a297fe39e8dd557d1f8deb0fb1a28c03f71b267f3a33459b2566975b1653a1238947ed05edca17ef64181b1f09d858a6e25bae4b0e245993d4ea77facba8ed0371bb9b8a6724475bcdc9edf9ead30b61cf6714758b7c93d1b725f86c2a66a07dd291ef566eaa5a59516823d57fd50557f1d938cc2fb61fe0e1acee6f9cb618a9210688a2965c52feabee66d660a5e7f158e363dc464fca2bb1cc856173366d5d20b5cd513a3aab8ebc5be2bd196b783b8773af2472abcea3e32e97938283f7b454769aa1c064c311c3342a755029ee338664999bd8d432080eafae3ca86b52ad2e321e9e634a46c1bd0d174e38bcd4c59a0f0a78c5906c015ef4daf6beb0500a59f4cae00cd46069ce60db2182e74561028e4462f59f639c89b8e254602d6ad9c212b7c2af5db9275e48c467539c6af678d6f09214182df848bd79a06df706f7c3fddfdd95e6f27326c6217ee446543a443f82b711f48c173a769ae8d1e92a986bc76fca732f088bbe04995ba61df5961d7fa0a45cd7467e11f20932c7a0b74c59318e86581c6b5095548' const _merkleValid = '0x5f40bccf997d221cd0e9cb6564643f9808a89a5e1c65ea5e6530c0b51c18487c00000000' - //const _outputValue = 490029088; + // const _outputValue = 490029088; const _outValueBytes = '0x2040351d00000000' beforeEach(async () => { await testInstance.setKeepInfo(0, 0, 0, _signerPubkeyX, _signerPubkeyY) await deployed.SystemStub.setCurrentDiff(currentDifficulty) await testInstance.setState(utils.states.AWAITING_BTC_FUNDING_PROOF) - await deployed.KeepStub.send(1000000, {from: accounts[0]}) + await deployed.KeepStub.send(1000000, { from: accounts[0] }) }) it('updates to active, stores UTXO info, deconstes funding info, logs Funded', async () => { @@ -987,7 +981,7 @@ contract('Deposit', accounts => { const signerBond = 10000000000 const initialTokenBalance = await deployed.TBTCStub.getBalance(beneficiary) - await testInstance.send(signerBond, {from: beneficiary}) + await testInstance.send(signerBond, { from: beneficiary }) await deployed.SystemStub.setDepositOwner(0, beneficiary) const initialBalance = await web3.eth.getBalance(beneficiary) @@ -997,11 +991,11 @@ contract('Deposit', accounts => { const balanceAfter = await web3.eth.getBalance(beneficiary) const balanceCheck = new BN(initialBalance).add(new BN(signerBond)) - assert.equal(balanceCheck, balanceAfter, 'funder bond not currectly returned') + assert.equal(balanceCheck, balanceAfter, 'funder bond not currectly returned') const endingTokenBalancce = await deployed.TBTCStub.getBalance(beneficiary) - const lotSize = await deployed.TBTCConstants.getLotSize.call() - const toMint = lotSize.mul(new BN(95)).div(new BN(100)); + const lotSize = await deployed.TBTCConstants.getLotSize.call() + const toMint = lotSize.mul(new BN(95)).div(new BN(100)) const tokenCheck = initialTokenBalance.add(new BN(toMint)) expect(tokenCheck, 'incorrect amount minted').to.eq.BN(endingTokenBalancce) }) @@ -1009,10 +1003,9 @@ contract('Deposit', accounts => { }) describe('provideECDSAFraudProof', async () => { - beforeEach(async () => { await testInstance.setState(utils.states.ACTIVE) - await deployed.KeepStub.send(1000000, {from: accounts[0]}) + await deployed.KeepStub.send(1000000, { from: accounts[0] }) }) it('executes', async () => { @@ -1062,8 +1055,8 @@ contract('Deposit', accounts => { describe('provideSPVFraudProof', async () => { // real tx from mainnet bitcoin const currentDiff = 6353030562983 - const txid = '0x7c48181cb5c030655eea651c5e9aa808983f646465cbe9d01c227d99cfbc405f' - const txid_le = '0x5f40bccf997d221cd0e9cb6564643f9808a89a5e1c65ea5e6530c0b51c18487c' + // const txid = '0x7c48181cb5c030655eea651c5e9aa808983f646465cbe9d01c227d99cfbc405f' + // const txidLE = '0x5f40bccf997d221cd0e9cb6564643f9808a89a5e1c65ea5e6530c0b51c18487c' const tx = '0x01000000000101913e39197867de39bff2c93c75173e086388ee7e8707c90ce4a02dd23f7d2c0d0000000000ffffffff012040351d0000000016001486e7303082a6a21d5837176bc808bf4828371ab602473044022046c3c852a2042ee01ffd7d8d252f297ccc67ae2aa1fac4170f50e8a90af5398802201585ffbbed6e812fb60c025d2f82ae115774279369610b0c76165b6c7132f2810121020c67643b5c862a1aa1afe0a77a28e51a21b08396a0acae69965b22d2a403fd1c4ec10800' const proof = '0x5f40bccf997d221cd0e9cb6564643f9808a89a5e1c65ea5e6530c0b51c18487c886f7da48f4ccfe49283c678dedb376c89853ba46d9a297fe39e8dd557d1f8deb0fb1a28c03f71b267f3a33459b2566975b1653a1238947ed05edca17ef64181b1f09d858a6e25bae4b0e245993d4ea77facba8ed0371bb9b8a6724475bcdc9edf9ead30b61cf6714758b7c93d1b725f86c2a66a07dd291ef566eaa5a59516823d57fd50557f1d938cc2fb61fe0e1acee6f9cb618a9210688a2965c52feabee66d660a5e7f158e363dc464fca2bb1cc856173366d5d20b5cd513a3aab8ebc5be2bd196b783b8773af2472abcea3e32e97938283f7b454769aa1c064c311c3342a755029ee338664999bd8d432080eafae3ca86b52ad2e321e9e634a46c1bd0d174e38bcd4c59a0f0a78c5906c015ef4daf6beb0500a59f4cae00cd46069ce60db2182e74561028e4462f59f639c89b8e254602d6ad9c212b7c2af5db9275e48c467539c6af678d6f09214182df848bd79a06df706f7c3fddfdd95e6f27326c6217ee446543a443f82b711f48c173a769ae8d1e92a986bc76fca732f088bbe04995ba61df5961d7fa0a45cd7467e11f20932c7a0b74c59318e86581c6b5095548' const index = 130 @@ -1076,7 +1069,7 @@ contract('Deposit', accounts => { await testInstance.setState(utils.states.ACTIVE) await deployed.SystemStub.setCurrentDiff(currentDiff) await testInstance.setUTXOInfo(prevoutValueBytes, 0, outpoint) - await deployed.KeepStub.send(1000000, {from: accounts[0]}) + await deployed.KeepStub.send(1000000, { from: accounts[0] }) }) it('executes', async () => { @@ -1167,20 +1160,19 @@ contract('Deposit', accounts => { }) it(`burns msg.sender's tokens`, async () => { - const caller = accounts[4] const beneficiary = accounts[5] - const lotSize = await deployed.TBTCConstants.getLotSize.call() + const lotSize = await deployed.TBTCConstants.getLotSize.call() const initialTokenBalance = await deployed.TBTCStub.getBalance(caller) await deployed.SystemStub.setDepositOwner(0, beneficiary) - await testInstance.purchaseSignerBondsAtAuction({from: caller}) + await testInstance.purchaseSignerBondsAtAuction({ from: caller }) const finalTokenBalance = await deployed.TBTCStub.getBalance(caller) const tokenCheck = new BN(finalTokenBalance).add( new BN(lotSize) ) - expect(tokenCheck,'tokens not burned correctly').to.eq.BN(initialTokenBalance) + expect(tokenCheck, 'tokens not burned correctly').to.eq.BN(initialTokenBalance) }) - + it('distributes beneficiary reward', async () => { const caller = accounts[4] const beneficiary = accounts[5] @@ -1188,16 +1180,15 @@ contract('Deposit', accounts => { const returned = await deployed.TBTCStub.balanceOf.call(caller) await deployed.SystemStub.setDepositOwner(0, beneficiary) - await testInstance.purchaseSignerBondsAtAuction({from: caller}) + await testInstance.purchaseSignerBondsAtAuction({ from: caller }) const finalTokenBalance = await deployed.TBTCStub.getBalance(beneficiary) const tokenCheck = new BN(initialTokenBalance).add( new BN(returned)) - expect(finalTokenBalance,'tokens not returned to beneficiary correctly').to.eq.BN(tokenCheck) + expect(finalTokenBalance, 'tokens not returned to beneficiary correctly').to.eq.BN(tokenCheck) }) it('distributes value to the caller', async () => { - const value = 1000000000000 const caller = accounts[4] const beneficiary = accounts[5] @@ -1205,18 +1196,17 @@ contract('Deposit', accounts => { const notifiedTime = block.timestamp const initialBalance = await web3.eth.getBalance(caller) - await testInstance.send(value, {from: accounts[0]}) + await testInstance.send(value, { from: accounts[0] }) await testInstance.setLiquidationAndCourtesyInitated(notifiedTime, 0) await deployed.SystemStub.setDepositOwner(0, beneficiary) - await testInstance.purchaseSignerBondsAtAuction({from: caller}) + await testInstance.purchaseSignerBondsAtAuction({ from: caller }) const finalBalance = await web3.eth.getBalance(caller) - - expect(new BN(finalBalance),'caller balance should increase').to.be.gte.BN(initialBalance) + + expect(new BN(finalBalance), 'caller balance should increase').to.be.gte.BN(initialBalance) }) it('returns keep funds if not fraud', async () => { - const value = 1000000000000 const block = await web3.eth.getBlock('latest') const notifiedTime = block.timestamp @@ -1224,18 +1214,17 @@ contract('Deposit', accounts => { const beneficiary = accounts[5] const initialBalance = await web3.eth.getBalance(deployed.KeepStub.address) - await testInstance.send(value, {from: accounts[0]}) + await testInstance.send(value, { from: accounts[0] }) await testInstance.setLiquidationAndCourtesyInitated(notifiedTime, 0) await deployed.SystemStub.setDepositOwner(0, beneficiary) - await testInstance.purchaseSignerBondsAtAuction({from: caller}) + await testInstance.purchaseSignerBondsAtAuction({ from: caller }) const finalBalance = await web3.eth.getBalance(deployed.KeepStub.address) - + assert(new BN(finalBalance).gtn(new BN(initialBalance)), 'caller balance should increase') }) it('burns if fraud', async () => { - const value = 1000000000000 const block = await web3.eth.getBlock('latest') const notifiedTime = block.timestamp @@ -1243,20 +1232,19 @@ contract('Deposit', accounts => { const beneficiary = accounts[5] const initialBalance = await web3.eth.getBalance(deployed.KeepStub.address) - await testInstance.send(value, {from: accounts[0]}) + await testInstance.send(value, { from: accounts[0] }) await testInstance.setState(utils.states.FRAUD_LIQUIDATION_IN_PROGRESS) await testInstance.setLiquidationAndCourtesyInitated(notifiedTime, 0) await deployed.SystemStub.setDepositOwner(0, beneficiary) - await testInstance.purchaseSignerBondsAtAuction({from: caller}) + await testInstance.purchaseSignerBondsAtAuction({ from: caller }) const finalBalance = await web3.eth.getBalance(deployed.KeepStub.address) - + expect(new BN(finalBalance)).to.eq.BN(initialBalance) }) }) describe('notifyCourtesyCall', async () => { - beforeEach(async () => { await testInstance.setState(utils.states.ACTIVE) await deployed.KeepStub.setBondAmount(0) @@ -1303,12 +1291,11 @@ contract('Deposit', accounts => { }) describe('exitCourtesyCall', async () => { - beforeEach(async () => { const block = await web3.eth.getBlock('latest') const blockTimestamp = block.timestamp const notifiedTime = blockTimestamp // not expired - const fundedTime = blockTimestamp // not expired + const fundedTime = blockTimestamp // not expired await deployed.KeepStub.setBondAmount(new BN('1000000000000000000000000', 10)) await deployed.SystemStub.setOraclePrice(new BN('1', 10)) await testInstance.setState(utils.states.COURTESY_CALL) @@ -1363,11 +1350,10 @@ contract('Deposit', accounts => { }) describe('notifyUndercollateralizedLiquidation', async () => { - beforeEach(async () => { await testInstance.setState(utils.states.ACTIVE) await deployed.KeepStub.setBondAmount(0) - await deployed.KeepStub.send(1000000, {from: accounts[0]}) + await deployed.KeepStub.send(1000000, { from: accounts[0] }) }) afterEach(async () => { @@ -1399,7 +1385,7 @@ contract('Deposit', accounts => { }) it('assert starts signer abort liquidation', async () => { - await deployed.KeepStub.send(1000000, {from: accounts[0]}) + await deployed.KeepStub.send(1000000, { from: accounts[0] }) await testInstance.notifyUndercollateralizedLiquidation() const bond = await web3.eth.getBalance(deployed.KeepStub.address) @@ -1420,10 +1406,10 @@ contract('Deposit', accounts => { beforeEach(async () => { const block = await web3.eth.getBlock('latest') const blockTimestamp = block.timestamp - courtesyTime = blockTimestamp - timer.toNumber() // has not expired + courtesyTime = blockTimestamp - timer.toNumber() // has not expired await testInstance.setState(utils.states.COURTESY_CALL) await testInstance.setLiquidationAndCourtesyInitated(0, courtesyTime) - await deployed.KeepStub.send(1000000, {from: accounts[0]}) + await deployed.KeepStub.send(1000000, { from: accounts[0] }) }) it('executes', async () => { @@ -1449,7 +1435,7 @@ contract('Deposit', accounts => { }) it('assert starts signer abort liquidation', async () => { - await deployed.KeepStub.send(1000000, {from: accounts[0]}) + await deployed.KeepStub.send(1000000, { from: accounts[0] }) await testInstance.notifyCourtesyTimeout() const bond = await web3.eth.getBalance(deployed.KeepStub.address) From b2e31cd063c4b90d02af3b1d03328e87fc472728 Mon Sep 17 00:00:00 2001 From: Nicholas Evans Date: Tue, 18 Jun 2019 03:44:32 -0500 Subject: [PATCH 12/27] Add back design decicion comment in provideBTCFundingProof --- implementation/contracts/deposit/DepositFunding.sol | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/implementation/contracts/deposit/DepositFunding.sol b/implementation/contracts/deposit/DepositFunding.sol index 1092ebc58..55824c371 100644 --- a/implementation/contracts/deposit/DepositFunding.sol +++ b/implementation/contracts/deposit/DepositFunding.sol @@ -291,6 +291,13 @@ library DepositFunding { require(_d.inAwaitingBTCFundingProof(), "Not awaiting funding"); + // Design decision: + // We COULD revoke the funder bond here if the funding proof timeout has elapsed + // HOWEVER, that would only create a situation where the funder loses eerything + // It would be a large punishment for a small crime (being slightly late) + // So if the funder manages to call this before anyone notifies of timeout + // We let them have a freebie + bytes8 _valueBytes; bytes32 txId = abi.encodePacked(_txVersion, _txInputVector, _txOutputVector, _locktime).hash256(); From e7d8aa15a81c0ccb92c03ac0762568c088045393 Mon Sep 17 00:00:00 2001 From: Nicholas Evans Date: Tue, 18 Jun 2019 14:27:21 -0500 Subject: [PATCH 13/27] Update variable names and NatSpec --- implementation/contracts/deposit/Deposit.sol | 40 +++++++++-------- .../contracts/deposit/DepositFunding.sol | 43 ++++++++++--------- .../contracts/deposit/DepositUtils.sol | 28 ++++++------ implementation/test/DepositTest.js | 16 +++---- 4 files changed, 68 insertions(+), 59 deletions(-) diff --git a/implementation/contracts/deposit/Deposit.sol b/implementation/contracts/deposit/Deposit.sol index 5a4c04f24..3de066970 100644 --- a/implementation/contracts/deposit/Deposit.sol +++ b/implementation/contracts/deposit/Deposit.sol @@ -175,22 +175,23 @@ contract Deposit { return true; } - /// @notice Anyone may notify the deposit of a funding proof to activate the deposit - /// @dev This is the happy-path of the funding flow. It means that we have succeeded - /// @param _txVersion 4-byte LE version number - /// @param _txInputVector All inputs prepended by the number of inputs encoded as a VarInt, max 0xFC inputs - /// @param _txOutputVector All outputs prepended by the number of outputs encoded as a VarInt, max 0xFC outputs - /// @param _locktime Final 4 bytes of the BTC transaction. Needed to calculate txId + /// @notice Anyone may notify the deposit of a funding proof during funding fraud + // We reward the funder the entire bond if this occurs + /// @dev Takes a pre-parsed transaction and calculates values needed to verify funding + /// @param _txVersion Transaction version number (4-byte LE) + /// @param _txInputVector All transaction inputs prepended by the number of inputs encoded as a VarInt, max 0xFC(252) inputs + /// @param _txOutputVector All transaction outputs prepended by the number of outputs encoded as a VarInt, max 0xFC(252) outputs + /// @param _txLocktime Final 4 bytes of the transaction /// @param _fundingOutputIndex Index of funding output in _txOutputVector (0-indexed) - /// @param _merkleProof The merkle proof of transaction inclusin in a block + /// @param _merkleProof The merkle proof of transaction inclusion in a block /// @param _txIndexInBlock Transaction index in the block (1-indexed) /// @param _bitcoinHeaders Single bytestring of 80-byte bitcoin headers, lowest height first - /// @return True if successful + /// @return True if no errors are thrown function provideFraudBTCFundingProof( bytes _txVersion, bytes _txInputVector, bytes _txOutputVector, - bytes _locktime, + bytes _txLocktime, uint8 _fundingOutputIndex, bytes _merkleProof, uint256 _txIndexInBlock, @@ -200,7 +201,7 @@ contract Deposit { _txVersion, _txInputVector, _txOutputVector, - _locktime, + _txLocktime, _fundingOutputIndex, _merkleProof, _txIndexInBlock, @@ -210,21 +211,22 @@ contract Deposit { } /// @notice Anyone may notify the deposit of a funding proof to activate the deposit - /// @dev This is the happy-path of the funding flow. It means that we have succeeded - /// @param _txVersion 4-byte LE version number - /// @param _txInputVector All inputs prepended by the number of inputs encoded as a VarInt, max 0xFC inputs - /// @param _txOutputVector All outputs prepended by the number of outputs encoded as a VarInt, max 0xFC outputs - /// @param _locktime Final 4 bytes of the BTC transaction. Needed to calculate txId + /// This is the happy-path of the funding flow. It means that we have succeeded + /// @dev Takes a pre-parsed transaction and calculates values needed to verify funding + /// @param _txVersion Transaction version number (4-byte LE) + /// @param _txInputVector All transaction inputs prepended by the number of inputs encoded as a VarInt, max 0xFC(252) inputs + /// @param _txOutputVector All transaction outputs prepended by the number of outputs encoded as a VarInt, max 0xFC(252) outputs + /// @param _txLocktime Final 4 bytes of the transaction /// @param _fundingOutputIndex Index of funding output in _txOutputVector (0-indexed) - /// @param _merkleProof The merkle proof of transaction inclusin in a block + /// @param _merkleProof The merkle proof of transaction inclusion in a block /// @param _txIndexInBlock Transaction index in the block (1-indexed) /// @param _bitcoinHeaders Single bytestring of 80-byte bitcoin headers, lowest height first - /// @return True if successful + /// @return True if no errors are thrown function provideBTCFundingProof( bytes _txVersion, bytes _txInputVector, bytes _txOutputVector, - bytes _locktime, + bytes _txLocktime, uint8 _fundingOutputIndex, bytes _merkleProof, uint256 _txIndexInBlock, @@ -234,7 +236,7 @@ contract Deposit { _txVersion, _txInputVector, _txOutputVector, - _locktime, + _txLocktime, _fundingOutputIndex, _merkleProof, _txIndexInBlock, diff --git a/implementation/contracts/deposit/DepositFunding.sol b/implementation/contracts/deposit/DepositFunding.sol index 55824c371..64ecedd72 100644 --- a/implementation/contracts/deposit/DepositFunding.sol +++ b/implementation/contracts/deposit/DepositFunding.sol @@ -220,24 +220,25 @@ library DepositFunding { fundingFraudTeardown(_d); } - /// @notice Anyone may notify the deposit of a funding proof to activate the deposit - /// @dev This is the happy-path of the funding flow. It means that we have succeeded + /// @notice Anyone may notify the deposit of a funding proof during funding fraud + // We reward the funder the entire bond if this occurs + /// @dev Takes a pre-parsed transaction and calculates values needed to verify funding /// @param _d Deposit storage pointer - /// @param _txVersion 4-byte LE version number - /// @param _txInputVector All inputs prepended by the number of inputs encoded as a VarInt, max 0xFC inputs - /// @param _txOutputVector All outputs prepended by the number of outputs encoded as a VarInt, max 0xFC outputs - /// @param _locktime Final 4 bytes of the BTC transaction. Needed to calculate txId + /// @param _txVersion Transaction version number (4-byte LE) + /// @param _txInputVector All transaction inputs prepended by the number of inputs encoded as a VarInt, max 0xFC(252) inputs + /// @param _txOutputVector All transaction outputs prepended by the number of outputs encoded as a VarInt, max 0xFC(252) outputs + /// @param _txLocktime Final 4 bytes of the transaction /// @param _fundingOutputIndex Index of funding output in _txOutputVector (0-indexed) - /// @param _merkleProof The merkle proof of transaction inclusin in a block + /// @param _merkleProof The merkle proof of transaction inclusion in a block /// @param _txIndexInBlock Transaction index in the block (1-indexed) /// @param _bitcoinHeaders Single bytestring of 80-byte bitcoin headers, lowest height first - /// @return True if successful + /// @return True if no errors are thrown function provideFraudBTCFundingProof( DepositUtils.Deposit storage _d, bytes _txVersion, bytes _txInputVector, bytes _txOutputVector, - bytes _locktime, + bytes _txLocktime, uint8 _fundingOutputIndex, bytes _merkleProof, uint256 _txIndexInBlock, @@ -247,7 +248,7 @@ library DepositFunding { require(_d.inFraudAwaitingBTCFundingProof(), "Not awaiting a funding proof during setup fraud"); bytes8 _valueBytes; - bytes32 txId = abi.encodePacked(_txVersion, _txInputVector, _txOutputVector, _locktime).hash256(); + bytes32 txId = abi.encodePacked(_txVersion, _txInputVector, _txOutputVector, _txLocktime).hash256(); _valueBytes = _d.findAndParseFundingOutput(_txOutputVector, _fundingOutputIndex); require(DepositUtils.bytes8LEToUint(_valueBytes) >= TBTCConstants.getLotSize(), "Deposit too small"); @@ -266,23 +267,24 @@ library DepositFunding { } /// @notice Anyone may notify the deposit of a funding proof to activate the deposit - /// @dev This is the happy-path of the funding flow. It means that we have succeeded + /// This is the happy-path of the funding flow. It means that we have succeeded + /// @dev Takes a pre-parsed transaction and calculates values needed to verify funding /// @param _d Deposit storage pointer - /// @param _txVersion 4-byte LE version number - /// @param _txInputVector All inputs prepended by the number of inputs encoded as a VarInt, max 0xFC inputs - /// @param _txOutputVector All outputs prepended by the number of outputs encoded as a VarInt, max 0xFC outputs - /// @param _locktime Final 4 bytes of the BTC transaction. Needed to calculate txId + /// @param _txVersion Transaction version number (4-byte LE) + /// @param _txInputVector All transaction inputs prepended by the number of inputs encoded as a VarInt, max 0xFC(252) inputs + /// @param _txOutputVector All transaction outputs prepended by the number of outputs encoded as a VarInt, max 0xFC(252) outputs + /// @param _txLocktime Final 4 bytes of the transaction /// @param _fundingOutputIndex Index of funding output in _txOutputVector (0-indexed) - /// @param _merkleProof The merkle proof of transaction inclusin in a block + /// @param _merkleProof The merkle proof of transaction inclusion in a block /// @param _txIndexInBlock Transaction index in the block (1-indexed) /// @param _bitcoinHeaders Single bytestring of 80-byte bitcoin headers, lowest height first - /// @return True if successful + /// @return True if no errors are thrown function provideBTCFundingProof( DepositUtils.Deposit storage _d, bytes _txVersion, bytes _txInputVector, bytes _txOutputVector, - bytes _locktime, + bytes _txLocktime, uint8 _fundingOutputIndex, bytes _merkleProof, uint256 _txIndexInBlock, @@ -299,9 +301,11 @@ library DepositFunding { // We let them have a freebie bytes8 _valueBytes; - bytes32 txId = abi.encodePacked(_txVersion, _txInputVector, _txOutputVector, _locktime).hash256(); + bytes32 txId = abi.encodePacked(_txVersion, _txInputVector, _txOutputVector, _txLocktime).hash256(); _valueBytes = _d.findAndParseFundingOutput(_txOutputVector, _fundingOutputIndex); + require(DepositUtils.bytes8LEToUint(_valueBytes) >= TBTCConstants.getLotSize(), "Deposit too small"); + _d.checkFundingProof(txId, _bitcoinHeaders.extractMerkleRootLE().toBytes32(), _merkleProof, _txIndexInBlock); _d.evaluateProofDifficulty(_bitcoinHeaders); @@ -326,5 +330,4 @@ library DepositFunding { return true; } - } diff --git a/implementation/contracts/deposit/DepositUtils.sol b/implementation/contracts/deposit/DepositUtils.sol index 0e1432175..afbd8c371 100644 --- a/implementation/contracts/deposit/DepositUtils.sol +++ b/implementation/contracts/deposit/DepositUtils.sol @@ -98,11 +98,11 @@ library DepositUtils { /// @notice Syntactically check an SPV proof for a bitcoin tx /// @dev Stateless SPV Proof verification documented elsewhere - /// @param _d deposit storage pointer - /// @param _bitcoinTx The bitcoin tx that is purportedly included in the header chain - /// @param _merkleProof The merkle proof of inclusion of the tx in the bitcoin block - /// @param _index The index of the tx in the Bitcoin block (1-indexed) - /// @param _bitcoinHeaders An array of tightly-packed bitcoin headers + /// @param _d Deposit storage pointer + /// @param _bitcoinTx The bitcoin tx that is purportedly included in the header chain + /// @param _merkleProof The merkle proof of inclusion of the tx in the bitcoin block + /// @param _index The index of the tx in the Bitcoin block (1-indexed) + /// @param _bitcoinHeaders An array of tightly-packed bitcoin headers /// @return The 32 byte transaction id (little-endian, not block-explorer) function checkProof( Deposit storage _d, @@ -167,7 +167,7 @@ library DepositUtils { bytes memory _output; uint256 _n = (_txOutputVector.slice(0, 1)).bytesToUint(); - require(_n < 0xfd, "VarInts not supported"); + require(_n < 0xfd, "VarInts not supported, Number of outputs cannot exceed 252"); // Find the output paying the signer PKH // This will fail if there are more than 256 outputs @@ -182,22 +182,26 @@ library DepositUtils { /// @notice Extracts the output at a given index in _txOutputVector /// @param _txOutputVector All outputs prepended by the number of outputs encoded as a VarInt, max 0xFC outputs - /// @param _fundingOutputIndex Index of funding output in _txOutputVector + /// @param _fundingOutputIndex Index of funding output in _txOutputVector (0-indexed) /// @return The specified output function _extractOutputAtIndex(bytes _txOutputVector, uint8 _fundingOutputIndex) internal pure returns (bytes) { - // Determine length of first ouput + // Determine length of first output + // offset starts at 1 to skip output number varint + // skip the 8 byte output value to get to length + // next two bytes used to calculate length uint _offset = 1; - uint _len = (_txOutputVector.slice(8 + _offset, 2)).determineOutputLength(); + uint _start = _offset + 8; + uint _length = (_txOutputVector.slice(_start, 2)).determineOutputLength(); // This loop moves forward, and then gets the len of the next one for (uint i = 0; i < _fundingOutputIndex; i++) { - _offset = _offset + _len; - _len = (_txOutputVector.slice(8, 2)).determineOutputLength(); + _offset = _offset + _length; + _length = (_txOutputVector.slice(8, 2)).determineOutputLength(); } // We now have the length and offset of the one we want - return _txOutputVector.slice(_offset, _len); + return _txOutputVector.slice(_offset, _length); } /// @notice Calculates the amount of value at auction right now diff --git a/implementation/test/DepositTest.js b/implementation/test/DepositTest.js index 89e56403e..2b2beb8f1 100644 --- a/implementation/test/DepositTest.js +++ b/implementation/test/DepositTest.js @@ -862,7 +862,7 @@ contract('Deposit', (accounts) => { const _txInputVector = `0x01913e39197867de39bff2c93c75173e086388ee7e8707c90ce4a02dd23f7d2c0d0000000000ffffffff` const _txOutputVector = '0x012040351d0000000016001486e7303082a6a21d5837176bc808bf4828371ab6' const _fundingOutputIndex = 0 - const _locktime = '0x4ec10800' + const _txLocktime = '0x4ec10800' const _txIndexInBlock = 130 const _bitcoinHeaders = '0x00e0ff3fd877ad23af1d0d3e0eb6a700d85b692975dacd36e47b1b00000000000000000095ba61df5961d7fa0a45cd7467e11f20932c7a0b74c59318e86581c6b509554876f6c65c114e2c17e42524d300000020994d3802da5adf80345261bcff2eb87ab7b70db786cb0000000000000000000003169efc259f6e4b5e1bfa469f06792d6f07976a098bff2940c8e7ed3105fdc5eff7c65c114e2c170c4dffc30000c020f898b7ea6a405728055b0627f53f42c57290fe78e0b91900000000000000000075472c91a94fa2aab73369c0686a58796949cf60976e530f6eb295320fa15a1b77f8c65c114e2c17387f1df00000002069137421fc274aa2c907dbf0ec4754285897e8aa36332b0000000000000000004308f2494b702c40e9d61991feb7a15b3be1d73ce988e354e52e7a4e611bd9c2a2f8c65c114e2c1740287df200000020ab63607b09395f856adaa69d553755d9ba5bd8d15da20a000000000000000000090ea7559cda848d97575cb9696c8e33ba7f38d18d5e2f8422837c354aec147839fbc65c114e2c175cf077d6000000200ab3612eac08a31a8fb1d9b5397f897db8d26f6cd83a230000000000000000006f4888720ecbf980ff9c983a8e2e60ad329cc7b130916c2bf2300ea54e412a9ed6fcc65c114e2c17d4fbb88500000020d3e51560f77628a26a8fad01c88f98bd6c9e4bc8703b180000000000000000008e2c6e62a1f4d45dd03be1e6692df89a4e3b1223a4dbdfa94cca94c04c22049992fdc65c114e2c17463edb5e' const _signerPubkeyX = '0xd4aee75e57179f7cd18adcbaa7e2fca4ff7b1b446df88bf0b4398e4a26965a6e' @@ -879,7 +879,7 @@ contract('Deposit', (accounts) => { it('updates to setup failed, logs SetupFailed, ', async () => { const blockNumber = await web3.eth.getBlock('latest').number - await testInstance.provideFraudBTCFundingProof(_version, _txInputVector, _txOutputVector, _locktime, _fundingOutputIndex, _merkleProof, _txIndexInBlock, _bitcoinHeaders) + await testInstance.provideFraudBTCFundingProof(_version, _txInputVector, _txOutputVector, _txLocktime, _fundingOutputIndex, _merkleProof, _txIndexInBlock, _bitcoinHeaders) const keepState = await testInstance.getKeepInfo.call() assert(keepState[0].eqn(0), 'Keep id not deconsted') @@ -898,7 +898,7 @@ contract('Deposit', (accounts) => { it('reverts if not awaiting a funding proof during setup fraud', async () => { try { await testInstance.setState(utils.states.START) - await testInstance.provideFraudBTCFundingProof(_version, _txInputVector, _txOutputVector, _locktime, _fundingOutputIndex, _merkleProof, _txIndexInBlock, _bitcoinHeaders) + await testInstance.provideFraudBTCFundingProof(_version, _txInputVector, _txOutputVector, _txLocktime, _fundingOutputIndex, _merkleProof, _txIndexInBlock, _bitcoinHeaders) } catch (e) { assert.include(e.message, 'Not awaiting a funding proof during setup fraud') } @@ -911,7 +911,7 @@ contract('Deposit', (accounts) => { const initialBalance = await web3.eth.getBalance(beneficiary) const signerBond = await web3.eth.getBalance(deployed.KeepStub.address) - await testInstance.provideFraudBTCFundingProof(_version, _txInputVector, _txOutputVector, _locktime, _fundingOutputIndex, _merkleProof, _txIndexInBlock, _bitcoinHeaders) + await testInstance.provideFraudBTCFundingProof(_version, _txInputVector, _txOutputVector, _txLocktime, _fundingOutputIndex, _merkleProof, _txIndexInBlock, _bitcoinHeaders) const balanceAfter = await web3.eth.getBalance(beneficiary) const balanceCheck = new BN(initialBalance).add(new BN(signerBond)) @@ -930,7 +930,7 @@ contract('Deposit', (accounts) => { const _txInputVector = `0x01913e39197867de39bff2c93c75173e086388ee7e8707c90ce4a02dd23f7d2c0d0000000000ffffffff` const _txOutputVector = '0x012040351d0000000016001486e7303082a6a21d5837176bc808bf4828371ab6' const _fundingOutputIndex = 0 - const _locktime = '0x4ec10800' + const _txLocktime = '0x4ec10800' const _txIndexInBlock = 130 const _bitcoinHeaders = '0x00e0ff3fd877ad23af1d0d3e0eb6a700d85b692975dacd36e47b1b00000000000000000095ba61df5961d7fa0a45cd7467e11f20932c7a0b74c59318e86581c6b509554876f6c65c114e2c17e42524d300000020994d3802da5adf80345261bcff2eb87ab7b70db786cb0000000000000000000003169efc259f6e4b5e1bfa469f06792d6f07976a098bff2940c8e7ed3105fdc5eff7c65c114e2c170c4dffc30000c020f898b7ea6a405728055b0627f53f42c57290fe78e0b91900000000000000000075472c91a94fa2aab73369c0686a58796949cf60976e530f6eb295320fa15a1b77f8c65c114e2c17387f1df00000002069137421fc274aa2c907dbf0ec4754285897e8aa36332b0000000000000000004308f2494b702c40e9d61991feb7a15b3be1d73ce988e354e52e7a4e611bd9c2a2f8c65c114e2c1740287df200000020ab63607b09395f856adaa69d553755d9ba5bd8d15da20a000000000000000000090ea7559cda848d97575cb9696c8e33ba7f38d18d5e2f8422837c354aec147839fbc65c114e2c175cf077d6000000200ab3612eac08a31a8fb1d9b5397f897db8d26f6cd83a230000000000000000006f4888720ecbf980ff9c983a8e2e60ad329cc7b130916c2bf2300ea54e412a9ed6fcc65c114e2c17d4fbb88500000020d3e51560f77628a26a8fad01c88f98bd6c9e4bc8703b180000000000000000008e2c6e62a1f4d45dd03be1e6692df89a4e3b1223a4dbdfa94cca94c04c22049992fdc65c114e2c17463edb5e' const _signerPubkeyX = '0xd4aee75e57179f7cd18adcbaa7e2fca4ff7b1b446df88bf0b4398e4a26965a6e' @@ -950,7 +950,7 @@ contract('Deposit', (accounts) => { it('updates to active, stores UTXO info, deconstes funding info, logs Funded', async () => { const blockNumber = await web3.eth.getBlock('latest').number - await testInstance.provideBTCFundingProof(_version, _txInputVector, _txOutputVector, _locktime, _fundingOutputIndex, _merkleProof, _txIndexInBlock, _bitcoinHeaders) + await testInstance.provideBTCFundingProof(_version, _txInputVector, _txOutputVector, _txLocktime, _fundingOutputIndex, _merkleProof, _txIndexInBlock, _bitcoinHeaders) const UTXOInfo = await testInstance.getUTXOInfo.call() assert.equal(UTXOInfo[0], _outValueBytes) @@ -970,7 +970,7 @@ contract('Deposit', (accounts) => { it('reverts if not awaiting funding proof', async () => { try { await testInstance.setState(utils.states.START) - await testInstance.provideBTCFundingProof(_version, _txInputVector, _txOutputVector, _locktime, _fundingOutputIndex, _merkleProof, _txIndexInBlock, _bitcoinHeaders) + await testInstance.provideBTCFundingProof(_version, _txInputVector, _txOutputVector, _txLocktime, _fundingOutputIndex, _merkleProof, _txIndexInBlock, _bitcoinHeaders) } catch (e) { assert.include(e.message, 'Not awaiting funding') } @@ -986,7 +986,7 @@ contract('Deposit', (accounts) => { const initialBalance = await web3.eth.getBalance(beneficiary) - await testInstance.provideBTCFundingProof(_version, _txInputVector, _txOutputVector, _locktime, _fundingOutputIndex, _merkleProof, _txIndexInBlock, _bitcoinHeaders) + await testInstance.provideBTCFundingProof(_version, _txInputVector, _txOutputVector, _txLocktime, _fundingOutputIndex, _merkleProof, _txIndexInBlock, _bitcoinHeaders) const balanceAfter = await web3.eth.getBalance(beneficiary) const balanceCheck = new BN(initialBalance).add(new BN(signerBond)) From ea9d4f53ddf0961a452bf5010cfcdf3162a4d1cf Mon Sep 17 00:00:00 2001 From: Nicholas Evans Date: Thu, 20 Jun 2019 04:29:49 -0500 Subject: [PATCH 14/27] Add validateAndParseFundingSPVProof function call validateAndParseFundingSPVProof from provideBTCFundingProof and provideFraudBTCFundingProof --- .../contracts/deposit/DepositFunding.sol | 88 ++++++++++++++----- .../contracts/deposit/DepositUtils.sol | 25 +++--- 2 files changed, 81 insertions(+), 32 deletions(-) diff --git a/implementation/contracts/deposit/DepositFunding.sol b/implementation/contracts/deposit/DepositFunding.sol index 64ecedd72..8d3fc53a2 100644 --- a/implementation/contracts/deposit/DepositFunding.sol +++ b/implementation/contracts/deposit/DepositFunding.sol @@ -127,6 +127,46 @@ library DepositFunding { fundingTeardown(_d); } + + /// @notice Validates the funding tx and parses information from it + /// @dev Takes a pre-parsed transaction and calculates values needed to verify funding + /// @param _d Deposit storage pointer + /// @param _txVersion Transaction version number (4-byte LE) + /// @param _txInputVector All transaction inputs prepended by the number of inputs encoded as a VarInt, max 0xFC(252) inputs + /// @param _txOutputVector All transaction outputs prepended by the number of outputs encoded as a VarInt, max 0xFC(252) outputs + /// @param _txLocktime Final 4 bytes of the transaction + /// @param _fundingOutputIndex Index of funding output in _txOutputVector (0-indexed) + /// @param _merkleProof The merkle proof of transaction inclusion in a block + /// @param _txIndexInBlock Transaction index in the block (1-indexed) + /// @param _bitcoinHeaders Single bytestring of 80-byte bitcoin headers, lowest height first + /// @return True if no errors are thrown + function validateAndParseFundingSPVProof( + DepositUtils.Deposit storage _d, + bytes _txVersion, + bytes _txInputVector, + bytes _txOutputVector, + bytes _txLocktime, + uint8 _fundingOutputIndex, + bytes _merkleProof, + uint256 _txIndexInBlock, + bytes _bitcoinHeaders) public view returns (bytes8 _valueBytes, bytes _utxoOutpoint){ + + bytes32 txId = abi.encodePacked(_txVersion, _txInputVector, _txOutputVector, _txLocktime).hash256(); + + _valueBytes = _d.findAndParseFundingOutput(_txOutputVector, _fundingOutputIndex); + require(DepositUtils.bytes8LEToUint(_valueBytes) >= TBTCConstants.getLotSize(), "Deposit too small"); + + + _d.checkFundingProof(txId, _bitcoinHeaders.extractMerkleRootLE().toBytes32(), _merkleProof, _txIndexInBlock); + _d.evaluateProofDifficulty(_bitcoinHeaders); + + // The utxoOutpoint is the LE TXID plus the index of the output as a 4-byte LE int + // _fundingOutputIndex is a uint8, so we know it is only 1 byte + // Therefore, pad with 3 more bytes + _utxoOutpoint = abi.encodePacked(txId, _fundingOutputIndex, hex"000000"); + + } + /// @notice we poll the Keep contract to retrieve our pubkey /// @dev We store the pubkey as 2 bytestrings, X and Y. /// @param _d deposit storage pointer @@ -248,14 +288,20 @@ library DepositFunding { require(_d.inFraudAwaitingBTCFundingProof(), "Not awaiting a funding proof during setup fraud"); bytes8 _valueBytes; - bytes32 txId = abi.encodePacked(_txVersion, _txInputVector, _txOutputVector, _txLocktime).hash256(); - - _valueBytes = _d.findAndParseFundingOutput(_txOutputVector, _fundingOutputIndex); - require(DepositUtils.bytes8LEToUint(_valueBytes) >= TBTCConstants.getLotSize(), "Deposit too small"); - - _d.checkFundingProof(txId, _bitcoinHeaders.extractMerkleRootLE().toBytes32(), _merkleProof, _txIndexInBlock); - _d.evaluateProofDifficulty(_bitcoinHeaders); - + bytes memory _utxoOutpoint; + + (_valueBytes, _utxoOutpoint) = validateAndParseFundingSPVProof( + _d, + _txVersion, + _txInputVector, + _txOutputVector, + _txLocktime, + _fundingOutputIndex, + _merkleProof, + _txIndexInBlock, + _bitcoinHeaders + ); + _d.setFailedSetup(); _d.logSetupFailed(); @@ -301,21 +347,23 @@ library DepositFunding { // We let them have a freebie bytes8 _valueBytes; - bytes32 txId = abi.encodePacked(_txVersion, _txInputVector, _txOutputVector, _txLocktime).hash256(); - - _valueBytes = _d.findAndParseFundingOutput(_txOutputVector, _fundingOutputIndex); - require(DepositUtils.bytes8LEToUint(_valueBytes) >= TBTCConstants.getLotSize(), "Deposit too small"); - - - _d.checkFundingProof(txId, _bitcoinHeaders.extractMerkleRootLE().toBytes32(), _merkleProof, _txIndexInBlock); - _d.evaluateProofDifficulty(_bitcoinHeaders); + bytes memory _utxoOutpoint; + + (_valueBytes, _utxoOutpoint) = validateAndParseFundingSPVProof( + _d, + _txVersion, + _txInputVector, + _txOutputVector, + _txLocktime, + _fundingOutputIndex, + _merkleProof, + _txIndexInBlock, + _bitcoinHeaders + ); // Write down the UTXO info and set to active. Congratulations :) - // The utxoOutpoint is the LE TXID plus the index of the output as a 4-byte LE int - // _fundingOutputIndex is a uint8, so we know it is only 1 byte - // Therefore, pad with 3 more bytes _d.utxoSizeBytes = _valueBytes; - _d.utxoOutpoint = abi.encodePacked(txId, _fundingOutputIndex, hex"000000"); + _d.utxoOutpoint = _utxoOutpoint; fundingTeardown(_d); _d.setActive(); diff --git a/implementation/contracts/deposit/DepositUtils.sol b/implementation/contracts/deposit/DepositUtils.sol index afbd8c371..2e2ba5a29 100644 --- a/implementation/contracts/deposit/DepositUtils.sol +++ b/implementation/contracts/deposit/DepositUtils.sol @@ -101,14 +101,14 @@ library DepositUtils { /// @param _d Deposit storage pointer /// @param _bitcoinTx The bitcoin tx that is purportedly included in the header chain /// @param _merkleProof The merkle proof of inclusion of the tx in the bitcoin block - /// @param _index The index of the tx in the Bitcoin block (1-indexed) + /// @param _txIndexInBlock The index of the tx in the Bitcoin block (1-indexed) /// @param _bitcoinHeaders An array of tightly-packed bitcoin headers /// @return The 32 byte transaction id (little-endian, not block-explorer) function checkProof( Deposit storage _d, bytes _bitcoinTx, bytes _merkleProof, - uint256 _index, + uint256 _txIndexInBlock, bytes _bitcoinHeaders ) public view returns (bytes32) { bytes memory _nIns; @@ -123,7 +123,7 @@ library DepositUtils { _txid.prove( _bitcoinHeaders.extractMerkleRootLE().toBytes32(), _merkleProof, - _index), + _txIndexInBlock), "Tx merkle proof is not valid for provided header and tx"); evaluateProofDifficulty(_d, _bitcoinHeaders); @@ -135,7 +135,7 @@ library DepositUtils { /// @dev Stateless SPV Proof verification documented elsewhere /// @param _d Deposit storage pointer /// @param _txId The ID of the Bitcoin transaction to check - /// @param _merkleRoot The Root of the merkle path + /// @param _merkleRoot The root of the merkle path /// @param _merkleProof The merkle proof of inclusion of the tx in the bitcoin block /// @param _txIndexInBlock 1-indexed transaction index in the block function checkFundingProof( @@ -153,9 +153,9 @@ library DepositUtils { "Tx merkle proof is not valid for provided header and tx"); } - /// @dev find funding ourput using provided index - /// @param _d deposit storage pointer - /// @param _txOutputVector All outputs prepended by the number of outputs encoded as a VarInt, max 0xFC outputs + /// @dev Find funding output using the provided index + /// @param _d Deposit storage pointer + /// @param _txOutputVector All transaction outputs prepended by the number of outputs encoded as a VarInt, max 0xFC outputs /// @param _fundingOutputIndex Index of funding output in _txOutputVector /// @return funding value (bytes8) function findAndParseFundingOutput( @@ -171,7 +171,7 @@ library DepositUtils { // Find the output paying the signer PKH // This will fail if there are more than 256 outputs - _output = _extractOutputAtIndex(_txOutputVector, _fundingOutputIndex); + _output = extractOutputAtIndex(_txOutputVector, _fundingOutputIndex); if (keccak256(_output.extractHash()) == keccak256(abi.encodePacked(signerPKH(_d)))) { _valueBytes = bytes8(_output.slice(0, 8).toBytes32()); return _valueBytes; @@ -181,11 +181,12 @@ library DepositUtils { } /// @notice Extracts the output at a given index in _txOutputVector - /// @param _txOutputVector All outputs prepended by the number of outputs encoded as a VarInt, max 0xFC outputs + /// @param _txOutputVector All transaction outputs prepended by the number of outputs encoded as a VarInt, max 0xFC outputs /// @param _fundingOutputIndex Index of funding output in _txOutputVector (0-indexed) /// @return The specified output - function _extractOutputAtIndex(bytes _txOutputVector, uint8 _fundingOutputIndex) internal pure returns (bytes) { - + function extractOutputAtIndex( + bytes _txOutputVector, + uint8 _fundingOutputIndex) public view returns (bytes) { // Determine length of first output // offset starts at 1 to skip output number varint // skip the 8 byte output value to get to length @@ -197,7 +198,7 @@ library DepositUtils { // This loop moves forward, and then gets the len of the next one for (uint i = 0; i < _fundingOutputIndex; i++) { _offset = _offset + _length; - _length = (_txOutputVector.slice(8, 2)).determineOutputLength(); + _length = (_txOutputVector.slice(8 + _offset, 2)).determineOutputLength(); } // We now have the length and offset of the one we want From 3423e2e6fd0e3c801a4f8ea83fe68445be3b6c15 Mon Sep 17 00:00:00 2001 From: Nicholas Evans Date: Thu, 20 Jun 2019 04:32:26 -0500 Subject: [PATCH 15/27] Tests for extractOutputAtIndex() --- implementation/test/DepositUtilsTest.js | 19 +++++++++++++++++++ .../contracts/deposit/TestDepositUtils.sol | 4 ++++ 2 files changed, 23 insertions(+) diff --git a/implementation/test/DepositUtilsTest.js b/implementation/test/DepositUtilsTest.js index 194e7d8ae..ef1920532 100644 --- a/implementation/test/DepositUtilsTest.js +++ b/implementation/test/DepositUtilsTest.js @@ -151,6 +151,25 @@ contract('DepositUtils', (accounts) => { }) }) + describe('extractOutputAtIndex()', async () => { + it('extracts outputs at specified indices', async () => { + let res + const _txOutputVector1 = '0x024897070000000000220020a4333e5612ab1a1043b25755c89b16d55184a42f81799e623e6bc39db8539c180000000000000000166a14edb1b5c2f39af0fec151732585b1049b07895211' + const _txOutputVector2 = '0x024db6000000000000160014455c0ea778752831d6fc25f6f8cf55dc49d335f040420f0000000000220020aedad4518f56379ef6f1f52f2e0fed64608006b3ccaff2253d847ddc90c91922' + const _txOutputVector3 = '0x044897070000000000220020a4333e5612ab1a1043b25755c89b16d55184a42f81799e623e6bc39db8539c180000000000000000166a14edb1b5c2f39af0fec151732585b1049b078952114db6000000000000160014455c0ea778752831d6fc25f6f8cf55dc49d335f040420f0000000000220020aedad4518f56379ef6f1f52f2e0fed64608006b3ccaff2253d847ddc90c91922' + res = await testUtilsInstance.extractOutputAtIndex.call(_txOutputVector1, 0) + assert.equal(res, '0x4897070000000000220020a4333e5612ab1a1043b25755c89b16d55184a42f81799e623e6bc39db8539c18') + res = await testUtilsInstance.extractOutputAtIndex.call(_txOutputVector1, 1) + assert.equal(res, '0x0000000000000000166a14edb1b5c2f39af0fec151732585b1049b07895211') + res = await testUtilsInstance.extractOutputAtIndex.call(_txOutputVector2, 0) + assert.equal(res, '0x4db6000000000000160014455c0ea778752831d6fc25f6f8cf55dc49d335f0') + res = await testUtilsInstance.extractOutputAtIndex.call(_txOutputVector2, 1) + assert.equal(res, '0x40420f0000000000220020aedad4518f56379ef6f1f52f2e0fed64608006b3ccaff2253d847ddc90c91922') + res = await testUtilsInstance.extractOutputAtIndex.call(_txOutputVector3, 3) + assert.equal(res, '0x40420f0000000000220020aedad4518f56379ef6f1f52f2e0fed64608006b3ccaff2253d847ddc90c91922') + }) + }) + describe('auctionValue()', async () => { it.skip('is TODO') }) diff --git a/implementation/test/contracts/deposit/TestDepositUtils.sol b/implementation/test/contracts/deposit/TestDepositUtils.sol index 80f4133f1..65fef3845 100644 --- a/implementation/test/contracts/deposit/TestDepositUtils.sol +++ b/implementation/test/contracts/deposit/TestDepositUtils.sol @@ -28,6 +28,10 @@ contract TestDepositUtils is TestDeposit { return self.checkProof(_bitcoinTx, _merkleProof, _index, _bitcoinHeaders); } + function extractOutputAtIndex(bytes _txOutputVector, uint8 _fundingOutputIndex) public view returns (bytes) { + return DepositUtils.extractOutputAtIndex(_txOutputVector, _fundingOutputIndex); + } + function auctionValue() public view returns (uint256) { return self.auctionValue(); } From 89a23e7034a83c2fe752b8d53360ff4aaa9182ac Mon Sep 17 00:00:00 2001 From: Nicholas Evans Date: Thu, 20 Jun 2019 04:33:20 -0500 Subject: [PATCH 16/27] Make bitcoin transaction data global --- implementation/test/DepositTest.js | 57 ++++++++++++------------------ 1 file changed, 22 insertions(+), 35 deletions(-) diff --git a/implementation/test/DepositTest.js b/implementation/test/DepositTest.js index 2b2beb8f1..e068244de 100644 --- a/implementation/test/DepositTest.js +++ b/implementation/test/DepositTest.js @@ -54,6 +54,26 @@ const TEST_DEPOSIT_DEPLOY = [ // const r = '0x9a40a074721355f427762f5e6d5cb16a0a9ada06011984e49fc81b3ce89cab6d' // const s = '0x234e909713e74a9a49bf9484a69968dabcb1953bf091fa3e31d48531695cf293' + +// real tx from mainnet bitcoin, interpreted as funding tx +// const tx = '0x01000000000101913e39197867de39bff2c93c75173e086388ee7e8707c90ce4a02dd23f7d2c0d0000000000ffffffff012040351d0000000016001486e7303082a6a21d5837176bc808bf4828371ab602473044022046c3c852a2042ee01ffd7d8d252f297ccc67ae2aa1fac4170f50e8a90af5398802201585ffbbed6e812fb60c025d2f82ae115774279369610b0c76165b6c7132f2810121020c67643b5c862a1aa1afe0a77a28e51a21b08396a0acae69965b22d2a403fd1c4ec10800' +// const txid = '0x7c48181cb5c030655eea651c5e9aa808983f646465cbe9d01c227d99cfbc405f'; +// const txidLE = '0x5f40bccf997d221cd0e9cb6564643f9808a89a5e1c65ea5e6530c0b51c18487c'; +const currentDifficulty = 6353030562983 +const _version = '0x01000000' +const _txInputVector = `0x01913e39197867de39bff2c93c75173e086388ee7e8707c90ce4a02dd23f7d2c0d0000000000ffffffff` +const _txOutputVector = '0x012040351d0000000016001486e7303082a6a21d5837176bc808bf4828371ab6' +const _fundingOutputIndex = 0 +const _txLocktime = '0x4ec10800' +const _txIndexInBlock = 130 +const _bitcoinHeaders = '0x00e0ff3fd877ad23af1d0d3e0eb6a700d85b692975dacd36e47b1b00000000000000000095ba61df5961d7fa0a45cd7467e11f20932c7a0b74c59318e86581c6b509554876f6c65c114e2c17e42524d300000020994d3802da5adf80345261bcff2eb87ab7b70db786cb0000000000000000000003169efc259f6e4b5e1bfa469f06792d6f07976a098bff2940c8e7ed3105fdc5eff7c65c114e2c170c4dffc30000c020f898b7ea6a405728055b0627f53f42c57290fe78e0b91900000000000000000075472c91a94fa2aab73369c0686a58796949cf60976e530f6eb295320fa15a1b77f8c65c114e2c17387f1df00000002069137421fc274aa2c907dbf0ec4754285897e8aa36332b0000000000000000004308f2494b702c40e9d61991feb7a15b3be1d73ce988e354e52e7a4e611bd9c2a2f8c65c114e2c1740287df200000020ab63607b09395f856adaa69d553755d9ba5bd8d15da20a000000000000000000090ea7559cda848d97575cb9696c8e33ba7f38d18d5e2f8422837c354aec147839fbc65c114e2c175cf077d6000000200ab3612eac08a31a8fb1d9b5397f897db8d26f6cd83a230000000000000000006f4888720ecbf980ff9c983a8e2e60ad329cc7b130916c2bf2300ea54e412a9ed6fcc65c114e2c17d4fbb88500000020d3e51560f77628a26a8fad01c88f98bd6c9e4bc8703b180000000000000000008e2c6e62a1f4d45dd03be1e6692df89a4e3b1223a4dbdfa94cca94c04c22049992fdc65c114e2c17463edb5e' +const _signerPubkeyX = '0xd4aee75e57179f7cd18adcbaa7e2fca4ff7b1b446df88bf0b4398e4a26965a6e' +const _signerPubkeyY = '0xe8bfb23428a4efecb3ebdc636139de9a568ed427fff20d28baa33ed48e9c44e1' +const _merkleProof = '0x5f40bccf997d221cd0e9cb6564643f9808a89a5e1c65ea5e6530c0b51c18487c886f7da48f4ccfe49283c678dedb376c89853ba46d9a297fe39e8dd557d1f8deb0fb1a28c03f71b267f3a33459b2566975b1653a1238947ed05edca17ef64181b1f09d858a6e25bae4b0e245993d4ea77facba8ed0371bb9b8a6724475bcdc9edf9ead30b61cf6714758b7c93d1b725f86c2a66a07dd291ef566eaa5a59516823d57fd50557f1d938cc2fb61fe0e1acee6f9cb618a9210688a2965c52feabee66d660a5e7f158e363dc464fca2bb1cc856173366d5d20b5cd513a3aab8ebc5be2bd196b783b8773af2472abcea3e32e97938283f7b454769aa1c064c311c3342a755029ee338664999bd8d432080eafae3ca86b52ad2e321e9e634a46c1bd0d174e38bcd4c59a0f0a78c5906c015ef4daf6beb0500a59f4cae00cd46069ce60db2182e74561028e4462f59f639c89b8e254602d6ad9c212b7c2af5db9275e48c467539c6af678d6f09214182df848bd79a06df706f7c3fddfdd95e6f27326c6217ee446543a443f82b711f48c173a769ae8d1e92a986bc76fca732f088bbe04995ba61df5961d7fa0a45cd7467e11f20932c7a0b74c59318e86581c6b5095548' +const _expectedUTXOoutpoint = '0x5f40bccf997d221cd0e9cb6564643f9808a89a5e1c65ea5e6530c0b51c18487c00000000' +// const _outputValue = 490029088; +const _outValueBytes = '0x2040351d00000000' + contract('Deposit', (accounts) => { let deployed let testInstance @@ -853,22 +873,7 @@ contract('Deposit', (accounts) => { }) describe('provideFraudBTCFundingProof', async () => { - // real tx from mainnet bitcoin, interpreted as funding tx - // const tx = '0x01000000000101913e39197867de39bff2c93c75173e086388ee7e8707c90ce4a02dd23f7d2c0d0000000000ffffffff012040351d0000000016001486e7303082a6a21d5837176bc808bf4828371ab602473044022046c3c852a2042ee01ffd7d8d252f297ccc67ae2aa1fac4170f50e8a90af5398802201585ffbbed6e812fb60c025d2f82ae115774279369610b0c76165b6c7132f2810121020c67643b5c862a1aa1afe0a77a28e51a21b08396a0acae69965b22d2a403fd1c4ec10800' - // const txid = '0x7c48181cb5c030655eea651c5e9aa808983f646465cbe9d01c227d99cfbc405f'; - // const txidLE = '0x5f40bccf997d221cd0e9cb6564643f9808a89a5e1c65ea5e6530c0b51c18487c'; - const currentDifficulty = 6353030562983 - const _version = '0x01000000' - const _txInputVector = `0x01913e39197867de39bff2c93c75173e086388ee7e8707c90ce4a02dd23f7d2c0d0000000000ffffffff` - const _txOutputVector = '0x012040351d0000000016001486e7303082a6a21d5837176bc808bf4828371ab6' - const _fundingOutputIndex = 0 - const _txLocktime = '0x4ec10800' - const _txIndexInBlock = 130 - const _bitcoinHeaders = '0x00e0ff3fd877ad23af1d0d3e0eb6a700d85b692975dacd36e47b1b00000000000000000095ba61df5961d7fa0a45cd7467e11f20932c7a0b74c59318e86581c6b509554876f6c65c114e2c17e42524d300000020994d3802da5adf80345261bcff2eb87ab7b70db786cb0000000000000000000003169efc259f6e4b5e1bfa469f06792d6f07976a098bff2940c8e7ed3105fdc5eff7c65c114e2c170c4dffc30000c020f898b7ea6a405728055b0627f53f42c57290fe78e0b91900000000000000000075472c91a94fa2aab73369c0686a58796949cf60976e530f6eb295320fa15a1b77f8c65c114e2c17387f1df00000002069137421fc274aa2c907dbf0ec4754285897e8aa36332b0000000000000000004308f2494b702c40e9d61991feb7a15b3be1d73ce988e354e52e7a4e611bd9c2a2f8c65c114e2c1740287df200000020ab63607b09395f856adaa69d553755d9ba5bd8d15da20a000000000000000000090ea7559cda848d97575cb9696c8e33ba7f38d18d5e2f8422837c354aec147839fbc65c114e2c175cf077d6000000200ab3612eac08a31a8fb1d9b5397f897db8d26f6cd83a230000000000000000006f4888720ecbf980ff9c983a8e2e60ad329cc7b130916c2bf2300ea54e412a9ed6fcc65c114e2c17d4fbb88500000020d3e51560f77628a26a8fad01c88f98bd6c9e4bc8703b180000000000000000008e2c6e62a1f4d45dd03be1e6692df89a4e3b1223a4dbdfa94cca94c04c22049992fdc65c114e2c17463edb5e' - const _signerPubkeyX = '0xd4aee75e57179f7cd18adcbaa7e2fca4ff7b1b446df88bf0b4398e4a26965a6e' - const _signerPubkeyY = '0xe8bfb23428a4efecb3ebdc636139de9a568ed427fff20d28baa33ed48e9c44e1' - const _merkleProof = '0x5f40bccf997d221cd0e9cb6564643f9808a89a5e1c65ea5e6530c0b51c18487c886f7da48f4ccfe49283c678dedb376c89853ba46d9a297fe39e8dd557d1f8deb0fb1a28c03f71b267f3a33459b2566975b1653a1238947ed05edca17ef64181b1f09d858a6e25bae4b0e245993d4ea77facba8ed0371bb9b8a6724475bcdc9edf9ead30b61cf6714758b7c93d1b725f86c2a66a07dd291ef566eaa5a59516823d57fd50557f1d938cc2fb61fe0e1acee6f9cb618a9210688a2965c52feabee66d660a5e7f158e363dc464fca2bb1cc856173366d5d20b5cd513a3aab8ebc5be2bd196b783b8773af2472abcea3e32e97938283f7b454769aa1c064c311c3342a755029ee338664999bd8d432080eafae3ca86b52ad2e321e9e634a46c1bd0d174e38bcd4c59a0f0a78c5906c015ef4daf6beb0500a59f4cae00cd46069ce60db2182e74561028e4462f59f639c89b8e254602d6ad9c212b7c2af5db9275e48c467539c6af678d6f09214182df848bd79a06df706f7c3fddfdd95e6f27326c6217ee446543a443f82b711f48c173a769ae8d1e92a986bc76fca732f088bbe04995ba61df5961d7fa0a45cd7467e11f20932c7a0b74c59318e86581c6b5095548' - + beforeEach(async () => { await testInstance.setKeepInfo(0, 0, 0, _signerPubkeyX, _signerPubkeyY) await deployed.SystemStub.setCurrentDiff(currentDifficulty) @@ -921,24 +926,6 @@ contract('Deposit', (accounts) => { }) describe('provideBTCFundingProof', async () => { - // real tx from mainnet bitcoin, interpreted as funding tx - // const tx = '0x01000000000101913e39197867de39bff2c93c75173e086388ee7e8707c90ce4a02dd23f7d2c0d0000000000ffffffff012040351d0000000016001486e7303082a6a21d5837176bc808bf4828371ab602473044022046c3c852a2042ee01ffd7d8d252f297ccc67ae2aa1fac4170f50e8a90af5398802201585ffbbed6e812fb60c025d2f82ae115774279369610b0c76165b6c7132f2810121020c67643b5c862a1aa1afe0a77a28e51a21b08396a0acae69965b22d2a403fd1c4ec10800' - // const txid = '0x7c48181cb5c030655eea651c5e9aa808983f646465cbe9d01c227d99cfbc405f'; - // const txidLE = '0x5f40bccf997d221cd0e9cb6564643f9808a89a5e1c65ea5e6530c0b51c18487c'; - const currentDifficulty = 6353030562983 - const _version = '0x01000000' - const _txInputVector = `0x01913e39197867de39bff2c93c75173e086388ee7e8707c90ce4a02dd23f7d2c0d0000000000ffffffff` - const _txOutputVector = '0x012040351d0000000016001486e7303082a6a21d5837176bc808bf4828371ab6' - const _fundingOutputIndex = 0 - const _txLocktime = '0x4ec10800' - const _txIndexInBlock = 130 - const _bitcoinHeaders = '0x00e0ff3fd877ad23af1d0d3e0eb6a700d85b692975dacd36e47b1b00000000000000000095ba61df5961d7fa0a45cd7467e11f20932c7a0b74c59318e86581c6b509554876f6c65c114e2c17e42524d300000020994d3802da5adf80345261bcff2eb87ab7b70db786cb0000000000000000000003169efc259f6e4b5e1bfa469f06792d6f07976a098bff2940c8e7ed3105fdc5eff7c65c114e2c170c4dffc30000c020f898b7ea6a405728055b0627f53f42c57290fe78e0b91900000000000000000075472c91a94fa2aab73369c0686a58796949cf60976e530f6eb295320fa15a1b77f8c65c114e2c17387f1df00000002069137421fc274aa2c907dbf0ec4754285897e8aa36332b0000000000000000004308f2494b702c40e9d61991feb7a15b3be1d73ce988e354e52e7a4e611bd9c2a2f8c65c114e2c1740287df200000020ab63607b09395f856adaa69d553755d9ba5bd8d15da20a000000000000000000090ea7559cda848d97575cb9696c8e33ba7f38d18d5e2f8422837c354aec147839fbc65c114e2c175cf077d6000000200ab3612eac08a31a8fb1d9b5397f897db8d26f6cd83a230000000000000000006f4888720ecbf980ff9c983a8e2e60ad329cc7b130916c2bf2300ea54e412a9ed6fcc65c114e2c17d4fbb88500000020d3e51560f77628a26a8fad01c88f98bd6c9e4bc8703b180000000000000000008e2c6e62a1f4d45dd03be1e6692df89a4e3b1223a4dbdfa94cca94c04c22049992fdc65c114e2c17463edb5e' - const _signerPubkeyX = '0xd4aee75e57179f7cd18adcbaa7e2fca4ff7b1b446df88bf0b4398e4a26965a6e' - const _signerPubkeyY = '0xe8bfb23428a4efecb3ebdc636139de9a568ed427fff20d28baa33ed48e9c44e1' - const _merkleProof = '0x5f40bccf997d221cd0e9cb6564643f9808a89a5e1c65ea5e6530c0b51c18487c886f7da48f4ccfe49283c678dedb376c89853ba46d9a297fe39e8dd557d1f8deb0fb1a28c03f71b267f3a33459b2566975b1653a1238947ed05edca17ef64181b1f09d858a6e25bae4b0e245993d4ea77facba8ed0371bb9b8a6724475bcdc9edf9ead30b61cf6714758b7c93d1b725f86c2a66a07dd291ef566eaa5a59516823d57fd50557f1d938cc2fb61fe0e1acee6f9cb618a9210688a2965c52feabee66d660a5e7f158e363dc464fca2bb1cc856173366d5d20b5cd513a3aab8ebc5be2bd196b783b8773af2472abcea3e32e97938283f7b454769aa1c064c311c3342a755029ee338664999bd8d432080eafae3ca86b52ad2e321e9e634a46c1bd0d174e38bcd4c59a0f0a78c5906c015ef4daf6beb0500a59f4cae00cd46069ce60db2182e74561028e4462f59f639c89b8e254602d6ad9c212b7c2af5db9275e48c467539c6af678d6f09214182df848bd79a06df706f7c3fddfdd95e6f27326c6217ee446543a443f82b711f48c173a769ae8d1e92a986bc76fca732f088bbe04995ba61df5961d7fa0a45cd7467e11f20932c7a0b74c59318e86581c6b5095548' - const _merkleValid = '0x5f40bccf997d221cd0e9cb6564643f9808a89a5e1c65ea5e6530c0b51c18487c00000000' - // const _outputValue = 490029088; - const _outValueBytes = '0x2040351d00000000' beforeEach(async () => { await testInstance.setKeepInfo(0, 0, 0, _signerPubkeyX, _signerPubkeyY) @@ -954,7 +941,7 @@ contract('Deposit', (accounts) => { const UTXOInfo = await testInstance.getUTXOInfo.call() assert.equal(UTXOInfo[0], _outValueBytes) - assert.equal(UTXOInfo[2], _merkleValid) + assert.equal(UTXOInfo[2], _expectedUTXOoutpoint) const keepState = await testInstance.getKeepInfo.call() assert(keepState[1].eqn(0), 'signingGroupRequestedAt not deconsted') From cf991a4ca8f63b801880d5a65c81920753a5c11e Mon Sep 17 00:00:00 2001 From: Nicholas Evans Date: Thu, 20 Jun 2019 17:01:19 -0500 Subject: [PATCH 17/27] Refactor Transaction proof to avoid code duplication --- .../contracts/deposit/DepositFunding.sol | 7 ++-- .../contracts/deposit/DepositLiquidation.sol | 2 +- .../contracts/deposit/DepositRedemption.sol | 9 +--- .../contracts/deposit/DepositUtils.sol | 42 ++++++++----------- implementation/test/DepositTest.js | 1 + implementation/test/DepositUtilsTest.js | 8 ++-- .../contracts/deposit/TestDepositUtils.sol | 4 +- 7 files changed, 30 insertions(+), 43 deletions(-) diff --git a/implementation/contracts/deposit/DepositFunding.sol b/implementation/contracts/deposit/DepositFunding.sol index 8d3fc53a2..24870fc96 100644 --- a/implementation/contracts/deposit/DepositFunding.sol +++ b/implementation/contracts/deposit/DepositFunding.sol @@ -127,7 +127,6 @@ library DepositFunding { fundingTeardown(_d); } - /// @notice Validates the funding tx and parses information from it /// @dev Takes a pre-parsed transaction and calculates values needed to verify funding /// @param _d Deposit storage pointer @@ -139,7 +138,7 @@ library DepositFunding { /// @param _merkleProof The merkle proof of transaction inclusion in a block /// @param _txIndexInBlock Transaction index in the block (1-indexed) /// @param _bitcoinHeaders Single bytestring of 80-byte bitcoin headers, lowest height first - /// @return True if no errors are thrown + /// @return The 8-byte LE UTXO size in satoshi, the 36byte outpoint function validateAndParseFundingSPVProof( DepositUtils.Deposit storage _d, bytes _txVersion, @@ -157,8 +156,8 @@ library DepositFunding { require(DepositUtils.bytes8LEToUint(_valueBytes) >= TBTCConstants.getLotSize(), "Deposit too small"); - _d.checkFundingProof(txId, _bitcoinHeaders.extractMerkleRootLE().toBytes32(), _merkleProof, _txIndexInBlock); - _d.evaluateProofDifficulty(_bitcoinHeaders); + _d.checkProofFromTxId(txId, _merkleProof, _txIndexInBlock, _bitcoinHeaders); + // The utxoOutpoint is the LE TXID plus the index of the output as a 4-byte LE int // _fundingOutputIndex is a uint8, so we know it is only 1 byte diff --git a/implementation/contracts/deposit/DepositLiquidation.sol b/implementation/contracts/deposit/DepositLiquidation.sol index 000d36d79..8336ffd28 100644 --- a/implementation/contracts/deposit/DepositLiquidation.sol +++ b/implementation/contracts/deposit/DepositLiquidation.sol @@ -185,7 +185,7 @@ library DepositLiquidation { ); require(!_d.inEndState(), "Contract has halted"); - _d.checkProof(_bitcoinTx, _merkleProof, _index, _bitcoinHeaders); + _d.checkProofFromTx(_bitcoinTx, _merkleProof, _index, _bitcoinHeaders); for (i = 0; i < _bitcoinTx.extractNumInputs(); i++) { _input = _bitcoinTx.extractInputAtIndex(i); if (keccak256(_input.extractOutpoint()) == keccak256(_d.utxoOutpoint)) { diff --git a/implementation/contracts/deposit/DepositRedemption.sol b/implementation/contracts/deposit/DepositRedemption.sol index 158d335fb..86b26f444 100644 --- a/implementation/contracts/deposit/DepositRedemption.sol +++ b/implementation/contracts/deposit/DepositRedemption.sol @@ -226,15 +226,8 @@ library DepositRedemption { require(_d.inRedemption(), "Redemption proof only allowed from redemption flow"); (_txid, _fundingOutputValue) = redemptionTransactionChecks(_d, _bitcoinTx); - // We don't use checkproof here because we need access to the parse info - require( - _txid.prove( - _bitcoinHeaders.extractMerkleRootLE().toBytes32(), - _merkleProof, - _index), - "Tx merkle proof is not valid for provided header"); - _d.evaluateProofDifficulty(_bitcoinHeaders); + _d.checkProofFromTxId(_txid, _merkleProof, _index, _bitcoinHeaders); require((_d.utxoSize().sub(_fundingOutputValue)) <= _d.initialRedemptionFee * 5, "Fee unexpectedly very high"); diff --git a/implementation/contracts/deposit/DepositUtils.sol b/implementation/contracts/deposit/DepositUtils.sol index 2e2ba5a29..aa337a6cd 100644 --- a/implementation/contracts/deposit/DepositUtils.sol +++ b/implementation/contracts/deposit/DepositUtils.sol @@ -104,7 +104,7 @@ library DepositUtils { /// @param _txIndexInBlock The index of the tx in the Bitcoin block (1-indexed) /// @param _bitcoinHeaders An array of tightly-packed bitcoin headers /// @return The 32 byte transaction id (little-endian, not block-explorer) - function checkProof( + function checkProofFromTx( Deposit storage _d, bytes _bitcoinTx, bytes _merkleProof, @@ -119,39 +119,33 @@ library DepositUtils { bytes32 _txid; (_nIns, _ins, _nOuts, _outs, _locktime, _txid) = _bitcoinTx.parseTransaction(); require(_txid != bytes32(0), "Failed tx parsing"); - require( - _txid.prove( - _bitcoinHeaders.extractMerkleRootLE().toBytes32(), - _merkleProof, - _txIndexInBlock), - "Tx merkle proof is not valid for provided header and tx"); - - evaluateProofDifficulty(_d, _bitcoinHeaders); - + checkProofFromTxId(_d, _txid, _merkleProof, _txIndexInBlock, _bitcoinHeaders); return _txid; } - /// @notice Syntactically check an SPV proof for a bitcoin tx - /// @dev Stateless SPV Proof verification documented elsewhere - /// @param _d Deposit storage pointer - /// @param _txId The ID of the Bitcoin transaction to check - /// @param _merkleRoot The root of the merkle path - /// @param _merkleProof The merkle proof of inclusion of the tx in the bitcoin block - /// @param _txIndexInBlock 1-indexed transaction index in the block - function checkFundingProof( + /// @notice Syntactically check an SPV proof for a bitcoin tx + /// @dev Stateless SPV Proof verification documented elsewhere + /// @param _d Deposit storage pointer + /// @param _txId The bitcoin txid of the tx that is purportedly included in the header chain + /// @param _merkleProof The merkle proof of inclusion of the tx in the bitcoin block + /// @param _txIndexInBlock The index of the tx in the Bitcoin block (1-indexed) + /// @param _bitcoinHeaders An array of tightly-packed bitcoin headers + function checkProofFromTxId( Deposit storage _d, bytes32 _txId, - bytes32 _merkleRoot, bytes _merkleProof, - uint256 _txIndexInBlock - ) public view { + uint256 _txIndexInBlock, + bytes _bitcoinHeaders) public view{ require( _txId.prove( - _merkleRoot, + _bitcoinHeaders.extractMerkleRootLE().toBytes32(), _merkleProof, - _txIndexInBlock), + _txIndexInBlock + ), "Tx merkle proof is not valid for provided header and tx"); - } + + evaluateProofDifficulty(_d, _bitcoinHeaders); +} /// @dev Find funding output using the provided index /// @param _d Deposit storage pointer diff --git a/implementation/test/DepositTest.js b/implementation/test/DepositTest.js index e068244de..6da6a2df9 100644 --- a/implementation/test/DepositTest.js +++ b/implementation/test/DepositTest.js @@ -56,6 +56,7 @@ const TEST_DEPOSIT_DEPLOY = [ // real tx from mainnet bitcoin, interpreted as funding tx +// tx source: https://www.blockchain.com/btc/tx/7c48181cb5c030655eea651c5e9aa808983f646465cbe9d01c227d99cfbc405f // const tx = '0x01000000000101913e39197867de39bff2c93c75173e086388ee7e8707c90ce4a02dd23f7d2c0d0000000000ffffffff012040351d0000000016001486e7303082a6a21d5837176bc808bf4828371ab602473044022046c3c852a2042ee01ffd7d8d252f297ccc67ae2aa1fac4170f50e8a90af5398802201585ffbbed6e812fb60c025d2f82ae115774279369610b0c76165b6c7132f2810121020c67643b5c862a1aa1afe0a77a28e51a21b08396a0acae69965b22d2a403fd1c4ec10800' // const txid = '0x7c48181cb5c030655eea651c5e9aa808983f646465cbe9d01c227d99cfbc405f'; // const txidLE = '0x5f40bccf997d221cd0e9cb6564643f9808a89a5e1c65ea5e6530c0b51c18487c'; diff --git a/implementation/test/DepositUtilsTest.js b/implementation/test/DepositUtilsTest.js index ef1920532..45b2745ec 100644 --- a/implementation/test/DepositUtilsTest.js +++ b/implementation/test/DepositUtilsTest.js @@ -123,17 +123,17 @@ contract('DepositUtils', (accounts) => { }) }) - describe('checkProof()', async () => { + describe('checkProofFromTx()', async () => { it('returns the correct _txid', async () => { await deployed.SystemStub.setCurrentDiff(6379265451411) - const res = await testUtilsInstance.checkProof.call(utils.TX.tx, utils.TX.proof, utils.TX.index, utils.HEADER_PROOFS.slice(-1)[0]) + const res = await testUtilsInstance.checkProofFromTx.call(utils.TX.tx, utils.TX.proof, utils.TX.index, utils.HEADER_PROOFS.slice(-1)[0]) assert.equal(res, utils.TX.tx_id_le) }) it('fails with a broken proof', async () => { try { await deployed.SystemStub.setCurrentDiff(6379265451411) - await testUtilsInstance.checkProof.call(utils.TX.tx, utils.TX.proof, 0, utils.HEADER_PROOFS.slice(-1)[0]) + await testUtilsInstance.checkProofFromTx.call(utils.TX.tx, utils.TX.proof, 0, utils.HEADER_PROOFS.slice(-1)[0]) assert(false, 'Test call did not error as expected') } catch (e) { assert.include(e.message, 'Tx merkle proof is not valid for provided header and tx') @@ -143,7 +143,7 @@ contract('DepositUtils', (accounts) => { it('fails with a broken tx', async () => { try { await deployed.SystemStub.setCurrentDiff(6379265451411) - await testUtilsInstance.checkProof.call('0x00', utils.TX.proof, 0, utils.HEADER_PROOFS.slice(-1)[0]) + await testUtilsInstance.checkProofFromTx.call('0x00', utils.TX.proof, 0, utils.HEADER_PROOFS.slice(-1)[0]) assert(false, 'Test call did not error as expected') } catch (e) { assert.include(e.message, 'Failed tx parsing') diff --git a/implementation/test/contracts/deposit/TestDepositUtils.sol b/implementation/test/contracts/deposit/TestDepositUtils.sol index 65fef3845..324ebe0c9 100644 --- a/implementation/test/contracts/deposit/TestDepositUtils.sol +++ b/implementation/test/contracts/deposit/TestDepositUtils.sol @@ -19,13 +19,13 @@ contract TestDepositUtils is TestDeposit { return self.evaluateProofDifficulty(_bitcoinHeaders); } - function checkProof( + function checkProofFromTx( bytes _bitcoinTx, bytes _merkleProof, uint256 _index, bytes _bitcoinHeaders ) public view returns (bytes32) { - return self.checkProof(_bitcoinTx, _merkleProof, _index, _bitcoinHeaders); + return self.checkProofFromTx(_bitcoinTx, _merkleProof, _index, _bitcoinHeaders); } function extractOutputAtIndex(bytes _txOutputVector, uint8 _fundingOutputIndex) public view returns (bytes) { From 6591b5f9fc524fbed9843d927709863809dc31e2 Mon Sep 17 00:00:00 2001 From: Nicholas Evans Date: Thu, 20 Jun 2019 17:29:19 -0500 Subject: [PATCH 18/27] Fix js lint error --- implementation/test/DepositTest.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/implementation/test/DepositTest.js b/implementation/test/DepositTest.js index 6da6a2df9..414897cdc 100644 --- a/implementation/test/DepositTest.js +++ b/implementation/test/DepositTest.js @@ -874,7 +874,6 @@ contract('Deposit', (accounts) => { }) describe('provideFraudBTCFundingProof', async () => { - beforeEach(async () => { await testInstance.setKeepInfo(0, 0, 0, _signerPubkeyX, _signerPubkeyY) await deployed.SystemStub.setCurrentDiff(currentDifficulty) @@ -927,7 +926,6 @@ contract('Deposit', (accounts) => { }) describe('provideBTCFundingProof', async () => { - beforeEach(async () => { await testInstance.setKeepInfo(0, 0, 0, _signerPubkeyX, _signerPubkeyY) await deployed.SystemStub.setCurrentDiff(currentDifficulty) From e5de3e1134a51f94a971fd63a04af8f3f4a0dc8c Mon Sep 17 00:00:00 2001 From: Nicholas Evans Date: Fri, 21 Jun 2019 15:06:49 -0500 Subject: [PATCH 19/27] Fix solidity linting warnings --- implementation/contracts/deposit/Deposit.sol | 6 +++--- implementation/contracts/deposit/DepositFunding.sol | 10 +++++----- implementation/contracts/deposit/DepositUtils.sol | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/implementation/contracts/deposit/Deposit.sol b/implementation/contracts/deposit/Deposit.sol index 3de066970..22c1c2a09 100644 --- a/implementation/contracts/deposit/Deposit.sol +++ b/implementation/contracts/deposit/Deposit.sol @@ -177,7 +177,7 @@ contract Deposit { /// @notice Anyone may notify the deposit of a funding proof during funding fraud // We reward the funder the entire bond if this occurs - /// @dev Takes a pre-parsed transaction and calculates values needed to verify funding + /// @dev Takes a pre-parsed transaction and calculates values needed to verify funding /// @param _txVersion Transaction version number (4-byte LE) /// @param _txInputVector All transaction inputs prepended by the number of inputs encoded as a VarInt, max 0xFC(252) inputs /// @param _txOutputVector All transaction outputs prepended by the number of outputs encoded as a VarInt, max 0xFC(252) outputs @@ -211,8 +211,8 @@ contract Deposit { } /// @notice Anyone may notify the deposit of a funding proof to activate the deposit - /// This is the happy-path of the funding flow. It means that we have succeeded - /// @dev Takes a pre-parsed transaction and calculates values needed to verify funding + /// This is the happy-path of the funding flow. It means that we have succeeded + /// @dev Takes a pre-parsed transaction and calculates values needed to verify funding /// @param _txVersion Transaction version number (4-byte LE) /// @param _txInputVector All transaction inputs prepended by the number of inputs encoded as a VarInt, max 0xFC(252) inputs /// @param _txOutputVector All transaction outputs prepended by the number of outputs encoded as a VarInt, max 0xFC(252) outputs diff --git a/implementation/contracts/deposit/DepositFunding.sol b/implementation/contracts/deposit/DepositFunding.sol index 24870fc96..f6daafe54 100644 --- a/implementation/contracts/deposit/DepositFunding.sol +++ b/implementation/contracts/deposit/DepositFunding.sol @@ -288,7 +288,7 @@ library DepositFunding { bytes8 _valueBytes; bytes memory _utxoOutpoint; - + (_valueBytes, _utxoOutpoint) = validateAndParseFundingSPVProof( _d, _txVersion, @@ -300,7 +300,7 @@ library DepositFunding { _txIndexInBlock, _bitcoinHeaders ); - + _d.setFailedSetup(); _d.logSetupFailed(); @@ -312,8 +312,8 @@ library DepositFunding { } /// @notice Anyone may notify the deposit of a funding proof to activate the deposit - /// This is the happy-path of the funding flow. It means that we have succeeded - /// @dev Takes a pre-parsed transaction and calculates values needed to verify funding + /// This is the happy-path of the funding flow. It means that we have succeeded + /// @dev Takes a pre-parsed transaction and calculates values needed to verify funding /// @param _d Deposit storage pointer /// @param _txVersion Transaction version number (4-byte LE) /// @param _txInputVector All transaction inputs prepended by the number of inputs encoded as a VarInt, max 0xFC(252) inputs @@ -347,7 +347,7 @@ library DepositFunding { bytes8 _valueBytes; bytes memory _utxoOutpoint; - + (_valueBytes, _utxoOutpoint) = validateAndParseFundingSPVProof( _d, _txVersion, diff --git a/implementation/contracts/deposit/DepositUtils.sol b/implementation/contracts/deposit/DepositUtils.sol index aa337a6cd..02a8522dc 100644 --- a/implementation/contracts/deposit/DepositUtils.sol +++ b/implementation/contracts/deposit/DepositUtils.sol @@ -145,7 +145,7 @@ library DepositUtils { "Tx merkle proof is not valid for provided header and tx"); evaluateProofDifficulty(_d, _bitcoinHeaders); -} + } /// @dev Find funding output using the provided index /// @param _d Deposit storage pointer @@ -179,11 +179,11 @@ library DepositUtils { /// @param _fundingOutputIndex Index of funding output in _txOutputVector (0-indexed) /// @return The specified output function extractOutputAtIndex( - bytes _txOutputVector, + bytes _txOutputVector, uint8 _fundingOutputIndex) public view returns (bytes) { // Determine length of first output // offset starts at 1 to skip output number varint - // skip the 8 byte output value to get to length + // skip the 8 byte output value to get to length // next two bytes used to calculate length uint _offset = 1; uint _start = _offset + 8; From e552ad6cd37ef43ba7f6de0e2c55123318380c7a Mon Sep 17 00:00:00 2001 From: Nicholas Evans Date: Fri, 28 Jun 2019 06:11:43 -0500 Subject: [PATCH 20/27] Add tests for modified bitcoins-spv functions --- .../contracts/deposit/DepositFunding.sol | 16 ++--- .../contracts/deposit/DepositUtils.sol | 35 ++++++---- implementation/package-lock.json | 4 +- implementation/test/DepositTest.js | 2 - implementation/test/DepositUtilsTest.js | 64 ++++++++++++++++++- .../contracts/deposit/TestDepositUtils.sol | 24 +++++++ 6 files changed, 118 insertions(+), 27 deletions(-) diff --git a/implementation/contracts/deposit/DepositFunding.sol b/implementation/contracts/deposit/DepositFunding.sol index f6daafe54..052ebd0b3 100644 --- a/implementation/contracts/deposit/DepositFunding.sol +++ b/implementation/contracts/deposit/DepositFunding.sol @@ -148,22 +148,19 @@ library DepositFunding { uint8 _fundingOutputIndex, bytes _merkleProof, uint256 _txIndexInBlock, - bytes _bitcoinHeaders) public view returns (bytes8 _valueBytes, bytes _utxoOutpoint){ - - bytes32 txId = abi.encodePacked(_txVersion, _txInputVector, _txOutputVector, _txLocktime).hash256(); + bytes _bitcoinHeaders + ) public view returns (bytes8 _valueBytes, bytes _utxoOutpoint){ + bytes32 txID = abi.encodePacked(_txVersion, _txInputVector, _txOutputVector, _txLocktime).hash256(); _valueBytes = _d.findAndParseFundingOutput(_txOutputVector, _fundingOutputIndex); require(DepositUtils.bytes8LEToUint(_valueBytes) >= TBTCConstants.getLotSize(), "Deposit too small"); + _d.checkProofFromTxId(txID, _merkleProof, _txIndexInBlock, _bitcoinHeaders); - _d.checkProofFromTxId(txId, _merkleProof, _txIndexInBlock, _bitcoinHeaders); - - - // The utxoOutpoint is the LE TXID plus the index of the output as a 4-byte LE int + // The utxoOutpoint is the LE txID plus the index of the output as a 4-byte LE int // _fundingOutputIndex is a uint8, so we know it is only 1 byte // Therefore, pad with 3 more bytes - _utxoOutpoint = abi.encodePacked(txId, _fundingOutputIndex, hex"000000"); - + _utxoOutpoint = abi.encodePacked(txID, _fundingOutputIndex, hex"000000"); } /// @notice we poll the Keep contract to retrieve our pubkey @@ -283,7 +280,6 @@ library DepositFunding { uint256 _txIndexInBlock, bytes _bitcoinHeaders ) public returns (bool) { - require(_d.inFraudAwaitingBTCFundingProof(), "Not awaiting a funding proof during setup fraud"); bytes8 _valueBytes; diff --git a/implementation/contracts/deposit/DepositUtils.sol b/implementation/contracts/deposit/DepositUtils.sol index 02a8522dc..184603729 100644 --- a/implementation/contracts/deposit/DepositUtils.sol +++ b/implementation/contracts/deposit/DepositUtils.sol @@ -135,23 +135,26 @@ library DepositUtils { bytes32 _txId, bytes _merkleProof, uint256 _txIndexInBlock, - bytes _bitcoinHeaders) public view{ + bytes _bitcoinHeaders + ) public view{ require( _txId.prove( _bitcoinHeaders.extractMerkleRootLE().toBytes32(), _merkleProof, _txIndexInBlock ), - "Tx merkle proof is not valid for provided header and tx"); + "Tx merkle proof is not valid for provided header and txId"); evaluateProofDifficulty(_d, _bitcoinHeaders); } - /// @dev Find funding output using the provided index + /// @notice Find and validate funding output in transaction output vector using the index + /// @dev Gets `_fundingOutputIndex` output from the output vector and validates if it's + /// Public Key Hash matches a Public Key Hash of the deposit. /// @param _d Deposit storage pointer /// @param _txOutputVector All transaction outputs prepended by the number of outputs encoded as a VarInt, max 0xFC outputs /// @param _fundingOutputIndex Index of funding output in _txOutputVector - /// @return funding value (bytes8) + /// @return Funding value function findAndParseFundingOutput( DepositUtils.Deposit storage _d, bytes _txOutputVector, @@ -160,11 +163,7 @@ library DepositUtils { bytes8 _valueBytes; bytes memory _output; - uint256 _n = (_txOutputVector.slice(0, 1)).bytesToUint(); - require(_n < 0xfd, "VarInts not supported, Number of outputs cannot exceed 252"); - // Find the output paying the signer PKH - // This will fail if there are more than 256 outputs _output = extractOutputAtIndex(_txOutputVector, _fundingOutputIndex); if (keccak256(_output.extractHash()) == keccak256(abi.encodePacked(signerPKH(_d)))) { _valueBytes = bytes8(_output.slice(0, 8).toBytes32()); @@ -180,19 +179,31 @@ library DepositUtils { /// @return The specified output function extractOutputAtIndex( bytes _txOutputVector, - uint8 _fundingOutputIndex) public view returns (bytes) { + uint8 _fundingOutputIndex + ) public view returns (bytes) { + // Transaction outputs vector consists of a number of outputs followed by a list of outputs: + // + // | outputs vector | + // | outputs number | output 1 | output 2... | + // | outputs number | value | script length | script | value | script length | script | + // + // Each output contains value (8 bytes), script length (VarInt) and a script. + + // extract output number to verify that it's not a varint. + uint256 _n = (_txOutputVector.slice(0, 1)).bytesToUint(); + require(_n < 0xfd, "VarInts not supported, Number of outputs cannot exceed 252"); + // Determine length of first output // offset starts at 1 to skip output number varint // skip the 8 byte output value to get to length // next two bytes used to calculate length uint _offset = 1; - uint _start = _offset + 8; - uint _length = (_txOutputVector.slice(_start, 2)).determineOutputLength(); + uint _length = (_txOutputVector.slice(_offset + 8, 2)).determineOutputLength(); // This loop moves forward, and then gets the len of the next one for (uint i = 0; i < _fundingOutputIndex; i++) { _offset = _offset + _length; - _length = (_txOutputVector.slice(8 + _offset, 2)).determineOutputLength(); + _length = (_txOutputVector.slice(_offset + 8, 2)).determineOutputLength(); } // We now have the length and offset of the one we want diff --git a/implementation/package-lock.json b/implementation/package-lock.json index 7d7e585fa..1fdfa1422 100644 --- a/implementation/package-lock.json +++ b/implementation/package-lock.json @@ -1839,8 +1839,8 @@ "dev": true }, "eslint-config-keep": { - "version": "git+https://github.com/keep-network/eslint-config-keep.git#484eaf3c145d9952e58d2b93ab56636041e240c0", - "from": "git+https://github.com/keep-network/eslint-config-keep.git#0.1.1", + "version": "git+https://github.com/keep-network/eslint-config-keep.git#8c51882cadeaeb2d40f34652fad6a1459071cc1b", + "from": "git+https://github.com/keep-network/eslint-config-keep.git#0.1.2", "dev": true, "requires": { "eslint-config-google": "^0.13.0" diff --git a/implementation/test/DepositTest.js b/implementation/test/DepositTest.js index 414897cdc..e4fa4be0d 100644 --- a/implementation/test/DepositTest.js +++ b/implementation/test/DepositTest.js @@ -18,7 +18,6 @@ const SystemStub = artifacts.require('SystemStub') const TestTBTCConstants = artifacts.require('TestTBTCConstants') const TestDeposit = artifacts.require('TestDeposit') -const TestDepositUtils = artifacts.require('TestDepositUtils') const BN = require('bn.js') const utils = require('./utils') @@ -40,7 +39,6 @@ const TEST_DEPOSIT_DEPLOY = [ { name: 'DepositRedemption', contract: DepositRedemption }, { name: 'DepositLiquidation', contract: DepositLiquidation }, { name: 'TestDeposit', contract: TestDeposit }, - { name: 'TestDepositUtils', contract: TestDepositUtils }, { name: 'KeepStub', contract: KeepStub }, { name: 'TBTCStub', contract: TBTCStub }, { name: 'SystemStub', contract: SystemStub }] diff --git a/implementation/test/DepositUtilsTest.js b/implementation/test/DepositUtilsTest.js index 45b2745ec..b3553eb7c 100644 --- a/implementation/test/DepositUtilsTest.js +++ b/implementation/test/DepositUtilsTest.js @@ -45,7 +45,6 @@ const TEST_DEPOSIT_UTILS_DEPLOY = [ contract('DepositUtils', (accounts) => { let deployed let testUtilsInstance - before(async () => { deployed = await utils.deploySystem(TEST_DEPOSIT_UTILS_DEPLOY) testUtilsInstance = deployed.TestDepositUtils @@ -151,6 +150,61 @@ contract('DepositUtils', (accounts) => { }) }) + describe('checkProofFromTxId()', async () => { + it('does not error', async () => { + try { + await deployed.SystemStub.setCurrentDiff(6379265451411) + await testUtilsInstance.checkProofFromTxId.call(utils.TX.tx_id_le, utils.TX.proof, utils.TX.index, utils.HEADER_PROOFS.slice(-1)[0]) + assert(true, 'passes proof validation') + } catch (e) { + assert.include(e.message, 'Failed tx parsing') + } + }) + + it('fails with a broken proof', async () => { + try { + await deployed.SystemStub.setCurrentDiff(6379265451411) + await testUtilsInstance.checkProofFromTxId.call(utils.TX.tx_id_le, utils.TX.proof, 0, utils.HEADER_PROOFS.slice(-1)[0]) + assert(false, 'Test call did not error as expected') + } catch (e) { + assert.include(e.message, 'Tx merkle proof is not valid for provided header and txId') + } + }) + + it('fails with a broken txId', async () => { + try { + await deployed.SystemStub.setCurrentDiff(6379265451411) + await testUtilsInstance.checkProofFromTxId.call('0x00', utils.TX.proof, 0, utils.HEADER_PROOFS.slice(-1)[0]) + assert(false, 'Test call did not error as expected') + } catch (e) { + assert.include(e.message, 'Tx merkle proof is not valid for provided header and txId') + } + }) + }) + + describe('findAndParseFundingOutput()', async () => { + const _txOutputVector = '0x012040351d0000000016001486e7303082a6a21d5837176bc808bf4828371ab6' + const _fundingOutputIndex = 0 + const _outValueBytes = '0x2040351d00000000' + const _signerPubkeyX = '0xd4aee75e57179f7cd18adcbaa7e2fca4ff7b1b446df88bf0b4398e4a26965a6e' + const _signerPubkeyY = '0xe8bfb23428a4efecb3ebdc636139de9a568ed427fff20d28baa33ed48e9c44e1' + it('correctly returns valuebytes', async () => { + await testUtilsInstance.setPubKey(_signerPubkeyX, _signerPubkeyY) + const valueBytes = await testUtilsInstance.findAndParseFundingOutput.call(_txOutputVector, _fundingOutputIndex) + assert.equal(_outValueBytes, valueBytes, 'Got incorrect value bytes from funding output') + }) + + it('fails with incorrect singer pubKey', async () => { + await testUtilsInstance.setPubKey('0x' + '11'.repeat(20), '0x' + '11'.repeat(20)) + try { + await testUtilsInstance.findAndParseFundingOutput.call(_txOutputVector, _fundingOutputIndex) + assert(false, 'Test call did not error as expected') + } catch (e) { + assert.include(e.message, 'Did not find output with correct PKH') + } + }) + }) + describe('extractOutputAtIndex()', async () => { it('extracts outputs at specified indices', async () => { let res @@ -167,6 +221,12 @@ contract('DepositUtils', (accounts) => { assert.equal(res, '0x40420f0000000000220020aedad4518f56379ef6f1f52f2e0fed64608006b3ccaff2253d847ddc90c91922') res = await testUtilsInstance.extractOutputAtIndex.call(_txOutputVector3, 3) assert.equal(res, '0x40420f0000000000220020aedad4518f56379ef6f1f52f2e0fed64608006b3ccaff2253d847ddc90c91922') + try { + res = await testUtilsInstance.extractOutputAtIndex.call(_txOutputVector1, 2) + assert(false, 'Test call did not error as expected') + } catch (e) { + assert.include(e.message, 'Slice out of bounds') + } }) }) @@ -208,6 +268,7 @@ contract('DepositUtils', (accounts) => { describe('signerPubkey()', async () => { it('returns the concatenated signer X and Y coordinates', async () => { + await testUtilsInstance.setPubKey('0x' + '00'.repeat(32), '0x' + '00'.repeat(32)) const signerPubkey = await testUtilsInstance.signerPubkey.call() assert.equal(signerPubkey, '0x' + '00'.repeat(64)) }) @@ -215,6 +276,7 @@ contract('DepositUtils', (accounts) => { describe('signerPKH()', async () => { it('returns the concatenated signer X and Y coordinates', async () => { + await testUtilsInstance.setPubKey('0x' + '00'.repeat(32), '0x' + '00'.repeat(32)) const signerPKH = await testUtilsInstance.signerPKH.call() assert.equal(signerPKH, utils.hash160('02' + '00'.repeat(32))) }) diff --git a/implementation/test/contracts/deposit/TestDepositUtils.sol b/implementation/test/contracts/deposit/TestDepositUtils.sol index 324ebe0c9..1323caf1c 100644 --- a/implementation/test/contracts/deposit/TestDepositUtils.sol +++ b/implementation/test/contracts/deposit/TestDepositUtils.sol @@ -28,6 +28,30 @@ contract TestDepositUtils is TestDeposit { return self.checkProofFromTx(_bitcoinTx, _merkleProof, _index, _bitcoinHeaders); } + function checkProofFromTxId( + bytes32 _bitcoinTxId, + bytes _merkleProof, + uint256 _index, + bytes _bitcoinHeaders + ) public view returns (bytes32) { + self.checkProofFromTxId(_bitcoinTxId, _merkleProof, _index, _bitcoinHeaders); + } + + function setPubKey( + bytes32 _signingGroupPubkeyX, + bytes32 _signingGroupPubkeyY + ) public { + self.signingGroupPubkeyX = _signingGroupPubkeyX; + self.signingGroupPubkeyY = _signingGroupPubkeyY; + } + + function findAndParseFundingOutput( + bytes _txOutputVector, + uint8 _fundingOutputIndex + ) public view returns (bytes8) { + return self.findAndParseFundingOutput(_txOutputVector, _fundingOutputIndex); + } + function extractOutputAtIndex(bytes _txOutputVector, uint8 _fundingOutputIndex) public view returns (bytes) { return DepositUtils.extractOutputAtIndex(_txOutputVector, _fundingOutputIndex); } From 1ead4563ec5c45f29039687601dfaf033f64b0ec Mon Sep 17 00:00:00 2001 From: Nicholas Evans Date: Fri, 28 Jun 2019 06:29:06 -0500 Subject: [PATCH 21/27] Add single output-length scenario for extractOutputAtIndex test --- implementation/test/DepositUtilsTest.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/implementation/test/DepositUtilsTest.js b/implementation/test/DepositUtilsTest.js index b3553eb7c..180b6e64c 100644 --- a/implementation/test/DepositUtilsTest.js +++ b/implementation/test/DepositUtilsTest.js @@ -211,6 +211,7 @@ contract('DepositUtils', (accounts) => { const _txOutputVector1 = '0x024897070000000000220020a4333e5612ab1a1043b25755c89b16d55184a42f81799e623e6bc39db8539c180000000000000000166a14edb1b5c2f39af0fec151732585b1049b07895211' const _txOutputVector2 = '0x024db6000000000000160014455c0ea778752831d6fc25f6f8cf55dc49d335f040420f0000000000220020aedad4518f56379ef6f1f52f2e0fed64608006b3ccaff2253d847ddc90c91922' const _txOutputVector3 = '0x044897070000000000220020a4333e5612ab1a1043b25755c89b16d55184a42f81799e623e6bc39db8539c180000000000000000166a14edb1b5c2f39af0fec151732585b1049b078952114db6000000000000160014455c0ea778752831d6fc25f6f8cf55dc49d335f040420f0000000000220020aedad4518f56379ef6f1f52f2e0fed64608006b3ccaff2253d847ddc90c91922' + const _txOutputVector4 = '0x012040351d0000000016001486e7303082a6a21d5837176bc808bf4828371ab6' res = await testUtilsInstance.extractOutputAtIndex.call(_txOutputVector1, 0) assert.equal(res, '0x4897070000000000220020a4333e5612ab1a1043b25755c89b16d55184a42f81799e623e6bc39db8539c18') res = await testUtilsInstance.extractOutputAtIndex.call(_txOutputVector1, 1) @@ -221,6 +222,8 @@ contract('DepositUtils', (accounts) => { assert.equal(res, '0x40420f0000000000220020aedad4518f56379ef6f1f52f2e0fed64608006b3ccaff2253d847ddc90c91922') res = await testUtilsInstance.extractOutputAtIndex.call(_txOutputVector3, 3) assert.equal(res, '0x40420f0000000000220020aedad4518f56379ef6f1f52f2e0fed64608006b3ccaff2253d847ddc90c91922') + res = await testUtilsInstance.extractOutputAtIndex.call(_txOutputVector4, 0) + assert.equal(res, '0x2040351d0000000016001486e7303082a6a21d5837176bc808bf4828371ab6') try { res = await testUtilsInstance.extractOutputAtIndex.call(_txOutputVector1, 2) assert(false, 'Test call did not error as expected') From 8f7b4397ae15885ed11265bd6e14b90a613ee06c Mon Sep 17 00:00:00 2001 From: Nicholas Evans Date: Thu, 4 Jul 2019 06:43:18 -0500 Subject: [PATCH 22/27] Update DespositUtilsTest to include more cases --- .../contracts/deposit/DepositFunding.sol | 42 +----- .../contracts/deposit/DepositUtils.sol | 50 +++++++- implementation/test/DepositTest.js | 1 - implementation/test/DepositUtilsTest.js | 120 +++++++++++++++--- .../contracts/deposit/TestDepositUtils.sol | 22 ++++ implementation/test/tx.json | 1 + 6 files changed, 172 insertions(+), 64 deletions(-) diff --git a/implementation/contracts/deposit/DepositFunding.sol b/implementation/contracts/deposit/DepositFunding.sol index 35450fb17..bb82e4fe8 100644 --- a/implementation/contracts/deposit/DepositFunding.sol +++ b/implementation/contracts/deposit/DepositFunding.sol @@ -127,41 +127,7 @@ library DepositFunding { fundingTeardown(_d); } - /// @notice Validates the funding tx and parses information from it - /// @dev Takes a pre-parsed transaction and calculates values needed to verify funding - /// @param _d Deposit storage pointer - /// @param _txVersion Transaction version number (4-byte LE) - /// @param _txInputVector All transaction inputs prepended by the number of inputs encoded as a VarInt, max 0xFC(252) inputs - /// @param _txOutputVector All transaction outputs prepended by the number of outputs encoded as a VarInt, max 0xFC(252) outputs - /// @param _txLocktime Final 4 bytes of the transaction - /// @param _fundingOutputIndex Index of funding output in _txOutputVector (0-indexed) - /// @param _merkleProof The merkle proof of transaction inclusion in a block - /// @param _txIndexInBlock Transaction index in the block (1-indexed) - /// @param _bitcoinHeaders Single bytestring of 80-byte bitcoin headers, lowest height first - /// @return The 8-byte LE UTXO size in satoshi, the 36byte outpoint - function validateAndParseFundingSPVProof( - DepositUtils.Deposit storage _d, - bytes _txVersion, - bytes _txInputVector, - bytes _txOutputVector, - bytes _txLocktime, - uint8 _fundingOutputIndex, - bytes _merkleProof, - uint256 _txIndexInBlock, - bytes _bitcoinHeaders - ) public view returns (bytes8 _valueBytes, bytes _utxoOutpoint){ - bytes32 txID = abi.encodePacked(_txVersion, _txInputVector, _txOutputVector, _txLocktime).hash256(); - - _valueBytes = _d.findAndParseFundingOutput(_txOutputVector, _fundingOutputIndex); - require(DepositUtils.bytes8LEToUint(_valueBytes) >= TBTCConstants.getLotSize(), "Deposit too small"); - - _d.checkProofFromTxId(txID, _merkleProof, _txIndexInBlock, _bitcoinHeaders); - - // The utxoOutpoint is the LE txID plus the index of the output as a 4-byte LE int - // _fundingOutputIndex is a uint8, so we know it is only 1 byte - // Therefore, pad with 3 more bytes - _utxoOutpoint = abi.encodePacked(txID, _fundingOutputIndex, hex"000000"); - } + /// @notice we poll the Keep contract to retrieve our pubkey /// @dev We store the pubkey as 2 bytestrings, X and Y. @@ -285,8 +251,7 @@ library DepositFunding { bytes8 _valueBytes; bytes memory _utxoOutpoint; - (_valueBytes, _utxoOutpoint) = validateAndParseFundingSPVProof( - _d, + (_valueBytes, _utxoOutpoint) = _d.validateAndParseFundingSPVProof( _txVersion, _txInputVector, _txOutputVector, @@ -344,8 +309,7 @@ library DepositFunding { bytes8 _valueBytes; bytes memory _utxoOutpoint; - (_valueBytes, _utxoOutpoint) = validateAndParseFundingSPVProof( - _d, + (_valueBytes, _utxoOutpoint) = _d.validateAndParseFundingSPVProof( _txVersion, _txInputVector, _txOutputVector, diff --git a/implementation/contracts/deposit/DepositUtils.sol b/implementation/contracts/deposit/DepositUtils.sol index 184603729..ce55b2b29 100644 --- a/implementation/contracts/deposit/DepositUtils.sol +++ b/implementation/contracts/deposit/DepositUtils.sol @@ -123,8 +123,8 @@ library DepositUtils { return _txid; } - /// @notice Syntactically check an SPV proof for a bitcoin tx - /// @dev Stateless SPV Proof verification documented elsewhere + /// @notice Syntactically check an SPV proof for a bitcoin transaction with its hash (ID) + /// @dev Stateless SPV Proof verification documented elsewhere (see github.com/summa-tx/bitcoin-spv) /// @param _d Deposit storage pointer /// @param _txId The bitcoin txid of the tx that is purportedly included in the header chain /// @param _merkleProof The merkle proof of inclusion of the tx in the bitcoin block @@ -170,7 +170,7 @@ library DepositUtils { return _valueBytes; } // If we don't return from inside the loop, we failed. - revert("Did not find output with correct PKH"); + revert("could not identify output funding the required public key hash"); } /// @notice Extracts the output at a given index in _txOutputVector @@ -197,19 +197,55 @@ library DepositUtils { // offset starts at 1 to skip output number varint // skip the 8 byte output value to get to length // next two bytes used to calculate length - uint _offset = 1; - uint _length = (_txOutputVector.slice(_offset + 8, 2)).determineOutputLength(); + uint _offset = 1 + 8; + uint _length = (_txOutputVector.slice(_offset, 2)).determineOutputLength(); // This loop moves forward, and then gets the len of the next one for (uint i = 0; i < _fundingOutputIndex; i++) { _offset = _offset + _length; - _length = (_txOutputVector.slice(_offset + 8, 2)).determineOutputLength(); + _length = (_txOutputVector.slice(_offset, 2)).determineOutputLength(); } // We now have the length and offset of the one we want - return _txOutputVector.slice(_offset, _length); + return _txOutputVector.slice(_offset - 8, _length); } + /// @notice Validates the funding tx and parses information from it + /// @dev Takes a pre-parsed transaction and calculates values needed to verify funding + /// @param _d Deposit storage pointer + /// @param _txVersion Transaction version number (4-byte LE) + /// @param _txInputVector All transaction inputs prepended by the number of inputs encoded as a VarInt, max 0xFC(252) inputs + /// @param _txOutputVector All transaction outputs prepended by the number of outputs encoded as a VarInt, max 0xFC(252) outputs + /// @param _txLocktime Final 4 bytes of the transaction + /// @param _fundingOutputIndex Index of funding output in _txOutputVector (0-indexed) + /// @param _merkleProof The merkle proof of transaction inclusion in a block + /// @param _txIndexInBlock Transaction index in the block (1-indexed) + /// @param _bitcoinHeaders Single bytestring of 80-byte bitcoin headers, lowest height first + /// @return The 8-byte LE UTXO size in satoshi, the 36byte outpoint + function validateAndParseFundingSPVProof( + DepositUtils.Deposit storage _d, + bytes _txVersion, + bytes _txInputVector, + bytes _txOutputVector, + bytes _txLocktime, + uint8 _fundingOutputIndex, + bytes _merkleProof, + uint256 _txIndexInBlock, + bytes _bitcoinHeaders + ) public view returns (bytes8 _valueBytes, bytes _utxoOutpoint){ + bytes32 txID = abi.encodePacked(_txVersion, _txInputVector, _txOutputVector, _txLocktime).hash256(); + + _valueBytes = findAndParseFundingOutput(_d, _txOutputVector, _fundingOutputIndex); + require(bytes8LEToUint(_valueBytes) >= TBTCConstants.getLotSize(), "Deposit too small"); + + checkProofFromTxId(_d, txID, _merkleProof, _txIndexInBlock, _bitcoinHeaders); + + // The utxoOutpoint is the LE txID plus the index of the output as a 4-byte LE int + // _fundingOutputIndex is a uint8, so we know it is only 1 byte + // Therefore, pad with 3 more bytes + _utxoOutpoint = abi.encodePacked(txID, _fundingOutputIndex, hex"000000"); + } + /// @notice Calculates the amount of value at auction right now /// @dev We calculate the % of the auction that has elapsed, then scale the value up /// @param _d deposit storage pointer diff --git a/implementation/test/DepositTest.js b/implementation/test/DepositTest.js index e4fa4be0d..44a8f6ca0 100644 --- a/implementation/test/DepositTest.js +++ b/implementation/test/DepositTest.js @@ -983,7 +983,6 @@ contract('Deposit', (accounts) => { const tokenCheck = initialTokenBalance.add(new BN(toMint)) expect(tokenCheck, 'incorrect amount minted').to.eq.BN(endingTokenBalancce) }) - it.skip('TODO: full test for validateAndParseFundingSPVProof', async () => {}) }) describe('provideECDSAFraudProof', async () => { diff --git a/implementation/test/DepositUtilsTest.js b/implementation/test/DepositUtilsTest.js index 180b6e64c..d618dc071 100644 --- a/implementation/test/DepositUtilsTest.js +++ b/implementation/test/DepositUtilsTest.js @@ -41,6 +41,25 @@ const TEST_DEPOSIT_UTILS_DEPLOY = [ { name: 'TBTCStub', contract: TBTCStub }, { name: 'SystemStub', contract: SystemStub }] +// real tx from mainnet bitcoin, interpreted as funding tx +// tx source: https://www.blockchain.com/btc/tx/7c48181cb5c030655eea651c5e9aa808983f646465cbe9d01c227d99cfbc405f +// const tx = '0x01000000000101913e39197867de39bff2c93c75173e086388ee7e8707c90ce4a02dd23f7d2c0d0000000000ffffffff012040351d0000000016001486e7303082a6a21d5837176bc808bf4828371ab602473044022046c3c852a2042ee01ffd7d8d252f297ccc67ae2aa1fac4170f50e8a90af5398802201585ffbbed6e812fb60c025d2f82ae115774279369610b0c76165b6c7132f2810121020c67643b5c862a1aa1afe0a77a28e51a21b08396a0acae69965b22d2a403fd1c4ec10800' +// const txid = '0x7c48181cb5c030655eea651c5e9aa808983f646465cbe9d01c227d99cfbc405f'; +// const txidLE = '0x5f40bccf997d221cd0e9cb6564643f9808a89a5e1c65ea5e6530c0b51c18487c'; +const currentDifficulty = 6353030562983 +const _version = '0x01000000' +const _txInputVector = `0x01913e39197867de39bff2c93c75173e086388ee7e8707c90ce4a02dd23f7d2c0d0000000000ffffffff` +const _txOutputVector = '0x012040351d0000000016001486e7303082a6a21d5837176bc808bf4828371ab6' +const _fundingOutputIndex = 0 +const _txLocktime = '0x4ec10800' +const _txIndexInBlock = 130 +const _bitcoinHeaders = '0x00e0ff3fd877ad23af1d0d3e0eb6a700d85b692975dacd36e47b1b00000000000000000095ba61df5961d7fa0a45cd7467e11f20932c7a0b74c59318e86581c6b509554876f6c65c114e2c17e42524d300000020994d3802da5adf80345261bcff2eb87ab7b70db786cb0000000000000000000003169efc259f6e4b5e1bfa469f06792d6f07976a098bff2940c8e7ed3105fdc5eff7c65c114e2c170c4dffc30000c020f898b7ea6a405728055b0627f53f42c57290fe78e0b91900000000000000000075472c91a94fa2aab73369c0686a58796949cf60976e530f6eb295320fa15a1b77f8c65c114e2c17387f1df00000002069137421fc274aa2c907dbf0ec4754285897e8aa36332b0000000000000000004308f2494b702c40e9d61991feb7a15b3be1d73ce988e354e52e7a4e611bd9c2a2f8c65c114e2c1740287df200000020ab63607b09395f856adaa69d553755d9ba5bd8d15da20a000000000000000000090ea7559cda848d97575cb9696c8e33ba7f38d18d5e2f8422837c354aec147839fbc65c114e2c175cf077d6000000200ab3612eac08a31a8fb1d9b5397f897db8d26f6cd83a230000000000000000006f4888720ecbf980ff9c983a8e2e60ad329cc7b130916c2bf2300ea54e412a9ed6fcc65c114e2c17d4fbb88500000020d3e51560f77628a26a8fad01c88f98bd6c9e4bc8703b180000000000000000008e2c6e62a1f4d45dd03be1e6692df89a4e3b1223a4dbdfa94cca94c04c22049992fdc65c114e2c17463edb5e' +const _signerPubkeyX = '0xd4aee75e57179f7cd18adcbaa7e2fca4ff7b1b446df88bf0b4398e4a26965a6e' +const _signerPubkeyY = '0xe8bfb23428a4efecb3ebdc636139de9a568ed427fff20d28baa33ed48e9c44e1' +const _merkleProof = '0x5f40bccf997d221cd0e9cb6564643f9808a89a5e1c65ea5e6530c0b51c18487c886f7da48f4ccfe49283c678dedb376c89853ba46d9a297fe39e8dd557d1f8deb0fb1a28c03f71b267f3a33459b2566975b1653a1238947ed05edca17ef64181b1f09d858a6e25bae4b0e245993d4ea77facba8ed0371bb9b8a6724475bcdc9edf9ead30b61cf6714758b7c93d1b725f86c2a66a07dd291ef566eaa5a59516823d57fd50557f1d938cc2fb61fe0e1acee6f9cb618a9210688a2965c52feabee66d660a5e7f158e363dc464fca2bb1cc856173366d5d20b5cd513a3aab8ebc5be2bd196b783b8773af2472abcea3e32e97938283f7b454769aa1c064c311c3342a755029ee338664999bd8d432080eafae3ca86b52ad2e321e9e634a46c1bd0d174e38bcd4c59a0f0a78c5906c015ef4daf6beb0500a59f4cae00cd46069ce60db2182e74561028e4462f59f639c89b8e254602d6ad9c212b7c2af5db9275e48c467539c6af678d6f09214182df848bd79a06df706f7c3fddfdd95e6f27326c6217ee446543a443f82b711f48c173a769ae8d1e92a986bc76fca732f088bbe04995ba61df5961d7fa0a45cd7467e11f20932c7a0b74c59318e86581c6b5095548' +const _expectedUTXOoutpoint = '0x5f40bccf997d221cd0e9cb6564643f9808a89a5e1c65ea5e6530c0b51c18487c00000000' +// const _outputValue = 490029088; +const _outValueBytes = '0x2040351d00000000' contract('DepositUtils', (accounts) => { let deployed @@ -151,9 +170,12 @@ contract('DepositUtils', (accounts) => { }) describe('checkProofFromTxId()', async () => { + before(async () => { + await deployed.SystemStub.setCurrentDiff(utils.TX.difficulty) + }) + it('does not error', async () => { try { - await deployed.SystemStub.setCurrentDiff(6379265451411) await testUtilsInstance.checkProofFromTxId.call(utils.TX.tx_id_le, utils.TX.proof, utils.TX.index, utils.HEADER_PROOFS.slice(-1)[0]) assert(true, 'passes proof validation') } catch (e) { @@ -163,7 +185,6 @@ contract('DepositUtils', (accounts) => { it('fails with a broken proof', async () => { try { - await deployed.SystemStub.setCurrentDiff(6379265451411) await testUtilsInstance.checkProofFromTxId.call(utils.TX.tx_id_le, utils.TX.proof, 0, utils.HEADER_PROOFS.slice(-1)[0]) assert(false, 'Test call did not error as expected') } catch (e) { @@ -171,13 +192,13 @@ contract('DepositUtils', (accounts) => { } }) - it('fails with a broken txId', async () => { + it('fails with bad difficulty', async () => { + await deployed.SystemStub.setCurrentDiff(1) try { - await deployed.SystemStub.setCurrentDiff(6379265451411) - await testUtilsInstance.checkProofFromTxId.call('0x00', utils.TX.proof, 0, utils.HEADER_PROOFS.slice(-1)[0]) + await testUtilsInstance.checkProofFromTxId.call(utils.TX.tx_id_le, utils.TX.proof, utils.TX.index, utils.HEADER_PROOFS.slice(-1)[0]) assert(false, 'Test call did not error as expected') } catch (e) { - assert.include(e.message, 'Tx merkle proof is not valid for provided header and txId') + assert.include(e.message, 'not at current or previous difficulty') } }) }) @@ -188,30 +209,35 @@ contract('DepositUtils', (accounts) => { const _outValueBytes = '0x2040351d00000000' const _signerPubkeyX = '0xd4aee75e57179f7cd18adcbaa7e2fca4ff7b1b446df88bf0b4398e4a26965a6e' const _signerPubkeyY = '0xe8bfb23428a4efecb3ebdc636139de9a568ed427fff20d28baa33ed48e9c44e1' + it('correctly returns valuebytes', async () => { await testUtilsInstance.setPubKey(_signerPubkeyX, _signerPubkeyY) const valueBytes = await testUtilsInstance.findAndParseFundingOutput.call(_txOutputVector, _fundingOutputIndex) assert.equal(_outValueBytes, valueBytes, 'Got incorrect value bytes from funding output') }) - it('fails with incorrect singer pubKey', async () => { + it('fails with incorrect signer pubKey', async () => { await testUtilsInstance.setPubKey('0x' + '11'.repeat(20), '0x' + '11'.repeat(20)) try { await testUtilsInstance.findAndParseFundingOutput.call(_txOutputVector, _fundingOutputIndex) assert(false, 'Test call did not error as expected') } catch (e) { - assert.include(e.message, 'Did not find output with correct PKH') + assert.include(e.message, 'could not identify output funding the required public key hash') } }) }) describe('extractOutputAtIndex()', async () => { - it('extracts outputs at specified indices', async () => { + it('extracts outputs at specified indicex (vector length 1)', async () => { + const _txOutputVector = '0x012040351d0000000016001486e7303082a6a21d5837176bc808bf4828371ab6' + const res = await testUtilsInstance.extractOutputAtIndex.call(_txOutputVector, 0) + assert.equal(res, '0x2040351d0000000016001486e7303082a6a21d5837176bc808bf4828371ab6') + }) + + it('extracts outputs at specified indices (vector length 2)', async () => { let res const _txOutputVector1 = '0x024897070000000000220020a4333e5612ab1a1043b25755c89b16d55184a42f81799e623e6bc39db8539c180000000000000000166a14edb1b5c2f39af0fec151732585b1049b07895211' const _txOutputVector2 = '0x024db6000000000000160014455c0ea778752831d6fc25f6f8cf55dc49d335f040420f0000000000220020aedad4518f56379ef6f1f52f2e0fed64608006b3ccaff2253d847ddc90c91922' - const _txOutputVector3 = '0x044897070000000000220020a4333e5612ab1a1043b25755c89b16d55184a42f81799e623e6bc39db8539c180000000000000000166a14edb1b5c2f39af0fec151732585b1049b078952114db6000000000000160014455c0ea778752831d6fc25f6f8cf55dc49d335f040420f0000000000220020aedad4518f56379ef6f1f52f2e0fed64608006b3ccaff2253d847ddc90c91922' - const _txOutputVector4 = '0x012040351d0000000016001486e7303082a6a21d5837176bc808bf4828371ab6' res = await testUtilsInstance.extractOutputAtIndex.call(_txOutputVector1, 0) assert.equal(res, '0x4897070000000000220020a4333e5612ab1a1043b25755c89b16d55184a42f81799e623e6bc39db8539c18') res = await testUtilsInstance.extractOutputAtIndex.call(_txOutputVector1, 1) @@ -219,18 +245,67 @@ contract('DepositUtils', (accounts) => { res = await testUtilsInstance.extractOutputAtIndex.call(_txOutputVector2, 0) assert.equal(res, '0x4db6000000000000160014455c0ea778752831d6fc25f6f8cf55dc49d335f0') res = await testUtilsInstance.extractOutputAtIndex.call(_txOutputVector2, 1) + assert.equal(res, '0x40420f0000000000220020aedad4518f56379ef6f1f52f2e0fed64608006b3ccaff2253d847ddc90c91922') + }) + + it('extracts outputs at specified index (vecor length 4)', async () => { + const _txOutputVector = '0x044897070000000000220020a4333e5612ab1a1043b25755c89b16d55184a42f81799e623e6bc39db8539c180000000000000000166a14edb1b5c2f39af0fec151732585b1049b078952114db6000000000000160014455c0ea778752831d6fc25f6f8cf55dc49d335f040420f0000000000220020aedad4518f56379ef6f1f52f2e0fed64608006b3ccaff2253d847ddc90c91922' + const res = await testUtilsInstance.extractOutputAtIndex.call(_txOutputVector, 3) assert.equal(res, '0x40420f0000000000220020aedad4518f56379ef6f1f52f2e0fed64608006b3ccaff2253d847ddc90c91922') - res = await testUtilsInstance.extractOutputAtIndex.call(_txOutputVector3, 3) - assert.equal(res, '0x40420f0000000000220020aedad4518f56379ef6f1f52f2e0fed64608006b3ccaff2253d847ddc90c91922') - res = await testUtilsInstance.extractOutputAtIndex.call(_txOutputVector4, 0) - assert.equal(res, '0x2040351d0000000016001486e7303082a6a21d5837176bc808bf4828371ab6') + }) + + it('fails to extract output from bad index', async () => { + const _txOutputVector= '0x024897070000000000220020a4333e5612ab1a1043b25755c89b16d55184a42f81799e623e6bc39db8539c180000000000000000166a14edb1b5c2f39af0fec151732585b1049b07895211' try { - res = await testUtilsInstance.extractOutputAtIndex.call(_txOutputVector1, 2) + res = await testUtilsInstance.extractOutputAtIndex.call(_txOutputVector, 2) assert(false, 'Test call did not error as expected') } catch (e) { assert.include(e.message, 'Slice out of bounds') } }) + + it('fails to extract output from varint prepended vector', async () => { + // we don't need to include the number of outputs suggested by the varint + const _txOutputVector= '0xfe123412344897070000000000220020a4333e5612ab1a1043b25755c89b16d55184a42f81799e623e6bc39db8539c180000000000000000166a14edb1b5c2f39af0fec151732585b1049b07895211' + try { + res = await testUtilsInstance.extractOutputAtIndex.call(_txOutputVector, 2) + assert(false, 'Test call did not error as expected') + } catch (e) { + assert.include(e.message, 'VarInts not supported, Number of outputs cannot exceed 252') + } + }) + }) + + describe('validateAndParseFundingSPVProof()', async () => { + before(async () => { + await testUtilsInstance.setPubKey(_signerPubkeyX, _signerPubkeyY) + await deployed.SystemStub.setCurrentDiff(currentDifficulty) + }) + + it('returns currect value and outpoint', async () => { + const parseResults = await testUtilsInstance.validateAndParseFundingSPVProof.call(_version, _txInputVector, _txOutputVector, _txLocktime, _fundingOutputIndex, _merkleProof, _txIndexInBlock, _bitcoinHeaders) + assert.equal(parseResults[0], _outValueBytes) + assert.equal(parseResults[1], _expectedUTXOoutpoint) + }) + + it('fails with bad _txInputVector', async () => { + try { + await testUtilsInstance.validateAndParseFundingSPVProof.call(_version, '0x' + '00'.repeat(32), _txOutputVector, _txLocktime, _fundingOutputIndex, _merkleProof, _txIndexInBlock, _bitcoinHeaders) + assert(false, 'Test call did not error as expected') + } catch (e) { + assert.include(e.message, 'Tx merkle proof is not valid for provided header and txId') + } + }) + + it('fails with insufficient difficulty', async () => { + const _badheaders = `0x00e0ff3fd877ad23af1d0d3e0eb6a700d85b692975dacd36e47b1b00000000000000000095ba61df5961d7fa0a45cd7467e11f20932c7a0b74c59318e86581c6b509554876f6c65c114e2c17e42524d300000020994d3802da5adf80345261bcff2eb87ab7b70db786cb0000000000000000000003169efc259f6e4b5e1bfa469f06792d6f07976a098bff2940c8e7ed3105fdc5eff7c65c114e2c170c4dffc30000c020f898b7ea6a405728055b0627f53f42c57290fe78e0b91900000000000000000075472c91a94fa2aab73369c0686a58796949cf60976e530f6eb295320fa15a1b77f8c65c114e2c17387f1df00000002069137421fc274aa2c907dbf0ec4754285897e8aa36332b0000000000000000004308f2494b702c40e9d61991feb7a15b3be1d73ce988e354e52e7a4e611bd9c2a2f8c65c114e2c1740287df200000020ab63607b09395f856adaa69d553755d9ba5bd8d15da20a000000000000000000090ea7559cda848d97575cb9696c8e33ba7f38d18d5e2f8422837c354aec147839fbc65c114e2c175cf077d6` + try { + await testUtilsInstance.validateAndParseFundingSPVProof.call(_version, _txInputVector, _txOutputVector, _txLocktime, _fundingOutputIndex, _merkleProof, _txIndexInBlock, _badheaders) + assert(false, 'Test call did not error as expected') + } catch (e) { + assert.include(e.message, 'Insufficient accumulated difficulty in header chain') + } + }) }) describe('auctionValue()', async () => { @@ -271,8 +346,19 @@ contract('DepositUtils', (accounts) => { describe('signerPubkey()', async () => { it('returns the concatenated signer X and Y coordinates', async () => { - await testUtilsInstance.setPubKey('0x' + '00'.repeat(32), '0x' + '00'.repeat(32)) + const _signerPubkeyX = '0xd4aee75e57179f7cd18adcbaa7e2fca4ff7b1b446df88bf0b4398e4a26965a6e' + const _signerPubkeyY = '0xe8bfb23428a4efecb3ebdc636139de9a568ed427fff20d28baa33ed48e9c44e1' + const _concatinatedKeys = '0xd4aee75e57179f7cd18adcbaa7e2fca4ff7b1b446df88bf0b4398e4a26965a6ee8bfb23428a4efecb3ebdc636139de9a568ed427fff20d28baa33ed48e9c44e1' + + await testUtilsInstance.setPubKey(_signerPubkeyX, _signerPubkeyY) const signerPubkey = await testUtilsInstance.signerPubkey.call() + assert.equal(signerPubkey, _concatinatedKeys) + }) + + it('returns base value for unset public key', async () => { + const newTestUtilsInstance = await TestDepositUtils.new() + const signerPubkey = await newTestUtilsInstance.signerPubkey.call() + console.log(signerPubkey) assert.equal(signerPubkey, '0x' + '00'.repeat(64)) }) }) diff --git a/implementation/test/contracts/deposit/TestDepositUtils.sol b/implementation/test/contracts/deposit/TestDepositUtils.sol index 1323caf1c..d69289a27 100644 --- a/implementation/test/contracts/deposit/TestDepositUtils.sol +++ b/implementation/test/contracts/deposit/TestDepositUtils.sol @@ -56,6 +56,28 @@ contract TestDepositUtils is TestDeposit { return DepositUtils.extractOutputAtIndex(_txOutputVector, _fundingOutputIndex); } + function validateAndParseFundingSPVProof( + bytes _txVersion, + bytes _txInputVector, + bytes _txOutputVector, + bytes _txLocktime, + uint8 _fundingOutputIndex, + bytes _merkleProof, + uint256 _txIndexInBlock, + bytes _bitcoinHeaders + ) public view returns (bytes8 _valueBytes, bytes _utxoOutpoint){ + return self.validateAndParseFundingSPVProof( + _txVersion, + _txInputVector, + _txOutputVector, + _txLocktime, + _fundingOutputIndex, + _merkleProof, + _txIndexInBlock, + _bitcoinHeaders + ); + } + function auctionValue() public view returns (uint256) { return self.auctionValue(); } diff --git a/implementation/test/tx.json b/implementation/test/tx.json index 9a021d8cd..15a005831 100644 --- a/implementation/test/tx.json +++ b/implementation/test/tx.json @@ -3,6 +3,7 @@ "proof": "0x0a16c4ad273ecc3539600c2379cfbf5c471468009021f7183bdacbc4294f5600e612a970ebbd9c1be055d53bdd912bebac13dda8ea4eebfb846baacd1761484ae9cf3056de160a3b6b1b05a97dbf67eade6eb277e9612b586e6e3f3cba9d2201f590b305616d37e31553fa194423408a095b34d68d12e93a3dc4cc89c2786df0453291b75ddeab43350db3159c85207837c6237bea5696008930d09a53c38028614299997a183b9bb628f6aa941d5eda74a52f6f35bcd9f936eabe362e625919514a26f65cca02c123694c4c587a3851d479b828d702aeec072e353bb482272b1ef6140611d6157e3f39f7bfa1795d02d1c1447f8b25ff4149c206c61e9a6adc968183ec2d9c2c0c7260fc921b71aaecf0b1b2cb961559f4a55c91734fd81fc6a996df2a38397797b69f9bab02ce29773117ff7f26becc2bae9e22833f504e217976a2e9c5680408e18d2da541b44e3df391f107c5b1a33962fb73422e23abd8a0b0d8426d97a1c9d72996b3bde0154193cbcb6d1656e2c60a194a920b235f8d40b1bb509d27093c69c09c0a3bcec5f23af86c83d908bf83c854ed29d29318c0c30f3f4e765c850033116610dee15be31eed68e69cdb05d567194402ebc8064c", "index": 1514, "height": 570522, + "difficulty": 6379265451411, "tx_id": "0x00564f29c4cbda3b18f72190006814475cbfcf79230c603935cc3e27adc4160a", "tx_id_le": "0x0a16c4ad273ecc3539600c2379cfbf5c471468009021f7183bdacbc4294f5600" } From 588b20412e59ab8d6f7174eba7c06ba98141f9db Mon Sep 17 00:00:00 2001 From: Nicholas Evans Date: Thu, 4 Jul 2019 07:11:55 -0500 Subject: [PATCH 23/27] Fix js lint errors --- implementation/test/DepositTest.js | 2 +- implementation/test/DepositUtilsTest.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/implementation/test/DepositTest.js b/implementation/test/DepositTest.js index 9de2aa8fe..d1f48d68b 100644 --- a/implementation/test/DepositTest.js +++ b/implementation/test/DepositTest.js @@ -966,7 +966,7 @@ contract('Deposit', (accounts) => { const initialTokenBalance = await deployed.TBTCStub.getBalance(beneficiary) await testInstance.send(signerBond, { from: beneficiary }) - + await deployed.TBTCSystemStub.setDepositOwner(0, beneficiary) const initialBalance = await web3.eth.getBalance(beneficiary) diff --git a/implementation/test/DepositUtilsTest.js b/implementation/test/DepositUtilsTest.js index 30ea5f70c..ab0e3d347 100644 --- a/implementation/test/DepositUtilsTest.js +++ b/implementation/test/DepositUtilsTest.js @@ -245,7 +245,7 @@ contract('DepositUtils', (accounts) => { res = await testUtilsInstance.extractOutputAtIndex.call(_txOutputVector2, 0) assert.equal(res, '0x4db6000000000000160014455c0ea778752831d6fc25f6f8cf55dc49d335f0') res = await testUtilsInstance.extractOutputAtIndex.call(_txOutputVector2, 1) - assert.equal(res, '0x40420f0000000000220020aedad4518f56379ef6f1f52f2e0fed64608006b3ccaff2253d847ddc90c91922') + assert.equal(res, '0x40420f0000000000220020aedad4518f56379ef6f1f52f2e0fed64608006b3ccaff2253d847ddc90c91922') }) it('extracts outputs at specified index (vecor length 4)', async () => { From 6fcbf11f8262cfb293508755f89aae972259bd45 Mon Sep 17 00:00:00 2001 From: Nicholas Evans Date: Tue, 9 Jul 2019 08:39:25 -0500 Subject: [PATCH 24/27] Fix DespositUtilsTest typos and improve naming --- .../contracts/deposit/DepositFunding.sol | 2 -- .../contracts/deposit/DepositUtils.sol | 2 +- implementation/test/DepositUtilsTest.js | 23 ++++++++----------- 3 files changed, 11 insertions(+), 16 deletions(-) diff --git a/implementation/contracts/deposit/DepositFunding.sol b/implementation/contracts/deposit/DepositFunding.sol index bb82e4fe8..0d8d27acc 100644 --- a/implementation/contracts/deposit/DepositFunding.sol +++ b/implementation/contracts/deposit/DepositFunding.sol @@ -127,8 +127,6 @@ library DepositFunding { fundingTeardown(_d); } - - /// @notice we poll the Keep contract to retrieve our pubkey /// @dev We store the pubkey as 2 bytestrings, X and Y. /// @param _d deposit storage pointer diff --git a/implementation/contracts/deposit/DepositUtils.sol b/implementation/contracts/deposit/DepositUtils.sol index ce55b2b29..9bbf69dbb 100644 --- a/implementation/contracts/deposit/DepositUtils.sol +++ b/implementation/contracts/deposit/DepositUtils.sol @@ -245,7 +245,7 @@ library DepositUtils { // Therefore, pad with 3 more bytes _utxoOutpoint = abi.encodePacked(txID, _fundingOutputIndex, hex"000000"); } - + /// @notice Calculates the amount of value at auction right now /// @dev We calculate the % of the auction that has elapsed, then scale the value up /// @param _d deposit storage pointer diff --git a/implementation/test/DepositUtilsTest.js b/implementation/test/DepositUtilsTest.js index ab0e3d347..9b9a314f9 100644 --- a/implementation/test/DepositUtilsTest.js +++ b/implementation/test/DepositUtilsTest.js @@ -56,6 +56,7 @@ const _txIndexInBlock = 130 const _bitcoinHeaders = '0x00e0ff3fd877ad23af1d0d3e0eb6a700d85b692975dacd36e47b1b00000000000000000095ba61df5961d7fa0a45cd7467e11f20932c7a0b74c59318e86581c6b509554876f6c65c114e2c17e42524d300000020994d3802da5adf80345261bcff2eb87ab7b70db786cb0000000000000000000003169efc259f6e4b5e1bfa469f06792d6f07976a098bff2940c8e7ed3105fdc5eff7c65c114e2c170c4dffc30000c020f898b7ea6a405728055b0627f53f42c57290fe78e0b91900000000000000000075472c91a94fa2aab73369c0686a58796949cf60976e530f6eb295320fa15a1b77f8c65c114e2c17387f1df00000002069137421fc274aa2c907dbf0ec4754285897e8aa36332b0000000000000000004308f2494b702c40e9d61991feb7a15b3be1d73ce988e354e52e7a4e611bd9c2a2f8c65c114e2c1740287df200000020ab63607b09395f856adaa69d553755d9ba5bd8d15da20a000000000000000000090ea7559cda848d97575cb9696c8e33ba7f38d18d5e2f8422837c354aec147839fbc65c114e2c175cf077d6000000200ab3612eac08a31a8fb1d9b5397f897db8d26f6cd83a230000000000000000006f4888720ecbf980ff9c983a8e2e60ad329cc7b130916c2bf2300ea54e412a9ed6fcc65c114e2c17d4fbb88500000020d3e51560f77628a26a8fad01c88f98bd6c9e4bc8703b180000000000000000008e2c6e62a1f4d45dd03be1e6692df89a4e3b1223a4dbdfa94cca94c04c22049992fdc65c114e2c17463edb5e' const _signerPubkeyX = '0xd4aee75e57179f7cd18adcbaa7e2fca4ff7b1b446df88bf0b4398e4a26965a6e' const _signerPubkeyY = '0xe8bfb23428a4efecb3ebdc636139de9a568ed427fff20d28baa33ed48e9c44e1' +const _concatenatedKeys = '0xd4aee75e57179f7cd18adcbaa7e2fca4ff7b1b446df88bf0b4398e4a26965a6ee8bfb23428a4efecb3ebdc636139de9a568ed427fff20d28baa33ed48e9c44e1' const _merkleProof = '0x5f40bccf997d221cd0e9cb6564643f9808a89a5e1c65ea5e6530c0b51c18487c886f7da48f4ccfe49283c678dedb376c89853ba46d9a297fe39e8dd557d1f8deb0fb1a28c03f71b267f3a33459b2566975b1653a1238947ed05edca17ef64181b1f09d858a6e25bae4b0e245993d4ea77facba8ed0371bb9b8a6724475bcdc9edf9ead30b61cf6714758b7c93d1b725f86c2a66a07dd291ef566eaa5a59516823d57fd50557f1d938cc2fb61fe0e1acee6f9cb618a9210688a2965c52feabee66d660a5e7f158e363dc464fca2bb1cc856173366d5d20b5cd513a3aab8ebc5be2bd196b783b8773af2472abcea3e32e97938283f7b454769aa1c064c311c3342a755029ee338664999bd8d432080eafae3ca86b52ad2e321e9e634a46c1bd0d174e38bcd4c59a0f0a78c5906c015ef4daf6beb0500a59f4cae00cd46069ce60db2182e74561028e4462f59f639c89b8e254602d6ad9c212b7c2af5db9275e48c467539c6af678d6f09214182df848bd79a06df706f7c3fddfdd95e6f27326c6217ee446543a443f82b711f48c173a769ae8d1e92a986bc76fca732f088bbe04995ba61df5961d7fa0a45cd7467e11f20932c7a0b74c59318e86581c6b5095548' const _expectedUTXOoutpoint = '0x5f40bccf997d221cd0e9cb6564643f9808a89a5e1c65ea5e6530c0b51c18487c00000000' // const _outputValue = 490029088; @@ -207,8 +208,6 @@ contract('DepositUtils', (accounts) => { const _txOutputVector = '0x012040351d0000000016001486e7303082a6a21d5837176bc808bf4828371ab6' const _fundingOutputIndex = 0 const _outValueBytes = '0x2040351d00000000' - const _signerPubkeyX = '0xd4aee75e57179f7cd18adcbaa7e2fca4ff7b1b446df88bf0b4398e4a26965a6e' - const _signerPubkeyY = '0xe8bfb23428a4efecb3ebdc636139de9a568ed427fff20d28baa33ed48e9c44e1' it('correctly returns valuebytes', async () => { await testUtilsInstance.setPubKey(_signerPubkeyX, _signerPubkeyY) @@ -228,7 +227,7 @@ contract('DepositUtils', (accounts) => { }) describe('extractOutputAtIndex()', async () => { - it('extracts outputs at specified indicex (vector length 1)', async () => { + it('extracts outputs at specified indices (vector length 1)', async () => { const _txOutputVector = '0x012040351d0000000016001486e7303082a6a21d5837176bc808bf4828371ab6' const res = await testUtilsInstance.extractOutputAtIndex.call(_txOutputVector, 0) assert.equal(res, '0x2040351d0000000016001486e7303082a6a21d5837176bc808bf4828371ab6') @@ -248,7 +247,7 @@ contract('DepositUtils', (accounts) => { assert.equal(res, '0x40420f0000000000220020aedad4518f56379ef6f1f52f2e0fed64608006b3ccaff2253d847ddc90c91922') }) - it('extracts outputs at specified index (vecor length 4)', async () => { + it('extracts outputs at specified index (vector length 4)', async () => { const _txOutputVector = '0x044897070000000000220020a4333e5612ab1a1043b25755c89b16d55184a42f81799e623e6bc39db8539c180000000000000000166a14edb1b5c2f39af0fec151732585b1049b078952114db6000000000000160014455c0ea778752831d6fc25f6f8cf55dc49d335f040420f0000000000220020aedad4518f56379ef6f1f52f2e0fed64608006b3ccaff2253d847ddc90c91922' const res = await testUtilsInstance.extractOutputAtIndex.call(_txOutputVector, 3) assert.equal(res, '0x40420f0000000000220020aedad4518f56379ef6f1f52f2e0fed64608006b3ccaff2253d847ddc90c91922') @@ -264,7 +263,7 @@ contract('DepositUtils', (accounts) => { } }) - it('fails to extract output from varint prepended vector', async () => { + it('fails to extract output from a vector with too big VarInt output counter', async () => { // we don't need to include the number of outputs suggested by the varint const _txOutputVector= '0xfe123412344897070000000000220020a4333e5612ab1a1043b25755c89b16d55184a42f81799e623e6bc39db8539c180000000000000000166a14edb1b5c2f39af0fec151732585b1049b07895211' try { @@ -282,7 +281,7 @@ contract('DepositUtils', (accounts) => { await deployed.TBTCSystemStub.setCurrentDiff(currentDifficulty) }) - it('returns currect value and outpoint', async () => { + it('returns correct value and outpoint', async () => { const parseResults = await testUtilsInstance.validateAndParseFundingSPVProof.call(_version, _txInputVector, _txOutputVector, _txLocktime, _fundingOutputIndex, _merkleProof, _txIndexInBlock, _bitcoinHeaders) assert.equal(parseResults[0], _outValueBytes) assert.equal(parseResults[1], _expectedUTXOoutpoint) @@ -346,13 +345,9 @@ contract('DepositUtils', (accounts) => { describe('signerPubkey()', async () => { it('returns the concatenated signer X and Y coordinates', async () => { - const _signerPubkeyX = '0xd4aee75e57179f7cd18adcbaa7e2fca4ff7b1b446df88bf0b4398e4a26965a6e' - const _signerPubkeyY = '0xe8bfb23428a4efecb3ebdc636139de9a568ed427fff20d28baa33ed48e9c44e1' - const _concatinatedKeys = '0xd4aee75e57179f7cd18adcbaa7e2fca4ff7b1b446df88bf0b4398e4a26965a6ee8bfb23428a4efecb3ebdc636139de9a568ed427fff20d28baa33ed48e9c44e1' - await testUtilsInstance.setPubKey(_signerPubkeyX, _signerPubkeyY) const signerPubkey = await testUtilsInstance.signerPubkey.call() - assert.equal(signerPubkey, _concatinatedKeys) + assert.equal(signerPubkey, _concatenatedKeys) }) it('returns base value for unset public key', async () => { @@ -364,9 +359,11 @@ contract('DepositUtils', (accounts) => { describe('signerPKH()', async () => { it('returns the concatenated signer X and Y coordinates', async () => { - await testUtilsInstance.setPubKey('0x' + '00'.repeat(32), '0x' + '00'.repeat(32)) + const expectedSignerPKH = '0xa99c23add58e3d0712278b2873c3c0bd21657115' + await testUtilsInstance.setPubKey(_signerPubkeyX, _signerPubkeyX) const signerPKH = await testUtilsInstance.signerPKH.call() - assert.equal(signerPKH, utils.hash160('02' + '00'.repeat(32))) + console.log(signerPKH) + assert.equal(signerPKH, expectedSignerPKH) }) }) From fbf93b2b1616ec323337a0319b9cf2d510c71177 Mon Sep 17 00:00:00 2001 From: Nicholas Evans Date: Tue, 9 Jul 2019 08:59:46 -0500 Subject: [PATCH 25/27] Fix sol lint warning --- implementation/contracts/DepositLog.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/implementation/contracts/DepositLog.sol b/implementation/contracts/DepositLog.sol index eb241c384..84aaabb74 100644 --- a/implementation/contracts/DepositLog.sol +++ b/implementation/contracts/DepositLog.sol @@ -94,7 +94,7 @@ contract DepositLog { /* solium-disable-next-line no-empty-blocks */ function approvedToLog(address _caller) public view returns (bool) { /* TODO: auth via system */ - _caller; + _caller; return true; } From d5d3ab687dfbaadc21c983bb24b0dd7145526cb0 Mon Sep 17 00:00:00 2001 From: Nicholas Evans Date: Wed, 10 Jul 2019 09:52:17 -0500 Subject: [PATCH 26/27] Remove console.log from DespositUtilsTest file --- implementation/test/DepositUtilsTest.js | 1 - 1 file changed, 1 deletion(-) diff --git a/implementation/test/DepositUtilsTest.js b/implementation/test/DepositUtilsTest.js index 9b9a314f9..c482d98d4 100644 --- a/implementation/test/DepositUtilsTest.js +++ b/implementation/test/DepositUtilsTest.js @@ -362,7 +362,6 @@ contract('DepositUtils', (accounts) => { const expectedSignerPKH = '0xa99c23add58e3d0712278b2873c3c0bd21657115' await testUtilsInstance.setPubKey(_signerPubkeyX, _signerPubkeyX) const signerPKH = await testUtilsInstance.signerPKH.call() - console.log(signerPKH) assert.equal(signerPKH, expectedSignerPKH) }) }) From 01a685f681f2ff2629ea0a191fe22aaa7a2957f8 Mon Sep 17 00:00:00 2001 From: Nicholas Evans Date: Tue, 16 Jul 2019 21:02:54 -0500 Subject: [PATCH 27/27] Import testDepositUtils in deposit.sol --- implementation/test/DepositTest.js | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/implementation/test/DepositTest.js b/implementation/test/DepositTest.js index bfaa1ece0..c16024ef6 100644 --- a/implementation/test/DepositTest.js +++ b/implementation/test/DepositTest.js @@ -18,6 +18,7 @@ const TBTCSystemStub = artifacts.require('TBTCSystemStub') const TestTBTCConstants = artifacts.require('TestTBTCConstants') const TestDeposit = artifacts.require('TestDeposit') +const TestDepositUtils = artifacts.require('TestDepositUtils') const BN = require('bn.js') const utils = require('./utils') @@ -39,6 +40,7 @@ const TEST_DEPOSIT_DEPLOY = [ { name: 'DepositRedemption', contract: DepositRedemption }, { name: 'DepositLiquidation', contract: DepositLiquidation }, { name: 'TestDeposit', contract: TestDeposit }, + { name: 'TestDepositUtils', contract: TestDepositUtils }, { name: 'KeepStub', contract: KeepStub }, { name: 'TBTCTokenStub', contract: TBTCTokenStub }, { name: 'TBTCSystemStub', contract: TBTCSystemStub }] @@ -976,12 +978,7 @@ contract('Deposit', (accounts) => { const beneficiary = accounts[4] await deployed.TBTCTokenStub.clearBalance(beneficiary) const signerBond = 10000000000 -<<<<<<< HEAD - const initialTokenBalance = await deployed.TBTCStub.getBalance(beneficiary) - -======= const initialTokenBalance = await deployed.TBTCTokenStub.balanceOf(beneficiary) ->>>>>>> master await testInstance.send(signerBond, { from: beneficiary }) await deployed.TBTCSystemStub.setDepositOwner(0, beneficiary) @@ -993,12 +990,8 @@ contract('Deposit', (accounts) => { const balanceCheck = new BN(initialBalance).add(new BN(signerBond)) assert.equal(balanceCheck, balanceAfter, 'funder bond not currectly returned') -<<<<<<< HEAD -======= const endingTokenBalancce = await deployed.TBTCTokenStub.balanceOf(beneficiary) ->>>>>>> master - const endingTokenBalancce = await deployed.TBTCStub.getBalance(beneficiary) const lotSize = await deployed.TBTCConstants.getLotSize.call() const toMint = lotSize.mul(new BN(95)).div(new BN(100)) const tokenCheck = initialTokenBalance.add(new BN(toMint))