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

Commit ffb3fda

Browse files
committed
Merge pull request ethereum#10 from ethers/tidy
separate out incentives; update README with live testnet info
2 parents af39ba8 + 8ec3fb5 commit ffb3fda

7 files changed

Lines changed: 132 additions & 106 deletions

File tree

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1-
# btcrelay
1+
# [BTC Relay](http://btcrelay.org)
22

3-
[![Join the chat at https://gitter.im/ethereum/btcrelay](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/ethereum/btcrelay?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
3+
[![Join the chat at https://gitter.im/ethereum/btcrelay](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/ethereum/btcrelay)
44

5-
btcrelay is an Ethereum contract for Bitcoin SPV. The main functionality it provides are:
5+
[BTC Relay](http://btcrelay.org) is an Ethereum contract for Bitcoin SPV. The main functionality it provides are:
66

77
1. verification of a Bitcoin transaction
88
1. optionally relay the Bitcoin transaction to any Ethereum contract
99
1. storage of Bitcoin block headers
1010
1. inspection of the latest Bitcoin block header that is stored
1111

12-
btcrelay is on the Olympic testnet. Use `web3.eth.namereg.addr('btcrelay')` or run Ethereum JSON-RPC (default port 8545) and click [here](http://cdn.rawgit.com/ethereum/btcrelay/master/examples/relayContractStatus.html)
12+
BTC Relay is [live](http://rawgit.com/ethereum/btcrelay/master/examples/relayContractStatus.html) on the testnet.
1313

1414

1515
## API

btcrelay.se

Lines changed: 4 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
inset('btcChain.se')
2+
inset('incentive.se')
23

34
# btcrelay can relay a transaction to any contract that has a function
45
# name 'processTransaction' with signature bytes,uint256:int256
@@ -17,6 +18,7 @@ extern relayDestination: [processTransaction:[bytes,uint256]:int256]
1718
# - "_ibIndex" is the block's index to internalBlock (see btcChain)
1819
# - "_score" is 1 more than the cumulative difficulty [see setInitialParent()]
1920
# - _ancestor stores 8 32bit ancestor indices for more efficient backtracking (see btcChain)
21+
# - _feeInfo is used for incentive.se (see m_getFeeInfo)
2022
data block[2**256](_info, _ancestor, _blockHeader[], _feeInfo)
2123

2224

@@ -27,9 +29,9 @@ data heaviestBlock
2729
data highScore
2830

2931
event Failure(errCode:indexed)
30-
event EthPayment()
3132

3233
def init():
34+
# gasPriceAndChangeRecipientFee in incentive.se
3335
self.gasPriceAndChangeRecipientFee = 50 * 10**9 * BYTES_16 # 50 shannon and left-align
3436
# TODO anything else to init ?
3537
# carefully test if adding anything to init() since
@@ -150,84 +152,6 @@ def storeBlockHeader(blockHeaderBytes:str):
150152

151153
return(0)
152154

153-
# 16 bytes last gas price and 16 bytes of change recipient fee
154-
data gasPriceAndChangeRecipientFee
155-
156-
macro m_getLastGasPrice():
157-
div(self.gasPriceAndChangeRecipientFee, BYTES_16)
158-
159-
macro m_getChangeRecipientFee():
160-
(0x00000000000000000000000000000000ffffffffffffffffffffffffffffffff & self.gasPriceAndChangeRecipientFee)
161-
162-
macro m_setGasPriceAndChangeRecipientFee($gasPrice, $changeRecipientFee):
163-
self.gasPriceAndChangeRecipientFee = ($gasPrice * BYTES_16) | $changeRecipientFee
164-
165-
def storeBlockWithFee(blockHeaderBytes:str, feeWei):
166-
beginGas = msg.gas
167-
res = self.storeBlockHeader(blockHeaderBytes)
168-
if res:
169-
blockHash = m_hashBlockHeader(blockHeaderBytes)
170-
m_setFeeInfo(blockHash, feeWei, tx.origin) # note: tx.origin
171-
remainingGas = msg.gas
172-
173-
174-
gLastGasPrice = m_getLastGasPrice()
175-
176-
# TODO can avoid the "else"
177-
if tx.gasprice < (1023*gLastGasPrice/1024) || tx.gasprice > (1025*gLastGasPrice/1024):
178-
currGP = gLastGasPrice
179-
else:
180-
currGP = tx.gasprice
181-
gChangeRecipientFee = 2 * currGP * (beginGas - remainingGas)
182-
gLastGasPrice = currGP
183-
184-
m_setGasPriceAndChangeRecipientFee(gLastGasPrice, gChangeRecipientFee)
185-
186-
return(res)
187-
188-
189-
macro m_setFeeInfo($blockHash, $feeWei, $feeRecipient):
190-
$word = m_shiftLeft($feeRecipient, 12*8) | $feeWei
191-
self.block[$blockHash]._feeInfo = $word
192-
193-
194-
macro m_getFeeInfo($blockHash):
195-
self.block[$blockHash]._feeInfo
196-
197-
def getFee(blockHash):
198-
# TODO macro
199-
return(0x0000000000000000000000000000000000000000ffffffffffffffffffffffff & m_getFeeInfo(blockHash))
200-
201-
def getFeeRecipient(blockHash):
202-
return(div(m_getFeeInfo(blockHash), 2**(12*8)))
203-
204-
# TODO callers must send EXACTLY the block's current fee
205-
# There are 2 types of amountWei: the "verifyTxFee" and "changeRecipientFee"
206-
# This does NOT return any funds to incorrect callers
207-
def feePaid(txBlockHash, amountWei):
208-
feeInfo = m_getFeeInfo(txBlockHash)
209-
feeRecipient = div(feeInfo, 2**(12*8))
210-
# TODO allow overpayment?
211-
if msg.value == amountWei: # must not allow overpayment, otherwise fees could be increased via changeFeeRecipient()
212-
send(feeRecipient, msg.value)
213-
return(1)
214-
return(0)
215-
216-
217-
# callers must send EXACTLY the block's current fee, AND feeWei must be LESS
218-
# than the block's current fee
219-
# This does NOT return any funds to incorrect callers
220-
def changeFeeRecipient(blockHash, feeWei, feeRecipient):
221-
if !self.feePaid(blockHash, m_getChangeRecipientFee(), value=msg.value):
222-
return(0)
223-
224-
# feeWei is only allowed to decrease
225-
if feeWei < self.getFee(blockHash): # TODO macro
226-
m_setFeeInfo(blockHash, feeWei, feeRecipient)
227-
return(1)
228-
229-
return(0)
230-
231155

232156
# returns 1 if tx is in the block given by 'txBlockHash' and the block is
233157
# in Bitcoin's main chain (ie not a fork)
@@ -237,12 +161,9 @@ def changeFeeRecipient(blockHash, feeWei, feeRecipient):
237161
# - 'txIndex' is the index of the tx within the block
238162
# - 'sibling' are the merkle siblings of tx
239163
def verifyTx(txHash, txIndex, sibling:arr, txBlockHash):
240-
ethFee = self.getFee(txBlockHash)
241-
if !self.feePaid(txBlockHash, ethFee, value=msg.value):
164+
if !self.feePaid(txBlockHash, m_getFeeAmount(txBlockHash), value=msg.value): # in incentive.se
242165
return(0)
243166

244-
log(type=EthPayment)
245-
246167
if self.within6Confirms(txBlockHash) || !self.inMainChain(txBlockHash):
247168
return(0)
248169

incentive.se

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
# Incentive for block header relayers is they can set a fee for use of
2+
# the header they store: they are the initial feeRecipient.
3+
# By paying a changeRecipientFee to feeRecipient, anyone can lower the fee and
4+
# become the feeRecipient: this is a mechanism to prevent excessive fees.
5+
#
6+
# Tested by test/test_fee.py
7+
8+
# first 16 bytes are the last gas price; last 16 bytes is the changeRecipientFee
9+
data gasPriceAndChangeRecipientFee
10+
11+
event EthPayment(recipient:indexed, amount)
12+
13+
14+
# sets _feeInfo for the block and updates gasPriceAndChangeRecipientFee
15+
def storeBlockWithFee(blockHeaderBytes:str, feeWei):
16+
beginGas = msg.gas
17+
res = self.storeBlockHeader(blockHeaderBytes)
18+
if res:
19+
blockHash = m_hashBlockHeader(blockHeaderBytes)
20+
m_setFeeInfo(blockHash, feeWei, tx.origin) # note: tx.origin
21+
remainingGas = msg.gas
22+
23+
24+
gLastGasPrice = m_getLastGasPrice()
25+
26+
# TODO can avoid the "else"
27+
if tx.gasprice < (1023*gLastGasPrice/1024) || tx.gasprice > (1025*gLastGasPrice/1024):
28+
currGP = gLastGasPrice
29+
else:
30+
currGP = tx.gasprice
31+
gChangeRecipientFee = 2 * currGP * (beginGas - remainingGas)
32+
gLastGasPrice = currGP
33+
34+
m_setGasPriceAndChangeRecipientFee(gLastGasPrice, gChangeRecipientFee)
35+
36+
return(res)
37+
38+
39+
# if the EXACT fee for given txBlockHash is provided, pay the feeRecipient
40+
# and return 1. otherwise return 0.
41+
# This does NOT return any funds to incorrect callers
42+
def feePaid(txBlockHash, amountWei):
43+
if msg.value == amountWei: # does not allow overpayment
44+
if msg.value > 0:
45+
feeRecipient = m_getFeeRecipient(txBlockHash)
46+
send(feeRecipient, msg.value)
47+
log(type=EthPayment, feeRecipient, msg.value)
48+
return(1)
49+
return(0)
50+
51+
52+
# callers must send EXACTLY the block's current fee, AND feeWei must be LESS
53+
# than the block's current fee
54+
# This does NOT return any funds to incorrect callers
55+
def changeFeeRecipient(blockHash, feeWei, feeRecipient):
56+
if !self.feePaid(blockHash, m_getChangeRecipientFee(), value=msg.value):
57+
return(0)
58+
59+
# feeWei is only allowed to decrease
60+
if feeWei < m_getFeeAmount(blockHash):
61+
m_setFeeInfo(blockHash, feeWei, feeRecipient)
62+
return(1)
63+
64+
return(0)
65+
66+
67+
def getFeeRecipient(blockHash):
68+
return(m_getFeeRecipient(blockHash))
69+
70+
def getFeeAmount(blockHash):
71+
return(m_getFeeAmount(blockHash))
72+
73+
def getChangeRecipientFee():
74+
return(m_getChangeRecipientFee())
75+
76+
77+
#
78+
# macros for a block's _feeInfo
79+
#
80+
# _feeInfo has first 20 bytes as the feeRecipient and
81+
# the last 12 bytes is the feeAmount
82+
#
83+
macro m_getFeeInfo($blockHash):
84+
self.block[$blockHash]._feeInfo
85+
86+
macro m_setFeeInfo($blockHash, $feeWei, $feeRecipient):
87+
$word = ($feeRecipient * BYTES_12) | $feeWei
88+
self.block[$blockHash]._feeInfo = $word
89+
90+
macro m_getFeeRecipient($blockHash):
91+
div(m_getFeeInfo($blockHash), BYTES_12)
92+
93+
macro m_getFeeAmount($blockHash):
94+
0x0000000000000000000000000000000000000000ffffffffffffffffffffffff & m_getFeeInfo($blockHash)
95+
96+
97+
#
98+
# macros for gasPriceAndChangeRecipientFee
99+
#
100+
macro m_getLastGasPrice():
101+
div(self.gasPriceAndChangeRecipientFee, BYTES_16)
102+
103+
macro m_getChangeRecipientFee():
104+
(0x00000000000000000000000000000000ffffffffffffffffffffffffffffffff & self.gasPriceAndChangeRecipientFee)
105+
106+
macro m_setGasPriceAndChangeRecipientFee($gasPrice, $changeRecipientFee):
107+
self.gasPriceAndChangeRecipientFee = ($gasPrice * BYTES_16) | $changeRecipientFee

test/btcrelay_fee.se

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,6 @@ def funcGetFeeInfo(blockHash):
66
def funcGetLastGasPrice():
77
return(m_getLastGasPrice())
88

9-
def funcGetChangeRecipientFee():
10-
return(m_getChangeRecipientFee())
11-
12-
def getChangeRecipientFee():
13-
return(m_getChangeRecipientFee())
149

1510
# TODO move this to btcBulkStoreHeaders.se and remove bulkStoreHeader()
1611
def bulkStoreHeaderWithFee(headersBytes:str, count, feeWei):

test/test_fee.py

Lines changed: 15 additions & 11 deletions
Large diffs are not rendered by default.

test/test_macros.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class TestMacros(object):
1616
ETHER = 10 ** 18
1717

1818
def setup_class(cls):
19-
tester.gas_limit = int(3.5e6) # include costs of debug methods
19+
tester.gas_limit = int(3.7e6) # include costs of debug methods
2020
cls.s = tester.state()
2121
cls.c = cls.s.abi_contract(cls.CONTRACT_DEBUG, endowment=2000*cls.ETHER)
2222
cls.snapshot = cls.s.snapshot()

test/test_txVerify.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -277,12 +277,11 @@ def testEachConfirmation(self):
277277
else:
278278
assert 0 == res == userEthBalance
279279

280-
assert eventArr == [{'_event_type': 'EthPayment'},
280+
assert eventArr == [
281281
{'_event_type': 'Failure',
282282
'errCode': self.ERR_RELAY_VERIFY
283283
}]
284284
eventArr.pop()
285-
eventArr.pop()
286285

287286

288287
def test7BlockValidTx(self):

0 commit comments

Comments
 (0)