├── .gitattributes ├── .gitignore ├── .prettierignore ├── LICENSE ├── README.md ├── contracts ├── BuyTokensWithBTC.sol ├── ContractWallet.sol ├── ETH2BTC.sol ├── GasTokenSubsidisedTransactions.sol ├── erc20SignedPayloadSwap.sol ├── summa-tx │ ├── BTCUtils.sol │ ├── BtcParser.sol │ ├── BytesLib.sol │ ├── SafeMath.sol │ ├── TypedMemView.sol │ ├── ValidateSPV.sol │ ├── ViewBTC.sol │ └── ViewSPV.sol └── test │ ├── ETH2BTC.test.sol │ └── lib │ └── ds-test │ ├── .gitignore │ ├── LICENSE │ ├── Makefile │ ├── default.nix │ ├── demo │ └── demo.sol │ └── src │ └── test.sol ├── hardhat.config.js ├── package-lock.json ├── package.json └── test └── hardhat-tests.js /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sol linguist-language=Solidity 2 | 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | .idea 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | .DS_Store 13 | # Directory for instrumented libs generated by jscoverage/JSCover 14 | lib-cov 15 | 16 | # Coverage directory used by tools like istanbul 17 | coverage 18 | 19 | # nyc test coverage 20 | .nyc_output 21 | 22 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 23 | .grunt 24 | 25 | # node-waf configuration 26 | .lock-wscript 27 | 28 | # Compiled binary addons (http://nodejs.org/api/addons.html) 29 | build/Release 30 | 31 | # Dependency directories 32 | node_modules 33 | jspm_packages 34 | 35 | # Optional npm cache directory 36 | .npm 37 | 38 | # Optional REPL history 39 | .node_repl_history 40 | 41 | node_modules 42 | .env 43 | coverage 44 | coverage.json 45 | typechain 46 | 47 | #Hardhat files 48 | cache 49 | artifacts 50 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | typechain/* 2 | .git/* 3 | .github/* 4 | artifacts/* 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 James Sangalli 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Learn solidity with examples! 2 | 3 | A repo full of smart contracts written in Solidity. 4 | 5 | ## Important 6 | 7 | This repo is for educational purposes only, use at your own risk. 8 | 9 | ## Getting started 10 | 11 | - Install the dependencies `npm i` 12 | - Run the dapp-tools tests with `npm run test` 13 | - Run the hardhat tests with `npm run hardhat-test` 14 | - Run the fuzzer on ETH2BTC with `echidna-test contracts/ETH2BTC.sol` 15 | - Explore the contracts in `./contracts` 16 | 17 | _Sample tests with dapp-tools (solidity) and hardhat (js) are included for educational purposes so you can choose which framework works best for you_ 18 | -------------------------------------------------------------------------------- /contracts/BuyTokensWithBTC.sol: -------------------------------------------------------------------------------- 1 | /* 2 | This contract allows anyone to buy tokens on Ethereum with Bitcoin by proving a Bitcoin transaction was sent 3 | to a particular address (of the token seller) and is then minted to an ethereum address provided by the bitcoin sender. 4 | 5 | Requires: 6 | this contract has a minter role in the token contract 7 | */ 8 | 9 | import "./summa-tx/BtcParser.sol"; 10 | import "./summa-tx/ViewSPV.sol"; 11 | pragma solidity ^0.5.10; 12 | 13 | interface ERC20Mint { 14 | function _mint(address account, uint256 amount) external; 15 | } 16 | 17 | contract BuyTokensWithBTC { 18 | bytes20 bitcoinAddressOfTokenSeller; 19 | address tokenContract; // can be any token type but for this example we use erc20 20 | uint256 deadline; //deadline for this token sale in block time 21 | mapping(bytes32 => bool) claimedBitcoinTransactions; 22 | uint256 rate; //rate of tokens to BTC 23 | ERC20Mint erc20Mint; 24 | 25 | constructor( 26 | bytes20 _bitcoinAddressOfTokenSeller, 27 | address _tokenContract, 28 | uint256 _deadline, 29 | uint256 _rate 30 | ) public { 31 | bitcoinAddressOfTokenSeller = _bitcoinAddressOfTokenSeller; 32 | tokenContract = _tokenContract; 33 | deadline = _deadline; 34 | rate = _rate; 35 | erc20Mint = ERC20Mint(_tokenContract); 36 | } 37 | 38 | //UI needs to protect user against sending BTC after the deadline with a margin of safety 39 | function isStillActive() public view returns (bool) { 40 | return deadline > block.timestamp; 41 | } 42 | 43 | //this function proves a btc transaction occurred, validates that it went to the right destination and mints coins back 44 | //to the same key that sent the btc. TODO make it work with a specified recipient address for minted coins 45 | function proveBTCTransactionAndMint( 46 | bytes memory rawTransaction, 47 | uint256 transactionHash, 48 | bytes32 _txid, 49 | bytes32 _merkleRoot, 50 | bytes29 _proof, 51 | uint256 _index 52 | ) public returns (bool) { 53 | require(deadline > block.timestamp); 54 | require(!claimedBitcoinTransactions[_txid]); //disallow reclaims 55 | require(ViewSPV.prove(_txid, _merkleRoot, _proof, _index)); 56 | bytes memory senderPubKey = BtcParser.getPubKeyFromTx(rawTransaction); 57 | //create ethereum address from bitcoin pubkey 58 | address senderAddress = address(bytes20(keccak256(senderPubKey))); 59 | //one would be change, the other the amount actually sent 60 | ( 61 | uint256 amt1, 62 | bytes20 address1, 63 | uint256 amt2, 64 | bytes20 address2 65 | ) = BtcParser.getFirstTwoOutputs(rawTransaction); 66 | if (address1 == bitcoinAddressOfTokenSeller) { 67 | uint256 amountToMint = amt1 * rate; 68 | _mintTokens(senderAddress, amountToMint); 69 | } 70 | if (address2 == bitcoinAddressOfTokenSeller) { 71 | uint256 amountToMint = amt2 * rate; 72 | _mintTokens(senderAddress, amountToMint); 73 | } 74 | claimedBitcoinTransactions[_txid] = true; 75 | return true; 76 | } 77 | 78 | function _mintTokens(address recipient, uint256 amount) internal { 79 | erc20Mint._mint(recipient, amount); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /contracts/ContractWallet.sol: -------------------------------------------------------------------------------- 1 | /* 2 | The purpose of this contract is to allow anyone to have move and store their ether via this contract 3 | with the assurance that someone can move the funds for them if they lose their keys. 4 | 5 | This is semi trustless as even a malicious guardian of the funds has to wait for the grace period 6 | before they can take the funds, the user can easily get their funds back if this happens 7 | as the grace period can be quite long. 8 | */ 9 | 10 | pragma solidity ^0.5.10; 11 | 12 | contract ContractWallet { 13 | uint256 public gracePeriodInterval; 14 | 15 | struct User { 16 | address userAddress; 17 | uint256 balance; 18 | //time after request for approval to move funds in which user can still move their money 19 | uint256 gracePeriod; 20 | address payable guardian; 21 | bool approvedToMove; 22 | } 23 | 24 | mapping(address => User) users; 25 | 26 | constructor(uint256 gracePeriodIntervalInit) public { 27 | gracePeriodInterval = gracePeriodIntervalInit; 28 | } 29 | 30 | function() external payable { 31 | //check if user has an account else reject 32 | require(users[msg.sender].guardian != address(0)); 33 | users[msg.sender].balance += msg.value; 34 | } 35 | 36 | function createUser(address payable guardian) public payable { 37 | require(users[msg.sender].guardian != address(0)); 38 | User memory user = User(msg.sender, msg.value, 0, guardian, false); 39 | users[msg.sender] = user; 40 | } 41 | 42 | function transferEther(address payable to, uint256 amount) public { 43 | require(users[msg.sender].balance >= amount); 44 | to.transfer(amount); 45 | users[msg.sender].balance -= amount; 46 | } 47 | 48 | //guardian suspects that the user has lost their key, initiates a move but must wait til gracePeriod is finished 49 | function guardianRequestToMoveFunds(address toMove) public { 50 | require(users[toMove].guardian == msg.sender); 51 | users[toMove].approvedToMove = true; 52 | //user can move their funds on their own before the grace period, 53 | //this is to protect malicious moving of funds by guardian 54 | users[toMove].gracePeriod = block.timestamp + gracePeriodInterval; 55 | } 56 | 57 | //user has not moved funds since grace period, we assume they have lost their keys 58 | function withdrawToGuardian(address toMove) public { 59 | require(users[toMove].guardian == msg.sender); 60 | require(users[toMove].gracePeriod < block.timestamp); 61 | users[toMove].guardian.transfer(users[toMove].balance); 62 | users[toMove].balance = 0; 63 | delete users[toMove]; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /contracts/ETH2BTC.sol: -------------------------------------------------------------------------------- 1 | import "./summa-tx/BtcParser.sol"; 2 | import "./summa-tx/ValidateSPV.sol"; 3 | pragma experimental ABIEncoderV2; 4 | pragma solidity ^0.5.10; 5 | 6 | contract ETH2BTC { 7 | struct Order { 8 | uint256 etherAmount; 9 | uint256 bitcoinAmountAtRate; 10 | uint256 dueTimestamp; 11 | address payable refundAddress; 12 | } 13 | 14 | mapping(bytes20 => Order[]) public orders; // btc address to Order 15 | address public btcrelayAddress; 16 | address payable public admin; 17 | uint256 public etherToBitcoinRate; 18 | bytes20 public bitcoinAddress; 19 | 20 | constructor( 21 | bytes20 btcAddress, 22 | address payable adminAddr, 23 | uint256 initialRate, 24 | uint256 initialFeeRatio 25 | ) public { 26 | admin = adminAddr; 27 | bitcoinAddress = btcAddress; 28 | //default mainnet 29 | etherToBitcoinRate = initialRate; 30 | } 31 | 32 | function setEtherToBitcoinRate(uint256 rate) public { 33 | require(msg.sender == admin); 34 | etherToBitcoinRate = rate; 35 | } 36 | 37 | //market maker can only withdraw the order funds on proof of a bitcoin transaction to the buyer 38 | function proveBitcoinTransaction( 39 | bytes memory rawTransaction, 40 | uint256 transactionHash, 41 | bytes32 _txid, 42 | bytes32 _merkleRoot, 43 | bytes memory _proof, 44 | uint256 _index 45 | ) public returns (bool) { 46 | bytes memory senderPubKey = BtcParser.getPubKeyFromTx(rawTransaction); 47 | bytes20 senderAddress = bytes20( 48 | sha256(abi.encodePacked(sha256(senderPubKey))) 49 | ); 50 | //require that the market maker sent the bitcoin 51 | require(senderAddress == bitcoinAddress); 52 | require(ValidateSPV.prove(_txid, _merkleRoot, _proof, _index)); 53 | //first output goes to the order maker by deriving their btc address 54 | //from their ether pub key 55 | ( 56 | uint256 amt1, 57 | bytes20 address1, 58 | uint256 amt2, 59 | bytes20 address2 60 | ) = BtcParser.getFirstTwoOutputs(rawTransaction); 61 | for (uint256 i = 0; i < orders[address1].length; i++) { 62 | //if two identical orders, simply grab the first one 63 | if (orders[address1][i].bitcoinAmountAtRate == amt1) { 64 | //once order is found, delete it 65 | //that way it is now claimed and can't be claimed multiple times 66 | //order can be claimed even if past due date, so long as the sender 67 | //hasn't already got a refund (in which case it would be refunded and the order deleted) 68 | //market maker should ensure they relay the bitcoin tx before expiry, else they could 69 | //be cheated out of their bitcoins by someone claiming to have not received them 70 | //when in fact they have but it hasn't been relayed 71 | delete orders[address1][i]; 72 | //withdraw the ether for the trade 73 | admin.transfer(orders[address1][i].etherAmount); 74 | return true; 75 | } 76 | } 77 | return false; 78 | } 79 | 80 | //sender can provide any bitcoin address they want to receive bitcoin on 81 | function placeOrder(bytes20 receiverBtcAddress, address payable refundAddr) 82 | public 83 | payable 84 | { 85 | require(msg.value > 0); 86 | //in case user doesn't set the refund address 87 | if (refundAddr == address(0)) refundAddr = msg.sender; 88 | uint256 btcAmount = msg.value * etherToBitcoinRate; 89 | //two weeks from order, should be processed well before this date but includes a margin of safety 90 | uint256 dueDate = block.timestamp + 1296000; 91 | Order memory newOrder = Order( 92 | msg.value, 93 | btcAmount, 94 | dueDate, 95 | refundAddr 96 | ); 97 | orders[receiverBtcAddress].push(newOrder); 98 | } 99 | 100 | function hashOrder(Order memory order) internal returns (bytes32) { 101 | return 102 | keccak256( 103 | abi.encodePacked( 104 | order.etherAmount, 105 | order.bitcoinAmountAtRate, 106 | order.dueTimestamp, 107 | order.refundAddress 108 | ) 109 | ); 110 | } 111 | 112 | // call this if bitcoin transaction never arrives and order is still present 113 | function getRefundForOrder(Order memory order, bytes20 orderOwner) public { 114 | bool orderIsPresent = false; 115 | uint256 pos = 0; 116 | bytes32 hashOfOrder = hashOrder(order); 117 | for (uint256 i = 0; i < orders[orderOwner].length; i++) { 118 | Order memory currentOrder = orders[orderOwner][i]; 119 | bytes32 orderHash = hashOrder(currentOrder); 120 | if (orderHash == hashOfOrder) { 121 | orderIsPresent = true; 122 | pos = i; 123 | break; 124 | } 125 | } 126 | require(orderIsPresent); 127 | require(order.dueTimestamp < block.timestamp); 128 | order.refundAddress.transfer(order.etherAmount); 129 | delete orders[orderOwner][pos]; 130 | } 131 | } 132 | 133 | contract ETH2BTCFuzz is ETH2BTC { 134 | function echidna_fuzz_setEtherToBitcoinRate() public view returns(bool) { 135 | return etherToBitcoinRate != 2**256 - 1; 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /contracts/GasTokenSubsidisedTransactions.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.10; 2 | 3 | contract ERC20Interface { 4 | function totalSupply() public view returns (uint256); 5 | 6 | function balanceOf(address tokenOwner) 7 | public 8 | view 9 | returns (uint256 balance); 10 | 11 | function allowance(address tokenOwner, address spender) 12 | public 13 | view 14 | returns (uint256 remaining); 15 | 16 | function transfer(address to, uint256 tokens) public returns (bool success); 17 | 18 | function approve(address spender, uint256 tokens) 19 | public 20 | returns (bool success); 21 | 22 | function transferFrom( 23 | address from, 24 | address to, 25 | uint256 tokens 26 | ) public returns (bool success); 27 | 28 | event Transfer(address indexed from, address indexed to, uint256 tokens); 29 | event Approval( 30 | address indexed tokenOwner, 31 | address indexed spender, 32 | uint256 tokens 33 | ); 34 | 35 | //gas token specific 36 | function free(uint256 value) public; 37 | } 38 | 39 | contract GasTokenSubsidizedTransactionsERC20 { 40 | address public admin; 41 | uint256 public priceOfGasToken; 42 | address gasTokenContractAddress = 43 | 0x0000000000b3F879cb30FE243b4Dfee438691c04; 44 | ERC20Interface gasTokenContract = ERC20Interface(gasTokenContractAddress); 45 | 46 | constructor(uint256 initialGasTokenPrice) public { 47 | admin = msg.sender; 48 | priceOfGasToken = initialGasTokenPrice; 49 | } 50 | 51 | //User must approve this contract for transfers 52 | function transferFromDelegate( 53 | address from, 54 | address to, 55 | uint256 amount, 56 | address contractToCall 57 | ) public payable { 58 | require(from == msg.sender); 59 | uint256 numberOfGasTokensToUse = msg.value / priceOfGasToken; 60 | gasTokenContract.free(numberOfGasTokensToUse); 61 | ERC20Interface transferFromContract = ERC20Interface(contractToCall); 62 | transferFromContract.transferFrom(from, to, amount); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /contracts/erc20SignedPayloadSwap.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.1; 2 | 3 | contract erc20 { 4 | function transferFrom( 5 | address to, 6 | address from, 7 | uint256 amount 8 | ) public returns (bool); 9 | 10 | function balanceOf(address owner) public returns (uint256); 11 | 12 | function allowance(address tokenOwner, address spender) 13 | public 14 | view 15 | returns (uint256 remaining); 16 | } 17 | 18 | contract erc20SignedPayloadSwap { 19 | mapping(bytes32 => address) claimedDeals; 20 | 21 | //requires that the seller has enabled an allowance to the contract 22 | function swapTokenForNativeCurrency( 23 | uint256 expiry, 24 | uint256 amount, 25 | address erc20ContractAddress, 26 | uint8 v, 27 | bytes32 r, 28 | bytes32 s 29 | ) public payable { 30 | bytes32 message = keccak256( 31 | abi.encodePacked(amount, expiry, msg.value, erc20ContractAddress) 32 | ); 33 | address seller = ecrecover(message, v, r, s); 34 | erc20 erc20Contract = erc20(erc20ContractAddress); 35 | require(erc20Contract.transferFrom(seller, msg.sender, amount)); 36 | bytes32 hashedTradeData = keccak256(abi.encodePacked(message, v, r, s)); 37 | require(claimedDeals[hashedTradeData] != seller); 38 | //don't allow the deal to be done multiple times 39 | claimedDeals[hashedTradeData] = seller; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /contracts/summa-tx/BTCUtils.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.10; 2 | 3 | /** @title BitcoinSPV */ 4 | /** @author Summa (https://summa.one) */ 5 | 6 | import {BytesLib} from "./BytesLib.sol"; 7 | import {SafeMath} from "./SafeMath.sol"; 8 | 9 | library BTCUtils { 10 | using BytesLib for bytes; 11 | using SafeMath for uint256; 12 | 13 | // The target at minimum Difficulty. Also the target of the genesis block 14 | uint256 public constant DIFF1_TARGET = 15 | 0xffff0000000000000000000000000000000000000000000000000000; 16 | 17 | uint256 public constant RETARGET_PERIOD = 2 * 7 * 24 * 60 * 60; // 2 weeks in seconds 18 | uint256 public constant RETARGET_PERIOD_BLOCKS = 2016; // 2 weeks in blocks 19 | 20 | /* ***** */ 21 | /* UTILS */ 22 | /* ***** */ 23 | 24 | /// @notice Determines the length of a VarInt in bytes 25 | /// @dev A VarInt of >1 byte is prefixed with a flag indicating its length 26 | /// @param _flag The first byte of a VarInt 27 | /// @return The number of non-flag bytes in the VarInt 28 | function determineVarIntDataLength(bytes memory _flag) 29 | internal 30 | pure 31 | returns (uint8) 32 | { 33 | if (uint8(_flag[0]) == 0xff) { 34 | return 8; // one-byte flag, 8 bytes data 35 | } 36 | if (uint8(_flag[0]) == 0xfe) { 37 | return 4; // one-byte flag, 4 bytes data 38 | } 39 | if (uint8(_flag[0]) == 0xfd) { 40 | return 2; // one-byte flag, 2 bytes data 41 | } 42 | 43 | return 0; // flag is data 44 | } 45 | 46 | /// @notice Changes the endianness of a byte array 47 | /// @dev Returns a new, backwards, bytes 48 | /// @param _b The bytes to reverse 49 | /// @return The reversed bytes 50 | function reverseEndianness(bytes memory _b) 51 | internal 52 | pure 53 | returns (bytes memory) 54 | { 55 | bytes memory _newValue = new bytes(_b.length); 56 | 57 | for (uint256 i = 0; i < _b.length; i++) { 58 | _newValue[_b.length - i - 1] = _b[i]; 59 | } 60 | 61 | return _newValue; 62 | } 63 | 64 | /// @notice Changes the endianness of a uint256 65 | /// @dev https://graphics.stanford.edu/~seander/bithacks.html#ReverseParallel 66 | /// @param _b The unsigned integer to reverse 67 | /// @return The reversed value 68 | function reverseUint256(uint256 _b) internal pure returns (uint256 v) { 69 | v = _b; 70 | 71 | // swap bytes 72 | v = 73 | ((v >> 8) & 74 | 0x00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF) | 75 | ((v & 76 | 0x00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF) << 77 | 8); 78 | // swap 2-byte long pairs 79 | v = 80 | ((v >> 16) & 81 | 0x0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF) | 82 | ((v & 83 | 0x0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF) << 84 | 16); 85 | // swap 4-byte long pairs 86 | v = 87 | ((v >> 32) & 88 | 0x00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF) | 89 | ((v & 90 | 0x00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF) << 91 | 32); 92 | // swap 8-byte long pairs 93 | v = 94 | ((v >> 64) & 95 | 0x0000000000000000FFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF) | 96 | ((v & 97 | 0x0000000000000000FFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF) << 98 | 64); 99 | // swap 16-byte long pairs 100 | v = (v >> 128) | (v << 128); 101 | } 102 | 103 | /// @notice Converts big-endian bytes to a uint 104 | /// @dev Traverses the byte array and sums the bytes 105 | /// @param _b The big-endian bytes-encoded integer 106 | /// @return The integer representation 107 | function bytesToUint(bytes memory _b) internal pure returns (uint256) { 108 | uint256 _number; 109 | 110 | for (uint256 i = 0; i < _b.length; i++) { 111 | _number = _number + uint8(_b[i]) * (2**(8 * (_b.length - (i + 1)))); 112 | } 113 | 114 | return _number; 115 | } 116 | 117 | /// @notice Get the last _num bytes from a byte array 118 | /// @param _b The byte array to slice 119 | /// @param _num The number of bytes to extract from the end 120 | /// @return The last _num bytes of _b 121 | function lastBytes(bytes memory _b, uint256 _num) 122 | internal 123 | pure 124 | returns (bytes memory) 125 | { 126 | uint256 _start = _b.length.sub(_num); 127 | 128 | return _b.slice(_start, _num); 129 | } 130 | 131 | /// @notice Implements bitcoin's hash160 (rmd160(sha2())) 132 | /// @dev abi.encodePacked changes the return to bytes instead of bytes32 133 | /// @param _b The pre-image 134 | /// @return The digest 135 | function hash160(bytes memory _b) internal pure returns (bytes memory) { 136 | return abi.encodePacked(ripemd160(abi.encodePacked(sha256(_b)))); 137 | } 138 | 139 | /// @notice Implements bitcoin's hash256 (double sha2) 140 | /// @dev abi.encodePacked changes the return to bytes instead of bytes32 141 | /// @param _b The pre-image 142 | /// @return The digest 143 | function hash256(bytes memory _b) internal pure returns (bytes32) { 144 | return 145 | abi.encodePacked(sha256(abi.encodePacked(sha256(_b)))).toBytes32(); 146 | } 147 | 148 | /// @notice Implements bitcoin's hash256 (double sha2) 149 | /// @dev sha2 is precompiled smart contract located at address(2) 150 | /// @param _b The pre-image 151 | /// @return The digest 152 | function hash256View(bytes memory _b) internal view returns (bytes32 res) { 153 | assembly { 154 | let ptr := mload(0x40) 155 | pop(staticcall(gas, 2, add(_b, 32), mload(_b), ptr, 32)) 156 | pop(staticcall(gas, 2, ptr, 32, ptr, 32)) 157 | res := mload(ptr) 158 | } 159 | } 160 | 161 | /* ************ */ 162 | /* Legacy Input */ 163 | /* ************ */ 164 | 165 | /// @notice Extracts the nth input from the vin (0-indexed) 166 | /// @dev Iterates over the vin. If you need to extract several, write a custom function 167 | /// @param _vin The vin as a tightly-packed byte array 168 | /// @param _index The 0-indexed location of the input to extract 169 | /// @return The input as a byte array 170 | function extractInputAtIndex(bytes memory _vin, uint8 _index) 171 | internal 172 | pure 173 | returns (bytes memory) 174 | { 175 | uint256 _len; 176 | bytes memory _remaining; 177 | 178 | uint256 _offset = 1; 179 | 180 | for (uint8 _i = 0; _i < _index; _i++) { 181 | _remaining = _vin.slice(_offset, _vin.length - _offset); 182 | _len = determineInputLength(_remaining); 183 | _offset = _offset + _len; 184 | } 185 | 186 | _remaining = _vin.slice(_offset, _vin.length - _offset); 187 | _len = determineInputLength(_remaining); 188 | return _vin.slice(_offset, _len); 189 | } 190 | 191 | /// @notice Determines whether an input is legacy 192 | /// @dev False if no scriptSig, otherwise True 193 | /// @param _input The input 194 | /// @return True for legacy, False for witness 195 | function isLegacyInput(bytes memory _input) internal pure returns (bool) { 196 | return _input.keccak256Slice(36, 1) != keccak256(hex"00"); 197 | } 198 | 199 | /// @notice Determines the length of an input from its scriptsig 200 | /// @dev 36 for outpoint, 1 for scriptsig length, 4 for sequence 201 | /// @param _input The input 202 | /// @return The length of the input in bytes 203 | function determineInputLength(bytes memory _input) 204 | internal 205 | pure 206 | returns (uint256) 207 | { 208 | uint8 _varIntDataLen; 209 | uint256 _scriptSigLen; 210 | (_varIntDataLen, _scriptSigLen) = extractScriptSigLen(_input); 211 | return 36 + 1 + _varIntDataLen + _scriptSigLen + 4; 212 | } 213 | 214 | /// @notice Extracts the LE sequence bytes from an input 215 | /// @dev Sequence is used for relative time locks 216 | /// @param _input The LEGACY input 217 | /// @return The sequence bytes (LE uint) 218 | function extractSequenceLELegacy(bytes memory _input) 219 | internal 220 | pure 221 | returns (bytes memory) 222 | { 223 | uint8 _varIntDataLen; 224 | uint256 _scriptSigLen; 225 | (_varIntDataLen, _scriptSigLen) = extractScriptSigLen(_input); 226 | return _input.slice(36 + 1 + _varIntDataLen + _scriptSigLen, 4); 227 | } 228 | 229 | /// @notice Extracts the sequence from the input 230 | /// @dev Sequence is a 4-byte little-endian number 231 | /// @param _input The LEGACY input 232 | /// @return The sequence number (big-endian uint) 233 | function extractSequenceLegacy(bytes memory _input) 234 | internal 235 | pure 236 | returns (uint32) 237 | { 238 | bytes memory _leSeqence = extractSequenceLELegacy(_input); 239 | bytes memory _beSequence = reverseEndianness(_leSeqence); 240 | return uint32(bytesToUint(_beSequence)); 241 | } 242 | 243 | /// @notice Extracts the VarInt-prepended scriptSig from the input in a tx 244 | /// @dev Will return hex"00" if passed a witness input 245 | /// @param _input The LEGACY input 246 | /// @return The length-prepended script sig 247 | function extractScriptSig(bytes memory _input) 248 | internal 249 | pure 250 | returns (bytes memory) 251 | { 252 | uint8 _varIntDataLen; 253 | uint256 _scriptSigLen; 254 | (_varIntDataLen, _scriptSigLen) = extractScriptSigLen(_input); 255 | return _input.slice(36, 1 + _varIntDataLen + _scriptSigLen); 256 | } 257 | 258 | /// @notice Determines the length of a scriptSig in an input 259 | /// @dev Will return 0 if passed a witness input 260 | /// @param _input The LEGACY input 261 | /// @return The length of the script sig 262 | function extractScriptSigLen(bytes memory _input) 263 | internal 264 | pure 265 | returns (uint8, uint256) 266 | { 267 | bytes memory _varIntTag = _input.slice(36, 1); 268 | uint8 _varIntDataLen = determineVarIntDataLength(_varIntTag); 269 | uint256 _len; 270 | if (_varIntDataLen == 0) { 271 | _len = uint8(_varIntTag[0]); 272 | } else { 273 | _len = bytesToUint( 274 | reverseEndianness(_input.slice(36 + 1, _varIntDataLen)) 275 | ); 276 | } 277 | return (_varIntDataLen, _len); 278 | } 279 | 280 | /* ************* */ 281 | /* Witness Input */ 282 | /* ************* */ 283 | 284 | /// @notice Extracts the LE sequence bytes from an input 285 | /// @dev Sequence is used for relative time locks 286 | /// @param _input The WITNESS input 287 | /// @return The sequence bytes (LE uint) 288 | function extractSequenceLEWitness(bytes memory _input) 289 | internal 290 | pure 291 | returns (bytes memory) 292 | { 293 | return _input.slice(37, 4); 294 | } 295 | 296 | /// @notice Extracts the sequence from the input in a tx 297 | /// @dev Sequence is a 4-byte little-endian number 298 | /// @param _input The WITNESS input 299 | /// @return The sequence number (big-endian uint) 300 | function extractSequenceWitness(bytes memory _input) 301 | internal 302 | pure 303 | returns (uint32) 304 | { 305 | bytes memory _leSeqence = extractSequenceLEWitness(_input); 306 | bytes memory _inputeSequence = reverseEndianness(_leSeqence); 307 | return uint32(bytesToUint(_inputeSequence)); 308 | } 309 | 310 | /// @notice Extracts the outpoint from the input in a tx 311 | /// @dev 32 byte tx id with 4 byte index 312 | /// @param _input The input 313 | /// @return The outpoint (LE bytes of prev tx hash + LE bytes of prev tx index) 314 | function extractOutpoint(bytes memory _input) 315 | internal 316 | pure 317 | returns (bytes memory) 318 | { 319 | return _input.slice(0, 36); 320 | } 321 | 322 | /// @notice Extracts the outpoint tx id from an input 323 | /// @dev 32 byte tx id 324 | /// @param _input The input 325 | /// @return The tx id (little-endian bytes) 326 | function extractInputTxIdLE(bytes memory _input) 327 | internal 328 | pure 329 | returns (bytes32) 330 | { 331 | return _input.slice(0, 32).toBytes32(); 332 | } 333 | 334 | /// @notice Extracts the outpoint index from an input 335 | /// @dev 32 byte tx id 336 | /// @param _input The input 337 | /// @return The tx id (big-endian bytes) 338 | function extractInputTxId(bytes memory _input) 339 | internal 340 | pure 341 | returns (bytes32) 342 | { 343 | bytes memory _leId = abi.encodePacked(extractInputTxIdLE(_input)); 344 | bytes memory _beId = reverseEndianness(_leId); 345 | return _beId.toBytes32(); 346 | } 347 | 348 | /// @notice Extracts the LE tx input index from the input in a tx 349 | /// @dev 4 byte tx index 350 | /// @param _input The input 351 | /// @return The tx index (little-endian bytes) 352 | function extractTxIndexLE(bytes memory _input) 353 | internal 354 | pure 355 | returns (bytes memory) 356 | { 357 | return _input.slice(32, 4); 358 | } 359 | 360 | /// @notice Extracts the tx input index from the input in a tx 361 | /// @dev 4 byte tx index 362 | /// @param _input The input 363 | /// @return The tx index (big-endian uint) 364 | function extractTxIndex(bytes memory _input) 365 | internal 366 | pure 367 | returns (uint32) 368 | { 369 | bytes memory _leIndex = extractTxIndexLE(_input); 370 | bytes memory _beIndex = reverseEndianness(_leIndex); 371 | return uint32(bytesToUint(_beIndex)); 372 | } 373 | 374 | /* ****** */ 375 | /* Output */ 376 | /* ****** */ 377 | 378 | /// @notice Determines the length of an output 379 | /// @dev 5 types: WPKH, WSH, PKH, SH, and OP_RETURN 380 | /// @param _output The output 381 | /// @return The length indicated by the prefix, error if invalid length 382 | function determineOutputLength(bytes memory _output) 383 | internal 384 | pure 385 | returns (uint256) 386 | { 387 | uint8 _len = uint8(_output.slice(8, 1)[0]); 388 | require(_len < 0xfd, "Multi-byte VarInts not supported"); 389 | 390 | return _len + 8 + 1; // 8 byte value, 1 byte for _len itself 391 | } 392 | 393 | /// @notice Extracts the output at a given index in the TxIns vector 394 | /// @dev Iterates over the vout. If you need to extract multiple, write a custom function 395 | /// @param _vout The _vout to extract from 396 | /// @param _index The 0-indexed location of the output to extract 397 | /// @return The specified output 398 | function extractOutputAtIndex(bytes memory _vout, uint8 _index) 399 | internal 400 | pure 401 | returns (bytes memory) 402 | { 403 | uint256 _len; 404 | bytes memory _remaining; 405 | 406 | uint256 _offset = 1; 407 | 408 | for (uint8 _i = 0; _i < _index; _i++) { 409 | _remaining = _vout.slice(_offset, _vout.length - _offset); 410 | _len = determineOutputLength(_remaining); 411 | _offset = _offset + _len; 412 | } 413 | 414 | _remaining = _vout.slice(_offset, _vout.length - _offset); 415 | _len = determineOutputLength(_remaining); 416 | return _vout.slice(_offset, _len); 417 | } 418 | 419 | /// @notice Extracts the output script length 420 | /// @dev Indexes the length prefix on the pk_script 421 | /// @param _output The output 422 | /// @return The 1 byte length prefix 423 | function extractOutputScriptLen(bytes memory _output) 424 | internal 425 | pure 426 | returns (bytes memory) 427 | { 428 | return _output.slice(8, 1); 429 | } 430 | 431 | /// @notice Extracts the value bytes from the output in a tx 432 | /// @dev Value is an 8-byte little-endian number 433 | /// @param _output The output 434 | /// @return The output value as LE bytes 435 | function extractValueLE(bytes memory _output) 436 | internal 437 | pure 438 | returns (bytes memory) 439 | { 440 | return _output.slice(0, 8); 441 | } 442 | 443 | /// @notice Extracts the value from the output in a tx 444 | /// @dev Value is an 8-byte little-endian number 445 | /// @param _output The output 446 | /// @return The output value 447 | function extractValue(bytes memory _output) internal pure returns (uint64) { 448 | bytes memory _leValue = extractValueLE(_output); 449 | bytes memory _beValue = reverseEndianness(_leValue); 450 | return uint64(bytesToUint(_beValue)); 451 | } 452 | 453 | /// @notice Extracts the data from an op return output 454 | /// @dev Returns hex"" if no data or not an op return 455 | /// @param _output The output 456 | /// @return Any data contained in the opreturn output, null if not an op return 457 | function extractOpReturnData(bytes memory _output) 458 | internal 459 | pure 460 | returns (bytes memory) 461 | { 462 | if (_output.keccak256Slice(9, 1) != keccak256(hex"6a")) { 463 | return hex""; 464 | } 465 | bytes memory _dataLen = _output.slice(10, 1); 466 | return _output.slice(11, bytesToUint(_dataLen)); 467 | } 468 | 469 | /// @notice Extracts the hash from the output script 470 | /// @dev Determines type by the length prefix and validates format 471 | /// @param _output The output 472 | /// @return The hash committed to by the pk_script, or null for errors 473 | function extractHash(bytes memory _output) 474 | internal 475 | pure 476 | returns (bytes memory) 477 | { 478 | if (uint8(_output.slice(9, 1)[0]) == 0) { 479 | uint256 _len = uint8(extractOutputScriptLen(_output)[0]) - 2; 480 | // Check for maliciously formatted witness outputs 481 | if (uint8(_output.slice(10, 1)[0]) != uint8(_len)) { 482 | return hex""; 483 | } 484 | return _output.slice(11, _len); 485 | } else { 486 | bytes32 _tag = _output.keccak256Slice(8, 3); 487 | // p2pkh 488 | if (_tag == keccak256(hex"1976a9")) { 489 | // Check for maliciously formatted p2pkh 490 | if ( 491 | uint8(_output.slice(11, 1)[0]) != 0x14 || 492 | _output.keccak256Slice(_output.length - 2, 2) != 493 | keccak256(hex"88ac") 494 | ) { 495 | return hex""; 496 | } 497 | return _output.slice(12, 20); 498 | //p2sh 499 | } else if (_tag == keccak256(hex"17a914")) { 500 | // Check for maliciously formatted p2sh 501 | if (uint8(_output.slice(_output.length - 1, 1)[0]) != 0x87) { 502 | return hex""; 503 | } 504 | return _output.slice(11, 20); 505 | } 506 | } 507 | return hex""; /* NB: will trigger on OPRETURN and non-standard that don't overrun */ 508 | } 509 | 510 | /* ********** */ 511 | /* Witness TX */ 512 | /* ********** */ 513 | 514 | /// @notice Checks that the vin passed up is properly formatted 515 | /// @dev Consider a vin with a valid vout in its scriptsig 516 | /// @param _vin Raw bytes length-prefixed input vector 517 | /// @return True if it represents a validly formatted vin 518 | function validateVin(bytes memory _vin) internal pure returns (bool) { 519 | uint256 _offset = 1; 520 | uint8 _nIns = uint8(_vin.slice(0, 1)[0]); 521 | 522 | // Not valid if it says there are too many or no inputs 523 | if (_nIns >= 0xfd || _nIns == 0) { 524 | return false; 525 | } 526 | 527 | for (uint8 i = 0; i < _nIns; i++) { 528 | // Grab the next input and determine its length. 529 | // Increase the offset by that much 530 | _offset += determineInputLength( 531 | _vin.slice(_offset, _vin.length - _offset) 532 | ); 533 | 534 | // Returns false we jump past the end 535 | if (_offset > _vin.length) { 536 | return false; 537 | } 538 | } 539 | 540 | // Returns false if we're not exactly at the end 541 | return _offset == _vin.length; 542 | } 543 | 544 | /// @notice Checks that the vin passed up is properly formatted 545 | /// @dev Consider a vin with a valid vout in its scriptsig 546 | /// @param _vout Raw bytes length-prefixed output vector 547 | /// @return True if it represents a validly formatted bout 548 | function validateVout(bytes memory _vout) internal pure returns (bool) { 549 | uint256 _offset = 1; 550 | uint8 _nOuts = uint8(_vout.slice(0, 1)[0]); 551 | 552 | // Not valid if it says there are too many or no inputs 553 | if (_nOuts >= 0xfd || _nOuts == 0) { 554 | return false; 555 | } 556 | 557 | for (uint8 i = 0; i < _nOuts; i++) { 558 | // Grab the next input and determine its length. 559 | // Increase the offset by that much 560 | _offset += determineOutputLength( 561 | _vout.slice(_offset, _vout.length - _offset) 562 | ); 563 | 564 | // Returns false we jump past the end 565 | if (_offset > _vout.length) { 566 | return false; 567 | } 568 | } 569 | 570 | // Returns false if we're not exactly at the end 571 | return _offset == _vout.length; 572 | } 573 | 574 | /* ************ */ 575 | /* Block Header */ 576 | /* ************ */ 577 | 578 | /// @notice Extracts the transaction merkle root from a block header 579 | /// @dev Use verifyHash256Merkle to verify proofs with this root 580 | /// @param _header The header 581 | /// @return The merkle root (little-endian) 582 | function extractMerkleRootLE(bytes memory _header) 583 | internal 584 | pure 585 | returns (bytes memory) 586 | { 587 | return _header.slice(36, 32); 588 | } 589 | 590 | /// @notice Extracts the transaction merkle root from a block header 591 | /// @dev Use verifyHash256Merkle to verify proofs with this root 592 | /// @param _header The header 593 | /// @return The merkle root (big-endian) 594 | function extractMerkleRootBE(bytes memory _header) 595 | internal 596 | pure 597 | returns (bytes memory) 598 | { 599 | return reverseEndianness(extractMerkleRootLE(_header)); 600 | } 601 | 602 | /// @notice Extracts the target from a block header 603 | /// @dev Target is a 256 bit number encoded as a 3-byte mantissa and 1 byte exponent 604 | /// @param _header The header 605 | /// @return The target threshold 606 | function extractTarget(bytes memory _header) 607 | internal 608 | pure 609 | returns (uint256) 610 | { 611 | bytes memory _m = _header.slice(72, 3); 612 | uint8 _e = uint8(_header[75]); 613 | uint256 _mantissa = bytesToUint(reverseEndianness(_m)); 614 | uint256 _exponent = _e - 3; 615 | 616 | return _mantissa * (256**_exponent); 617 | } 618 | 619 | /// @notice Calculate difficulty from the difficulty 1 target and current target 620 | /// @dev Difficulty 1 is 0x1d00ffff on mainnet and testnet 621 | /// @dev Difficulty 1 is a 256 bit number encoded as a 3-byte mantissa and 1 byte exponent 622 | /// @param _target The current target 623 | /// @return The block difficulty (bdiff) 624 | function calculateDifficulty(uint256 _target) 625 | internal 626 | pure 627 | returns (uint256) 628 | { 629 | // Difficulty 1 calculated from 0x1d00ffff 630 | return DIFF1_TARGET.div(_target); 631 | } 632 | 633 | /// @notice Extracts the previous block's hash from a block header 634 | /// @dev Block headers do NOT include block number :( 635 | /// @param _header The header 636 | /// @return The previous block's hash (little-endian) 637 | function extractPrevBlockLE(bytes memory _header) 638 | internal 639 | pure 640 | returns (bytes memory) 641 | { 642 | return _header.slice(4, 32); 643 | } 644 | 645 | /// @notice Extracts the previous block's hash from a block header 646 | /// @dev Block headers do NOT include block number :( 647 | /// @param _header The header 648 | /// @return The previous block's hash (big-endian) 649 | function extractPrevBlockBE(bytes memory _header) 650 | internal 651 | pure 652 | returns (bytes memory) 653 | { 654 | return reverseEndianness(extractPrevBlockLE(_header)); 655 | } 656 | 657 | /// @notice Extracts the timestamp from a block header 658 | /// @dev Time is not 100% reliable 659 | /// @param _header The header 660 | /// @return The timestamp (little-endian bytes) 661 | function extractTimestampLE(bytes memory _header) 662 | internal 663 | pure 664 | returns (bytes memory) 665 | { 666 | return _header.slice(68, 4); 667 | } 668 | 669 | /// @notice Extracts the timestamp from a block header 670 | /// @dev Time is not 100% reliable 671 | /// @param _header The header 672 | /// @return The timestamp (uint) 673 | function extractTimestamp(bytes memory _header) 674 | internal 675 | pure 676 | returns (uint32) 677 | { 678 | return 679 | uint32(bytesToUint(reverseEndianness(extractTimestampLE(_header)))); 680 | } 681 | 682 | /// @notice Extracts the expected difficulty from a block header 683 | /// @dev Does NOT verify the work 684 | /// @param _header The header 685 | /// @return The difficulty as an integer 686 | function extractDifficulty(bytes memory _header) 687 | internal 688 | pure 689 | returns (uint256) 690 | { 691 | return calculateDifficulty(extractTarget(_header)); 692 | } 693 | 694 | /// @notice Concatenates and hashes two inputs for merkle proving 695 | /// @param _a The first hash 696 | /// @param _b The second hash 697 | /// @return The double-sha256 of the concatenated hashes 698 | function _hash256MerkleStep(bytes memory _a, bytes memory _b) 699 | internal 700 | pure 701 | returns (bytes32) 702 | { 703 | return hash256(abi.encodePacked(_a, _b)); 704 | } 705 | 706 | /// @notice Verifies a Bitcoin-style merkle tree 707 | /// @dev Leaves are 0-indexed. 708 | /// @param _proof The proof. Tightly packed LE sha256 hashes. The last hash is the root 709 | /// @param _index The index of the leaf 710 | /// @return true if the proof is valid, else false 711 | function verifyHash256Merkle(bytes memory _proof, uint256 _index) 712 | internal 713 | pure 714 | returns (bool) 715 | { 716 | // Not an even number of hashes 717 | if (_proof.length % 32 != 0) { 718 | return false; 719 | } 720 | 721 | // Special case for coinbase-only blocks 722 | if (_proof.length == 32) { 723 | return true; 724 | } 725 | 726 | // Should never occur 727 | if (_proof.length == 64) { 728 | return false; 729 | } 730 | 731 | uint256 _idx = _index; 732 | bytes32 _root = _proof.slice(_proof.length - 32, 32).toBytes32(); 733 | bytes32 _current = _proof.slice(0, 32).toBytes32(); 734 | 735 | for (uint256 i = 1; i < (_proof.length.div(32)) - 1; i++) { 736 | if (_idx % 2 == 1) { 737 | _current = _hash256MerkleStep( 738 | _proof.slice(i * 32, 32), 739 | abi.encodePacked(_current) 740 | ); 741 | } else { 742 | _current = _hash256MerkleStep( 743 | abi.encodePacked(_current), 744 | _proof.slice(i * 32, 32) 745 | ); 746 | } 747 | _idx = _idx >> 1; 748 | } 749 | return _current == _root; 750 | } 751 | 752 | /* 753 | NB: https://github.com/bitcoin/bitcoin/blob/78dae8caccd82cfbfd76557f1fb7d7557c7b5edb/src/pow.cpp#L49-L72 754 | NB: We get a full-bitlength target from this. For comparison with 755 | header-encoded targets we need to mask it with the header target 756 | e.g. (full & truncated) == truncated 757 | */ 758 | /// @notice performs the bitcoin difficulty retarget 759 | /// @dev implements the Bitcoin algorithm precisely 760 | /// @param _previousTarget the target of the previous period 761 | /// @param _firstTimestamp the timestamp of the first block in the difficulty period 762 | /// @param _secondTimestamp the timestamp of the last block in the difficulty period 763 | /// @return the new period's target threshold 764 | function retargetAlgorithm( 765 | uint256 _previousTarget, 766 | uint256 _firstTimestamp, 767 | uint256 _secondTimestamp 768 | ) internal pure returns (uint256) { 769 | uint256 _elapsedTime = _secondTimestamp.sub(_firstTimestamp); 770 | 771 | // Normalize ratio to factor of 4 if very long or very short 772 | if (_elapsedTime < RETARGET_PERIOD.div(4)) { 773 | _elapsedTime = RETARGET_PERIOD.div(4); 774 | } 775 | if (_elapsedTime > RETARGET_PERIOD.mul(4)) { 776 | _elapsedTime = RETARGET_PERIOD.mul(4); 777 | } 778 | 779 | /* 780 | NB: high targets e.g. ffff0020 can cause overflows here 781 | so we divide it by 256**2, then multiply by 256**2 later 782 | we know the target is evenly divisible by 256**2, so this isn't an issue 783 | */ 784 | 785 | uint256 _adjusted = _previousTarget.div(65536).mul(_elapsedTime); 786 | return _adjusted.div(RETARGET_PERIOD).mul(65536); 787 | } 788 | } 789 | -------------------------------------------------------------------------------- /contracts/summa-tx/BtcParser.sol: -------------------------------------------------------------------------------- 1 | // Bitcoin transaction parsing library 2 | 3 | // Copyright 2016 rain 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | // https://en.bitcoin.it/wiki/Protocol_documentation#tx 18 | // 19 | // Raw Bitcoin transaction structure: 20 | // 21 | // field | size | type | description 22 | // version | 4 | int32 | transaction version number 23 | // n_tx_in | 1-9 | var_int | number of transaction inputs 24 | // tx_in | 41+ | tx_in[] | list of transaction inputs 25 | // n_tx_out | 1-9 | var_int | number of transaction outputs 26 | // tx_out | 9+ | tx_out[] | list of transaction outputs 27 | // lock_time | 4 | uint32 | block number / timestamp at which tx locked 28 | // 29 | // Transaction input (tx_in) structure: 30 | // 31 | // field | size | type | description 32 | // previous | 36 | outpoint | Previous output transaction reference 33 | // script_len | 1-9 | var_int | Length of the signature script 34 | // sig_script | ? | uchar[] | Script for confirming transaction authorization 35 | // sequence | 4 | uint32 | Sender transaction version 36 | // 37 | // OutPoint structure: 38 | // 39 | // field | size | type | description 40 | // hash | 32 | char[32] | The hash of the referenced transaction 41 | // index | 4 | uint32 | The index of this output in the referenced transaction 42 | // 43 | // Transaction output (tx_out) structure: 44 | // 45 | // field | size | type | description 46 | // value | 8 | int64 | Transaction value (Satoshis) 47 | // pk_script_len | 1-9 | var_int | Length of the public key script 48 | // pk_script | ? | uchar[] | Public key as a Bitcoin script. 49 | // 50 | // Variable integers (var_int) can be encoded differently depending 51 | // on the represented value, to save space. Variable integers always 52 | // precede an array of a variable length data type (e.g. tx_in). 53 | // 54 | // Variable integer encodings as a function of represented value: 55 | // 56 | // value | bytes | format 57 | // <0xFD (253) | 1 | uint8 58 | // <=0xFFFF (65535)| 3 | 0xFD followed by length as uint16 59 | // <=0xFFFF FFFF | 5 | 0xFE followed by length as uint32 60 | // - | 9 | 0xFF followed by length as uint64 61 | // 62 | // Public key scripts `pk_script` are set on the output and can 63 | // take a number of forms. The regular transaction script is 64 | // called 'pay-to-pubkey-hash' (P2PKH): 65 | // 66 | // OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG 67 | // 68 | // OP_x are Bitcoin script opcodes. The bytes representation (including 69 | // the 0x14 20-byte stack push) is: 70 | // 71 | // 0x76 0xA9 0x14 0x88 0xAC 72 | // 73 | // The is the ripemd160 hash of the sha256 hash of 74 | // the public key, preceded by a network version byte. (21 bytes total) 75 | // 76 | // Network version bytes: 0x00 (mainnet); 0x6f (testnet); 0x34 (namecoin) 77 | // 78 | // The Bitcoin address is derived from the pubKeyHash. The binary form is the 79 | // pubKeyHash, plus a checksum at the end. The checksum is the first 4 bytes 80 | // of the (32 byte) double sha256 of the pubKeyHash. (25 bytes total) 81 | // This is converted to base58 to form the publicly used Bitcoin address. 82 | // Mainnet P2PKH transaction scripts are to addresses beginning with '1'. 83 | // 84 | // P2SH ('pay to script hash') scripts only supply a script hash. The spender 85 | // must then provide the script that would allow them to redeem this output. 86 | // This allows for arbitrarily complex scripts to be funded using only a 87 | // hash of the script, and moves the onus on providing the script from 88 | // the spender to the redeemer. 89 | // 90 | // The P2SH script format is simple: 91 | // 92 | // OP_HASH160 OP_EQUAL 93 | // 94 | // 0xA9 0x14 0x87 95 | // 96 | // The is the ripemd160 hash of the sha256 hash of the 97 | // redeem script. The P2SH address is derived from the scriptHash. 98 | // Addresses are the scriptHash with a version prefix of 5, encoded as 99 | // Base58check. These addresses begin with a '3'. 100 | pragma solidity ^0.5.10; 101 | 102 | // parse a raw bitcoin transaction byte array 103 | library BtcParser { 104 | // Convert a variable integer into something useful and return it and 105 | // the index to after it. 106 | function parseVarInt(bytes memory txBytes, uint256 pos) 107 | public 108 | returns (uint256, uint256) 109 | { 110 | // the first byte tells us how big the integer is 111 | uint8 ibit = uint8(txBytes[pos]); 112 | pos += 1; // skip ibit 113 | 114 | if (ibit < 0xfd) { 115 | return (ibit, pos); 116 | } else if (ibit == 0xfd) { 117 | return (getBytesLE(txBytes, pos, 16), pos + 2); 118 | } else if (ibit == 0xfe) { 119 | return (getBytesLE(txBytes, pos, 32), pos + 4); 120 | } else if (ibit == 0xff) { 121 | return (getBytesLE(txBytes, pos, 64), pos + 8); 122 | } 123 | } 124 | 125 | // convert little endian bytes to uint 126 | function getBytesLE( 127 | bytes memory data, 128 | uint256 pos, 129 | uint256 bits 130 | ) public returns (uint256) { 131 | if (bits == 8) { 132 | return uint8(data[pos]); 133 | } else if (bits == 16) { 134 | return 135 | uint16(uint8(data[pos])) + uint16(uint8(data[pos + 1])) * 2**8; 136 | } else if (bits == 32) { 137 | return 138 | uint32(uint8(data[pos])) + 139 | uint32(uint8(data[pos + 1])) * 140 | 2**8 + 141 | uint32(uint8(data[pos + 2])) * 142 | 2**16 + 143 | uint32(uint8(data[pos + 3])) * 144 | 2**24; 145 | } else if (bits == 64) { 146 | return 147 | uint64(uint8(data[pos])) + 148 | uint64(uint8(data[pos + 1])) * 149 | 2**8 + 150 | uint64(uint8(data[pos + 2])) * 151 | 2**16 + 152 | uint64(uint8(data[pos + 3])) * 153 | 2**24 + 154 | uint64(uint8(data[pos + 4])) * 155 | 2**32 + 156 | uint64(uint8(data[pos + 5])) * 157 | 2**40 + 158 | uint64(uint8(data[pos + 6])) * 159 | 2**48 + 160 | uint64(uint8(data[pos + 7])) * 161 | 2**56; 162 | } 163 | } 164 | 165 | // scan the full transaction bytes and return the first two output 166 | // values (in satoshis) and addresses (in binary) 167 | function getFirstTwoOutputs(bytes memory txBytes) 168 | public 169 | returns ( 170 | uint256, 171 | bytes20, 172 | uint256, 173 | bytes20 174 | ) 175 | { 176 | uint256 pos; 177 | uint256[] memory input_script_lens = new uint256[](2); 178 | uint256[] memory output_script_lens = new uint256[](2); 179 | uint256[] memory script_starts = new uint256[](2); 180 | uint256[] memory output_values = new uint256[](2); 181 | bytes20[] memory output_addresses = new bytes20[](2); 182 | 183 | pos = 4; // skip version 184 | 185 | (input_script_lens, pos) = scanInputs(txBytes, pos, 0); 186 | 187 | (output_values, script_starts, output_script_lens, pos) = scanOutputs( 188 | txBytes, 189 | pos, 190 | 2 191 | ); 192 | 193 | for (uint256 i = 0; i < 2; i++) { 194 | bytes20 pkhash = parseOutputScript( 195 | txBytes, 196 | script_starts[i], 197 | output_script_lens[i] 198 | ); 199 | output_addresses[i] = pkhash; 200 | } 201 | 202 | return ( 203 | output_values[0], 204 | output_addresses[0], 205 | output_values[1], 206 | output_addresses[1] 207 | ); 208 | } 209 | 210 | // Check whether `btcAddress` is in the transaction outputs *and* 211 | // whether *at least* `value` has been sent to it. 212 | function checkValueSent( 213 | bytes memory txBytes, 214 | bytes20 btcAddress, 215 | uint256 value 216 | ) public returns (bool) { 217 | uint256 pos = 4; // skip version 218 | (, pos) = scanInputs(txBytes, pos, 0); // find end of inputs 219 | 220 | // scan *all* the outputs and find where they are 221 | ( 222 | uint256[] memory output_values, 223 | uint256[] memory script_starts, 224 | uint256[] memory output_script_lens, 225 | 226 | ) = scanOutputs(txBytes, pos, 0); 227 | 228 | // look at each output and check whether it at least value to btcAddress 229 | for (uint256 i = 0; i < output_values.length; i++) { 230 | bytes20 pkhash = parseOutputScript( 231 | txBytes, 232 | script_starts[i], 233 | output_script_lens[i] 234 | ); 235 | if (pkhash == btcAddress && output_values[i] >= value) { 236 | return true; 237 | } 238 | } 239 | } 240 | 241 | // scan the inputs and find the script lengths. 242 | // return an array of script lengths and the end position 243 | // of the inputs. 244 | // takes a 'stop' argument which sets the maximum number of 245 | // outputs to scan through. stop=0 => scan all. 246 | function scanInputs( 247 | bytes memory txBytes, 248 | uint256 pos, 249 | uint256 stop 250 | ) public returns (uint256[] memory, uint256) { 251 | uint256 n_inputs; 252 | uint256 halt; 253 | uint256 script_len; 254 | 255 | (n_inputs, pos) = parseVarInt(txBytes, pos); 256 | 257 | if (stop == 0 || stop > n_inputs) { 258 | halt = n_inputs; 259 | } else { 260 | halt = stop; 261 | } 262 | 263 | uint256[] memory script_lens = new uint256[](halt); 264 | 265 | for (uint8 i = 0; i < halt; i++) { 266 | pos += 36; // skip outpoint 267 | (script_len, pos) = parseVarInt(txBytes, pos); 268 | script_lens[i] = script_len; 269 | pos += script_len + 4; // skip sig_script, seq 270 | } 271 | 272 | return (script_lens, pos); 273 | } 274 | 275 | // scan the outputs and find the values and script lengths. 276 | // return array of values, array of script lengths and the 277 | // end position of the outputs. 278 | // takes a 'stop' argument which sets the maximum number of 279 | // outputs to scan through. stop=0 => scan all. 280 | function scanOutputs( 281 | bytes memory txBytes, 282 | uint256 pos, 283 | uint256 stop 284 | ) 285 | public 286 | returns ( 287 | uint256[] memory, 288 | uint256[] memory, 289 | uint256[] memory, 290 | uint256 291 | ) 292 | { 293 | uint256 n_outputs; 294 | uint256 halt; 295 | uint256 script_len; 296 | 297 | (n_outputs, pos) = parseVarInt(txBytes, pos); 298 | 299 | if (stop == 0 || stop > n_outputs) { 300 | halt = n_outputs; 301 | } else { 302 | halt = stop; 303 | } 304 | 305 | uint256[] memory script_starts = new uint256[](halt); 306 | uint256[] memory script_lens = new uint256[](halt); 307 | uint256[] memory output_values = new uint256[](halt); 308 | 309 | for (uint256 i = 0; i < halt; i++) { 310 | output_values[i] = getBytesLE(txBytes, pos, 64); 311 | pos += 8; 312 | 313 | (script_len, pos) = parseVarInt(txBytes, pos); 314 | script_starts[i] = pos; 315 | script_lens[i] = script_len; 316 | pos += script_len; 317 | } 318 | 319 | return (output_values, script_starts, script_lens, pos); 320 | } 321 | 322 | // Slice 20 contiguous bytes from bytes `data`, starting at `start` 323 | function sliceBytes20(bytes memory data, uint256 start) 324 | public 325 | returns (bytes20) 326 | { 327 | uint160 slice = 0; 328 | for (uint160 i = 0; i < 20; i++) { 329 | slice += uint160(uint8(data[i + start])) << (8 * (19 - i)); 330 | } 331 | return bytes20(slice); 332 | } 333 | 334 | // returns true if the bytes located in txBytes by pos and 335 | // script_len represent a P2PKH script 336 | function isP2PKH( 337 | bytes memory txBytes, 338 | uint256 pos, 339 | uint256 script_len 340 | ) public returns (bool) { 341 | return 342 | (script_len == 25) && // 20 byte pubkeyhash + 5 bytes of script 343 | (txBytes[pos] == 0x76) && // OP_DUP 344 | (txBytes[pos + 1] == 0xa9) && // OP_HASH160 345 | (txBytes[pos + 2] == 0x14) && // bytes to push 346 | (txBytes[pos + 23] == 0x88) && // OP_EQUALVERIFY 347 | (txBytes[pos + 24] == 0xac); // OP_CHECKSIG 348 | } 349 | 350 | // returns true if the bytes located in txBytes by pos and 351 | // script_len represent a P2SH script 352 | function isP2SH( 353 | bytes memory txBytes, 354 | uint256 pos, 355 | uint256 script_len 356 | ) public returns (bool) { 357 | return 358 | (script_len == 23) && // 20 byte scripthash + 3 bytes of script 359 | (txBytes[pos + 0] == 0xa9) && // OP_HASH160 360 | (txBytes[pos + 1] == 0x14) && // bytes to push 361 | (txBytes[pos + 22] == 0x87); // OP_EQUAL 362 | } 363 | 364 | // Get the pubkeyhash / scripthash from an output script. Assumes 365 | // pay-to-pubkey-hash (P2PKH) or pay-to-script-hash (P2SH) outputs. 366 | // Returns the pubkeyhash/ scripthash, or zero if unknown output. 367 | function parseOutputScript( 368 | bytes memory txBytes, 369 | uint256 pos, 370 | uint256 script_len 371 | ) public returns (bytes20) { 372 | if (isP2PKH(txBytes, pos, script_len)) { 373 | return sliceBytes20(txBytes, pos + 3); 374 | } else if (isP2SH(txBytes, pos, script_len)) { 375 | return sliceBytes20(txBytes, pos + 2); 376 | } else { 377 | return bytes20(0); 378 | } 379 | } 380 | 381 | //NOTE: Only supports segwit txs 382 | //https://bitcoin.stackexchange.com/questions/79723/where-is-the-pubkey-for-segwit-inputs 383 | //if multisig, it will just grab the first pubkey 384 | function getPubKeyFromTx(bytes memory txBytes) 385 | public 386 | returns (bytes memory) 387 | { 388 | uint256 pos = 0; 389 | bytes memory pubkey; 390 | for (uint256 i = 0; i < txBytes.length; i++) { 391 | //byte with value 0x21 is used to show the start of the pubkey in the raw tx 392 | if (txBytes[i] == 0x21) { 393 | pos = i + 1; 394 | break; 395 | } 396 | } 397 | uint256 index = 0; 398 | for (uint256 i = pos; i < pos + 33; i++) { 399 | pubkey[index] = txBytes[i]; 400 | index++; 401 | } 402 | return pubkey; 403 | } 404 | 405 | function getSegtwitSignature(bytes memory txBytes) 406 | public 407 | returns (bytes memory) 408 | { 409 | uint256 pos = 0; 410 | bytes memory signature; 411 | for (uint256 i = 0; i < txBytes.length; i++) { 412 | //byte with value 0x47 is used to show the start of the signature in the raw tx 413 | if (txBytes[i] == 0x47) { 414 | pos = i + 1; 415 | break; 416 | } 417 | } 418 | uint256 index = 0; 419 | for (uint256 i = pos; i < pos + 71; i++) { 420 | signature[index] = txBytes[i]; 421 | index++; 422 | } 423 | return signature; 424 | } 425 | } 426 | -------------------------------------------------------------------------------- /contracts/summa-tx/BytesLib.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.10; 2 | 3 | /* 4 | 5 | https://github.com/GNSPS/solidity-bytes-utils/ 6 | 7 | This is free and unencumbered software released into the public domain. 8 | 9 | Anyone is free to copy, modify, publish, use, compile, sell, or 10 | distribute this software, either in source code form or as a compiled 11 | binary, for any purpose, commercial or non-commercial, and by any 12 | means. 13 | 14 | In jurisdictions that recognize copyright laws, the author or authors 15 | of this software dedicate any and all copyright interest in the 16 | software to the public domain. We make this dedication for the benefit 17 | of the public at large and to the detriment of our heirs and 18 | successors. We intend this dedication to be an overt act of 19 | relinquishment in perpetuity of all present and future rights to this 20 | software under copyright law. 21 | 22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 23 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 24 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 25 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 26 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 27 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 28 | OTHER DEALINGS IN THE SOFTWARE. 29 | 30 | For more information, please refer to 31 | */ 32 | 33 | /** @title BytesLib **/ 34 | /** @author https://github.com/GNSPS **/ 35 | 36 | library BytesLib { 37 | function concat(bytes memory _preBytes, bytes memory _postBytes) 38 | internal 39 | pure 40 | returns (bytes memory) 41 | { 42 | bytes memory tempBytes; 43 | 44 | assembly { 45 | // Get a location of some free memory and store it in tempBytes as 46 | // Solidity does for memory variables. 47 | tempBytes := mload(0x40) 48 | 49 | // Store the length of the first bytes array at the beginning of 50 | // the memory for tempBytes. 51 | let length := mload(_preBytes) 52 | mstore(tempBytes, length) 53 | 54 | // Maintain a memory counter for the current write location in the 55 | // temp bytes array by adding the 32 bytes for the array length to 56 | // the starting location. 57 | let mc := add(tempBytes, 0x20) 58 | // Stop copying when the memory counter reaches the length of the 59 | // first bytes array. 60 | let end := add(mc, length) 61 | 62 | for { 63 | // Initialize a copy counter to the start of the _preBytes data, 64 | // 32 bytes into its memory. 65 | let cc := add(_preBytes, 0x20) 66 | } lt(mc, end) { 67 | // Increase both counters by 32 bytes each iteration. 68 | mc := add(mc, 0x20) 69 | cc := add(cc, 0x20) 70 | } { 71 | // Write the _preBytes data into the tempBytes memory 32 bytes 72 | // at a time. 73 | mstore(mc, mload(cc)) 74 | } 75 | 76 | // Add the length of _postBytes to the current length of tempBytes 77 | // and store it as the new length in the first 32 bytes of the 78 | // tempBytes memory. 79 | length := mload(_postBytes) 80 | mstore(tempBytes, add(length, mload(tempBytes))) 81 | 82 | // Move the memory counter back from a multiple of 0x20 to the 83 | // actual end of the _preBytes data. 84 | mc := end 85 | // Stop copying when the memory counter reaches the new combined 86 | // length of the arrays. 87 | end := add(mc, length) 88 | 89 | for { 90 | let cc := add(_postBytes, 0x20) 91 | } lt(mc, end) { 92 | mc := add(mc, 0x20) 93 | cc := add(cc, 0x20) 94 | } { 95 | mstore(mc, mload(cc)) 96 | } 97 | 98 | // Update the free-memory pointer by padding our last write location 99 | // to 32 bytes: add 31 bytes to the end of tempBytes to move to the 100 | // next 32 byte block, then round down to the nearest multiple of 101 | // 32. If the sum of the length of the two arrays is zero then add 102 | // one before rounding down to leave a blank 32 bytes (the length block with 0). 103 | mstore( 104 | 0x40, 105 | and( 106 | add(add(end, iszero(add(length, mload(_preBytes)))), 31), 107 | not(31) // Round down to the nearest 32 bytes. 108 | ) 109 | ) 110 | } 111 | 112 | return tempBytes; 113 | } 114 | 115 | function concatStorage(bytes storage _preBytes, bytes memory _postBytes) 116 | internal 117 | { 118 | assembly { 119 | // Read the first 32 bytes of _preBytes storage, which is the length 120 | // of the array. (We don't need to use the offset into the slot 121 | // because arrays use the entire slot.) 122 | let fslot := sload(_preBytes_slot) 123 | // Arrays of 31 bytes or less have an even value in their slot, 124 | // while longer arrays have an odd value. The actual length is 125 | // the slot divided by two for odd values, and the lowest order 126 | // byte divided by two for even values. 127 | // If the slot is even, bitwise and the slot with 255 and divide by 128 | // two to get the length. If the slot is odd, bitwise and the slot 129 | // with -1 and divide by two. 130 | let slength := div( 131 | and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 132 | 2 133 | ) 134 | let mlength := mload(_postBytes) 135 | let newlength := add(slength, mlength) 136 | // slength can contain both the length and contents of the array 137 | // if length < 32 bytes so let's prepare for that 138 | // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage 139 | switch add(lt(slength, 32), lt(newlength, 32)) 140 | case 2 { 141 | // Since the new array still fits in the slot, we just need to 142 | // update the contents of the slot. 143 | // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length 144 | sstore( 145 | _preBytes_slot, 146 | // all the modifications to the slot are inside this 147 | // next block 148 | add( 149 | // we can just add to the slot contents because the 150 | // bytes we want to change are the LSBs 151 | fslot, 152 | add( 153 | mul( 154 | div( 155 | // load the bytes from memory 156 | mload(add(_postBytes, 0x20)), 157 | // zero all bytes to the right 158 | exp(0x100, sub(32, mlength)) 159 | ), 160 | // and now shift left the number of bytes to 161 | // leave space for the length in the slot 162 | exp(0x100, sub(32, newlength)) 163 | ), 164 | // increase length by the double of the memory 165 | // bytes length 166 | mul(mlength, 2) 167 | ) 168 | ) 169 | ) 170 | } 171 | case 1 { 172 | // The stored value fits in the slot, but the combined value 173 | // will exceed it. 174 | // get the keccak hash to get the contents of the array 175 | mstore(0x0, _preBytes_slot) 176 | let sc := add(keccak256(0x0, 0x20), div(slength, 32)) 177 | 178 | // save new length 179 | sstore(_preBytes_slot, add(mul(newlength, 2), 1)) 180 | 181 | // The contents of the _postBytes array start 32 bytes into 182 | // the structure. Our first read should obtain the `submod` 183 | // bytes that can fit into the unused space in the last word 184 | // of the stored array. To get this, we read 32 bytes starting 185 | // from `submod`, so the data we read overlaps with the array 186 | // contents by `submod` bytes. Masking the lowest-order 187 | // `submod` bytes allows us to add that value directly to the 188 | // stored value. 189 | 190 | let submod := sub(32, slength) 191 | let mc := add(_postBytes, submod) 192 | let end := add(_postBytes, mlength) 193 | let mask := sub(exp(0x100, submod), 1) 194 | 195 | sstore( 196 | sc, 197 | add( 198 | and( 199 | fslot, 200 | 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00 201 | ), 202 | and(mload(mc), mask) 203 | ) 204 | ) 205 | 206 | for { 207 | mc := add(mc, 0x20) 208 | sc := add(sc, 1) 209 | } lt(mc, end) { 210 | sc := add(sc, 1) 211 | mc := add(mc, 0x20) 212 | } { 213 | sstore(sc, mload(mc)) 214 | } 215 | 216 | mask := exp(0x100, sub(mc, end)) 217 | 218 | sstore(sc, mul(div(mload(mc), mask), mask)) 219 | } 220 | default { 221 | // get the keccak hash to get the contents of the array 222 | mstore(0x0, _preBytes_slot) 223 | // Start copying to the last used word of the stored array. 224 | let sc := add(keccak256(0x0, 0x20), div(slength, 32)) 225 | 226 | // save new length 227 | sstore(_preBytes_slot, add(mul(newlength, 2), 1)) 228 | 229 | // Copy over the first `submod` bytes of the new data as in 230 | // case 1 above. 231 | let slengthmod := mod(slength, 32) 232 | let mlengthmod := mod(mlength, 32) 233 | let submod := sub(32, slengthmod) 234 | let mc := add(_postBytes, submod) 235 | let end := add(_postBytes, mlength) 236 | let mask := sub(exp(0x100, submod), 1) 237 | 238 | sstore(sc, add(sload(sc), and(mload(mc), mask))) 239 | 240 | for { 241 | sc := add(sc, 1) 242 | mc := add(mc, 0x20) 243 | } lt(mc, end) { 244 | sc := add(sc, 1) 245 | mc := add(mc, 0x20) 246 | } { 247 | sstore(sc, mload(mc)) 248 | } 249 | 250 | mask := exp(0x100, sub(mc, end)) 251 | 252 | sstore(sc, mul(div(mload(mc), mask), mask)) 253 | } 254 | } 255 | } 256 | 257 | function slice( 258 | bytes memory _bytes, 259 | uint256 _start, 260 | uint256 _length 261 | ) internal pure returns (bytes memory res) { 262 | require(_bytes.length >= (_start + _length), "Slice out of bounds"); 263 | 264 | assembly { 265 | // Alloc bytes array with additional 32 bytes afterspace and assign it's size 266 | res := mload(0x40) 267 | mstore(0x40, add(add(res, 64), _length)) 268 | mstore(res, _length) 269 | 270 | // Compute distance between source and destination pointers 271 | let diff := sub(res, add(_bytes, _start)) 272 | 273 | for { 274 | let src := add(add(_bytes, 32), _start) 275 | let end := add(src, _length) 276 | } lt(src, end) { 277 | src := add(src, 32) 278 | } { 279 | mstore(add(src, diff), mload(src)) 280 | } 281 | } 282 | } 283 | 284 | function toAddress(bytes memory _bytes, uint256 _start) 285 | internal 286 | pure 287 | returns (address) 288 | { 289 | require( 290 | _bytes.length >= (_start + 20), 291 | "Address conversion out of bounds." 292 | ); 293 | address tempAddress; 294 | 295 | assembly { 296 | tempAddress := div( 297 | mload(add(add(_bytes, 0x20), _start)), 298 | 0x1000000000000000000000000 299 | ) 300 | } 301 | 302 | return tempAddress; 303 | } 304 | 305 | function toUint(bytes memory _bytes, uint256 _start) 306 | internal 307 | pure 308 | returns (uint256) 309 | { 310 | require( 311 | _bytes.length >= (_start + 32), 312 | "Uint conversion out of bounds." 313 | ); 314 | uint256 tempUint; 315 | 316 | assembly { 317 | tempUint := mload(add(add(_bytes, 0x20), _start)) 318 | } 319 | 320 | return tempUint; 321 | } 322 | 323 | function equal(bytes memory _preBytes, bytes memory _postBytes) 324 | internal 325 | pure 326 | returns (bool) 327 | { 328 | bool success = true; 329 | 330 | assembly { 331 | let length := mload(_preBytes) 332 | 333 | // if lengths don't match the arrays are not equal 334 | switch eq(length, mload(_postBytes)) 335 | case 1 { 336 | // cb is a circuit breaker in the for loop since there's 337 | // no said feature for inline assembly loops 338 | // cb = 1 - don't breaker 339 | // cb = 0 - break 340 | let cb := 1 341 | 342 | let mc := add(_preBytes, 0x20) 343 | let end := add(mc, length) 344 | 345 | for { 346 | let cc := add(_postBytes, 0x20) 347 | // the next line is the loop condition: 348 | // while(uint(mc < end) + cb == 2) 349 | } eq(add(lt(mc, end), cb), 2) { 350 | mc := add(mc, 0x20) 351 | cc := add(cc, 0x20) 352 | } { 353 | // if any of these checks fails then arrays are not equal 354 | if iszero(eq(mload(mc), mload(cc))) { 355 | // unsuccess: 356 | success := 0 357 | cb := 0 358 | } 359 | } 360 | } 361 | default { 362 | // unsuccess: 363 | success := 0 364 | } 365 | } 366 | 367 | return success; 368 | } 369 | 370 | function equalStorage(bytes storage _preBytes, bytes memory _postBytes) 371 | internal 372 | view 373 | returns (bool) 374 | { 375 | bool success = true; 376 | 377 | assembly { 378 | // we know _preBytes_offset is 0 379 | let fslot := sload(_preBytes_slot) 380 | // Decode the length of the stored array like in concatStorage(). 381 | let slength := div( 382 | and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 383 | 2 384 | ) 385 | let mlength := mload(_postBytes) 386 | 387 | // if lengths don't match the arrays are not equal 388 | switch eq(slength, mlength) 389 | case 1 { 390 | // slength can contain both the length and contents of the array 391 | // if length < 32 bytes so let's prepare for that 392 | // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage 393 | if iszero(iszero(slength)) { 394 | switch lt(slength, 32) 395 | case 1 { 396 | // blank the last byte which is the length 397 | fslot := mul(div(fslot, 0x100), 0x100) 398 | 399 | if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) { 400 | // unsuccess: 401 | success := 0 402 | } 403 | } 404 | default { 405 | // cb is a circuit breaker in the for loop since there's 406 | // no said feature for inline assembly loops 407 | // cb = 1 - don't breaker 408 | // cb = 0 - break 409 | let cb := 1 410 | 411 | // get the keccak hash to get the contents of the array 412 | mstore(0x0, _preBytes_slot) 413 | let sc := keccak256(0x0, 0x20) 414 | 415 | let mc := add(_postBytes, 0x20) 416 | let end := add(mc, mlength) 417 | 418 | // the next line is the loop condition: 419 | // while(uint(mc < end) + cb == 2) 420 | for { 421 | 422 | } eq(add(lt(mc, end), cb), 2) { 423 | sc := add(sc, 1) 424 | mc := add(mc, 0x20) 425 | } { 426 | if iszero(eq(sload(sc), mload(mc))) { 427 | // unsuccess: 428 | success := 0 429 | cb := 0 430 | } 431 | } 432 | } 433 | } 434 | } 435 | default { 436 | // unsuccess: 437 | success := 0 438 | } 439 | } 440 | 441 | return success; 442 | } 443 | 444 | function toBytes32(bytes memory _source) 445 | internal 446 | pure 447 | returns (bytes32 result) 448 | { 449 | bytes memory tempEmptyStringTest = bytes(_source); 450 | if (tempEmptyStringTest.length == 0) { 451 | return 0x0; 452 | } 453 | 454 | assembly { 455 | result := mload(add(_source, 32)) 456 | } 457 | } 458 | 459 | function keccak256Slice( 460 | bytes memory _bytes, 461 | uint256 _start, 462 | uint256 _length 463 | ) internal pure returns (bytes32 result) { 464 | require(_bytes.length >= (_start + _length), "Slice out of bounds"); 465 | 466 | assembly { 467 | result := keccak256(add(add(_bytes, 32), _start), _length) 468 | } 469 | } 470 | } 471 | -------------------------------------------------------------------------------- /contracts/summa-tx/SafeMath.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.10; 2 | 3 | /* 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2016 Smart Contract Solutions, Inc. 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining 9 | a copy of this software and associated documentation files (the 10 | "Software"), to deal in the Software without restriction, including 11 | without limitation the rights to use, copy, modify, merge, publish, 12 | distribute, sublicense, and/or sell copies of the Software, and to 13 | permit persons to whom the Software is furnished to do so, subject to 14 | the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included 17 | in all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 20 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 22 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 23 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 24 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 25 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | */ 27 | 28 | /** 29 | * @title SafeMath 30 | * @dev Math operations with safety checks that throw on error 31 | */ 32 | library SafeMath { 33 | /** 34 | * @dev Multiplies two numbers, throws on overflow. 35 | */ 36 | function mul(uint256 _a, uint256 _b) internal pure returns (uint256 c) { 37 | // Gas optimization: this is cheaper than asserting 'a' not being zero, but the 38 | // benefit is lost if 'b' is also tested. 39 | // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 40 | if (_a == 0) { 41 | return 0; 42 | } 43 | 44 | c = _a * _b; 45 | require(c / _a == _b, "Overflow during multiplication."); 46 | return c; 47 | } 48 | 49 | /** 50 | * @dev Integer division of two numbers, truncating the quotient. 51 | */ 52 | function div(uint256 _a, uint256 _b) internal pure returns (uint256) { 53 | // assert(_b > 0); // Solidity automatically throws when dividing by 0 54 | // uint256 c = _a / _b; 55 | // assert(_a == _b * c + _a % _b); // There is no case in which this doesn't hold 56 | return _a / _b; 57 | } 58 | 59 | /** 60 | * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend). 61 | */ 62 | function sub(uint256 _a, uint256 _b) internal pure returns (uint256) { 63 | require(_b <= _a, "Underflow during subtraction."); 64 | return _a - _b; 65 | } 66 | 67 | /** 68 | * @dev Adds two numbers, throws on overflow. 69 | */ 70 | function add(uint256 _a, uint256 _b) internal pure returns (uint256 c) { 71 | c = _a + _b; 72 | require(c >= _a, "Overflow during addition."); 73 | return c; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /contracts/summa-tx/TypedMemView.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | pragma solidity >=0.5.10; 3 | 4 | import {SafeMath} from "./SafeMath.sol"; 5 | 6 | library TypedMemView { 7 | using SafeMath for uint256; 8 | 9 | // Why does this exist? 10 | // the solidity `bytes memory` type has a few weaknesses. 11 | // 1. You can't index ranges effectively 12 | // 2. You can't slice without copying 13 | // 3. The underlying data may represent any type 14 | // 4. Solidity never deallocates memory, and memory costs grow 15 | // superlinearly 16 | 17 | // By using a memory view instead of a `bytes memory` we get the following 18 | // advantages: 19 | // 1. Slices are done on the stack, by manipulating the pointer 20 | // 2. We can index arbitrary ranges and quickly convert them to stack types 21 | // 3. We can insert type info into the pointer, and typecheck at runtime 22 | 23 | // This makes `TypedMemView` a useful tool for efficient zero-copy 24 | // algorithms. 25 | 26 | // Why bytes29? 27 | // We want to avoid confusion between views, digests, and other common 28 | // types so we chose a large and uncommonly used odd number of bytes 29 | // 30 | // Note that while bytes are left-aligned in a word, integers and addresses 31 | // are right-aligned. This means when working in assembly we have to 32 | // account for the 3 unused bytes on the righthand side 33 | // 34 | // First 5 bytes are a type flag. 35 | // - ff_ffff_fffe is reserved for unknown type. 36 | // - ff_ffff_ffff is reserved for invalid types/errors. 37 | // next 12 are memory address 38 | // next 12 are len 39 | // bottom 3 bytes are empty 40 | 41 | // Assumptions: 42 | // - non-modification of memory. 43 | // - No Solidity updates 44 | // - - wrt free mem point 45 | // - - wrt bytes representation in memory 46 | // - - wrt memory addressing in general 47 | 48 | // Usage: 49 | // - create type constants 50 | // - use `assertType` for runtime type assertions 51 | // - - unfortunately we can't do this at compile time yet :( 52 | // - recommended: implement modifiers that perform type checking 53 | // - - e.g. 54 | // - - `uint40 constant MY_TYPE = 3;` 55 | // - - ` modifer onlyMyType(bytes29 myView) { myView.assertType(MY_TYPE); }` 56 | // - instantiate a typed view from a bytearray using `ref` 57 | // - use `index` to inspect the contents of the view 58 | // - use `slice` to create smaller views into the same memory 59 | // - - `slice` can increase the offset 60 | // - - `slice can decrease the length` 61 | // - - must specify the output type of `slice` 62 | // - - `slice` will return a null view if you try to overrun 63 | // - - make sure to explicitly check for this with `notNull` or `assertType` 64 | // - use `equal` for typed comparisons. 65 | 66 | // The null view 67 | bytes29 public constant NULL = 68 | hex"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"; 69 | uint256 constant LOW_12_MASK = 0xffffffffffffffffffffffff; 70 | uint8 constant TWELVE_BYTES = 96; 71 | 72 | /** 73 | * @notice Returns the encoded hex character that represents the lower 4 bits of the argument. 74 | * @param _b The byte 75 | * @return char - The encoded hex character 76 | */ 77 | function nibbleHex(uint8 _b) internal pure returns (uint8 char) { 78 | // This can probably be done more efficiently, but it's only in error 79 | // paths, so we don't really care :) 80 | uint8 _nibble = _b | 0xf0; // set top 4, keep bottom 4 81 | if (_nibble == 0xf0) { 82 | return 0x30; 83 | } // 0 84 | if (_nibble == 0xf1) { 85 | return 0x31; 86 | } // 1 87 | if (_nibble == 0xf2) { 88 | return 0x32; 89 | } // 2 90 | if (_nibble == 0xf3) { 91 | return 0x33; 92 | } // 3 93 | if (_nibble == 0xf4) { 94 | return 0x34; 95 | } // 4 96 | if (_nibble == 0xf5) { 97 | return 0x35; 98 | } // 5 99 | if (_nibble == 0xf6) { 100 | return 0x36; 101 | } // 6 102 | if (_nibble == 0xf7) { 103 | return 0x37; 104 | } // 7 105 | if (_nibble == 0xf8) { 106 | return 0x38; 107 | } // 8 108 | if (_nibble == 0xf9) { 109 | return 0x39; 110 | } // 9 111 | if (_nibble == 0xfa) { 112 | return 0x61; 113 | } // a 114 | if (_nibble == 0xfb) { 115 | return 0x62; 116 | } // b 117 | if (_nibble == 0xfc) { 118 | return 0x63; 119 | } // c 120 | if (_nibble == 0xfd) { 121 | return 0x64; 122 | } // d 123 | if (_nibble == 0xfe) { 124 | return 0x65; 125 | } // e 126 | if (_nibble == 0xff) { 127 | return 0x66; 128 | } // f 129 | } 130 | 131 | /** 132 | * @notice Returns a uint16 containing the hex-encoded byte. 133 | * @param _b The byte 134 | * @return encoded - The hex-encoded byte 135 | */ 136 | function byteHex(uint8 _b) internal pure returns (uint16 encoded) { 137 | encoded |= nibbleHex(_b >> 4); // top 4 bits 138 | encoded <<= 8; 139 | encoded |= nibbleHex(_b); // lower 4 bits 140 | } 141 | 142 | /** 143 | * @notice Encodes the uint256 to hex. `first` contains the encoded top 16 bytes. 144 | * `second` contains the encoded lower 16 bytes. 145 | * 146 | * @param _b The 32 bytes as uint256 147 | * @return first - The top 16 bytes 148 | * @return second - The bottom 16 bytes 149 | */ 150 | function encodeHex(uint256 _b) 151 | internal 152 | pure 153 | returns (uint256 first, uint256 second) 154 | { 155 | for (uint8 i = 31; i > 15; i -= 1) { 156 | uint8 _byte = uint8(_b >> (i * 8)); 157 | first |= byteHex(_byte); 158 | if (i != 16) { 159 | first <<= 16; 160 | } 161 | } 162 | 163 | // abusing underflow here =_= 164 | for (uint8 i = 15; i < 255; i -= 1) { 165 | uint8 _byte = uint8(_b >> (i * 8)); 166 | second |= byteHex(_byte); 167 | if (i != 0) { 168 | second <<= 16; 169 | } 170 | } 171 | } 172 | 173 | /** 174 | * @notice Changes the endianness of a uint256. 175 | * @dev https://graphics.stanford.edu/~seander/bithacks.html#ReverseParallel 176 | * @param _b The unsigned integer to reverse 177 | * @return v - The reversed value 178 | */ 179 | function reverseUint256(uint256 _b) internal pure returns (uint256 v) { 180 | v = _b; 181 | 182 | // swap bytes 183 | v = 184 | ((v >> 8) & 185 | 0x00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF) | 186 | ((v & 187 | 0x00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF) << 188 | 8); 189 | // swap 2-byte long pairs 190 | v = 191 | ((v >> 16) & 192 | 0x0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF) | 193 | ((v & 194 | 0x0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF) << 195 | 16); 196 | // swap 4-byte long pairs 197 | v = 198 | ((v >> 32) & 199 | 0x00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF) | 200 | ((v & 201 | 0x00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF) << 202 | 32); 203 | // swap 8-byte long pairs 204 | v = 205 | ((v >> 64) & 206 | 0x0000000000000000FFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF) | 207 | ((v & 208 | 0x0000000000000000FFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF) << 209 | 64); 210 | // swap 16-byte long pairs 211 | v = (v >> 128) | (v << 128); 212 | } 213 | 214 | /** 215 | * @notice Create a mask with the highest `_len` bits set. 216 | * @param _len The length 217 | * @return mask - The mask 218 | */ 219 | function leftMask(uint8 _len) private pure returns (uint256 mask) { 220 | // ugly. redo without assembly? 221 | assembly { 222 | // solium-disable-previous-line security/no-inline-assembly 223 | mask := sar( 224 | sub(_len, 1), 225 | 0x8000000000000000000000000000000000000000000000000000000000000000 226 | ) 227 | } 228 | } 229 | 230 | /** 231 | * @notice Return the null view. 232 | * @return bytes29 - The null view 233 | */ 234 | function nullView() internal pure returns (bytes29) { 235 | return NULL; 236 | } 237 | 238 | /** 239 | * @notice Check if the view is null. 240 | * @return bool - True if the view is null 241 | */ 242 | function isNull(bytes29 memView) internal pure returns (bool) { 243 | return memView == NULL; 244 | } 245 | 246 | /** 247 | * @notice Check if the view is not null. 248 | * @return bool - True if the view is not null 249 | */ 250 | function notNull(bytes29 memView) internal pure returns (bool) { 251 | return !isNull(memView); 252 | } 253 | 254 | /** 255 | * @notice Check if the view is of a valid type and points to a valid location 256 | * in memory. 257 | * @dev We perform this check by examining solidity's unallocated memory 258 | * pointer and ensuring that the view's upper bound is less than that. 259 | * @param memView The view 260 | * @return ret - True if the view is valid 261 | */ 262 | function isValid(bytes29 memView) internal pure returns (bool ret) { 263 | if (typeOf(memView) == 0xffffffffff) { 264 | return false; 265 | } 266 | uint256 _end = end(memView); 267 | assembly { 268 | // solium-disable-previous-line security/no-inline-assembly 269 | ret := not(gt(_end, mload(0x40))) 270 | } 271 | } 272 | 273 | /** 274 | * @notice Require that a typed memory view be valid. 275 | * @dev Returns the view for easy chaining. 276 | * @param memView The view 277 | * @return bytes29 - The validated view 278 | */ 279 | function assertValid(bytes29 memView) internal pure returns (bytes29) { 280 | require(isValid(memView), "Validity assertion failed"); 281 | return memView; 282 | } 283 | 284 | /** 285 | * @notice Return true if the memview is of the expected type. Otherwise false. 286 | * @param memView The view 287 | * @param _expected The expected type 288 | * @return bool - True if the memview is of the expected type 289 | */ 290 | function isType(bytes29 memView, uint40 _expected) 291 | internal 292 | pure 293 | returns (bool) 294 | { 295 | return typeOf(memView) == _expected; 296 | } 297 | 298 | /** 299 | * @notice Require that a typed memory view has a specific type. 300 | * @dev Returns the view for easy chaining. 301 | * @param memView The view 302 | * @param _expected The expected type 303 | * @return bytes29 - The view with validated type 304 | */ 305 | function assertType(bytes29 memView, uint40 _expected) 306 | internal 307 | pure 308 | returns (bytes29) 309 | { 310 | if (!isType(memView, _expected)) { 311 | (, uint256 g) = encodeHex(uint256(typeOf(memView))); 312 | (, uint256 e) = encodeHex(uint256(_expected)); 313 | string memory err = string( 314 | abi.encodePacked( 315 | "Type assertion failed. Got 0x", 316 | uint80(g), 317 | ". Expected 0x", 318 | uint80(e) 319 | ) 320 | ); 321 | revert(err); 322 | } 323 | return memView; 324 | } 325 | 326 | /** 327 | * @notice Return an identical view with a different type. 328 | * @param memView The view 329 | * @param _newType The new type 330 | * @return newView - The new view with the specified type 331 | */ 332 | function castTo(bytes29 memView, uint40 _newType) 333 | internal 334 | pure 335 | returns (bytes29 newView) 336 | { 337 | // then | in the new type 338 | assembly { 339 | // solium-disable-previous-line security/no-inline-assembly 340 | // shift off the top 5 bytes 341 | newView := or(newView, shr(40, shl(40, memView))) 342 | newView := or(newView, shl(216, _newType)) 343 | } 344 | } 345 | 346 | /** 347 | * @notice Unsafe raw pointer construction. This should generally not be called 348 | * directly. Prefer `ref` wherever possible. 349 | * @dev Unsafe raw pointer construction. This should generally not be called 350 | * directly. Prefer `ref` wherever possible. 351 | * @param _type The type 352 | * @param _loc The memory address 353 | * @param _len The length 354 | * @return newView - The new view with the specified type, location and length 355 | */ 356 | function unsafeBuildUnchecked( 357 | uint256 _type, 358 | uint256 _loc, 359 | uint256 _len 360 | ) private pure returns (bytes29 newView) { 361 | assembly { 362 | // solium-disable-previous-line security/no-inline-assembly 363 | newView := shl(96, or(newView, _type)) // insert type 364 | newView := shl(96, or(newView, _loc)) // insert loc 365 | newView := shl(24, or(newView, _len)) // empty bottom 3 bytes 366 | } 367 | } 368 | 369 | /** 370 | * @notice Instantiate a new memory view. This should generally not be called 371 | * directly. Prefer `ref` wherever possible. 372 | * @dev Instantiate a new memory view. This should generally not be called 373 | * directly. Prefer `ref` wherever possible. 374 | * @param _type The type 375 | * @param _loc The memory address 376 | * @param _len The length 377 | * @return newView - The new view with the specified type, location and length 378 | */ 379 | function build( 380 | uint256 _type, 381 | uint256 _loc, 382 | uint256 _len 383 | ) internal pure returns (bytes29 newView) { 384 | uint256 _end = _loc.add(_len); 385 | assembly { 386 | // solium-disable-previous-line security/no-inline-assembly 387 | if gt(_end, mload(0x40)) { 388 | _end := 0 389 | } 390 | } 391 | if (_end == 0) { 392 | return NULL; 393 | } 394 | newView = unsafeBuildUnchecked(_type, _loc, _len); 395 | } 396 | 397 | /** 398 | * @notice Instantiate a memory view from a byte array. 399 | * @dev Note that due to Solidity memory representation, it is not possible to 400 | * implement a deref, as the `bytes` type stores its len in memory. 401 | * @param arr The byte array 402 | * @param newType The type 403 | * @return bytes29 - The memory view 404 | */ 405 | function ref(bytes memory arr, uint40 newType) 406 | internal 407 | pure 408 | returns (bytes29) 409 | { 410 | uint256 _len = arr.length; 411 | 412 | uint256 _loc; 413 | assembly { 414 | // solium-disable-previous-line security/no-inline-assembly 415 | _loc := add(arr, 0x20) // our view is of the data, not the struct 416 | } 417 | 418 | return build(newType, _loc, _len); 419 | } 420 | 421 | /** 422 | * @notice Return the associated type information. 423 | * @param memView The memory view 424 | * @return _type - The type associated with the view 425 | */ 426 | function typeOf(bytes29 memView) internal pure returns (uint40 _type) { 427 | assembly { 428 | // solium-disable-previous-line security/no-inline-assembly 429 | // 216 == 256 - 40 430 | _type := shr(216, memView) // shift out lower 24 bytes 431 | } 432 | } 433 | 434 | /** 435 | * @notice Optimized type comparison. Checks that the 5-byte type flag is equal. 436 | * @param left The first view 437 | * @param right The second view 438 | * @return bool - True if the 5-byte type flag is equal 439 | */ 440 | function sameType(bytes29 left, bytes29 right) 441 | internal 442 | pure 443 | returns (bool) 444 | { 445 | return (left ^ right) >> (2 * TWELVE_BYTES) == 0; 446 | } 447 | 448 | /** 449 | * @notice Return the memory address of the underlying bytes. 450 | * @param memView The view 451 | * @return _loc - The memory address 452 | */ 453 | function loc(bytes29 memView) internal pure returns (uint96 _loc) { 454 | uint256 _mask = LOW_12_MASK; // assembly can't use globals 455 | assembly { 456 | // solium-disable-previous-line security/no-inline-assembly 457 | // 120 bits = 12 bytes (the encoded loc) + 3 bytes (empty low space) 458 | _loc := and(shr(120, memView), _mask) 459 | } 460 | } 461 | 462 | /** 463 | * @notice The number of memory words this memory view occupies, rounded up. 464 | * @param memView The view 465 | * @return uint256 - The number of memory words 466 | */ 467 | function words(bytes29 memView) internal pure returns (uint256) { 468 | return uint256(len(memView)).add(32) / 32; 469 | } 470 | 471 | /** 472 | * @notice The in-memory footprint of a fresh copy of the view. 473 | * @param memView The view 474 | * @return uint256 - The in-memory footprint of a fresh copy of the view. 475 | */ 476 | function footprint(bytes29 memView) internal pure returns (uint256) { 477 | return words(memView) * 32; 478 | } 479 | 480 | /** 481 | * @notice The number of bytes of the view. 482 | * @param memView The view 483 | * @return _len - The length of the view 484 | */ 485 | function len(bytes29 memView) internal pure returns (uint96 _len) { 486 | uint256 _mask = LOW_12_MASK; // assembly can't use globals 487 | assembly { 488 | // solium-disable-previous-line security/no-inline-assembly 489 | _len := and(shr(24, memView), _mask) 490 | } 491 | } 492 | 493 | /** 494 | * @notice Returns the endpoint of `memView`. 495 | * @param memView The view 496 | * @return uint256 - The endpoint of `memView` 497 | */ 498 | function end(bytes29 memView) internal pure returns (uint256) { 499 | return loc(memView) + len(memView); 500 | } 501 | 502 | /** 503 | * @notice Safe slicing without memory modification. 504 | * @param memView The view 505 | * @param _index The start index 506 | * @param _len The length 507 | * @param newType The new type 508 | * @return bytes29 - The new view 509 | */ 510 | function slice( 511 | bytes29 memView, 512 | uint256 _index, 513 | uint256 _len, 514 | uint40 newType 515 | ) internal pure returns (bytes29) { 516 | uint256 _loc = loc(memView); 517 | 518 | // Ensure it doesn't overrun the view 519 | if (_loc.add(_index).add(_len) > end(memView)) { 520 | return NULL; 521 | } 522 | 523 | _loc = _loc.add(_index); 524 | return build(newType, _loc, _len); 525 | } 526 | 527 | /** 528 | * @notice Shortcut to `slice`. Gets a view representing the first `_len` bytes. 529 | * @param memView The view 530 | * @param _len The length 531 | * @param newType The new type 532 | * @return bytes29 - The new view 533 | */ 534 | function prefix( 535 | bytes29 memView, 536 | uint256 _len, 537 | uint40 newType 538 | ) internal pure returns (bytes29) { 539 | return slice(memView, 0, _len, newType); 540 | } 541 | 542 | /** 543 | * @notice Shortcut to `slice`. Gets a view representing the last `_len` byte. 544 | * @param memView The view 545 | * @param _len The length 546 | * @param newType The new type 547 | * @return bytes29 - The new view 548 | */ 549 | function postfix( 550 | bytes29 memView, 551 | uint256 _len, 552 | uint40 newType 553 | ) internal pure returns (bytes29) { 554 | return slice(memView, uint256(len(memView)).sub(_len), _len, newType); 555 | } 556 | 557 | /** 558 | * @notice Construct an error message for an indexing overrun. 559 | * @param _loc The memory address 560 | * @param _len The length 561 | * @param _index The index 562 | * @param _slice The slice where the overrun occurred 563 | * @return err - The err 564 | */ 565 | function indexErrOverrun( 566 | uint256 _loc, 567 | uint256 _len, 568 | uint256 _index, 569 | uint256 _slice 570 | ) internal pure returns (string memory err) { 571 | (, uint256 a) = encodeHex(_loc); 572 | (, uint256 b) = encodeHex(_len); 573 | (, uint256 c) = encodeHex(_index); 574 | (, uint256 d) = encodeHex(_slice); 575 | err = string( 576 | abi.encodePacked( 577 | "TypedMemView/index - Overran the view. Slice is at 0x", 578 | uint48(a), 579 | " with length 0x", 580 | uint48(b), 581 | ". Attempted to index at offset 0x", 582 | uint48(c), 583 | " with length 0x", 584 | uint48(d), 585 | "." 586 | ) 587 | ); 588 | } 589 | 590 | /** 591 | * @notice Load up to 32 bytes from the view onto the stack. 592 | * @dev Returns a bytes32 with only the `_bytes` highest bytes set. 593 | * This can be immediately cast to a smaller fixed-length byte array. 594 | * To automatically cast to an integer, use `indexUint`. 595 | * @param memView The view 596 | * @param _index The index 597 | * @param _bytes The bytes 598 | * @return result - The 32 byte result 599 | */ 600 | function index( 601 | bytes29 memView, 602 | uint256 _index, 603 | uint8 _bytes 604 | ) internal pure returns (bytes32 result) { 605 | if (_bytes == 0) { 606 | return bytes32(0); 607 | } 608 | if (_index.add(_bytes) > len(memView)) { 609 | revert( 610 | indexErrOverrun( 611 | loc(memView), 612 | len(memView), 613 | _index, 614 | uint256(_bytes) 615 | ) 616 | ); 617 | } 618 | require( 619 | _bytes <= 32, 620 | "TypedMemView/index - Attempted to index more than 32 bytes" 621 | ); 622 | 623 | uint8 bitLength = _bytes * 8; 624 | uint256 _loc = loc(memView); 625 | uint256 _mask = leftMask(bitLength); 626 | assembly { 627 | // solium-disable-previous-line security/no-inline-assembly 628 | result := and(mload(add(_loc, _index)), _mask) 629 | } 630 | } 631 | 632 | /** 633 | * @notice Parse an unsigned integer from the view at `_index`. 634 | * @dev Requires that the view have >= `_bytes` bytes following that index. 635 | * @param memView The view 636 | * @param _index The index 637 | * @param _bytes The bytes 638 | * @return result - The unsigned integer 639 | */ 640 | function indexUint( 641 | bytes29 memView, 642 | uint256 _index, 643 | uint8 _bytes 644 | ) internal pure returns (uint256 result) { 645 | return uint256(index(memView, _index, _bytes)) >> ((32 - _bytes) * 8); 646 | } 647 | 648 | /** 649 | * @notice Parse an unsigned integer from LE bytes. 650 | * @param memView The view 651 | * @param _index The index 652 | * @param _bytes The bytes 653 | * @return result - The unsigned integer 654 | */ 655 | function indexLEUint( 656 | bytes29 memView, 657 | uint256 _index, 658 | uint8 _bytes 659 | ) internal pure returns (uint256 result) { 660 | return reverseUint256(uint256(index(memView, _index, _bytes))); 661 | } 662 | 663 | /** 664 | * @notice Parse an address from the view at `_index`. Requires that the view have >= 20 bytes 665 | * following that index. 666 | * @param memView The view 667 | * @param _index The index 668 | * @return address - The address 669 | */ 670 | function indexAddress(bytes29 memView, uint256 _index) 671 | internal 672 | pure 673 | returns (address) 674 | { 675 | return address(uint160(indexUint(memView, _index, 20))); 676 | } 677 | 678 | /** 679 | * @notice Return the keccak256 hash of the underlying memory 680 | * @param memView The view 681 | * @return digest - The keccak256 hash of the underlying memory 682 | */ 683 | function keccak(bytes29 memView) internal pure returns (bytes32 digest) { 684 | uint256 _loc = loc(memView); 685 | uint256 _len = len(memView); 686 | assembly { 687 | // solium-disable-previous-line security/no-inline-assembly 688 | digest := keccak256(_loc, _len) 689 | } 690 | } 691 | 692 | /** 693 | * @notice Return the sha2 digest of the underlying memory. 694 | * @dev We explicitly deallocate memory afterwards. 695 | * @param memView The view 696 | * @return digest - The sha2 hash of the underlying memory 697 | */ 698 | function sha2(bytes29 memView) internal view returns (bytes32 digest) { 699 | uint256 _loc = loc(memView); 700 | uint256 _len = len(memView); 701 | assembly { 702 | // solium-disable-previous-line security/no-inline-assembly 703 | let ptr := mload(0x40) 704 | pop(staticcall(gas(), 2, _loc, _len, ptr, 0x20)) // sha2 #1 705 | digest := mload(ptr) 706 | } 707 | } 708 | 709 | /** 710 | * @notice Implements bitcoin's hash160 (rmd160(sha2())) 711 | * @param memView The pre-image 712 | * @return digest - the Digest 713 | */ 714 | function hash160(bytes29 memView) internal view returns (bytes20 digest) { 715 | uint256 _loc = loc(memView); 716 | uint256 _len = len(memView); 717 | assembly { 718 | // solium-disable-previous-line security/no-inline-assembly 719 | let ptr := mload(0x40) 720 | pop(staticcall(gas(), 2, _loc, _len, ptr, 0x20)) // sha2 721 | pop(staticcall(gas(), 3, ptr, 0x20, ptr, 0x20)) // rmd160 722 | digest := mload(add(ptr, 0xc)) // return value is 0-prefixed. 723 | } 724 | } 725 | 726 | /** 727 | * @notice Implements bitcoin's hash256 (double sha2) 728 | * @param memView A view of the preimage 729 | * @return digest - the Digest 730 | */ 731 | function hash256(bytes29 memView) internal view returns (bytes32 digest) { 732 | uint256 _loc = loc(memView); 733 | uint256 _len = len(memView); 734 | assembly { 735 | // solium-disable-previous-line security/no-inline-assembly 736 | let ptr := mload(0x40) 737 | pop(staticcall(gas(), 2, _loc, _len, ptr, 0x20)) // sha2 #1 738 | pop(staticcall(gas(), 2, ptr, 0x20, ptr, 0x20)) // sha2 #2 739 | digest := mload(ptr) 740 | } 741 | } 742 | 743 | /** 744 | * @notice Return true if the underlying memory is equal. Else false. 745 | * @param left The first view 746 | * @param right The second view 747 | * @return bool - True if the underlying memory is equal 748 | */ 749 | function untypedEqual(bytes29 left, bytes29 right) 750 | internal 751 | pure 752 | returns (bool) 753 | { 754 | return 755 | (loc(left) == loc(right) && len(left) == len(right)) || 756 | keccak(left) == keccak(right); 757 | } 758 | 759 | /** 760 | * @notice Return false if the underlying memory is equal. Else true. 761 | * @param left The first view 762 | * @param right The second view 763 | * @return bool - False if the underlying memory is equal 764 | */ 765 | function untypedNotEqual(bytes29 left, bytes29 right) 766 | internal 767 | pure 768 | returns (bool) 769 | { 770 | return !untypedEqual(left, right); 771 | } 772 | 773 | /** 774 | * @notice Compares type equality. 775 | * @dev Shortcuts if the pointers are identical, otherwise compares type and digest. 776 | * @param left The first view 777 | * @param right The second view 778 | * @return bool - True if the types are the same 779 | */ 780 | function equal(bytes29 left, bytes29 right) internal pure returns (bool) { 781 | return 782 | left == right || 783 | (typeOf(left) == typeOf(right) && keccak(left) == keccak(right)); 784 | } 785 | 786 | /** 787 | * @notice Compares type inequality. 788 | * @dev Shortcuts if the pointers are identical, otherwise compares type and digest. 789 | * @param left The first view 790 | * @param right The second view 791 | * @return bool - True if the types are not the same 792 | */ 793 | function notEqual(bytes29 left, bytes29 right) 794 | internal 795 | pure 796 | returns (bool) 797 | { 798 | return !equal(left, right); 799 | } 800 | 801 | /** 802 | * @notice Copy the view to a location, return an unsafe memory reference 803 | * @dev Super Dangerous direct memory access. 804 | * 805 | * This reference can be overwritten if anything else modifies memory (!!!). 806 | * As such it MUST be consumed IMMEDIATELY. 807 | * This function is private to prevent unsafe usage by callers. 808 | * @param memView The view 809 | * @param _newLoc The new location 810 | * @return written - the unsafe memory reference 811 | */ 812 | function unsafeCopyTo(bytes29 memView, uint256 _newLoc) 813 | private 814 | view 815 | returns (bytes29 written) 816 | { 817 | require(notNull(memView), "TypedMemView/copyTo - Null pointer deref"); 818 | require( 819 | isValid(memView), 820 | "TypedMemView/copyTo - Invalid pointer deref" 821 | ); 822 | uint256 _len = len(memView); 823 | uint256 _oldLoc = loc(memView); 824 | 825 | uint256 ptr; 826 | assembly { 827 | // solium-disable-previous-line security/no-inline-assembly 828 | ptr := mload(0x40) 829 | // revert if we're writing in occupied memory 830 | if gt(ptr, _newLoc) { 831 | revert(0x60, 0x20) // empty revert message 832 | } 833 | 834 | // use the identity precompile to copy 835 | // guaranteed not to fail, so pop the success 836 | pop(staticcall(gas(), 4, _oldLoc, _len, _newLoc, _len)) 837 | } 838 | 839 | written = unsafeBuildUnchecked(typeOf(memView), _newLoc, _len); 840 | } 841 | 842 | /** 843 | * @notice Copies the referenced memory to a new loc in memory, returning a `bytes` pointing to 844 | * the new memory 845 | * @dev Shortcuts if the pointers are identical, otherwise compares type and digest. 846 | * @param memView The view 847 | * @return ret - The view pointing to the new memory 848 | */ 849 | function clone(bytes29 memView) internal view returns (bytes memory ret) { 850 | uint256 ptr; 851 | uint256 _len = len(memView); 852 | assembly { 853 | // solium-disable-previous-line security/no-inline-assembly 854 | ptr := mload(0x40) // load unused memory pointer 855 | ret := ptr 856 | } 857 | unsafeCopyTo(memView, ptr + 0x20); 858 | assembly { 859 | // solium-disable-previous-line security/no-inline-assembly 860 | mstore(0x40, add(add(ptr, _len), 0x20)) // write new unused pointer 861 | mstore(ptr, _len) // write len of new array (in bytes) 862 | } 863 | } 864 | 865 | /** 866 | * @notice Join the views in memory, return an unsafe reference to the memory. 867 | * @dev Super Dangerous direct memory access. 868 | * 869 | * This reference can be overwritten if anything else modifies memory (!!!). 870 | * As such it MUST be consumed IMMEDIATELY. 871 | * This function is private to prevent unsafe usage by callers. 872 | * @param memViews The views 873 | * @return unsafeView - The conjoined view pointing to the new memory 874 | */ 875 | function unsafeJoin(bytes29[] memory memViews, uint256 _location) 876 | private 877 | view 878 | returns (bytes29 unsafeView) 879 | { 880 | assembly { 881 | // solium-disable-previous-line security/no-inline-assembly 882 | let ptr := mload(0x40) 883 | // revert if we're writing in occupied memory 884 | if gt(ptr, _location) { 885 | revert(0x60, 0x20) // empty revert message 886 | } 887 | } 888 | 889 | uint256 _offset = 0; 890 | for (uint256 i = 0; i < memViews.length; i++) { 891 | bytes29 memView = memViews[i]; 892 | unsafeCopyTo(memView, _location + _offset); 893 | _offset += len(memView); 894 | } 895 | unsafeView = unsafeBuildUnchecked(0, _location, _offset); 896 | } 897 | 898 | /** 899 | * @notice Produce the keccak256 digest of the concatenated contents of multiple views. 900 | * @param memViews The views 901 | * @return bytes32 - The keccak256 digest 902 | */ 903 | function joinKeccak(bytes29[] memory memViews) 904 | internal 905 | view 906 | returns (bytes32) 907 | { 908 | uint256 ptr; 909 | assembly { 910 | // solium-disable-previous-line security/no-inline-assembly 911 | ptr := mload(0x40) // load unused memory pointer 912 | } 913 | return keccak(unsafeJoin(memViews, ptr)); 914 | } 915 | 916 | /** 917 | * @notice Produce the sha256 digest of the concatenated contents of multiple views. 918 | * @param memViews The views 919 | * @return bytes32 - The sha256 digest 920 | */ 921 | function joinSha2(bytes29[] memory memViews) 922 | internal 923 | view 924 | returns (bytes32) 925 | { 926 | uint256 ptr; 927 | assembly { 928 | // solium-disable-previous-line security/no-inline-assembly 929 | ptr := mload(0x40) // load unused memory pointer 930 | } 931 | return sha2(unsafeJoin(memViews, ptr)); 932 | } 933 | 934 | /** 935 | * @notice copies all views, joins them into a new bytearray. 936 | * @param memViews The views 937 | * @return ret - The new byte array 938 | */ 939 | function join(bytes29[] memory memViews) 940 | internal 941 | view 942 | returns (bytes memory ret) 943 | { 944 | uint256 ptr; 945 | assembly { 946 | // solium-disable-previous-line security/no-inline-assembly 947 | ptr := mload(0x40) // load unused memory pointer 948 | } 949 | 950 | bytes29 _newView = unsafeJoin(memViews, ptr + 0x20); 951 | uint256 _written = len(_newView); 952 | uint256 _footprint = footprint(_newView); 953 | 954 | assembly { 955 | // solium-disable-previous-line security/no-inline-assembly 956 | // store the legnth 957 | mstore(ptr, _written) 958 | // new pointer is old + 0x20 + the footprint of the body 959 | mstore(0x40, add(add(ptr, _footprint), 0x20)) 960 | ret := ptr 961 | } 962 | } 963 | } 964 | -------------------------------------------------------------------------------- /contracts/summa-tx/ValidateSPV.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.10; 2 | 3 | /** @title ValidateSPV*/ 4 | /** @author Summa (https://summa.one) */ 5 | 6 | import {BytesLib} from "./BytesLib.sol"; 7 | import {SafeMath} from "./SafeMath.sol"; 8 | import {BTCUtils} from "./BTCUtils.sol"; 9 | 10 | library ValidateSPV { 11 | using BTCUtils for bytes; 12 | using BTCUtils for uint256; 13 | using BytesLib for bytes; 14 | using SafeMath for uint256; 15 | 16 | enum InputTypes { 17 | NONE, 18 | LEGACY, 19 | COMPATIBILITY, 20 | WITNESS 21 | } 22 | enum OutputTypes { 23 | NONE, 24 | WPKH, 25 | WSH, 26 | OP_RETURN, 27 | PKH, 28 | SH, 29 | NONSTANDARD 30 | } 31 | 32 | uint256 constant ERR_BAD_LENGTH = 33 | 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; 34 | uint256 constant ERR_INVALID_CHAIN = 35 | 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe; 36 | uint256 constant ERR_LOW_WORK = 37 | 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd; 38 | 39 | function getErrBadLength() internal pure returns (uint256) { 40 | return ERR_BAD_LENGTH; 41 | } 42 | 43 | function getErrInvalidChain() internal pure returns (uint256) { 44 | return ERR_INVALID_CHAIN; 45 | } 46 | 47 | function getErrLowWork() internal pure returns (uint256) { 48 | return ERR_LOW_WORK; 49 | } 50 | 51 | /// @notice Validates a tx inclusion in the block 52 | /// @param _txid The txid (LE) 53 | /// @param _merkleRoot The merkle root (as in the block header) 54 | /// @param _intermediateNodes The proof's intermediate nodes (digests between leaf and root) 55 | /// @param _index The leaf's index in the tree (0-indexed) 56 | /// @return true if fully valid, false otherwise 57 | function prove( 58 | bytes32 _txid, 59 | bytes32 _merkleRoot, 60 | bytes memory _intermediateNodes, 61 | uint256 _index 62 | ) internal pure returns (bool) { 63 | // Shortcut the empty-block case 64 | if ( 65 | _txid == _merkleRoot && 66 | _index == 0 && 67 | _intermediateNodes.length == 0 68 | ) { 69 | return true; 70 | } 71 | 72 | bytes memory _proof = abi.encodePacked( 73 | _txid, 74 | _intermediateNodes, 75 | _merkleRoot 76 | ); 77 | // If the Merkle proof failed, bubble up error 78 | return _proof.verifyHash256Merkle(_index); 79 | } 80 | 81 | /// @notice Hashes transaction to get txid 82 | /// @dev Supports Legacy and Witness 83 | /// @param _version 4-bytes version 84 | /// @param _vin Raw bytes length-prefixed input vector 85 | /// @param _vout Raw bytes length-prefixed output vector 86 | /// @ param _locktime 4-byte tx locktime 87 | /// @return 32-byte transaction id, little endian 88 | function calculateTxId( 89 | bytes memory _version, 90 | bytes memory _vin, 91 | bytes memory _vout, 92 | bytes memory _locktime 93 | ) internal pure returns (bytes32) { 94 | // Get transaction hash double-Sha256(version + nIns + inputs + nOuts + outputs + locktime) 95 | return abi.encodePacked(_version, _vin, _vout, _locktime).hash256(); 96 | } 97 | 98 | /// @notice Parses a tx input from raw input bytes 99 | /// @dev Supports Legacy and Witness inputs 100 | /// @param _input Raw bytes tx input 101 | /// @return Tx input sequence number, tx hash, and index 102 | function parseInput(bytes memory _input) 103 | internal 104 | pure 105 | returns ( 106 | uint32 _sequence, 107 | bytes32 _hash, 108 | uint32 _index, 109 | uint8 _inputType 110 | ) 111 | { 112 | // NB: If the scriptsig is exactly 00, we are witness. 113 | // Otherwise we are compatibility 114 | if (_input.keccak256Slice(36, 1) != keccak256(hex"00")) { 115 | _sequence = _input.extractSequenceLegacy(); 116 | bytes32 _witnessTag = _input.keccak256Slice(36, 3); 117 | 118 | if ( 119 | _witnessTag == keccak256(hex"220020") || 120 | _witnessTag == keccak256(hex"160014") 121 | ) { 122 | _inputType = uint8(InputTypes.COMPATIBILITY); 123 | } else { 124 | _inputType = uint8(InputTypes.LEGACY); 125 | } 126 | } else { 127 | _sequence = _input.extractSequenceWitness(); 128 | _inputType = uint8(InputTypes.WITNESS); 129 | } 130 | 131 | return ( 132 | _sequence, 133 | _input.extractInputTxId(), 134 | _input.extractTxIndex(), 135 | _inputType 136 | ); 137 | } 138 | 139 | /// @notice Parses a tx output from raw output bytes 140 | /// @dev Differentiates by output script prefix, handles legacy and witness 141 | /// @param _output Raw bytes tx output 142 | /// @return Tx output value, output type, payload 143 | function parseOutput(bytes memory _output) 144 | internal 145 | pure 146 | returns ( 147 | uint64 _value, 148 | uint8 _outputType, 149 | bytes memory _payload 150 | ) 151 | { 152 | _value = _output.extractValue(); 153 | 154 | if (_output.keccak256Slice(9, 1) == keccak256(hex"6a")) { 155 | // OP_RETURN 156 | _outputType = uint8(OutputTypes.OP_RETURN); 157 | _payload = _output.extractOpReturnData(); 158 | } else { 159 | bytes32 _prefixHash = _output.keccak256Slice(8, 2); 160 | if (_prefixHash == keccak256(hex"2200")) { 161 | // P2WSH 162 | _outputType = uint8(OutputTypes.WSH); 163 | _payload = _output.slice(11, 32); 164 | } else if (_prefixHash == keccak256(hex"1600")) { 165 | // P2WPKH 166 | _outputType = uint8(OutputTypes.WPKH); 167 | _payload = _output.slice(11, 20); 168 | } else if (_prefixHash == keccak256(hex"1976")) { 169 | // PKH 170 | _outputType = uint8(OutputTypes.PKH); 171 | _payload = _output.slice(12, 20); 172 | } else if (_prefixHash == keccak256(hex"17a9")) { 173 | // SH 174 | _outputType = uint8(OutputTypes.SH); 175 | _payload = _output.slice(11, 20); 176 | } else { 177 | _outputType = uint8(OutputTypes.NONSTANDARD); 178 | } 179 | } 180 | 181 | return (_value, _outputType, _payload); 182 | } 183 | 184 | /// @notice Parses a block header struct from a bytestring 185 | /// @dev Block headers are always 80 bytes, see Bitcoin docs 186 | /// @return Header digest, version, previous block header hash, merkle root, timestamp, target, nonce 187 | function parseHeader(bytes memory _header) 188 | internal 189 | pure 190 | returns ( 191 | bytes32 _digest, 192 | uint32 _version, 193 | bytes32 _prevHash, 194 | bytes32 _merkleRoot, 195 | uint32 _timestamp, 196 | uint256 _target, 197 | uint32 _nonce 198 | ) 199 | { 200 | // If header has an invalid length, bubble up error 201 | if (_header.length != 80) { 202 | return ( 203 | _digest, 204 | _version, 205 | _prevHash, 206 | _merkleRoot, 207 | _timestamp, 208 | _target, 209 | _nonce 210 | ); 211 | } 212 | 213 | _digest = abi 214 | .encodePacked(_header.hash256()) 215 | .reverseEndianness() 216 | .toBytes32(); 217 | _version = uint32( 218 | _header.slice(0, 4).reverseEndianness().bytesToUint() 219 | ); 220 | _prevHash = _header.extractPrevBlockLE().toBytes32(); 221 | _merkleRoot = _header.extractMerkleRootLE().toBytes32(); 222 | _timestamp = _header.extractTimestamp(); 223 | _target = _header.extractTarget(); 224 | _nonce = uint32(_header.slice(76, 4).reverseEndianness().bytesToUint()); 225 | 226 | return ( 227 | _digest, 228 | _version, 229 | _prevHash, 230 | _merkleRoot, 231 | _timestamp, 232 | _target, 233 | _nonce 234 | ); 235 | } 236 | 237 | /// @notice Checks validity of header chain 238 | /// @notice Compares the hash of each header to the prevHash in the next header 239 | /// @param _headers Raw byte array of header chain 240 | /// @return The total accumulated difficulty of the header chain, or an error code 241 | function validateHeaderChain(bytes memory _headers) 242 | internal 243 | view 244 | returns (uint256 _totalDifficulty) 245 | { 246 | // Check header chain length 247 | if (_headers.length % 80 != 0) { 248 | return ERR_BAD_LENGTH; 249 | } 250 | 251 | // Initialize header start index 252 | bytes32 _digest; 253 | 254 | _totalDifficulty = 0; 255 | 256 | for (uint256 _start = 0; _start < _headers.length; _start += 80) { 257 | // ith header start index and ith header 258 | bytes memory _header = _headers.slice(_start, 80); 259 | 260 | // After the first header, check that headers are in a chain 261 | if (_start != 0) { 262 | if (!validateHeaderPrevHash(_header, _digest)) { 263 | return ERR_INVALID_CHAIN; 264 | } 265 | } 266 | 267 | // ith header target 268 | uint256 _target = _header.extractTarget(); 269 | 270 | // Require that the header has sufficient work 271 | _digest = _header.hash256View(); 272 | if (uint256(_digest).reverseUint256() > _target) { 273 | return ERR_LOW_WORK; 274 | } 275 | 276 | // Add ith header difficulty to difficulty sum 277 | _totalDifficulty = _totalDifficulty.add( 278 | _target.calculateDifficulty() 279 | ); 280 | } 281 | } 282 | 283 | /// @notice Checks validity of header work 284 | /// @param _digest Header digest 285 | /// @param _target The target threshold 286 | /// @return true if header work is valid, false otherwise 287 | function validateHeaderWork(bytes32 _digest, uint256 _target) 288 | internal 289 | pure 290 | returns (bool) 291 | { 292 | if (_digest == bytes32(0)) { 293 | return false; 294 | } 295 | return (abi.encodePacked(_digest).reverseEndianness().bytesToUint() < 296 | _target); 297 | } 298 | 299 | /// @notice Checks validity of header chain 300 | /// @dev Compares current header prevHash to previous header's digest 301 | /// @param _header The raw bytes header 302 | /// @param _prevHeaderDigest The previous header's digest 303 | /// @return true if header chain is valid, false otherwise 304 | function validateHeaderPrevHash( 305 | bytes memory _header, 306 | bytes32 _prevHeaderDigest 307 | ) internal pure returns (bool) { 308 | // Extract prevHash of current header 309 | bytes32 _prevHash = _header.extractPrevBlockLE().toBytes32(); 310 | 311 | // Compare prevHash of current header to previous header's digest 312 | if (_prevHash != _prevHeaderDigest) { 313 | return false; 314 | } 315 | 316 | return true; 317 | } 318 | } 319 | -------------------------------------------------------------------------------- /contracts/summa-tx/ViewBTC.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.10; 2 | 3 | /** @title BitcoinSPV */ 4 | /** @author Summa (https://summa.one) */ 5 | 6 | import {TypedMemView} from "./TypedMemView.sol"; 7 | import {SafeMath} from "./SafeMath.sol"; 8 | 9 | library ViewBTC { 10 | using TypedMemView for bytes29; 11 | using SafeMath for uint256; 12 | 13 | // The target at minimum Difficulty. Also the target of the genesis block 14 | uint256 public constant DIFF1_TARGET = 15 | 0xffff0000000000000000000000000000000000000000000000000000; 16 | 17 | uint256 public constant RETARGET_PERIOD = 2 * 7 * 24 * 60 * 60; // 2 weeks in seconds 18 | uint256 public constant RETARGET_PERIOD_BLOCKS = 2016; // 2 weeks in blocks 19 | 20 | enum BTCTypes { 21 | Unknown, // 0x0 22 | CompactInt, // 0x1 23 | ScriptSig, // 0x2 - with length prefix 24 | Outpoint, // 0x3 25 | TxIn, // 0x4 26 | IntermediateTxIns, // 0x5 - used in vin parsing 27 | Vin, // 0x6 28 | ScriptPubkey, // 0x7 - with length prefix 29 | PKH, // 0x8 - the 20-byte payload digest 30 | WPKH, // 0x9 - the 20-byte payload digest 31 | WSH, // 0xa - the 32-byte payload digest 32 | SH, // 0xb - the 20-byte payload digest 33 | OpReturnPayload, // 0xc 34 | TxOut, // 0xd 35 | IntermediateTxOuts, // 0xe - used in vout parsing 36 | Vout, // 0xf 37 | Header, // 0x10 38 | HeaderArray, // 0x11 39 | MerkleNode, // 0x12 40 | MerkleStep, // 0x13 41 | MerkleArray // 0x14 42 | } 43 | 44 | // TODO: any way to bubble up more info? 45 | /// @notice requires `memView` to be of a specified type 46 | /// @param memView a 29-byte view with a 5-byte type 47 | /// @param t the expected type (e.g. BTCTypes.Outpoint, BTCTypes.TxIn, etc) 48 | /// @return passes if it is the correct type, errors if not 49 | modifier typeAssert(bytes29 memView, BTCTypes t) { 50 | memView.assertType(uint40(t)); 51 | _; 52 | } 53 | 54 | /// Revert with an error message re: non-minimal VarInts 55 | function revertNonMinimal(bytes29 ref) 56 | private 57 | pure 58 | returns (string memory) 59 | { 60 | (, uint256 g) = TypedMemView.encodeHex( 61 | ref.indexUint(0, uint8(ref.len())) 62 | ); 63 | string memory err = string( 64 | abi.encodePacked("Non-minimal var int. Got 0x", uint144(g)) 65 | ); 66 | revert(err); 67 | } 68 | 69 | /// @notice reads a compact int from the view at the specified index 70 | /// @param memView a 29-byte view with a 5-byte type 71 | /// @param _index the index 72 | /// @return the compact int at the specified index 73 | function indexCompactInt(bytes29 memView, uint256 _index) 74 | internal 75 | pure 76 | returns (uint64 number) 77 | { 78 | uint256 flag = memView.indexUint(_index, 1); 79 | if (flag <= 0xfc) { 80 | return uint64(flag); 81 | } else if (flag == 0xfd) { 82 | number = uint64(memView.indexLEUint(_index + 1, 2)); 83 | if (compactIntLength(number) != 3) { 84 | revertNonMinimal(memView.slice(_index, 3, 0)); 85 | } 86 | } else if (flag == 0xfe) { 87 | number = uint64(memView.indexLEUint(_index + 1, 4)); 88 | if (compactIntLength(number) != 5) { 89 | revertNonMinimal(memView.slice(_index, 5, 0)); 90 | } 91 | } else if (flag == 0xff) { 92 | number = uint64(memView.indexLEUint(_index + 1, 8)); 93 | if (compactIntLength(number) != 9) { 94 | revertNonMinimal(memView.slice(_index, 9, 0)); 95 | } 96 | } 97 | } 98 | 99 | /// @notice gives the total length (in bytes) of a CompactInt-encoded number 100 | /// @param number the number as uint64 101 | /// @return the compact integer as uint8 102 | function compactIntLength(uint64 number) internal pure returns (uint8) { 103 | if (number <= 0xfc) { 104 | return 1; 105 | } else if (number <= 0xffff) { 106 | return 3; 107 | } else if (number <= 0xffffffff) { 108 | return 5; 109 | } else { 110 | return 9; 111 | } 112 | } 113 | 114 | /// @notice extracts the LE txid from an outpoint 115 | /// @param _outpoint the outpoint 116 | /// @return the LE txid 117 | function txidLE(bytes29 _outpoint) 118 | internal 119 | pure 120 | typeAssert(_outpoint, BTCTypes.Outpoint) 121 | returns (bytes32) 122 | { 123 | return _outpoint.index(0, 32); 124 | } 125 | 126 | /// @notice extracts the index as an integer from the outpoint 127 | /// @param _outpoint the outpoint 128 | /// @return the index 129 | function outpointIdx(bytes29 _outpoint) 130 | internal 131 | pure 132 | typeAssert(_outpoint, BTCTypes.Outpoint) 133 | returns (uint32) 134 | { 135 | return uint32(_outpoint.indexLEUint(32, 4)); 136 | } 137 | 138 | /// @notice extracts the outpoint from an input 139 | /// @param _input the input 140 | /// @return the outpoint as a typed memory 141 | function outpoint(bytes29 _input) 142 | internal 143 | pure 144 | typeAssert(_input, BTCTypes.TxIn) 145 | returns (bytes29) 146 | { 147 | return _input.slice(0, 36, uint40(BTCTypes.Outpoint)); 148 | } 149 | 150 | /// @notice extracts the script sig from an input 151 | /// @param _input the input 152 | /// @return the script sig as a typed memory 153 | function scriptSig(bytes29 _input) 154 | internal 155 | pure 156 | typeAssert(_input, BTCTypes.TxIn) 157 | returns (bytes29) 158 | { 159 | uint64 scriptLength = indexCompactInt(_input, 36); 160 | return 161 | _input.slice( 162 | 36, 163 | compactIntLength(scriptLength) + scriptLength, 164 | uint40(BTCTypes.ScriptSig) 165 | ); 166 | } 167 | 168 | /// @notice extracts the sequence from an input 169 | /// @param _input the input 170 | /// @return the sequence 171 | function sequence(bytes29 _input) 172 | internal 173 | pure 174 | typeAssert(_input, BTCTypes.TxIn) 175 | returns (uint32) 176 | { 177 | uint64 scriptLength = indexCompactInt(_input, 36); 178 | uint256 scriptEnd = 36 + compactIntLength(scriptLength) + scriptLength; 179 | return uint32(_input.indexLEUint(scriptEnd, 4)); 180 | } 181 | 182 | /// @notice determines the length of the first input in an array of inputs 183 | /// @param _inputs the vin without its length prefix 184 | /// @return the input length 185 | function inputLength(bytes29 _inputs) 186 | internal 187 | pure 188 | typeAssert(_inputs, BTCTypes.IntermediateTxIns) 189 | returns (uint256) 190 | { 191 | uint64 scriptLength = indexCompactInt(_inputs, 36); 192 | return 193 | uint256(compactIntLength(scriptLength)) + 194 | uint256(scriptLength) + 195 | 36 + 196 | 4; 197 | } 198 | 199 | /// @notice extracts the input at a specified index 200 | /// @param _vin the vin 201 | /// @param _index the index of the desired input 202 | /// @return the desired input 203 | function indexVin(bytes29 _vin, uint256 _index) 204 | internal 205 | pure 206 | typeAssert(_vin, BTCTypes.Vin) 207 | returns (bytes29) 208 | { 209 | uint256 _nIns = uint256(indexCompactInt(_vin, 0)); 210 | uint256 _viewLen = _vin.len(); 211 | require(_index < _nIns, "Vin read overrun"); 212 | 213 | uint256 _offset = uint256(compactIntLength(uint64(_nIns))); 214 | bytes29 _remaining; 215 | for (uint256 _i = 0; _i < _index; _i += 1) { 216 | _remaining = _vin.postfix( 217 | _viewLen.sub(_offset), 218 | uint40(BTCTypes.IntermediateTxIns) 219 | ); 220 | _offset += inputLength(_remaining); 221 | } 222 | 223 | _remaining = _vin.postfix( 224 | _viewLen.sub(_offset), 225 | uint40(BTCTypes.IntermediateTxIns) 226 | ); 227 | uint256 _len = inputLength(_remaining); 228 | return _vin.slice(_offset, _len, uint40(BTCTypes.TxIn)); 229 | } 230 | 231 | /// @notice extracts the raw LE bytes of the output value 232 | /// @param _output the output 233 | /// @return the raw LE bytes of the output value 234 | function valueBytes(bytes29 _output) 235 | internal 236 | pure 237 | typeAssert(_output, BTCTypes.TxOut) 238 | returns (bytes8) 239 | { 240 | return bytes8(_output.index(0, 8)); 241 | } 242 | 243 | /// @notice extracts the value from an output 244 | /// @param _output the output 245 | /// @return the value 246 | function value(bytes29 _output) 247 | internal 248 | pure 249 | typeAssert(_output, BTCTypes.TxOut) 250 | returns (uint64) 251 | { 252 | return uint64(_output.indexLEUint(0, 8)); 253 | } 254 | 255 | /// @notice extracts the scriptPubkey from an output 256 | /// @param _output the output 257 | /// @return the scriptPubkey 258 | function scriptPubkey(bytes29 _output) 259 | internal 260 | pure 261 | typeAssert(_output, BTCTypes.TxOut) 262 | returns (bytes29) 263 | { 264 | uint64 scriptLength = indexCompactInt(_output, 8); 265 | return 266 | _output.slice( 267 | 8, 268 | compactIntLength(scriptLength) + scriptLength, 269 | uint40(BTCTypes.ScriptPubkey) 270 | ); 271 | } 272 | 273 | /// @notice determines the length of the first output in an array of outputs 274 | /// @param _outputs the vout without its length prefix 275 | /// @return the output length 276 | function outputLength(bytes29 _outputs) 277 | internal 278 | pure 279 | typeAssert(_outputs, BTCTypes.IntermediateTxOuts) 280 | returns (uint256) 281 | { 282 | uint64 scriptLength = indexCompactInt(_outputs, 8); 283 | return 284 | uint256(compactIntLength(scriptLength)) + uint256(scriptLength) + 8; 285 | } 286 | 287 | /// @notice extracts the output at a specified index 288 | /// @param _vout the vout 289 | /// @param _index the index of the desired output 290 | /// @return the desired output 291 | function indexVout(bytes29 _vout, uint256 _index) 292 | internal 293 | pure 294 | typeAssert(_vout, BTCTypes.Vout) 295 | returns (bytes29) 296 | { 297 | uint256 _nOuts = uint256(indexCompactInt(_vout, 0)); 298 | uint256 _viewLen = _vout.len(); 299 | require(_index < _nOuts, "Vout read overrun"); 300 | 301 | uint256 _offset = uint256(compactIntLength(uint64(_nOuts))); 302 | bytes29 _remaining; 303 | for (uint256 _i = 0; _i < _index; _i += 1) { 304 | _remaining = _vout.postfix( 305 | _viewLen - _offset, 306 | uint40(BTCTypes.IntermediateTxOuts) 307 | ); 308 | _offset += outputLength(_remaining); 309 | } 310 | 311 | _remaining = _vout.postfix( 312 | _viewLen - _offset, 313 | uint40(BTCTypes.IntermediateTxOuts) 314 | ); 315 | uint256 _len = outputLength(_remaining); 316 | return _vout.slice(_offset, _len, uint40(BTCTypes.TxOut)); 317 | } 318 | 319 | /// @notice extracts the Op Return Payload 320 | /// @param _spk the scriptPubkey 321 | /// @return the Op Return Payload (or null if not a valid Op Return output) 322 | function opReturnPayload(bytes29 _spk) 323 | internal 324 | pure 325 | typeAssert(_spk, BTCTypes.ScriptPubkey) 326 | returns (bytes29) 327 | { 328 | uint64 _bodyLength = indexCompactInt(_spk, 0); 329 | uint64 _payloadLen = uint64(_spk.indexUint(2, 1)); 330 | if ( 331 | _bodyLength > 77 || 332 | _bodyLength < 4 || 333 | _spk.indexUint(1, 1) != 0x6a || 334 | _spk.indexUint(2, 1) != _bodyLength - 2 335 | ) { 336 | return TypedMemView.nullView(); 337 | } 338 | return _spk.slice(3, _payloadLen, uint40(BTCTypes.OpReturnPayload)); 339 | } 340 | 341 | /// @notice extracts the payload from a scriptPubkey 342 | /// @param _spk the scriptPubkey 343 | /// @return the payload (or null if not a valid PKH, SH, WPKH, or WSH output) 344 | function payload(bytes29 _spk) 345 | internal 346 | pure 347 | typeAssert(_spk, BTCTypes.ScriptPubkey) 348 | returns (bytes29) 349 | { 350 | uint256 _spkLength = _spk.len(); 351 | uint256 _bodyLength = indexCompactInt(_spk, 0); 352 | if ( 353 | _bodyLength > 0x22 || 354 | _bodyLength < 0x16 || 355 | _bodyLength + 1 != _spkLength 356 | ) { 357 | return TypedMemView.nullView(); 358 | } 359 | 360 | // Legacy 361 | if ( 362 | _bodyLength == 0x19 && 363 | _spk.indexUint(0, 4) == 0x1976a914 && 364 | _spk.indexUint(_spkLength - 2, 2) == 0x88ac 365 | ) { 366 | return _spk.slice(4, 20, uint40(BTCTypes.PKH)); 367 | } else if ( 368 | _bodyLength == 0x17 && 369 | _spk.indexUint(0, 3) == 0x17a914 && 370 | _spk.indexUint(_spkLength - 1, 1) == 0x87 371 | ) { 372 | return _spk.slice(3, 20, uint40(BTCTypes.SH)); 373 | } 374 | 375 | // Witness v0 376 | if (_spk.indexUint(1, 1) == 0) { 377 | uint256 _payloadLen = _spk.indexUint(2, 1); 378 | if ( 379 | (_bodyLength != 0x22 && _bodyLength != 0x16) || 380 | _payloadLen != _bodyLength - 2 381 | ) { 382 | return TypedMemView.nullView(); 383 | } 384 | uint40 newType = uint40( 385 | _payloadLen == 0x20 ? BTCTypes.WSH : BTCTypes.WPKH 386 | ); 387 | return _spk.slice(3, _payloadLen, newType); 388 | } 389 | 390 | return TypedMemView.nullView(); 391 | } 392 | 393 | /// @notice (loosely) verifies an spk and converts to a typed memory 394 | /// @dev will return null in error cases. Will not check for disabled opcodes. 395 | /// @param _spk the spk 396 | /// @return the typed spk (or null if error) 397 | function tryAsSPK(bytes29 _spk) 398 | internal 399 | pure 400 | typeAssert(_spk, BTCTypes.Unknown) 401 | returns (bytes29) 402 | { 403 | if (_spk.len() == 0) { 404 | return TypedMemView.nullView(); 405 | } 406 | uint64 _len = indexCompactInt(_spk, 0); 407 | if (_spk.len() == compactIntLength(_len) + _len) { 408 | return _spk.castTo(uint40(BTCTypes.ScriptPubkey)); 409 | } else { 410 | return TypedMemView.nullView(); 411 | } 412 | } 413 | 414 | /// @notice verifies the vin and converts to a typed memory 415 | /// @dev will return null in error cases 416 | /// @param _vin the vin 417 | /// @return the typed vin (or null if error) 418 | function tryAsVin(bytes29 _vin) 419 | internal 420 | pure 421 | typeAssert(_vin, BTCTypes.Unknown) 422 | returns (bytes29) 423 | { 424 | if (_vin.len() == 0) { 425 | return TypedMemView.nullView(); 426 | } 427 | uint64 _nIns = indexCompactInt(_vin, 0); 428 | uint256 _viewLen = _vin.len(); 429 | if (_nIns == 0) { 430 | return TypedMemView.nullView(); 431 | } 432 | 433 | uint256 _offset = uint256(compactIntLength(_nIns)); 434 | for (uint256 i = 0; i < _nIns; i++) { 435 | if (_offset >= _viewLen) { 436 | // We've reached the end, but are still trying to read more 437 | return TypedMemView.nullView(); 438 | } 439 | bytes29 _remaining = _vin.postfix( 440 | _viewLen - _offset, 441 | uint40(BTCTypes.IntermediateTxIns) 442 | ); 443 | _offset += inputLength(_remaining); 444 | } 445 | if (_offset != _viewLen) { 446 | return TypedMemView.nullView(); 447 | } 448 | return _vin.castTo(uint40(BTCTypes.Vin)); 449 | } 450 | 451 | /// @notice verifies the vout and converts to a typed memory 452 | /// @dev will return null in error cases 453 | /// @param _vout the vout 454 | /// @return the typed vout (or null if error) 455 | function tryAsVout(bytes29 _vout) 456 | internal 457 | pure 458 | typeAssert(_vout, BTCTypes.Unknown) 459 | returns (bytes29) 460 | { 461 | if (_vout.len() == 0) { 462 | return TypedMemView.nullView(); 463 | } 464 | uint64 _nOuts = indexCompactInt(_vout, 0); 465 | uint256 _viewLen = _vout.len(); 466 | if (_nOuts == 0) { 467 | return TypedMemView.nullView(); 468 | } 469 | 470 | uint256 _offset = uint256(compactIntLength(_nOuts)); 471 | for (uint256 i = 0; i < _nOuts; i++) { 472 | if (_offset >= _viewLen) { 473 | // We've reached the end, but are still trying to read more 474 | return TypedMemView.nullView(); 475 | } 476 | bytes29 _remaining = _vout.postfix( 477 | _viewLen - _offset, 478 | uint40(BTCTypes.IntermediateTxOuts) 479 | ); 480 | _offset += outputLength(_remaining); 481 | } 482 | if (_offset != _viewLen) { 483 | return TypedMemView.nullView(); 484 | } 485 | return _vout.castTo(uint40(BTCTypes.Vout)); 486 | } 487 | 488 | /// @notice verifies the header and converts to a typed memory 489 | /// @dev will return null in error cases 490 | /// @param _header the header 491 | /// @return the typed header (or null if error) 492 | function tryAsHeader(bytes29 _header) 493 | internal 494 | pure 495 | typeAssert(_header, BTCTypes.Unknown) 496 | returns (bytes29) 497 | { 498 | if (_header.len() != 80) { 499 | return TypedMemView.nullView(); 500 | } 501 | return _header.castTo(uint40(BTCTypes.Header)); 502 | } 503 | 504 | /// @notice Index a header array. 505 | /// @dev Errors on overruns 506 | /// @param _arr The header array 507 | /// @param index The 0-indexed location of the header to get 508 | /// @return the typed header at `index` 509 | function indexHeaderArray(bytes29 _arr, uint256 index) 510 | internal 511 | pure 512 | typeAssert(_arr, BTCTypes.HeaderArray) 513 | returns (bytes29) 514 | { 515 | uint256 _start = index.mul(80); 516 | return _arr.slice(_start, 80, uint40(BTCTypes.Header)); 517 | } 518 | 519 | /// @notice verifies the header array and converts to a typed memory 520 | /// @dev will return null in error cases 521 | /// @param _arr the header array 522 | /// @return the typed header array (or null if error) 523 | function tryAsHeaderArray(bytes29 _arr) 524 | internal 525 | pure 526 | typeAssert(_arr, BTCTypes.Unknown) 527 | returns (bytes29) 528 | { 529 | if (_arr.len() % 80 != 0) { 530 | return TypedMemView.nullView(); 531 | } 532 | return _arr.castTo(uint40(BTCTypes.HeaderArray)); 533 | } 534 | 535 | /// @notice verifies the merkle array and converts to a typed memory 536 | /// @dev will return null in error cases 537 | /// @param _arr the merkle array 538 | /// @return the typed merkle array (or null if error) 539 | function tryAsMerkleArray(bytes29 _arr) 540 | internal 541 | pure 542 | typeAssert(_arr, BTCTypes.Unknown) 543 | returns (bytes29) 544 | { 545 | if (_arr.len() % 32 != 0) { 546 | return TypedMemView.nullView(); 547 | } 548 | return _arr.castTo(uint40(BTCTypes.MerkleArray)); 549 | } 550 | 551 | /// @notice extracts the merkle root from the header 552 | /// @param _header the header 553 | /// @return the merkle root 554 | function merkleRoot(bytes29 _header) 555 | internal 556 | pure 557 | typeAssert(_header, BTCTypes.Header) 558 | returns (bytes32) 559 | { 560 | return _header.index(36, 32); 561 | } 562 | 563 | /// @notice extracts the target from the header 564 | /// @param _header the header 565 | /// @return the target 566 | function target(bytes29 _header) 567 | internal 568 | pure 569 | typeAssert(_header, BTCTypes.Header) 570 | returns (uint256) 571 | { 572 | uint256 _mantissa = _header.indexLEUint(72, 3); 573 | uint256 _exponent = _header.indexUint(75, 1).sub(3); 574 | return _mantissa.mul(256**_exponent); 575 | } 576 | 577 | /// @notice calculates the difficulty from a target 578 | /// @param _target the target 579 | /// @return the difficulty 580 | function toDiff(uint256 _target) internal pure returns (uint256) { 581 | return DIFF1_TARGET.div(_target); 582 | } 583 | 584 | /// @notice extracts the difficulty from the header 585 | /// @param _header the header 586 | /// @return the difficulty 587 | function diff(bytes29 _header) 588 | internal 589 | pure 590 | typeAssert(_header, BTCTypes.Header) 591 | returns (uint256) 592 | { 593 | return toDiff(target(_header)); 594 | } 595 | 596 | /// @notice extracts the timestamp from the header 597 | /// @param _header the header 598 | /// @return the timestamp 599 | function time(bytes29 _header) 600 | internal 601 | pure 602 | typeAssert(_header, BTCTypes.Header) 603 | returns (uint32) 604 | { 605 | return uint32(_header.indexLEUint(68, 4)); 606 | } 607 | 608 | /// @notice extracts the parent hash from the header 609 | /// @param _header the header 610 | /// @return the parent hash 611 | function parent(bytes29 _header) 612 | internal 613 | pure 614 | typeAssert(_header, BTCTypes.Header) 615 | returns (bytes32) 616 | { 617 | return _header.index(4, 32); 618 | } 619 | 620 | /// @notice calculates the Proof of Work hash of the header 621 | /// @param _header the header 622 | /// @return the Proof of Work hash 623 | function workHash(bytes29 _header) 624 | internal 625 | view 626 | typeAssert(_header, BTCTypes.Header) 627 | returns (bytes32) 628 | { 629 | return _header.hash256(); 630 | } 631 | 632 | /// @notice calculates the Proof of Work hash of the header, and converts to an integer 633 | /// @param _header the header 634 | /// @return the Proof of Work hash as an integer 635 | function work(bytes29 _header) 636 | internal 637 | view 638 | typeAssert(_header, BTCTypes.Header) 639 | returns (uint256) 640 | { 641 | return TypedMemView.reverseUint256(uint256(workHash(_header))); 642 | } 643 | 644 | /// @notice Concatenates and hashes two inputs for merkle proving 645 | /// @dev Not recommended to call directly. 646 | /// @param _a The first hash 647 | /// @param _b The second hash 648 | /// @return The double-sha256 of the concatenated hashes 649 | function _merkleStep(bytes32 _a, bytes32 _b) 650 | internal 651 | view 652 | returns (bytes32 digest) 653 | { 654 | assembly { 655 | // solium-disable-previous-line security/no-inline-assembly 656 | let ptr := mload(0x40) 657 | mstore(ptr, _a) 658 | mstore(add(ptr, 0x20), _b) 659 | pop(staticcall(gas, 2, ptr, 0x40, ptr, 0x20)) // sha2 #1 660 | pop(staticcall(gas, 2, ptr, 0x20, ptr, 0x20)) // sha2 #2 661 | digest := mload(ptr) 662 | } 663 | } 664 | 665 | /// @notice verifies a merkle proof 666 | /// @param _leaf the leaf 667 | /// @param _proof the merkle proof 668 | /// @param _root the merkle root 669 | /// @param _index the index 670 | /// @return true if valid, false if otherwise 671 | function checkMerkle( 672 | bytes32 _leaf, 673 | bytes29 _proof, 674 | bytes32 _root, 675 | uint256 _index 676 | ) internal view typeAssert(_proof, BTCTypes.MerkleArray) returns (bool) { 677 | uint256 nodes = _proof.len() / 32; 678 | if (nodes == 0) { 679 | return _leaf == _root; 680 | } 681 | 682 | uint256 _idx = _index; 683 | bytes32 _current = _leaf; 684 | 685 | for (uint256 i = 0; i < nodes; i++) { 686 | bytes32 _next = _proof.index(i * 32, 32); 687 | if (_idx % 2 == 1) { 688 | _current = _merkleStep(_next, _current); 689 | } else { 690 | _current = _merkleStep(_current, _next); 691 | } 692 | _idx >>= 1; 693 | } 694 | 695 | return _current == _root; 696 | } 697 | 698 | /// @notice performs the bitcoin difficulty retarget 699 | /// @dev implements the Bitcoin algorithm precisely 700 | /// @param _previousTarget the target of the previous period 701 | /// @param _firstTimestamp the timestamp of the first block in the difficulty period 702 | /// @param _secondTimestamp the timestamp of the last block in the difficulty period 703 | /// @return the new period's target threshold 704 | function retargetAlgorithm( 705 | uint256 _previousTarget, 706 | uint256 _firstTimestamp, 707 | uint256 _secondTimestamp 708 | ) internal pure returns (uint256) { 709 | uint256 _elapsedTime = _secondTimestamp.sub(_firstTimestamp); 710 | 711 | // Normalize ratio to factor of 4 if very long or very short 712 | if (_elapsedTime < RETARGET_PERIOD.div(4)) { 713 | _elapsedTime = RETARGET_PERIOD.div(4); 714 | } 715 | if (_elapsedTime > RETARGET_PERIOD.mul(4)) { 716 | _elapsedTime = RETARGET_PERIOD.mul(4); 717 | } 718 | 719 | /* 720 | NB: high targets e.g. ffff0020 can cause overflows here 721 | so we divide it by 256**2, then multiply by 256**2 later 722 | we know the target is evenly divisible by 256**2, so this isn't an issue 723 | */ 724 | uint256 _adjusted = _previousTarget.div(65536).mul(_elapsedTime); 725 | return _adjusted.div(RETARGET_PERIOD).mul(65536); 726 | } 727 | } 728 | -------------------------------------------------------------------------------- /contracts/summa-tx/ViewSPV.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.10; 2 | 3 | /** @title ViewSPV */ 4 | /** @author Summa (https://summa.one) */ 5 | 6 | import {TypedMemView} from "./TypedMemView.sol"; 7 | import {ViewBTC} from "./ViewBTC.sol"; 8 | import {SafeMath} from "./SafeMath.sol"; 9 | 10 | library ViewSPV { 11 | using TypedMemView for bytes; 12 | using TypedMemView for bytes29; 13 | using ViewBTC for bytes29; 14 | using SafeMath for uint256; 15 | 16 | uint256 constant ERR_BAD_LENGTH = 17 | 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; 18 | uint256 constant ERR_INVALID_CHAIN = 19 | 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe; 20 | uint256 constant ERR_LOW_WORK = 21 | 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd; 22 | 23 | function getErrBadLength() internal pure returns (uint256) { 24 | return ERR_BAD_LENGTH; 25 | } 26 | 27 | function getErrInvalidChain() internal pure returns (uint256) { 28 | return ERR_INVALID_CHAIN; 29 | } 30 | 31 | function getErrLowWork() internal pure returns (uint256) { 32 | return ERR_LOW_WORK; 33 | } 34 | 35 | /// @notice requires `memView` to be of a specified type 36 | /// @param memView a 29-byte view with a 5-byte type 37 | /// @param t the expected type (e.g. BTCTypes.Outpoint, BTCTypes.TxIn, etc) 38 | /// @return passes if it is the correct type, errors if not 39 | modifier typeAssert(bytes29 memView, ViewBTC.BTCTypes t) { 40 | memView.assertType(uint40(t)); 41 | _; 42 | } 43 | 44 | /// @notice Validates a tx inclusion in the block 45 | /// @dev `index` is not a reliable indicator of location within a block 46 | /// @param _txid The txid (LE) 47 | /// @param _merkleRoot The merkle root (as in the block header) 48 | /// @param _intermediateNodes The proof's intermediate nodes (digests between leaf and root) 49 | /// @param _index The leaf's index in the tree (0-indexed) 50 | /// @return true if fully valid, false otherwise 51 | function prove( 52 | bytes32 _txid, 53 | bytes32 _merkleRoot, 54 | bytes29 _intermediateNodes, 55 | uint256 _index 56 | ) 57 | internal 58 | view 59 | typeAssert(_intermediateNodes, ViewBTC.BTCTypes.MerkleArray) 60 | returns (bool) 61 | { 62 | // Shortcut the empty-block case 63 | if ( 64 | _txid == _merkleRoot && _index == 0 && _intermediateNodes.len() == 0 65 | ) { 66 | return true; 67 | } 68 | 69 | return 70 | ViewBTC.checkMerkle(_txid, _intermediateNodes, _merkleRoot, _index); 71 | } 72 | 73 | /// @notice Hashes transaction to get txid 74 | /// @dev Supports Legacy and Witness 75 | /// @param _version 4-bytes version 76 | /// @param _vin Raw bytes length-prefixed input vector 77 | /// @param _vout Raw bytes length-prefixed output vector 78 | /// @param _locktime 4-byte tx locktime 79 | /// @return 32-byte transaction id, little endian 80 | function calculateTxId( 81 | bytes4 _version, 82 | bytes29 _vin, 83 | bytes29 _vout, 84 | bytes4 _locktime 85 | ) 86 | internal 87 | view 88 | typeAssert(_vin, ViewBTC.BTCTypes.Vin) 89 | typeAssert(_vout, ViewBTC.BTCTypes.Vout) 90 | returns (bytes32) 91 | { 92 | // TODO: write in assembly 93 | return 94 | abi 95 | .encodePacked(_version, _vin.clone(), _vout.clone(), _locktime) 96 | .ref(0) 97 | .hash256(); 98 | } 99 | 100 | // TODO: add test for checkWork 101 | /// @notice Checks validity of header work 102 | /// @param _header Header view 103 | /// @param _target The target threshold 104 | /// @return true if header work is valid, false otherwise 105 | function checkWork(bytes29 _header, uint256 _target) 106 | internal 107 | view 108 | typeAssert(_header, ViewBTC.BTCTypes.Header) 109 | returns (bool) 110 | { 111 | return _header.work() < _target; 112 | } 113 | 114 | /// @notice Checks validity of header chain 115 | /// @dev Compares current header parent to previous header's digest 116 | /// @param _header The raw bytes header 117 | /// @param _prevHeaderDigest The previous header's digest 118 | /// @return true if the connect is valid, false otherwise 119 | function checkParent(bytes29 _header, bytes32 _prevHeaderDigest) 120 | internal 121 | pure 122 | typeAssert(_header, ViewBTC.BTCTypes.Header) 123 | returns (bool) 124 | { 125 | return _header.parent() == _prevHeaderDigest; 126 | } 127 | 128 | /// @notice Checks validity of header chain 129 | /// @notice Compares the hash of each header to the prevHash in the next header 130 | /// @param _headers Raw byte array of header chain 131 | /// @return The total accumulated difficulty of the header chain, or an error code 132 | function checkChain(bytes29 _headers) 133 | internal 134 | view 135 | typeAssert(_headers, ViewBTC.BTCTypes.HeaderArray) 136 | returns (uint256 _totalDifficulty) 137 | { 138 | bytes32 _digest; 139 | uint256 _headerCount = _headers.len() / 80; 140 | for (uint256 i = 0; i < _headerCount; i += 1) { 141 | bytes29 _header = _headers.indexHeaderArray(i); 142 | if (i != 0) { 143 | if (!checkParent(_header, _digest)) { 144 | return ERR_INVALID_CHAIN; 145 | } 146 | } 147 | _digest = _header.workHash(); 148 | uint256 _work = TypedMemView.reverseUint256(uint256(_digest)); 149 | uint256 _target = _header.target(); 150 | 151 | if (_work > _target) { 152 | return ERR_LOW_WORK; 153 | } 154 | 155 | _totalDifficulty += ViewBTC.toDiff(_target); 156 | } 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /contracts/test/ETH2BTC.test.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.10; 2 | 3 | import "../ETH2BTC.sol"; 4 | import "./lib/ds-test/src/test.sol"; 5 | 6 | contract ETH2BTCTest is DSTest { 7 | ETH2BTC eth2BTCTest; 8 | 9 | function setUp() public { 10 | eth2BTCTest = new ETH2BTC( 11 | bytes20(0xbE086099e0Ff00fC0cfbC77A8Dd09375aE889FBD), 12 | msg.sender, 13 | 30, 14 | 100 15 | ); 16 | } 17 | 18 | function testFail_set_rate_not_admin() public { 19 | ETH2BTC eth2BTCWithZeroAddressAdmin = new ETH2BTC( 20 | bytes20(0xbE086099e0Ff00fC0cfbC77A8Dd09375aE889FBD), 21 | address(0), 22 | 30, 23 | 100 24 | ); 25 | eth2BTCWithZeroAddressAdmin.setEtherToBitcoinRate(10); 26 | } 27 | 28 | function test_basic_sanity() public { 29 | assertTrue(true); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /contracts/test/lib/ds-test/.gitignore: -------------------------------------------------------------------------------- 1 | /.dapple 2 | /build 3 | /out 4 | -------------------------------------------------------------------------------- /contracts/test/lib/ds-test/Makefile: -------------------------------------------------------------------------------- 1 | all:; dapp build 2 | 3 | test: 4 | -dapp --use solc:0.4.23 build 5 | -dapp --use solc:0.4.26 build 6 | -dapp --use solc:0.5.17 build 7 | -dapp --use solc:0.6.12 build 8 | -dapp --use solc:0.7.5 build 9 | 10 | demo: 11 | DAPP_SRC=demo dapp --use solc:0.7.5 build 12 | -hevm dapp-test --verbose 3 13 | 14 | .PHONY: test demo 15 | -------------------------------------------------------------------------------- /contracts/test/lib/ds-test/default.nix: -------------------------------------------------------------------------------- 1 | { solidityPackage, dappsys }: solidityPackage { 2 | name = "ds-test"; 3 | src = ./src; 4 | } 5 | -------------------------------------------------------------------------------- /contracts/test/lib/ds-test/demo/demo.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.4.23; 3 | 4 | import "../src/test.sol"; 5 | 6 | contract DemoTest is DSTest { 7 | function test_this() public pure { 8 | require(true); 9 | } 10 | 11 | function test_logs() public { 12 | emit log("-- log(string)"); 13 | emit log("a string"); 14 | 15 | emit log("-- log_named_uint(string, uint)"); 16 | log_named_uint("uint", 512); 17 | 18 | emit log("-- log_named_int(string, int)"); 19 | log_named_int("int", -512); 20 | 21 | emit log("-- log_named_address(string, address)"); 22 | log_named_address("address", address(this)); 23 | 24 | emit log("-- log_named_bytes32(string, bytes32)"); 25 | log_named_bytes32("bytes32", "a string"); 26 | 27 | emit log("-- log_named_bytes(string, bytes)"); 28 | log_named_bytes("bytes", hex"cafefe"); 29 | 30 | emit log("-- log_named_string(string, string)"); 31 | log_named_string("string", "a string"); 32 | 33 | emit log("-- log_named_decimal_uint(string, uint, uint)"); 34 | log_named_decimal_uint("decimal uint", 1.0e18, 18); 35 | 36 | emit log("-- log_named_decimal_int(string, int, uint)"); 37 | log_named_decimal_int("decimal int", -1.0e18, 18); 38 | } 39 | 40 | event log_old_named_uint(bytes32, uint256); 41 | 42 | function test_old_logs() public { 43 | log_old_named_uint("key", 500); 44 | log_named_bytes32("bkey", "val"); 45 | } 46 | 47 | function test_trace() public view { 48 | this.echo("string 1", "string 2"); 49 | } 50 | 51 | function test_multiline() public { 52 | emit log( 53 | "a multiline\\n" 54 | "string" 55 | ); 56 | emit log( 57 | "a multiline " 58 | "string" 59 | ); 60 | log_bytes("a string"); 61 | log_bytes( 62 | "a multiline\n" 63 | "string" 64 | ); 65 | log_bytes( 66 | "a multiline\\n" 67 | "string" 68 | ); 69 | emit log(unicode"Ώ"); 70 | logs(hex"0000"); 71 | log_named_bytes("0x0000", hex"0000"); 72 | logs(hex"ff"); 73 | } 74 | 75 | function echo(string memory s1, string memory s2) 76 | public 77 | pure 78 | returns (string memory, string memory) 79 | { 80 | return (s1, s2); 81 | } 82 | 83 | function prove_this(uint256 x) public { 84 | log_named_uint("sym x", x); 85 | assertGt(x + 1, 0); 86 | } 87 | 88 | function test_logn() public { 89 | assembly { 90 | log0(0x01, 0x02) 91 | log1(0x01, 0x02, 0x03) 92 | log2(0x01, 0x02, 0x03, 0x04) 93 | log3(0x01, 0x02, 0x03, 0x04, 0x05) 94 | } 95 | } 96 | 97 | event MyEvent(uint256, uint256 indexed, uint256, uint256 indexed); 98 | 99 | function test_events() public { 100 | emit MyEvent(1, 2, 3, 4); 101 | } 102 | 103 | function test_asserts() public { 104 | string memory err = "this test has failed!"; 105 | emit log("## assertTrue(bool)\n"); 106 | assertTrue(false); 107 | emit log("\n"); 108 | assertTrue(false, err); 109 | 110 | emit log("\n## assertEq(address,address)\n"); 111 | assertEq(address(this), msg.sender); 112 | emit log("\n"); 113 | assertEq(address(this), msg.sender, err); 114 | 115 | emit log("\n## assertEq32(bytes32,bytes32)\n"); 116 | assertEq32("bytes 1", "bytes 2"); 117 | emit log("\n"); 118 | assertEq32("bytes 1", "bytes 2", err); 119 | 120 | emit log("\n## assertEq(bytes32,bytes32)\n"); 121 | assertEq32("bytes 1", "bytes 2"); 122 | emit log("\n"); 123 | assertEq32("bytes 1", "bytes 2", err); 124 | 125 | emit log("\n## assertEq(uint,uint)\n"); 126 | assertEq(uint256(0), 1); 127 | emit log("\n"); 128 | assertEq(uint256(0), 1, err); 129 | 130 | emit log("\n## assertEq(int,int)\n"); 131 | assertEq(-1, -2); 132 | emit log("\n"); 133 | assertEq(-1, -2, err); 134 | 135 | emit log("\n## assertEqDecimal(int,int,uint)\n"); 136 | assertEqDecimal(-1.0e18, -1.1e18, 18); 137 | emit log("\n"); 138 | assertEqDecimal(-1.0e18, -1.1e18, 18, err); 139 | 140 | emit log("\n## assertEqDecimal(uint,uint,uint)\n"); 141 | assertEqDecimal(uint256(1.0e18), 1.1e18, 18); 142 | emit log("\n"); 143 | assertEqDecimal(uint256(1.0e18), 1.1e18, 18, err); 144 | 145 | emit log("\n## assertGt(uint,uint)\n"); 146 | assertGt(uint256(0), 0); 147 | emit log("\n"); 148 | assertGt(uint256(0), 0, err); 149 | 150 | emit log("\n## assertGt(int,int)\n"); 151 | assertGt(-1, -1); 152 | emit log("\n"); 153 | assertGt(-1, -1, err); 154 | 155 | emit log("\n## assertGtDecimal(int,int,uint)\n"); 156 | assertGtDecimal(-2.0e18, -1.1e18, 18); 157 | emit log("\n"); 158 | assertGtDecimal(-2.0e18, -1.1e18, 18, err); 159 | 160 | emit log("\n## assertGtDecimal(uint,uint,uint)\n"); 161 | assertGtDecimal(uint256(1.0e18), 1.1e18, 18); 162 | emit log("\n"); 163 | assertGtDecimal(uint256(1.0e18), 1.1e18, 18, err); 164 | 165 | emit log("\n## assertGe(uint,uint)\n"); 166 | assertGe(uint256(0), 1); 167 | emit log("\n"); 168 | assertGe(uint256(0), 1, err); 169 | 170 | emit log("\n## assertGe(int,int)\n"); 171 | assertGe(-1, 0); 172 | emit log("\n"); 173 | assertGe(-1, 0, err); 174 | 175 | emit log("\n## assertGeDecimal(int,int,uint)\n"); 176 | assertGeDecimal(-2.0e18, -1.1e18, 18); 177 | emit log("\n"); 178 | assertGeDecimal(-2.0e18, -1.1e18, 18, err); 179 | 180 | emit log("\n## assertGeDecimal(uint,uint,uint)\n"); 181 | assertGeDecimal(uint256(1.0e18), 1.1e18, 18); 182 | emit log("\n"); 183 | assertGeDecimal(uint256(1.0e18), 1.1e18, 18, err); 184 | 185 | emit log("\n## assertLt(uint,uint)\n"); 186 | assertLt(uint256(0), 0); 187 | emit log("\n"); 188 | assertLt(uint256(0), 0, err); 189 | 190 | emit log("\n## assertLt(int,int)\n"); 191 | assertLt(-1, -1); 192 | emit log("\n"); 193 | assertLt(-1, -1, err); 194 | 195 | emit log("\n## assertLtDecimal(int,int,uint)\n"); 196 | assertLtDecimal(-1.0e18, -1.1e18, 18); 197 | emit log("\n"); 198 | assertLtDecimal(-1.0e18, -1.1e18, 18, err); 199 | 200 | emit log("\n## assertLtDecimal(uint,uint,uint)\n"); 201 | assertLtDecimal(uint256(2.0e18), 1.1e18, 18); 202 | emit log("\n"); 203 | assertLtDecimal(uint256(2.0e18), 1.1e18, 18, err); 204 | 205 | emit log("\n## assertLe(uint,uint)\n"); 206 | assertLe(uint256(1), 0); 207 | emit log("\n"); 208 | assertLe(uint256(1), 0, err); 209 | 210 | emit log("\n## assertLe(int,int)\n"); 211 | assertLe(0, -1); 212 | emit log("\n"); 213 | assertLe(0, -1, err); 214 | 215 | emit log("\n## assertLeDecimal(int,int,uint)\n"); 216 | assertLeDecimal(-1.0e18, -1.1e18, 18); 217 | emit log("\n"); 218 | assertLeDecimal(-1.0e18, -1.1e18, 18, err); 219 | 220 | emit log("\n## assertLeDecimal(uint,uint,uint)\n"); 221 | assertLeDecimal(uint256(2.0e18), 1.1e18, 18); 222 | emit log("\n"); 223 | assertLeDecimal(uint256(2.0e18), 1.1e18, 18, err); 224 | 225 | emit log("\n## assertEq(string,string)\n"); 226 | string memory s1 = "string 1"; 227 | string memory s2 = "string 2"; 228 | assertEq(s1, s2); 229 | emit log("\n"); 230 | assertEq(s1, s2, err); 231 | 232 | emit log("\n## assertEq0(bytes,bytes)\n"); 233 | assertEq0(hex"abcdef01", hex"abcdef02"); 234 | log("\n"); 235 | assertEq0(hex"abcdef01", hex"abcdef02", err); 236 | } 237 | } 238 | 239 | contract DemoTestWithSetUp { 240 | function setUp() public {} 241 | 242 | function test_pass() public pure {} 243 | } 244 | -------------------------------------------------------------------------------- /contracts/test/lib/ds-test/src/test.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | 3 | // This program is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation, either version 3 of the License, or 6 | // (at your option) any later version. 7 | 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see . 15 | 16 | pragma solidity >=0.4.23; 17 | 18 | contract DSTest { 19 | event log(string); 20 | event logs(bytes); 21 | 22 | event log_address(address); 23 | event log_bytes32(bytes32); 24 | event log_int(int256); 25 | event log_uint(uint256); 26 | event log_bytes(bytes); 27 | event log_string(string); 28 | 29 | event log_named_address(string key, address val); 30 | event log_named_bytes32(string key, bytes32 val); 31 | event log_named_decimal_int(string key, int256 val, uint256 decimals); 32 | event log_named_decimal_uint(string key, uint256 val, uint256 decimals); 33 | event log_named_int(string key, int256 val); 34 | event log_named_uint(string key, uint256 val); 35 | event log_named_bytes(string key, bytes val); 36 | event log_named_string(string key, string val); 37 | 38 | bool public IS_TEST = true; 39 | bool public failed; 40 | 41 | address constant HEVM_ADDRESS = 42 | address(bytes20(uint160(uint256(keccak256("hevm cheat code"))))); 43 | 44 | modifier mayRevert() { 45 | _; 46 | } 47 | modifier testopts(string memory) { 48 | _; 49 | } 50 | 51 | function fail() internal { 52 | failed = true; 53 | } 54 | 55 | modifier logs_gas() { 56 | uint256 startGas = gasleft(); 57 | _; 58 | uint256 endGas = gasleft(); 59 | emit log_named_uint("gas", startGas - endGas); 60 | } 61 | 62 | function assertTrue(bool condition) internal { 63 | if (!condition) { 64 | emit log("Error: Assertion Failed"); 65 | fail(); 66 | } 67 | } 68 | 69 | function assertTrue(bool condition, string memory err) internal { 70 | if (!condition) { 71 | emit log_named_string("Error", err); 72 | assertTrue(condition); 73 | } 74 | } 75 | 76 | function assertEq(address a, address b) internal { 77 | if (a != b) { 78 | emit log("Error: a == b not satisfied [address]"); 79 | emit log_named_address(" Expected", b); 80 | emit log_named_address(" Actual", a); 81 | fail(); 82 | } 83 | } 84 | 85 | function assertEq( 86 | address a, 87 | address b, 88 | string memory err 89 | ) internal { 90 | if (a != b) { 91 | emit log_named_string("Error", err); 92 | assertEq(a, b); 93 | } 94 | } 95 | 96 | function assertEq(bytes32 a, bytes32 b) internal { 97 | if (a != b) { 98 | emit log("Error: a == b not satisfied [bytes32]"); 99 | emit log_named_bytes32(" Expected", b); 100 | emit log_named_bytes32(" Actual", a); 101 | fail(); 102 | } 103 | } 104 | 105 | function assertEq( 106 | bytes32 a, 107 | bytes32 b, 108 | string memory err 109 | ) internal { 110 | if (a != b) { 111 | emit log_named_string("Error", err); 112 | assertEq(a, b); 113 | } 114 | } 115 | 116 | function assertEq32(bytes32 a, bytes32 b) internal { 117 | assertEq(a, b); 118 | } 119 | 120 | function assertEq32( 121 | bytes32 a, 122 | bytes32 b, 123 | string memory err 124 | ) internal { 125 | assertEq(a, b, err); 126 | } 127 | 128 | function assertEq(int256 a, int256 b) internal { 129 | if (a != b) { 130 | emit log("Error: a == b not satisfied [int]"); 131 | emit log_named_int(" Expected", b); 132 | emit log_named_int(" Actual", a); 133 | fail(); 134 | } 135 | } 136 | 137 | function assertEq( 138 | int256 a, 139 | int256 b, 140 | string memory err 141 | ) internal { 142 | if (a != b) { 143 | emit log_named_string("Error", err); 144 | assertEq(a, b); 145 | } 146 | } 147 | 148 | function assertEq(uint256 a, uint256 b) internal { 149 | if (a != b) { 150 | emit log("Error: a == b not satisfied [uint]"); 151 | emit log_named_uint(" Expected", b); 152 | emit log_named_uint(" Actual", a); 153 | fail(); 154 | } 155 | } 156 | 157 | function assertEq( 158 | uint256 a, 159 | uint256 b, 160 | string memory err 161 | ) internal { 162 | if (a != b) { 163 | emit log_named_string("Error", err); 164 | assertEq(a, b); 165 | } 166 | } 167 | 168 | function assertEqDecimal( 169 | int256 a, 170 | int256 b, 171 | uint256 decimals 172 | ) internal { 173 | if (a != b) { 174 | emit log("Error: a == b not satisfied [decimal int]"); 175 | emit log_named_decimal_int(" Expected", b, decimals); 176 | emit log_named_decimal_int(" Actual", a, decimals); 177 | fail(); 178 | } 179 | } 180 | 181 | function assertEqDecimal( 182 | int256 a, 183 | int256 b, 184 | uint256 decimals, 185 | string memory err 186 | ) internal { 187 | if (a != b) { 188 | emit log_named_string("Error", err); 189 | assertEqDecimal(a, b, decimals); 190 | } 191 | } 192 | 193 | function assertEqDecimal( 194 | uint256 a, 195 | uint256 b, 196 | uint256 decimals 197 | ) internal { 198 | if (a != b) { 199 | emit log("Error: a == b not satisfied [decimal uint]"); 200 | emit log_named_decimal_uint(" Expected", b, decimals); 201 | emit log_named_decimal_uint(" Actual", a, decimals); 202 | fail(); 203 | } 204 | } 205 | 206 | function assertEqDecimal( 207 | uint256 a, 208 | uint256 b, 209 | uint256 decimals, 210 | string memory err 211 | ) internal { 212 | if (a != b) { 213 | emit log_named_string("Error", err); 214 | assertEqDecimal(a, b, decimals); 215 | } 216 | } 217 | 218 | function assertGt(uint256 a, uint256 b) internal { 219 | if (a <= b) { 220 | emit log("Error: a > b not satisfied [uint]"); 221 | emit log_named_uint(" Value a", a); 222 | emit log_named_uint(" Value b", b); 223 | fail(); 224 | } 225 | } 226 | 227 | function assertGt( 228 | uint256 a, 229 | uint256 b, 230 | string memory err 231 | ) internal { 232 | if (a <= b) { 233 | emit log_named_string("Error", err); 234 | assertGt(a, b); 235 | } 236 | } 237 | 238 | function assertGt(int256 a, int256 b) internal { 239 | if (a <= b) { 240 | emit log("Error: a > b not satisfied [int]"); 241 | emit log_named_int(" Value a", a); 242 | emit log_named_int(" Value b", b); 243 | fail(); 244 | } 245 | } 246 | 247 | function assertGt( 248 | int256 a, 249 | int256 b, 250 | string memory err 251 | ) internal { 252 | if (a <= b) { 253 | emit log_named_string("Error", err); 254 | assertGt(a, b); 255 | } 256 | } 257 | 258 | function assertGtDecimal( 259 | int256 a, 260 | int256 b, 261 | uint256 decimals 262 | ) internal { 263 | if (a <= b) { 264 | emit log("Error: a > b not satisfied [decimal int]"); 265 | emit log_named_decimal_int(" Value a", a, decimals); 266 | emit log_named_decimal_int(" Value b", b, decimals); 267 | fail(); 268 | } 269 | } 270 | 271 | function assertGtDecimal( 272 | int256 a, 273 | int256 b, 274 | uint256 decimals, 275 | string memory err 276 | ) internal { 277 | if (a <= b) { 278 | emit log_named_string("Error", err); 279 | assertGtDecimal(a, b, decimals); 280 | } 281 | } 282 | 283 | function assertGtDecimal( 284 | uint256 a, 285 | uint256 b, 286 | uint256 decimals 287 | ) internal { 288 | if (a <= b) { 289 | emit log("Error: a > b not satisfied [decimal uint]"); 290 | emit log_named_decimal_uint(" Value a", a, decimals); 291 | emit log_named_decimal_uint(" Value b", b, decimals); 292 | fail(); 293 | } 294 | } 295 | 296 | function assertGtDecimal( 297 | uint256 a, 298 | uint256 b, 299 | uint256 decimals, 300 | string memory err 301 | ) internal { 302 | if (a <= b) { 303 | emit log_named_string("Error", err); 304 | assertGtDecimal(a, b, decimals); 305 | } 306 | } 307 | 308 | function assertGe(uint256 a, uint256 b) internal { 309 | if (a < b) { 310 | emit log("Error: a >= b not satisfied [uint]"); 311 | emit log_named_uint(" Value a", a); 312 | emit log_named_uint(" Value b", b); 313 | fail(); 314 | } 315 | } 316 | 317 | function assertGe( 318 | uint256 a, 319 | uint256 b, 320 | string memory err 321 | ) internal { 322 | if (a < b) { 323 | emit log_named_string("Error", err); 324 | assertGe(a, b); 325 | } 326 | } 327 | 328 | function assertGe(int256 a, int256 b) internal { 329 | if (a < b) { 330 | emit log("Error: a >= b not satisfied [int]"); 331 | emit log_named_int(" Value a", a); 332 | emit log_named_int(" Value b", b); 333 | fail(); 334 | } 335 | } 336 | 337 | function assertGe( 338 | int256 a, 339 | int256 b, 340 | string memory err 341 | ) internal { 342 | if (a < b) { 343 | emit log_named_string("Error", err); 344 | assertGe(a, b); 345 | } 346 | } 347 | 348 | function assertGeDecimal( 349 | int256 a, 350 | int256 b, 351 | uint256 decimals 352 | ) internal { 353 | if (a < b) { 354 | emit log("Error: a >= b not satisfied [decimal int]"); 355 | emit log_named_decimal_int(" Value a", a, decimals); 356 | emit log_named_decimal_int(" Value b", b, decimals); 357 | fail(); 358 | } 359 | } 360 | 361 | function assertGeDecimal( 362 | int256 a, 363 | int256 b, 364 | uint256 decimals, 365 | string memory err 366 | ) internal { 367 | if (a < b) { 368 | emit log_named_string("Error", err); 369 | assertGeDecimal(a, b, decimals); 370 | } 371 | } 372 | 373 | function assertGeDecimal( 374 | uint256 a, 375 | uint256 b, 376 | uint256 decimals 377 | ) internal { 378 | if (a < b) { 379 | emit log("Error: a >= b not satisfied [decimal uint]"); 380 | emit log_named_decimal_uint(" Value a", a, decimals); 381 | emit log_named_decimal_uint(" Value b", b, decimals); 382 | fail(); 383 | } 384 | } 385 | 386 | function assertGeDecimal( 387 | uint256 a, 388 | uint256 b, 389 | uint256 decimals, 390 | string memory err 391 | ) internal { 392 | if (a < b) { 393 | emit log_named_string("Error", err); 394 | assertGeDecimal(a, b, decimals); 395 | } 396 | } 397 | 398 | function assertLt(uint256 a, uint256 b) internal { 399 | if (a >= b) { 400 | emit log("Error: a < b not satisfied [uint]"); 401 | emit log_named_uint(" Value a", a); 402 | emit log_named_uint(" Value b", b); 403 | fail(); 404 | } 405 | } 406 | 407 | function assertLt( 408 | uint256 a, 409 | uint256 b, 410 | string memory err 411 | ) internal { 412 | if (a >= b) { 413 | emit log_named_string("Error", err); 414 | assertLt(a, b); 415 | } 416 | } 417 | 418 | function assertLt(int256 a, int256 b) internal { 419 | if (a >= b) { 420 | emit log("Error: a < b not satisfied [int]"); 421 | emit log_named_int(" Value a", a); 422 | emit log_named_int(" Value b", b); 423 | fail(); 424 | } 425 | } 426 | 427 | function assertLt( 428 | int256 a, 429 | int256 b, 430 | string memory err 431 | ) internal { 432 | if (a >= b) { 433 | emit log_named_string("Error", err); 434 | assertLt(a, b); 435 | } 436 | } 437 | 438 | function assertLtDecimal( 439 | int256 a, 440 | int256 b, 441 | uint256 decimals 442 | ) internal { 443 | if (a >= b) { 444 | emit log("Error: a < b not satisfied [decimal int]"); 445 | emit log_named_decimal_int(" Value a", a, decimals); 446 | emit log_named_decimal_int(" Value b", b, decimals); 447 | fail(); 448 | } 449 | } 450 | 451 | function assertLtDecimal( 452 | int256 a, 453 | int256 b, 454 | uint256 decimals, 455 | string memory err 456 | ) internal { 457 | if (a >= b) { 458 | emit log_named_string("Error", err); 459 | assertLtDecimal(a, b, decimals); 460 | } 461 | } 462 | 463 | function assertLtDecimal( 464 | uint256 a, 465 | uint256 b, 466 | uint256 decimals 467 | ) internal { 468 | if (a >= b) { 469 | emit log("Error: a < b not satisfied [decimal uint]"); 470 | emit log_named_decimal_uint(" Value a", a, decimals); 471 | emit log_named_decimal_uint(" Value b", b, decimals); 472 | fail(); 473 | } 474 | } 475 | 476 | function assertLtDecimal( 477 | uint256 a, 478 | uint256 b, 479 | uint256 decimals, 480 | string memory err 481 | ) internal { 482 | if (a >= b) { 483 | emit log_named_string("Error", err); 484 | assertLtDecimal(a, b, decimals); 485 | } 486 | } 487 | 488 | function assertLe(uint256 a, uint256 b) internal { 489 | if (a > b) { 490 | emit log("Error: a <= b not satisfied [uint]"); 491 | emit log_named_uint(" Value a", a); 492 | emit log_named_uint(" Value b", b); 493 | fail(); 494 | } 495 | } 496 | 497 | function assertLe( 498 | uint256 a, 499 | uint256 b, 500 | string memory err 501 | ) internal { 502 | if (a > b) { 503 | emit log_named_string("Error", err); 504 | assertLe(a, b); 505 | } 506 | } 507 | 508 | function assertLe(int256 a, int256 b) internal { 509 | if (a > b) { 510 | emit log("Error: a <= b not satisfied [int]"); 511 | emit log_named_int(" Value a", a); 512 | emit log_named_int(" Value b", b); 513 | fail(); 514 | } 515 | } 516 | 517 | function assertLe( 518 | int256 a, 519 | int256 b, 520 | string memory err 521 | ) internal { 522 | if (a > b) { 523 | emit log_named_string("Error", err); 524 | assertLe(a, b); 525 | } 526 | } 527 | 528 | function assertLeDecimal( 529 | int256 a, 530 | int256 b, 531 | uint256 decimals 532 | ) internal { 533 | if (a > b) { 534 | emit log("Error: a <= b not satisfied [decimal int]"); 535 | emit log_named_decimal_int(" Value a", a, decimals); 536 | emit log_named_decimal_int(" Value b", b, decimals); 537 | fail(); 538 | } 539 | } 540 | 541 | function assertLeDecimal( 542 | int256 a, 543 | int256 b, 544 | uint256 decimals, 545 | string memory err 546 | ) internal { 547 | if (a > b) { 548 | emit log_named_string("Error", err); 549 | assertLeDecimal(a, b, decimals); 550 | } 551 | } 552 | 553 | function assertLeDecimal( 554 | uint256 a, 555 | uint256 b, 556 | uint256 decimals 557 | ) internal { 558 | if (a > b) { 559 | emit log("Error: a <= b not satisfied [decimal uint]"); 560 | emit log_named_decimal_uint(" Value a", a, decimals); 561 | emit log_named_decimal_uint(" Value b", b, decimals); 562 | fail(); 563 | } 564 | } 565 | 566 | function assertLeDecimal( 567 | uint256 a, 568 | uint256 b, 569 | uint256 decimals, 570 | string memory err 571 | ) internal { 572 | if (a > b) { 573 | emit log_named_string("Error", err); 574 | assertGeDecimal(a, b, decimals); 575 | } 576 | } 577 | 578 | function assertEq(string memory a, string memory b) internal { 579 | if (keccak256(abi.encodePacked(a)) != keccak256(abi.encodePacked(b))) { 580 | emit log("Error: a == b not satisfied [string]"); 581 | emit log_named_string(" Value a", a); 582 | emit log_named_string(" Value b", b); 583 | fail(); 584 | } 585 | } 586 | 587 | function assertEq( 588 | string memory a, 589 | string memory b, 590 | string memory err 591 | ) internal { 592 | if (keccak256(abi.encodePacked(a)) != keccak256(abi.encodePacked(b))) { 593 | emit log_named_string("Error", err); 594 | assertEq(a, b); 595 | } 596 | } 597 | 598 | function checkEq0(bytes memory a, bytes memory b) 599 | internal 600 | pure 601 | returns (bool ok) 602 | { 603 | ok = true; 604 | if (a.length == b.length) { 605 | for (uint256 i = 0; i < a.length; i++) { 606 | if (a[i] != b[i]) { 607 | ok = false; 608 | } 609 | } 610 | } else { 611 | ok = false; 612 | } 613 | } 614 | 615 | function assertEq0(bytes memory a, bytes memory b) internal { 616 | if (!checkEq0(a, b)) { 617 | emit log("Error: a == b not satisfied [bytes]"); 618 | emit log_named_bytes(" Expected", a); 619 | emit log_named_bytes(" Actual", b); 620 | fail(); 621 | } 622 | } 623 | 624 | function assertEq0( 625 | bytes memory a, 626 | bytes memory b, 627 | string memory err 628 | ) internal { 629 | if (!checkEq0(a, b)) { 630 | emit log_named_string("Error", err); 631 | assertEq0(a, b); 632 | } 633 | } 634 | } 635 | -------------------------------------------------------------------------------- /hardhat.config.js: -------------------------------------------------------------------------------- 1 | require("@nomiclabs/hardhat-waffle"); 2 | require("@typechain/hardhat"); 3 | 4 | /** 5 | * @type import('hardhat/config').HardhatUserConfig 6 | */ 7 | module.exports = { 8 | solidity: "0.5.10", 9 | }; 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "learn-solidity-with-examples", 3 | "version": "1.0.0", 4 | "description": "A repo full of solidity smart contracts", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "dapp test", 8 | "hardhat-test": "npx hardhat test", 9 | "lint:fix": "prettier . --write" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/James-Sangalli/learn-solidity-with-examples.git" 14 | }, 15 | "author": "James Sangalli", 16 | "license": "MIT", 17 | "bugs": { 18 | "url": "https://github.com/James-Sangalli/learn-solidity-with-examples/issues" 19 | }, 20 | "homepage": "https://github.com/James-Sangalli/learn-solidity-with-examples#readme", 21 | "dependencies": { 22 | "hardhat": "^2.8.4", 23 | "ethers": "^5.5.4" 24 | }, 25 | "devDependencies": { 26 | "@nomiclabs/hardhat-ethers": "^2.0.5", 27 | "@nomiclabs/hardhat-waffle": "^2.0.2", 28 | "@typechain/hardhat": "^4.0.0", 29 | "typechain": "^5.0.0", 30 | "chai": "^4.3.6", 31 | "prettier": "^2.5.1", 32 | "prettier-plugin-solidity": "^1.0.0-beta.19" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /test/hardhat-tests.js: -------------------------------------------------------------------------------- 1 | const { expect } = require("chai"); 2 | const { ethers } = require("hardhat"); 3 | 4 | describe("ETH2BTC.sol", function () { 5 | let contract; 6 | let owner, user1; 7 | 8 | beforeEach(async () => { 9 | [owner, user1] = await ethers.getSigners(); 10 | const ETH2BTC = await ethers.getContractFactory("ETH2BTC"); 11 | contract = await ETH2BTC.deploy( 12 | "0xbe086099e0ff00fc0cfbc77a8dd09375ae889fbd", 13 | owner.address, 14 | 30, 15 | 100 16 | ); 17 | await contract.deployed(); 18 | }); 19 | 20 | it("Should be able to set a rate as admin", async function () { 21 | await contract.setEtherToBitcoinRate(10); 22 | const rate = await contract.etherToBitcoinRate(); 23 | expect(rate).to.equal(10, "rate should be set to 10"); 24 | }); 25 | }); 26 | --------------------------------------------------------------------------------