forked from ethereum/btcrelay
-
Notifications
You must be signed in to change notification settings - Fork 1
/
incentive.se
155 lines (124 loc) · 5.18 KB
/
incentive.se
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# Incentive for block header relayers is they can set a fee for use of
# the header they store: they are the initial feeRecipient.
# By paying a changeRecipientFee to feeRecipient, anyone can lower the fee and
# become the feeRecipient: this is a mechanism to prevent excessive fees.
#
# Tested by test/test_fee.py
# first 16 bytes are the last gas price; last 16 bytes is the changeRecipientFee
data gasPriceAndChangeRecipientFee
event EthPayment(recipient:indexed, amount)
# sets _feeInfo for the block and updates gasPriceAndChangeRecipientFee
def storeBlockWithFee(blockHeaderBytes:str, feeWei):
return(self.storeBlockWithFeeAndRecipient(blockHeaderBytes, feeWei, msg.sender))
# this indirection is needed by test_fee.py, but a configurable recipient may turn out useful
def storeBlockWithFeeAndRecipient(blockHeaderBytes:str, feeWei, feeRecipient):
beginGas = msg.gas
res = self.storeBlockHeader(blockHeaderBytes)
if res:
blockHash = m_dblShaFlip(blockHeaderBytes)
m_setFeeInfo(blockHash, feeWei, feeRecipient)
remainingGas = msg.gas
# currGP is tx.gasprice clamped within 1/1024 of gLastGasPrice
# (1/1024 is the same factor used for adjusting Ethereum's block gas limit)
gLastGasPrice = m_getLastGasPrice()
minGP = 1023*gLastGasPrice/1024
maxGP = 1025*gLastGasPrice/1024
if tx.gasprice < minGP:
currGP = minGP
elif tx.gasprice > maxGP:
currGP = maxGP
else:
currGP = tx.gasprice
gChangeRecipientFee = 2 * currGP * (beginGas - remainingGas) # 2X the cost of storing the header
m_setGasPriceAndChangeRecipientFee(currGP, gChangeRecipientFee)
return(res)
# if sufficient fee for 'txBlockHash' is provided, pay the feeRecipient
# and return 1. otherwise return 0.
# It is the recipient's responsibility to accept the fee.
# This does NOT return any funds to incorrect callers
def feePaid(txBlockHash, amountWei):
if msg.value >= amountWei:
if msg.value > 0:
feeRecipient = m_getFeeRecipient(txBlockHash)
if self.depthCheck(0):
send(feeRecipient, msg.value) # recipient's responsibility to accept the fee
log(type=EthPayment, feeRecipient, msg.value) # send() may fail, but that's recipient's responsibility
else:
~invalid()
return(1)
return(0)
# callers must sufficiently send the block's current fee, AND feeWei must be LESS
# than the block's current fee
# This does NOT return any funds to incorrect callers
def changeFeeRecipient(blockHash, feeWei, feeRecipient):
if !self.feePaid(blockHash, m_getChangeRecipientFee(), value=msg.value):
return(0)
# feeWei is only allowed to decrease
if feeWei < m_getFeeAmount(blockHash):
m_setFeeInfo(blockHash, feeWei, feeRecipient)
return(1)
return(0)
def getFeeRecipient(blockHash):
return(m_getFeeRecipient(blockHash))
def getFeeAmount(blockHash):
return(m_getFeeAmount(blockHash))
def getChangeRecipientFee():
return(m_getChangeRecipientFee())
# since calling depthCheck itself is a CALL, this function
# depthCheck returns 1 if n+1 CALL depth is available.
# Thus calling depthCheck(0) will return 1 if a CALL is available: ie CALL
# depth is at most 1023.
#
# Important note if porting/using this in Solidity, and make sure to test
# boundary conditions CALL depth carefully -- from Martin Swende:
# In Solidity, all internal "calls" are implemented using JUMP:s, since
# CALL is at least 5 times as expensive. And JUMP does not increase the
# CALL stack.
#
# So in order to do depth-check in Solidity, this won't work:
# contract foo{
# depth_check(n) return (uint){
# if (n > 0) return depth_check(n-1)
# else return 1;
# }
# }
#
# Instead, a level of explicit casting is required to trick Solidity to use
# call, like this:
#
# contract foo{
# depth_check(n) return(uint){
# if (n > 0) return foo(self).depth_check(n-1)
# else return 1;
# }
# }
def depthCheck(n):
if n:
return(self.depthCheck(n-1))
else:
return(1)
#
# macros for a block's _feeInfo
#
# _feeInfo has first 20 bytes as the feeRecipient and
# the last 12 bytes is the feeAmount
#
macro m_getFeeInfo($blockHash):
self.block[$blockHash]._feeInfo
macro m_setFeeInfo($blockHash, $feeWei, $feeRecipient):
if $feeWei > 0xffffffffffffffffffffffff:
$feeWei = 0xffffffffffffffffffffffff
self.block[$blockHash]._feeInfo = ($feeRecipient * BYTES_12) | $feeWei
macro m_getFeeRecipient($blockHash):
div(m_getFeeInfo($blockHash), BYTES_12)
macro m_getFeeAmount($blockHash):
0x0000000000000000000000000000000000000000ffffffffffffffffffffffff & m_getFeeInfo($blockHash)
#
# macros for gasPriceAndChangeRecipientFee
#
macro m_getLastGasPrice():
div(self.gasPriceAndChangeRecipientFee, BYTES_16)
macro m_getChangeRecipientFee():
(0x00000000000000000000000000000000ffffffffffffffffffffffffffffffff & self.gasPriceAndChangeRecipientFee)
macro m_setGasPriceAndChangeRecipientFee($gasPrice, $changeRecipientFee):
self.gasPriceAndChangeRecipientFee = ($gasPrice * BYTES_16) | $changeRecipientFee