Skip to content
This repository was archived by the owner on Feb 11, 2025. It is now read-only.

Commit e3d0619

Browse files
committed
contracts and README
1 parent def5fd6 commit e3d0619

6 files changed

Lines changed: 1011 additions & 0 deletions

File tree

README.md

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
# [BTC Relay](http://btcrelay.org)
2+
3+
[![Join the chat at https://gitter.im/ethereum/btcrelay](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/ethereum/btcrelay)
4+
5+
[BTC Relay](http://btcrelay.org) is an Ethereum contract for Bitcoin SPV. The main functionality it provides are:
6+
7+
1. verification of a Bitcoin transaction
8+
1. optionally relay the Bitcoin transaction to any Ethereum contract
9+
1. storage of Bitcoin block headers
10+
1. inspection of the latest Bitcoin block header that is stored
11+
12+
BTC Relay is [live](http://rawgit.com/ethereum/btcrelay/master/examples/relayContractStatus.html) on the testnet.
13+
14+
15+
## API
16+
17+
18+
##### verifyTx(transactionHash, transactionIndex, merkleSibling, blockHash)
19+
20+
Verifies the presence of a transaction on the Bitcoin blockchain, primarily that the transaction is on Bitcoin's main chain and has at least 6 confirmations.
21+
22+
* `transactionHash` - hash of the transaction, as `int256`
23+
* `transactionIndex` - transaction's index within the block, as `int256`
24+
* `merkleSibling` - array of the hashes of sibling transactions comprising the Merkle proof, as `int256[]`
25+
* `blockHash` - hash of the block that contains the transaction, as `int256`
26+
27+
Returns `int256`
28+
* `1` if transaction is verified to be on the Bitcoin blockchain
29+
* `0` otherwise
30+
31+
*Note:* See [examples/sampleCall.html](examples/sampleCall.html) including use of [bitcoin-proof](https://www.npmjs.com/package/bitcoin-proof) for constructing `merkleSibling`.
32+
33+
---
34+
35+
##### relayTx(rawTransaction, transactionHash, transactionIndex, merkleSibling, blockHash, contractAddress)
36+
37+
Verifies a Bitcoin transaction per `verifyTx()` and relays the verified transaction to the specified Ethereum contract.
38+
39+
* `rawTransaction` - hex string of the raw transaction, as `bytes`
40+
* `transactionHash` - hash of the transaction, as `int256`
41+
* `transactionIndex` - transaction's index within the block, as `int256`
42+
* `merkleSibling` - array of the hashes of sibling transactions comprising the Merkle proof, as `int256[]`
43+
* `blockHash` - hash of the block that contains the transaction, as `int256`
44+
* `contractAddress` - address of the Ethereum contract that will receive the verified Bitcoin transaction, as `int256`
45+
46+
The Ethereum contract should have a function of signature `processTransaction(rawTransaction, transactionHash)` and is what will be invoked by `relayTx` if the transaction passes verification. For an example, see [example-btc-eth](example-btc-eth)
47+
48+
Returns `int256`
49+
* value returned by the Ethereum contract's `processTransaction` function
50+
* `0` otherwise
51+
52+
----
53+
54+
##### storeBlockHeader(blockHeader)
55+
56+
Store a single block header if it is valid, such as a valid Proof-of-Work and the previous block it reference exists.
57+
58+
* `blockHeader` - raw `bytes` of the block header (not the hex string, but the actual bytes).
59+
60+
Returns `int256`
61+
* block height of the header if it was successfully stored
62+
* `0` otherwise
63+
64+
----
65+
66+
##### bulkStoreHeader(bytesOfHeaders, numberOfHeaders)
67+
68+
Store multiple block headers if they are valid.
69+
70+
* `bytesOfHeaders` - raw `bytes` of the block headers (not the hex string, but the actual bytes), with one following immediately the other.
71+
* `numberOfHeaders` - `int256` count of the number of headers being stored.
72+
73+
Returns `int256`
74+
* block height of the last header if all block headers were successfully stored
75+
* `0` if any of the block headers were not successfully stored
76+
77+
*Note:* See [deploy/relayTest/testBulkDeploy.yaml](deploy/relayTest/testBulkDeploy.yaml) for an example of the data for storing multiple headers. Also, to avoid exceeding Ethereum's block gas limit, a guideline is to store only 5 headers at time.
78+
79+
----
80+
81+
##### getBlockHeader(blockHash)
82+
83+
Get the 80 byte block header for a given `blockHash`.
84+
85+
* `blockHash` - hash of the block as `int256`
86+
87+
Returns `bytes`
88+
* block header, always as 80 bytes (all zeros if header does not exist)
89+
90+
----
91+
92+
##### getBlockHash(blockHeight)
93+
94+
Get the block hash for a given `blockHeight`.
95+
96+
* `blockHeight` - height of the block as `int256`. Minimum value is `1`.
97+
98+
Returns `int256`
99+
* block hash
100+
* `0` if not found
101+
102+
----
103+
104+
##### getAverageBlockDifficulty()
105+
106+
Returns the difference between the cumulative difficulty of the latest block and the 10th block prior.
107+
108+
This is provided in case an Ethereum contract wants to use the Bitcoin network difficulty as a data feed for some purpose.
109+
110+
----
111+
112+
##### getBlockchainHead(), getLastBlockHeight(), others
113+
114+
`getBlockchainHead` - returns the hash of the latest block, as`int256`
115+
116+
`getLastBlockHeight` - returns the block height of the latest block, as `int256`
117+
118+
See [BitcoinRelayAbi.js](examples/BitcoinRelayABI.js) for other APIs and [relayContractStatus.html](examples/relayContractStatus.html) for an example of calling some of them.
119+
120+
----
121+
122+
## Examples
123+
124+
* [sampleCall.html](examples/sampleCall.html) for calling `verifyTx` including use of [bitcoin-proof](https://www.npmjs.com/package/bitcoin-proof) for constructing `merkleSibling`.
125+
126+
* [example-btc-eth](example-btc-eth) for relaying a Bitcoin transaction to an Ethereum contract using `relayTx`.
127+
128+
* [relayContractStatus.html](examples/relayContractStatus.html) for calling other basic functions.
129+
130+
131+
## Development
132+
133+
Requirements
134+
* [Serpent](https://github.com/ethereum/serpent)
135+
* [pyethereum](https://github.com/ethereum/pyethereum) (for tests)
136+
* [pyepm](https://github.com/etherex/pyepm) (for deployment)
137+
138+
#### Running tests
139+
140+
Exclude slow tests:
141+
```
142+
py.test test/ -s -m "not slow"
143+
```
144+
145+
Run slow tests without veryslow tests
146+
```
147+
py.test test/ -s -m "slow and not veryslow"
148+
```
149+
150+
All tests:
151+
```
152+
py.test test/ -s
153+
```
154+
155+
156+
## License
157+
158+
[MIT](LICENSE)

btcBulkStoreHeaders.se

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
inset('btcrelay.se')
2+
3+
# store 'count' number of Bitcoin blockheaders represented as one
4+
# continuous 'headersBytes' (which should have length 80*count
5+
# since a single Bitcoin block header is 80 bytes)
6+
def bulkStoreHeader(headersBytes:str, count):
7+
HEADER_SIZE = 80
8+
9+
offset = 0
10+
endIndex = HEADER_SIZE
11+
12+
i = 0
13+
while i < count:
14+
currHeader = slice(headersBytes, chars=offset, chars=endIndex)
15+
res = self.storeBlockHeader(currHeader)
16+
17+
offset += HEADER_SIZE
18+
endIndex += HEADER_SIZE
19+
i += 1
20+
21+
return(res)

btcChain.se

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
inset('constants.se')
2+
3+
# btcChain is required by btcrelay and is a separate file to improve
4+
# clarity: it has ancestor management and its
5+
# main method is inMainChain() which is tested by test_btcChain
6+
7+
macro NUM_ANCESTOR_DEPTHS: 8
8+
9+
# list for internal usage only that allows a 32 byte blockHash to be looked up
10+
# with a 32bit int
11+
# This is not designed to be used for anything else, eg it contains all block
12+
# hashes and nothing can be assumed about which blocks are on the main chain
13+
data internalBlock[2^50]
14+
15+
# counter for next available slot in internalBlock
16+
# 0 means no blocks stored yet and is used for the special of storing 1st block
17+
# which cannot compute Bitcoin difficulty since it doesn't have the 2016th parent
18+
data ibIndex
19+
20+
21+
# save the ancestors for a block, as well as updating the height
22+
# ntoe: this is internal/private so make it into a macro
23+
macro m_saveAncestors($blockHashArg, $hashPrevBlockArg):
24+
with $blockHash = $blockHashArg:
25+
with $hashPrevBlock = $blockHashArg:
26+
self.internalBlock[self.ibIndex] = blockHash
27+
m_setIbIndex(blockHash, self.ibIndex)
28+
self.ibIndex += 1
29+
30+
m_setHeight(blockHash, m_getHeight(hashPrevBlock) + 1)
31+
32+
# 8 indexes into internalBlock can be stored inside one ancestor (32 byte) word
33+
ancWord = 0
34+
35+
# the first ancestor is the index to hashPrevBlock, and write it to ancWord
36+
prevIbIndex = m_getIbIndex(hashPrevBlock)
37+
m_mwrite32(ref(ancWord), prevIbIndex)
38+
39+
# update ancWord with the remaining indexes
40+
i = 1
41+
while i < NUM_ANCESTOR_DEPTHS:
42+
depth = m_getAncDepth(i)
43+
44+
if m_getHeight(blockHash) % depth == 1:
45+
m_mwrite32(ref(ancWord) + 4*i, prevIbIndex)
46+
else:
47+
m_mwrite32(ref(ancWord) + 4*i, m_getAncestor(hashPrevBlock, i))
48+
i += 1
49+
50+
# write the ancestor word to storage
51+
self.block[blockHash]._ancestor = ancWord
52+
53+
54+
# returns 1 if 'txBlockHash' is in the main chain, ie not a fork
55+
# otherwise returns 0
56+
def inMainChain(txBlockHash):
57+
txBlockHeight = m_getHeight(txBlockHash)
58+
59+
# By assuming that a block with height 0 does not exist, we can do
60+
# this optimization and immediate say that txBlockHash is not in the main chain.
61+
# However, the consequence is that
62+
# the genesis block must be at height 1 instead of 0 [see setInitialParent()]
63+
if !txBlockHeight:
64+
return(0)
65+
66+
return(self.fastGetBlockHash(txBlockHeight) == txBlockHash)
67+
68+
69+
# returns the blockHash at the given blockHeight
70+
# blockHeight must be greater than 0 (otherwise infinite loop since minimum height is 1)
71+
def getBlockHash(blockHeight):
72+
if blockHeight < 1 || blockHeight > m_getHeight(self.heaviestBlock):
73+
return(0)
74+
return(self.fastGetBlockHash(blockHeight))
75+
76+
77+
# for internal use only since callers must ensure 2 things:
78+
# * blockHeight is greater than 0 (otherwise infinite loop since
79+
# minimum height is 1)
80+
# * blockHeight is less than the height of heaviestBlock, otherwise the
81+
# heaviestBlock is returned
82+
def fastGetBlockHash(blockHeight):
83+
blockHash = self.heaviestBlock
84+
anc_index = NUM_ANCESTOR_DEPTHS - 1
85+
86+
while m_getHeight(blockHash) > blockHeight:
87+
while m_getHeight(blockHash) - blockHeight < m_getAncDepth(anc_index) && anc_index > 0:
88+
anc_index -= 1
89+
blockHash = self.internalBlock[m_getAncestor(blockHash, anc_index)]
90+
91+
return(blockHash)
92+
93+
94+
#
95+
# macros
96+
#
97+
98+
99+
# a block's _ancestor storage slot contains 8 indexes into internalBlock, so
100+
# this macro returns the index that can be used to lookup the desired ancestor
101+
# eg. for combined usage, self.internalBlock[m_getAncestor(someBlock, 2)] will
102+
# return the block hash of someBlock's 3rd ancestor
103+
macro m_getAncestor($blockHash, $whichAncestor):
104+
div(sload(ref(self.block[$blockHash]._ancestor)) * 2**(32*$whichAncestor), BYTES_28)
105+
106+
107+
# index should be 0 to 7, so this returns 1, 5, 25 ... 78125
108+
macro m_getAncDepth($index):
109+
5**$index
110+
111+
112+
# write $int32 to memory at $addrLoc
113+
# This is useful for writing 32bit ints inside one 32 byte word
114+
macro m_mwrite32($addrLoc, $int32):
115+
with $addr = $addrLoc:
116+
with $fourBytes = $int32:
117+
mstore8($addr, byte(28, $fourBytes))
118+
mstore8($addr + 1, byte(29, $fourBytes))
119+
mstore8($addr + 2, byte(30, $fourBytes))
120+
mstore8($addr + 3, byte(31, $fourBytes))
121+
122+
123+
# write $int24 to memory at $addrLoc
124+
# This is useful for writing 24bit ints inside one 32 byte word
125+
macro m_mwrite24($addrLoc, $int24):
126+
with $addr = $addrLoc:
127+
with $threeBytes = $int24:
128+
mstore8($addr, byte(29, $threeBytes))
129+
mstore8($addr + 1, byte(30, $threeBytes))
130+
mstore8($addr + 2, byte(31, $threeBytes))
131+
132+
133+
# write $int16 to memory at $addrLoc
134+
# This is useful for writing 16bit ints inside one 32 byte word
135+
macro m_mwrite16($addrLoc, $int16):
136+
with $addr = $addrLoc:
137+
with $twoBytes = $int16:
138+
mstore8($addr, byte(30, $twoBytes))
139+
mstore8($addr + 1, byte(31, $twoBytes))
140+
141+
142+
# log ancestors
143+
# def logAnc(blockHash):
144+
# log(11111)
145+
# log(blockHash)
146+
# i = 0
147+
# while i < NUM_ANCESTOR_DEPTHS:
148+
# anc = m_getAncestor(blockHash, i)
149+
# # anc = self.block[blockHash]._ancestor[i]
150+
# log(anc)
151+
# i += 1
152+
# log(22222)

0 commit comments

Comments
 (0)