-
Notifications
You must be signed in to change notification settings - Fork 5
/
transactions.tex
445 lines (379 loc) · 21.5 KB
/
transactions.tex
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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
\section{Transaction types}
\label{sect:transactions}
Transactions, more appropriately operations, specify state transitions to
be applied to the state of the blockchain. As opposed to for example Ethereum,
where only one type of transaction exists and all additional logic is enforced
via smart contracts, \aet\ has many different kinds of transactions.
These are provided as convenient built-in functionality for frequently used
features, while all other functionality can still be realized via smart
contracts.
Every state transition caused by such a transaction has a computational
complexity, both in terms of storage size and execution, and given that we are
building an open, permissionless network, we need to measure and possibly
regulate the amount of computation used. We refer to this measure as "gas" and
each block has a maximum amount of gas it can contain.
This gas must be bought via the natively available currency and therefore only
accounts with enough currency can submit valid transactions, although it is
possible to author transactions on behalf of others. All gas, or simply fees,
are paid to miners.
To prevent anyone from spending currency that is not theirs, among other
things, transactions are authenticated either via digital signatures using
Ed25519 \cite{bernstein2012high, bernstein2006curve25519} or by user specified
logic in generalized accounts (\ref{sect:ga}).
Replay protection is achieved via a strictly increasing nonce \cite{Syverson}.
\subsection{Sending coins}
\label{sect:aespend}
The most basic transaction is the \textbf{spend transaction} used to
transfer coins from sender to receiver. The receiver can either be an account,
oracle or contract.
In addition to coin transfers, a sender can also attach an arbitrary binary
payload, which can for example be used for proof of existence, registering a
hash or file on the blockchain.
\subsection{Aeternity naming system}
\label{sect:aens}
By default all objects addressable within the blockchain are identified by
256 bit numbers. Just like users of the web prefer remembering DNS names over
IP addresses, users of \aet\ have the option to using names. Currently all
names have the extension \textit{.chain}, e.g. \textit{emin.chain}.
Major challenges for a naming system are to offer a reasonably fair system to
distribute names and discourage name squatting. To achieve those two goals we
settled on using a first price auction for short names, which are assumed to be
more coveted. Names longer than twelve characters can be registered instantly.
The auction has two parameters, the initial starting bid and closing timeout
after the last bit, which are adjusted based on the name being auctioned.
For example, the 4 character name \textit{emin}, starts with an initial
bid of 134.6269 coins and the auction would be open 29760 blocks (approx 62
days) after the last valid bid.
Each bid must be at least 5\% higher than the previous one in order to be
valid. Every successful bid will lock up the given number of coins and free
up the coins of the previously highest bid.
The actual process of claiming a name requires a bit of setup to prevent
\textit{front running}, where an observer snatches up a name before someone
else by observing unconfirmed transactions in the network and submitting a
competing registration attempt.
To prevent against front running, the first step in reserving a name is the
\textbf{preclaim transaction}. The pre-claim contains the hash of a combination
of the desired name and a random number (called \textit{salt}). An observer is
unable to guess the combination of name and random number, which prevents the
front running.
After the preclaim is accepted, the claimant reveals the name and salt in the
\textbf{claim transaction}. If name and salt produce the hash in the preclaim,
then they can either claim the name directly or an auction is started.
There is however still a potential for front running by putting a preclaim and
claim transaction into a block upon seeing an unconfirmed claim. This is
mitigated by requiring the preclaim and the claim for a given name to be in
different generations and therefore different blocks.
If the claim triggered an auction, bids can be submitted by sending claims with
the desired name, salt set to zero and a greater amount of coins than any
previous bids.
Once the name has been registered, an \textbf{update transaction} is needed to
point the name to something, for example an account. Additionally, there is
a \textbf{transfer transaction} to change the owner of a name and a
\textbf{revoke transaction} to free the name.
And even without active revocation, names expire after a while, unless renewed
in time with an update transaction.
When a name has been assigned to an owner and an update transaction has pointed
this name to an account, then one can use the \textit{name hash} of the name
instead of an account in, for example, a spend transaction.
It is important to realize that the names are part of the blockchain
logic. A user should not trust any third party to perform a name lookup on
chain and then substitute the name by an account. If a user want to
transfer tokens to Emin, the user should put the name hash of
\textit{emin.chain} in the transaction and sign this transaction.
\subsection{Aeternity oracles}
\label{sect:aeoracle}
Oracles are a mechanism to bring arbitrary external data onto the blockchain,
which can then be used in smart contracts. This can be sensor data, news
events, stock prices, results of a match, supply chain data, etc.
Assessing authenticity of external data
\cite{zhang2016town,guarnizo2019pdfs, adler2018astraea} is still a somewhat
open problem but can be solved if the availability of public key crypto
systems are available to all parties. But in general oracles provide data
without robust security guarantees.
Oracles are announced to the chain by a \textbf{register oracle transaction}.
This specifies in what format the oracle expects its queries and in what format
it is going to respond. The register oracle transaction also includes the fee
of the queries to this oracle. Each query must supply that fee in order to be
answered.
After an oracle has been registered on chain, any user can post a \textbf{query
oracle transaction} with a properly formatted query.
Oracle operators monitor the blockchain for queries and ideally post
\textbf{oracle response transactions} with answers in the predefined format.
This makes the answer available on the blockchain and thus also available to
any smart contract.
It is worth noting, that any oracle answers are by default publicly available
and thus special care would need to be taken in order to make it private.
\subsubsection{Data as a service}
External data may come from a large database, possibly also accessible in
different
ways, but via the oracle made accessible on the blockchain. Typically
one could think of supply chain data. If supply chain data is
accessible via a trusted oracle, one could post an oracle query for
last transaction on a specific item one ordered. Although the answer
on such a query may be interesting and valuable in itself, the main
purpose of asking for it would be to use it in a contract to transfer
some tokens (goods have arrived in harbour, 20\% of tokens are
transferred).
The above supply chain data may be anonymous enough to appear on a
blockchain. There is, however a privacy issue, external data that is
put on chain is made public. So, even if there might be an interesting
use case, one must be careful with for example personal data. If one
would have an airline oracle that given a last name and booking
reference returns flight data ``date'', ``from'' and ``destination''
airport, then this becomes public data. Having a contract pay the
travel agent when the oracle returns that the correct date and flight has
been booked, is therefore a bad idea. Even encrypting or decrypting the
data in the contract would be a bad idea, since contract state and
operations are visible.
Moreover, one cannot get paid for the same data twice, because the
first time it is posted, it becomes public. Therefore, typical data
normally is rather anonymous or invaluable to others than involved
parties, or is already/will become public, such as the weather or the
outcome of a match. Point is that one can use data that becomes
available in the future to base contractual decisions upon.
\subsubsection{Timing}
Users that post a query would normally want a response rather
quickly. Therefore, they can specify query TTL, either absolute
or relative key-block heights. A relative query TTL of 2 assures
that if the oracle does not answer within 2 key-blocks after the query
is accepted onchain, the query fee is not paid. In fact, an answer that is too
late, will not make it on chain and no contract can use it in a
decision.
Oracles have a specific lifetime, supplied in block height when
registering the oracle. After that block height, queries to the oracle
are no longer resulting in a response. The lifetime of an oracle can
be extended using an \textbf{extend oracle transaction}.
\subsubsection{A lottery example}
An example of the use of oracles and contracts can be illustrated with
a little lottery example. Note that all computation on a blockchain
should be deterministic in order to be able to validate the
results. If not exactly the same, the corresponding state hashes will
differ. As a consequence there is no random number generator in the
Sophia language\footnote{Even if some kind of random function would be
offered, it would be deterministic and hence have a predictable
outcome}.
Running a simple lottery in which users buy a ticket and after a while
one draws one of the tickets as the winner, is somehow depending on
some kind of fair randomness. If the number is known or computable at
the start of the buying process, one might be able to figure out what
ticket to buy to win the lottery. But if we close the lottery and then
ask an oracle for a random number, then a trusted, but disconnected
computation can be used to draw the winner.
So assume there is such an oracle, monitoring the chain and registered
with a reasonable query fee covering for its cost of operation. The
oracle has an identifier, for the example say
\begin{quote}
\textit{"ok\_shEHMV8Q2F1HR86pcyF7DYpudg8hnvJwJuVE3berWpbktnL2R"}.
\end{quote}
We can now write a Sophia contract that takes this oracle identifier
as input of its initialization and uses it for random numbers in the
lottery game.
\begin{verbatim}
include "List.aes"
contract Lottery =
record state = { participants : list(address),
price_sum : int(),
close_height : int(),
oracle : oracle(int, int),
query : option(oracle_query(int, int)) }
entrypoint init(rand : oracle(int, int)) : state =
{ participants = [],
price_sum = 0,
close_height = 0,
oracle = rand,
query = None }
\end{verbatim}
The contract stores a list of participants in its state, a price sum
that increases for each ticket bought, a closing height after which no
new tickets can be purchased. The initial closing height is zero,
because initially there is no ongoing lottery. The state also contains
an oracle and an optional query. This query will be instantiated when
the lottery is closed and a ticket is drawn.
The creator of this contract may now start the lottery by supplying a relative
closing height,
for example, 20 key-blocks from that the transaction gets on chain;
approximately one hour. Any user can then buy a ticket.
\begin{verbatim}
stateful payable entrypoint start(n : int) =
require( Call.caller == Contract.creator, "not creator" )
require( state.price_sum == 0, "lottery ongoing" )
require( n > 1, "block in future")
put(state{ participants = [],
close_height = Chain.block_height + n })
stateful payable entrypoint buy() =
require( state.close_height > Chain.block_height, "lottery closed" )
require( Call.value == 10, "price ticket 10" )
put(state{ participants = Call.caller :: state.participants,
price_sum = state.price_sum + 8 }) // we take 20%
\end{verbatim}
Note that a lot of conditions are checked, resulting in abortion of
the contract when falsified. In general, making the contract safe
requires thinking through a large number of possible scenarios in
which things may not work out as expected.
For example, if no users buy a ticket\footnote{The price of the ticket
is set to $10$ aettos instead of a more realistic
$10\_000\_000\_000\_000\_000$ to
make the code more readable}, the contract creator must be
able to restart the lottery at a later time, but the creator should
not be able to restart as soon as there are participants. Similarly,
one should not allow participants to buy tickets when the lottery is
closed.
The keyword \verb+payable+ expresses that we expect the participants
to add a token amount to the contract call transaction, which is
checked by comparing \verb+Call.value+. Similarly, the contract
creator needs to pay something into the contract to cover the oracle query
fee\footnote{Note that there is no check in the contract that it has
enough funds to query the oracle after starting a lottery. The
participants are at risk here} in case there are too few
participants.
When the closing height is reached, someone, most likely the creator
of the contract, asks the oracle to draw a number between 1 and the
number of participants. This call returns the query., such that anyone
can monitor the chain to see if the query has arrived. When that's the
case, the winner, or anyone else, can call the claim function, which
will transfer the price sum to the winner.
\begin{verbatim}
stateful entrypoint draw() : oracle_query(int, int) =
require( Chain.block_height > state.close_height, "ongoing lottery" )
require( state.price_sum > 0, "no ongoing lottery" )
require( state.query == None, "already drawn" )
let q =
Oracle.query(state.oracle, List.length(state.participants) - 1,
Oracle.query_fee(state.oracle),
RelativeTTL(5), RelativeTTL(480))
put(state{query = Some(q)})
q
stateful entrypoint claim() : option(address) =
switch(state.query)
None => abort( "no drawing" )
Some(query) =>
switch( Oracle.get_answer(state.oracle, query))
None => abort("waiting for query")
Some(winner) =>
let winner_account = List.get(winner, state.participants)
// Spend to winner
Chain.spend(winner_account, state.price_sum)
put(state{ price_sum = 0, query = None })
Some(winner_account)
\end{verbatim}
The reason to make it possible for anyone to call these functions is
to ensure that the contract creator can force progress to start a new
lottery and the winner to be able to claim even if the contract owner
is not around. The TTLs in the query assure that the oracle has
approximately 15 minutes to answer, long enough to even get it out
in busy times. Within a day one then has to claim the price sum,
otherwise, because the query answer is then no longer on chain.
This contract is far from fully secure, but illustrates an example of
how oracles and contracts can be used together.
\subsection{Generalized accounts}
\label{sect:ga}
Generalized accounts are a way to provide more flexibility to authenticating
transactions. This can, for example, be useful when one would allow users to
sign transactions with other cryptographic primitives than the
default\footnote{Some hardware devices may be restricted to other cryptographic
signing algorithms than the default on the \blockchain.} EdDSA as mentioned in
Sect.\ \ref{sect:transactions}.
If a user wants to have a generalized account, then they must
provide a smart contract in a \textbf{attach transaction}. This
contract is thereafter attached to the given account. The contract
must have an authentication function that returns a boolean whether or
not authentication is successful. The attach transaction itself is
just like all previously mentioned transactions signed in the default
way. It turns a normal account into a generalized account, and
\textit{there is no way back}.
When an account is a generalized account, any transaction can
be wrapped in a so called \textbf{meta transaction}. That is, one
prepares an ordinary transaction in the usual way, but with a nonce set to
zero. After that, one adds additional fee, gas and authentication data to
run the smart contract. When this transaction is processed, the
authentication function in the smart contract associated with the
account is called with the provided authentication data as input. If
the authentication fails the transaction is discarded, otherwise its
inner transaction is processed.
The following smart contract is an example that allows signing with
the ECDSA algorithm \cite{johnson2001elliptic} and the popular elliptic curve
Secp256k1, used for example by Bitcoin and Ethereum
\cite{bos2014elliptic, mayer2016ecdsa}.
\begin{verbatim}
contract ECDSAAuth =
record state = { nonce : int, owner : bytes(20) }
entrypoint init(owner' : bytes(20)) = { nonce = 1, owner = owner' }
stateful entrypoint authorize(n : int, s : bytes(65)) : bool =
require(n >= state.nonce, "Nonce too low")
require(n =< state.nonce, "Nonce too high")
put(state{ nonce = n + 1 })
switch(Auth.tx_hash)
None => abort("Not in Auth context")
Some(tx_hash) =>
Crypto.ecverify_secp256k1(to_sign(tx_hash, n), state.owner, s)
function to_sign(h : hash, n : int) : hash =
Crypto.blake2b((h, n))
\end{verbatim}
The contract is initialized by providing the public key used for
signing and the nonce (in the contract state) is set to 1. The
authentication function takes two parameters, the nonce and the signature.
The authorization function checks that the nonce is correct, and then
proceeds to fetch the TX hash from the contract environment using
\verb+Auth.tx_hash+. In this example the signature is for the Blake2b
hash of the tuple of the transaction hash and the nonce). The
authorization finally checks that the private key used for signing the
hash was from the owner.
By attaching this contract to an aeternity account, users can sign
aeternity transactions with their bitcoin private key. They need to
keep track, of course, what nonces they have used for this contract,
to provide the right next nonce.
\subsubsection{Security considerations}
Before the authentication is performed, there is no account that one
can charge for the computational effort of running the authentication
function. After all, anyone could wrap a transaction in a \textbf{meta
transaction} and submit it. It would be an easy attack to empty a
generalized account if the account had to pay for failed authorization
attempts. So, the gas for authentication is only charged when
successful. This opens up for another unpleasant attack.
Since there is no cost involved for the user to run an authentication
function, but the miner needs to spend execution cycles, one could
potentially write a complex function as authentication function and
extract resources from a miner by calling one's own authentication
function with failing input data. This is mitigated by not allowing
expensive chain operations in an authentication call. Moreover, miners
are free to implement any sophisticated rules for accepting
transactions in their mining pool, such that they can reject this
behaviour when observed.
Using different signature algorithms is only one of many possible uses
of generalized accounts. Other uses cases can be multi-sig, spending
limits (per week/month), limiting the transaction types, and more. For
these applications smart contracts have to be written. Utmost care
needs to be taken when implementing the authorization
function in these smart contracts. If the contract does not enforce
integrity checking or replay protection, then it will be vulnerable to
abuse.
\subsection{Financing transaction costs}
\label{sect:payingfor}
In order to make users enthusiastic about a blockchain application,
one may want them to try it for free. However, there are always costs
involved for transaction and gas. This means that a new user has to
buy tokens at some exchange to pay for the fees. This can be considered
a hurdle for adoption. Of course, one can ask a user for an account and put
some tokens on it, but then those tokens can be used for anything.
The \aet\ solution is more powerful and can be used to pay for
just specific transactions. It can be used to pay for both transaction
fee and gas cost of a contract call.
Assume a game played via a contract on the blockchain. One interacts
with the game, by calling the contract. In order to get more users for
the game, the game provider could make an App that visualizes
the game and asks for a next move. This App could automatically create
an \aet\ account, even without the user being aware of it. This
account can be used to sign transactions on the blockchain, but there
are no tokens in the account. This move is then encoded in a
transaction signed by the players account in the app. The transaction
is submitted to the game provider, who inspects it to see that this
is indeed a move in the game and wraps it in a \textbf{payingfor
transaction} signed by the game provider. The gas and fee are now
paid by the game provider's account.
Clearly this is also a way to have some trustful cross-chain
activity. The App user could provide the game provider with funds on a
different blockchain.
One can pay for any transaction apart from the payingfor transaction
itself. So even a generalized accounts meta transaction can be paid
for, as long as it recursively does not contain any other payingfor
transaction.