Skip to content

Commit

Permalink
Btc view (summa-tx#158)
Browse files Browse the repository at this point in the history
* feature: first pass at BTCView

* refactor: views are now bytes29 to avoid type confusion with digests

* refactor: simplify and test castTo

* test: test for isValid

* refactor: clarify loc/len and fix linting

* test: add test for len function

* bug: fix improper mask genreation by deleting the function

* chore: more comments and explanation

* Added docs to ViewBTC.sol (summa-tx#160)

* added honey bunches of docs

* fixed doc errors

* fixed stuff

* removed todo, edited some docs

* chore: finish test scaffolding for ViewBTC

* bug: type error in ViewBTCTest

* feature: ViewSPV contract

* lint: indentation

* chore: rename lib and natspec title

* tests: scaffoled test file for ViewSPV

* document clarification

* feature: writeTo and join

* refactor: distinguish between build and uncheckedBuild. refactor unsafe functions

* WIP: `viewBTC` js tests (summa-tx#165)

* first test

* more tests, cleaned up BTCUtils.test.js

* wrong function

* added more tests

* cleaned up file, added more tests

* feature: hex encoded errors for typedmemview

* fixed opReturnPayload test, added scriptPubkey function in ViewBTCtest.sol

* tests: indexVin and indexVout

- fix indexVin & indexVout
- fix tryAsVin & tryAsVout
- fancy hex errors in typedmemview
- add tests for indexing vin/vout
- remove vectors for non-minimal varints in vin/vout
- misc cleanup to truffle config
- linting
- type to uint conversion comments

* refactor: improve/fix nibbleHex

* finished nasty verifyHash256Merkle function test

* tests: Tests and bugfixes for ViewSPV

* added notes

* moved type conversion and validation to ViewSPVTest.sol

* accidentally reverted change

* added more type assertions

* notes on tryAsVout test

* lint: solidity assembly

* feature: error message for non-minimal varints

* added new errors for Non-minimal varints

* refactor: improve non-minimal varint error

* bug: don't be so lazy

* fixed test cases that deal with non-minimal var ints

* updated error tests, needs cleaned up

* pushing latest

* fixed bugs

* finished payload, work and workHash tests

* separated out error cases

* Organized ViewBTC tests

* updated test descriptions

* bug: fix payload p2sh path

* added test case to extractHash

* chore: improved comments for typedmemview

* chore: remove duplicate test vector

Co-authored-by: James Prestwich <[email protected]>

* Test for `checkWork` (summa-tx#173)

* added some checkWork tests

* removed only

* lint error

* opt: use identity function for copying

* refactor: clean up clone

* refactor: remove legacy files and update CheckBitcoinSigs

* refactor: redelete BTCUtils

* lint: whitespace

* version: bump sol to 4.0.0

* feature: tryAsSPK

* refactor: slightly clean up uncompressed pubkey handling

Co-authored-by: Erin Hales <[email protected]>
Co-authored-by: Erin Hales <[email protected]>
  • Loading branch information
3 people authored Jul 3, 2020
1 parent 6102ece commit 0cb765c
Show file tree
Hide file tree
Showing 22 changed files with 2,072 additions and 2,899 deletions.
674 changes: 0 additions & 674 deletions solidity/contracts/BTCUtils.sol

This file was deleted.

359 changes: 0 additions & 359 deletions solidity/contracts/BTCUtilsDelegate.sol

This file was deleted.

423 changes: 0 additions & 423 deletions solidity/contracts/BytesLib.sol

This file was deleted.

70 changes: 37 additions & 33 deletions solidity/contracts/CheckBitcoinSigs.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,12 @@ pragma solidity ^0.5.10;
/** @title CheckBitcoinSigs */
/** @author Summa (https://summa.one) */

import {BytesLib} from "./BytesLib.sol";
import {BTCUtils} from "./BTCUtils.sol";

import {TypedMemView} from "./TypedMemView.sol";

library CheckBitcoinSigs {

using BytesLib for bytes;
using BTCUtils for bytes;
using TypedMemView for bytes29;
using TypedMemView for bytes;

/// @notice Derives an Ethereum Account address from a pubkey
/// @dev The address is the last 20 bytes of the keccak256 of the address
Expand All @@ -19,33 +17,39 @@ library CheckBitcoinSigs {
function accountFromPubkey(bytes memory _pubkey) internal pure returns (address) {
require(_pubkey.length == 64, "Pubkey must be 64-byte raw, uncompressed key.");

// keccak hash of uncompressed unprefixed pubkey
bytes32 _digest = keccak256(_pubkey);
bytes32 _digest = _pubkey.ref(0).keccak();
return address(uint256(_digest));
}

/// @notice Calculates the p2wpkh output script of a pubkey
/// @dev Compresses keys to 33 bytes as required by Bitcoin
/// @dev Compresses keys to 33 bytes as required by Bitcoin wpkh
/// @param _pubkey The public key, compressed or uncompressed
/// @return The p2wkph output script
function p2wpkhFromPubkey(bytes memory _pubkey) internal pure returns (bytes memory) {
bytes memory _compressedPubkey;
uint8 _prefix;

if (_pubkey.length == 64) {
_prefix = uint8(_pubkey[_pubkey.length - 1]) % 2 == 1 ? 3 : 2;
_compressedPubkey = abi.encodePacked(_prefix, _pubkey.slice(0, 32));
} else if (_pubkey.length == 65) {
_prefix = uint8(_pubkey[_pubkey.length - 1]) % 2 == 1 ? 3 : 2;
_compressedPubkey = abi.encodePacked(_prefix, _pubkey.slice(1, 32));
} else {
_compressedPubkey = _pubkey;
function p2wpkhFromPubkey(bytes memory _pubkey) internal view returns (bytes memory) {
bytes29 _pubkeyView = _pubkey.ref(0);

bytes20 _digest;
if (_pubkey.length == 33) {_digest = _pubkeyView.hash160();}
else if (_pubkey.length == 64) {
// This is less memory efficient than it ought to be
// But we immediately deallocate it, so it's usually no issue
uint8 prefix = uint8(_pubkey[_pubkey.length - 1]) % 2 + 2;
bytes memory _x = _pubkeyView.prefix(32, 0).clone();
bytes memory _compressed = abi.encodePacked(prefix, _x);
_digest = _compressed.ref(0).hash160();
// Deallocate memory
assembly {
// solium-disable-previous-line security/no-inline-assembly
mstore(0x40, _x)
}
}

require(_compressedPubkey.length == 33, "Witness PKH requires compressed keys");
require(
_digest != bytes20(0),
"CheckBitcoinSigs/p2wpkhFromPubkey -- Invalid pubkey length. expected 64 or 33"
);

bytes memory _pubkeyHash = _compressedPubkey.hash160();
return abi.encodePacked(hex"0014", _pubkeyHash);
return abi.encodePacked(hex"0014", _digest);
}

/// @notice checks a signed message's validity under a pubkey
Expand All @@ -63,7 +67,7 @@ library CheckBitcoinSigs {
bytes32 _r,
bytes32 _s
) internal pure returns (bool) {
require(_pubkey.length == 64, "Requires uncompressed unprefixed pubkey");
require(_pubkey.length == 64, "CheckBitcoinSigs/checkSig -- Requires uncompressed unprefixed pubkey");
address _expected = accountFromPubkey(_pubkey);
address _actual = ecrecover(_digest, _v, _r, _s);
return _actual == _expected;
Expand All @@ -85,9 +89,8 @@ library CheckBitcoinSigs {
uint8 _v,
bytes32 _r,
bytes32 _s
) internal pure returns (bool) {
require(_pubkey.length == 64, "Requires uncompressed unprefixed pubkey");

) internal view returns (bool) {
require(_pubkey.length == 64, "CheckBitcoinSigs/checkBitcoinSig -- Requires uncompressed unprefixed pubkey");
bool _isExpectedSigner = keccak256(p2wpkhFromPubkey(_pubkey)) == keccak256(_p2wpkhOutputScript); // is it the expected signer?
if (!_isExpectedSigner) {return false;}

Expand Down Expand Up @@ -133,7 +136,8 @@ library CheckBitcoinSigs {
bytes8 _inputValue, // 8-byte LE
bytes8 _outputValue, // 8-byte LE
bytes memory _outputScript // lenght-prefixed output script
) internal pure returns (bytes32) {
) internal view returns (bytes32) {

// Fixes elements to easily make a 1-in 1-out sighash digest
// Does not support timelocks
bytes memory _scriptCode = abi.encodePacked(
Expand All @@ -142,10 +146,10 @@ library CheckBitcoinSigs {
hex"88ac"); // equal, checksig
bytes32 _hashOutputs = abi.encodePacked(
_outputValue, // 8-byte LE
_outputScript).hash256();
_outputScript).ref(0).hash256();
bytes memory _sighashPreimage = abi.encodePacked(
hex"01000000", // version
_outpoint.hash256(), // hashPrevouts
_outpoint.ref(0).hash256(), // hashPrevouts
hex"8cb9012517c817fead650287d61bdd9c68803b6bf9c64133dcab3e65b5a50cb9", // hashSequence(00000000)
_outpoint, // outpoint
_scriptCode, // p2wpkh script code
Expand All @@ -155,7 +159,7 @@ library CheckBitcoinSigs {
hex"00000000", // nLockTime
hex"01000000" // SIGHASH_ALL
);
return _sighashPreimage.hash256();
return _sighashPreimage.ref(0).hash256();
}

/// @notice calculates the signature hash of a Bitcoin transaction with the provided details
Expand All @@ -172,7 +176,7 @@ library CheckBitcoinSigs {
bytes8 _inputValue, // 8-byte LE
bytes8 _outputValue, // 8-byte LE
bytes20 _outputPKH // 20-byte hash160
) internal pure returns (bytes32) {
) internal view returns (bytes32) {
return wpkhSpendSighash(
_outpoint,
_inputPKH,
Expand All @@ -198,7 +202,7 @@ library CheckBitcoinSigs {
bytes8 _inputValue, // 8-byte LE
bytes8 _outputValue, // 8-byte LE
bytes20 _outputPKH // 20-byte hash160
) internal pure returns (bytes32) {
) internal view returns (bytes32) {
return wpkhToWpkhSighash(_outpoint, _inputPKH, _inputValue, _outputValue, _outputPKH);
}

Expand Down
116 changes: 0 additions & 116 deletions solidity/contracts/CheckBitcoinSigsDelegate.sol

This file was deleted.

Loading

0 comments on commit 0cb765c

Please sign in to comment.