From 2d89a4a1e306335fcd3f08a0bf85d9a44edc16e6 Mon Sep 17 00:00:00 2001 From: Marcelo Salhab Brogliato Date: Wed, 23 Jun 2021 11:22:53 -0700 Subject: [PATCH] Nano Contracts (draft) --- .../Nano Contracts - Diagrams.drawio | 1 + .../Nano Contracts - Diagrams.monopic | Bin 0 -> 4501 bytes .../nano-contracts-execution-order.svg | 3 + text/0000-nano-contracts.md | 692 ++++++++++++++++++ 4 files changed, 696 insertions(+) create mode 100644 text/0000-images/Nano Contracts - Diagrams.drawio create mode 100644 text/0000-images/Nano Contracts - Diagrams.monopic create mode 100644 text/0000-images/nano-contracts-execution-order.svg create mode 100644 text/0000-nano-contracts.md diff --git a/text/0000-images/Nano Contracts - Diagrams.drawio b/text/0000-images/Nano Contracts - Diagrams.drawio new file mode 100644 index 0000000..58729ff --- /dev/null +++ b/text/0000-images/Nano Contracts - Diagrams.drawio @@ -0,0 +1 @@ +7VvbcpswEP0aP7aDkMH4MXWSdqbtNDPpNEnfZJBtEhl5ZDnG/fpyETcjEmIYS07sPAQtEkjaPWe1KzGAk2X4laHV4if1MBmYhhcO4OXANAEw7OhfLNmlEmdspoI58z1RqRDc+v+wEBpCuvE9vK5U5JQS7q+qQpcGAXZ5RYYYo9tqtRkl1beu0BzXBLcuInXpne/xhRiFZRTyb9ifL3g+YHFnibLKQrBeII9uSyJ4NYATRilPr5bhBJN48rJ5SdtdN9zNO8ZwwNs08De//oLl792fu5vHmTH97iwfrU8wfcozIhsxYNFZvstmAHvRhIgiZXxB5zRA5KqQfmF0E3g4fo0RlYo6PyhdRUIQCR8x5zuhXbThNBIt+JKIuzMa8AkllCVvhI4R/0XytC9xBxrHLERrumEufmGgwgw5YnPMX6gHCtVENo3pEnO2ixoyTBD3n6sdQcK45nm9Yv6jC6GCN6hjeMDsuxv2nEw+6EMVOPT5fdz8s2mJ4kPp1mUoHp0UdlkhiAZ/n1WLCw/JI6ysWDRLSlm7NWf0KceVxBKM5KfIEqBKS7A0soTRR7cEW6Ul2DWKnoK6dWwXPse3K5QMeBv54qoy0XqVeseZH8ZGkUxvZk0NCvAJKSngOvnlCnjGjOPwZRXUZ0w0sKDwiWJRMBbFbeFhh0K0KDnXTNb7FI9UOL0cXyVwCaA0wusIjtJpCQqlmICHOMruGjuQ3Cq8XNBqe1ZVoF5oqtSvI+E8u2fO605jI1MvGht/lMV850W6aHpD/ejVuT7tqjoB2FNUChrRaE9XeS86kNpY/ZrvQIbrXbdtGGqkkqGyblYoqu6VVFMUsPSiKABqU6Qurik7YONFB3xSYQ2ALRFkKvXx4BDvdAxTeEcRbntTGCo1hXoakocSpiDEX61jM3grrVYtJc/ExgWCpph8Qe7TPKmUKSSgAZZqDRF/HkTXbqQMzOpx8myGbdfNm5bueKPxtKTgTrw+3OP1PF+ujNh1Sl2+V2Jvm7BSjGadcpfvldhbm4LSPA2oJy95KPH7Z2JvInaomtjNeqCjHN5KIJeN+3XIOSohZ2oUYXXlXh2Zt7UZQKU7yVk3K8wLT5R5HRfLmXfqWDFh9sK80NlL/41UM69EXRog+bU9qpNaR5nD00DzUIJmScR1RnMTmvPkvjI0axQVdQyPgZZIbhsRKUayLCKSmMYZyU1IhpZqJI/OEVEp0GkDuQYNHwly9dMEPKyfJjgJyHkIOzMp5GzXwdNZT5AzdFsKa7A1/p6dZ3YeT3Mkw3ouioeSw3tnJDchWfkyGDrqkaz+kAvMPmXR+5BL1s0K3iQKPAm8WdjxhjK8OeYU2nY/eMv3YXXxnFCDsPNAvL3d4/aJ0LZp3s6fiSRNLxhDu1KFVXzIcF168v5RRRtUeR0ae6aSPrHfA4qSRfMJ29LRzly3t6Wum7W92BI8ii3J9ickq3ztXUulVXmZfmGM+toUti1Ls1VcPQXCQ+OsPbn2jrYkiIrF98QpUouvsuHVfw== \ No newline at end of file diff --git a/text/0000-images/Nano Contracts - Diagrams.monopic b/text/0000-images/Nano Contracts - Diagrams.monopic new file mode 100644 index 0000000000000000000000000000000000000000..583d70822fb201b804ed7ee17d1bb7ca18b927c5 GIT binary patch literal 4501 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 + +
b15
b15
b16
b16
b14
b14
tx1
tx1
tx2
tx2
tx3
tx3
tx4
tx4
tx5
tx5
tx6
tx6
tx7
tx7
tx8
tx8
tx9
tx9
tx0
tx0
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/text/0000-nano-contracts.md b/text/0000-nano-contracts.md new file mode 100644 index 0000000..c6897c6 --- /dev/null +++ b/text/0000-nano-contracts.md @@ -0,0 +1,692 @@ +- Feature Name: nano_contracts +- Start Date: 2021-06-14 +- RFC PR: (leave this empty) +- Hathor Issue: (leave this empty) +- Author: Marcelo Salhab Brogliato + +# Summary +[summary]: #summary + +Nano contracts enable users to create distributed programs to execute complex operations. It is a simplified version of Ethereum's smart contracts. + +# Motivation +[motivation]: #motivation + +Nano contracts enable the community to create decentralized applications where the rules are immutable and users can interact with them. + +It is expected that this new feature will enable other use cases to migrate to Hathor. + +# Guide-level explanation +[guide-level-explanation]: #guide-level-explanation + +## Basics + +Hathor currently implements the UTXO model which means each transaction has one or more outputs and each output can be spent just once. Any attempt to spent an output more than once is considered a double spending attempt and the consensus algorithm will ensure that at most one of the attemps will be executed (and all the others will be voided). + +Each output controls some funds (e.g. 1 HTR) and has a script with the rules to spend it (i.e. transfer the funds). A very common script is the Pay-to-Pubkey-Hash (P2PH) which means the ouput can only be spent by the owner of the private key associated with it. Another common script is the Pay-to-Script-Hash (P2SH) which enables multisig among other rules. The multisig rule requires M out of N valid signatures to spend the output. + +It is called the UTXO model because the funds are "stored" in Unspent Transaction Outputs that can be used only once. So, one can say that a transaction spends some utxos and create new ones. In other words, the transaction spent the inputs (the utxos of other transactions) and create new outputs that will be used in the future. + +Wallets are managers of private keys and utxos. When Alice sends 10 HTR to Bob, Alice's wallet creates a transactions that spends one of Alice's utxos and create a new output that can be spent only by Bob. Let's say Alice has a total of 14 HTR split in two utxos (one with 6 HTR and the other with 8 HTR), so Alice's wallet must create a transaction with two inputs (one for each utxo) and two outputs. The first output is for Bob with 10 HTR and the second output is for herself with a change of 4 HTR. After the transaction is confirmed by the network, Alice will have spent her two utxos that will never be used again, and she will have created two new utxos. Transaction 3 in the image below is Alice's transaction. + +
+     ┌───────────────────────────────────┐           ┌───────────────────────────────────┐
+     │           Transaction 1           │           │           Transaction 3           │
+     └┬────────────────┬────────────────┬┘           └┬────────────────┬────────────────┬┘
+      │┌──────────────┐│┌──────────────┐│             │┌──────────────┐│┌──────────────┐│
+◀─────┼┼─  Input 1    │││   Output 1  ◀┼┼─────────────┼┼─  Input 1    │││   Output 1   ││
+      │└──────────────┘││    6 HTR     ││             │├──────────────┤││    10 HTR    ││
+      │                │└──────────────┘│      ┌──────┼┼─  Input 2    ││└──────────────┘│
+      │                │┌──────────────┐│      │      │└──────────────┘│┌──────────────┐│
+      │                ││   Output 2   ││      │      │                ││   Output 2   ││
+      │                ││    1 HTR     ││      │      │                ││     4 HTR    ││
+      │                │└──────────────┘│      │      │                │└──────────────┘│
+      │                │┌──────────────┐│      │      └────────────────┴────────────────┘
+      │                ││   Output 3   ││      │
+      │                ││    2 HTR     ││      │
+      │                │└──────────────┘│      │
+      └────────────────┴────────────────┘      │
+                                               │
+     ┌───────────────────────────────────┐     │
+     │           Transaction 2           │     │
+     └┬────────────────┬────────────────┬┘     │
+      │┌──────────────┐│┌──────────────┐│      │
+◀─────┼┼─  Input 1    │││   Output 1   ││      │
+      │└──────────────┘││    4 HTR     ││      │
+      │                │└──────────────┘│      │
+      │                │┌──────────────┐│      │
+      │                ││   Output 2  ◀┼┼──────┘
+      │                ││    8 HTR     ││
+      │                │└──────────────┘│
+      │                │┌──────────────┐│
+      │                ││   Output 3   ││
+      │                ││    8 HTR     ││
+      │                │└──────────────┘│
+      └────────────────┴────────────────┘
+
+ +In the image above, we say that __Transaction 3__ is spending outputs from __Transaction 1__ and __Transaction 2__. More specifically, we say that __Input 1__ of __Transaction 3__ is spending the first output of __Transaction 1__, while __Input 2__ of __Transaction 3__ is spending the second output of __Transaction 2__. + +## Nano Contracts + +The following table summarizes the primary differences between transactions and nano contracts. + +``` + │ Transactions │ Nano Contracts +─────────────────────────────┼────────────────────┼───────────────────────── +Where are the funds stored? │ Independent UTXOs │ Inside the contracts +Who controls the funds? │ Scripts │ Methods +State │ Stateless │ Stateful +Validation time │ Real-time │ After first block +``` + +Let's do a more in-depth analysis of each row of this comparison table. + +### Where are the funds stored? + +While transactions store funds in independent UTXOs, nano contracts aggregate the funds in themselves. + +For example, let's say that Alice has 1,000 HTR split into four independent UTXOs. By independent, it means that each UTXO can be spent regardless of the others. In other words, they do not have to stick together. In fact, Alice can split her tokens into as many UTXOs as she wants, and each of them can use a different address. + +On the other hand, nano contracts store the funds combined inside themselves. When Alice sends 1,000 HTR to a nano contract, her deposit is combined to whatever have been previous deposited in that nano contract. In this case, there is no such thing as UTXO for nano contracts. + +### Who controls the funds? + +Each UTXO has a script that will determine the rules to transfer its funds. A common type of script is the P2PKH where the funds can be transferred by the owner of a given private key. + +Nano contracts work differently because the funds are combined inside themselves. Nano contracts have methods that decide whether a deposit or withdraw can occur. In other words, if Alice wants to deposit 1,000 HTR in a given nano contract, she will call a method that will decide whether she can do it or not. Methods can be executed multiple times. + +While each UTXO can be spent only once, a nano contract's method can be called multiple times. + +### State + +Another difference is that transactions are stateless while nano contracts are stateful. + +Being stateless means that, when a transaction's script is deciding whether the funds can be transferred or not, it has no record of previous transfers and the decision has to be handled based entirely on information that comes with it. + +On the other hand, nano contracts have backing storages where they can keep track of previous interactions. So, when Alice deposit 1,000 HTR in a nano contract to bet Brazil will win the World Cup, the contract can record that Alice has bet 1,000 HTR in Brazil. + +As transactions are stateless, the order they are validated does not matter. Even if two or more transactions want to spend the same UTXO, they can all be valid. For clarity, in case of conflicting transactions, the consensus algorithm will void all of them except one. + +As nano contracts are stateful, the result of a call to a nano contract's method might depend on previous calls. In other words, the order the calls are validated impacts the results. + +### Blueprints + +Nano contracts are instances of blueprints, just like objects are instances of classes in object-oriented programming languages. + +It means that all variables and methods are defined in blueprints. A nano contracts is created through a call to a blueprint's constructor. + +The separation between blueprints and nano contracts is what makes nano contracts safe and simple to use. For instance, if a user trust the Lottery Blueprint, they can safely create a Lottery Nano Contract with a specific setting through the initialization parameters of the blueprint. + +A blueprints can inherit from other blueprint. In this case, when a nano contract's method is called, the full node will look up the method in the blueprint. If it is not found, the full node will recursively look up the method in the blueprint's parent. + +Notice that blueprints do not have state. Only nano contracts have states. + +The image below shows four nano contracts created from the Betting Blueprint. Each nano contract has its own state, which means they are independent and represent different bettings. + +
+                                    ┌──────────────────────────────────────────┐
+┌─────────────────────┐      ┌──────│Nano Contract #1                          │
+│  Betting Blueprint  │      │      │Id: 0000a0d9337455b8ddb09607e7c9ee0a718...│
+├─────────────────────┤      │      └──────────────────────────────────────────┘
+│                     │      │      ┌──────────────────────────────────────────┐
+│ constructor(...)    │      ├──────│Nano Contract #2                          │
+│                     │      │      │Id: 00007867ff1fc65e13feb6e5aec591453d1...│
+│ make_a_bet(...)     │◀─────┤      └──────────────────────────────────────────┘
+│                     │      │      ┌──────────────────────────────────────────┐
+│ collect_prize(...)  │      ├──────┤Nano Contract #3                          │
+│                     │      │      │Id: 0000038740a7abea17b7315316779ebb686...│
+│ set_result(...)     │      │      └──────────────────────────────────────────┘
+│                     │      │      ┌──────────────────────────────────────────┐
+└─────────────────────┘      └──────┤Nano Contract #4                          │
+                                    │Id: 0000b09db96194fc3cb09f5e1afd6fca65c...│
+                                    └──────────────────────────────────────────┘
+
+ +### Interaction between Nano Contracts and Transactions + +There are three ways to interact with a nano contract: + +1. Deposit tokens into the nano contract. +2. Withdraw tokens from the nano contract. +3. Call a method of the nano contract. + +The deposit is made through a transaction with an output that calls a method of the nano contract. The method will validate whether the deposit is valid. For instance, the transaction below is depositing 1,000 HTR into the Nano Contract #1: + +
+┌─────────────────────────────────────────────┐
+│                Transaction A                │
+├──────────────────────┬──────────────────────┤
+│        Inputs        │       Outputs        │
+├──────────────────────┼──────────────────────┤
+│                      │  1,000 HTR           │
+│       200 HTR        │  Nano Contract #1    │
+│                      │  make_a_bet(BRA)     │
+├──────────────────────┼──────────────────────┤
+│                      │                      │
+│       900 HTR        │  100 HTR (change)    │
+│                      │                      │
+└──────────────────────┴──────────────────────┘
+
+ +The withdrawal is made through a transaction with an input that calls a method of the nano contract. The method will validate whether the widthdrawal is valid. For instance, the transaction below is withdrawing 2,500 HTR from the Nano Contract #1: + +
+┌────────────────────────────────────────────────────────────┐
+│                       Transaction B                        │
+├─────────────────────┬──────────────────────────────────────┤
+│       Inputs        │               Outputs                │
+├─────────────────────┼──────────────────────────────────────┤
+│  2,500 HTR          │  2,500 HTR                           │
+│  Nano Contract #1   │  P2PKH                               │
+│  collect_prize()    │  HBpGrjZcYNVJrZTy4Yct7HtUdLM1ToCPi3  │
+└─────────────────────┴──────────────────────────────────────┘
+
+ +Finally, a simple call to a method is similar to a deposit or withdrawal but with amount zero. + +Notice that a single transaction can have multiple deposits, withdrawals, and calls, even to different nano contracts. This allows a multitude of advanced uses. + +For instance, the transaction below is withdrawing 1,000 HTR from Nano Contract #1, depositing 200 HTR into Nano Contract #2, depositing 300 HTR in Nano Contract #3, and sending 500 HTR to an address. + +
+┌────────────────────────────────────────────────────────────┐
+│                       Transaction C                        │
+├─────────────────────┬──────────────────────────────────────┤
+│       Inputs        │               Outputs                │
+├─────────────────────┼──────────────────────────────────────┤
+│  1,000 HTR          │  200 HTR                             │
+│  Nano Contract #1   │  Nano Contract #2                    │
+│  collect_prize()    │  make_a_bet(BRA)                     │
+├─────────────────────┼──────────────────────────────────────┤
+│                     │  300 HTR                             │
+│                     │  Nano Contract #3                    │
+│                     │  make_a_bet(BRA)                     │
+├─────────────────────┼──────────────────────────────────────┤
+│                     │  500 HTR                             │
+│                     │  P2PKH                               │
+│                     │  HDEJ453QjK7F7jipQebRymsRpnFG8j2DHk  │
+└─────────────────────┴──────────────────────────────────────┘
+
+ +To clarify, transactions will only be valid if the sum of the inputs equals the sum of the outputs. + +### Validation Time + +Transactions are validated as soon as they arrive in the full node. If they are valid, they are immediately added to the DAG of transactions. Otherwise, they are discarded. + +On the other hand, as to nano contracts, the result of a call to a method depends on the order the calls are executed. It means that the full node can only execute calls when the order of execution is well defined and the same for all peers in the network. + +The order of execution cannot depend on the order the calls arrived in the full node. So, when can a call be safely executed? Only after it is confirmed by a block. It means that calls in the mempool must wait to be confirmed. + +### Order of Execution + +The calls execution order is defined by the accumulated weight of the transactions, from higher to lower. + +Inside a transaction, the calls are executed following the order of the inputs and then following the order of the outputs. + +For instance, let's say all transactions in the image below are making calls to nano contracts. The order of execution will be: + +![fig-nano-contract-execution-order](./0000-images/nano-contracts-execution-order.svg "Nano Contract Execution Order") + +TODO Add `acc_weight` to each tx in the image above and explain the ordering here. + +## Examples + +### Group Betting + +Betting using data from the real world requires an Oracle. Imagine a predict-a-score game where any person can place a bet and the funds will be split among the winners. In this case, each participants will transfer funds to the Nano Contract. At the end of the game, an Oracle will provide the game results and the winners will be able to collect their prize. Such Nano Contract would have the following blueprint: + +```python +Result = NewType('Result', str) + +class BetItem: + total: Amount + bets: Dict[Address, Amount] + +class Bet(Blueprint): + bets: Dict[Result, BetItem] + withdrawals: Dict[Address, Amount] + total: Amount + oracle_script: TxOutputScript + date_last_offer: Timestamp + final_result: Result + token_id: TokenId + + @public + def make_a_bet(self, address: Address, amount: Amount, score: str) -> None: + """Make a bet.""" + if timestamp > date_last_offer: + self.fail('cannot place bets after 2021-01-01 17:00') + if txout.token_id != self.token_id: + self.fail('invalid token') + self.bets[score].add_bet(address, amount) + self.total += amount + + @public + def set_result(self, result: Signed[Result]) -> None: + """Set result. Used by the oracle.""" + assert result.checksig(self.oracle_script) + self.final_result = result.data + + @public + def withdraw(self, address: SignedAddress, amount: Amount) -> None: + """Withdraw funds after the result is given.""" + if not self.result: + self.fail('result not available yet') + if not address.validate(): + self.fail('proof of ownership failed') + allowed = self.get_max_withdrawal(address) + if amount > allowed: + self.fail('amount is greater than allowed') + self.withdrawals[address] += amount + + def get_winner_amount(self, address) -> Amount: + item = self.bets[self.final_result] + return item.bets[address] * item.total / self.total + + def get_max_withdrawal(self, address) -> Amount: + winner_amount = self.get_winner_amount(address) + return winner_amount - self.withdrawals[address] +``` + +### Liquidity Pool + +TODO How to allow slippage? Credit system where users will withdraw in a second transaction. +TODO How to handle multiple calls? Add an execution context? + +#### Swap Transaction + +Swap(20 TKB => 10 TKA) + +Slippage: Send extra TKB and get the remaining in credits. + +in: +- 10 TKA, `swap()` +- 20 TKB, regular txin + +out: +- 20 TKB, `swap()` +- 10 TKA, script p2pkh + +#### Add Liquidity + +AddLiquidity(20 TKB, 10 TKA) + +Slippage: Send extra TKB or TKA and get the remaining in credits. + +in: +- 10 TKA, regular txin +- 20 TKB, regular txin + +out: +- 10 TKA, `add_liquidity()` +- 20 TKB, `add_liquidity()` + +```python +class MyContext(ExecutionContext): + in: Amount + out: Amount + +class LiquidityPools(Blueprint): + token_a_id: TokenId + token_b_id: TokenId + + credit_a: Dict[Address, Amount] + credit_b: Dict[Address, Amount] + + @public + def swap(...): + total_a, total_b = self.reserve() + + if txin.token_id != self.token_a_id: + self.fail('not implemented') + + in_a = tx.balance(self.token_a_id) + out_b = tx.balance(self.token_b_id) + + used_b = in_a * total_b / total_a + if used_b > out_b: + self.fail('not implemented') + + self.credit_b[address] += out_b - used_b + + def add_liquidity(...): + pass + + def remove_liquidity(...): + pass + + def claim_fee(...): + pass + + def withdraw_credits(...): + pass + + def pair(...): + return (self.token_a_id, self.token_b_id) + + def token(): + pass + + def reserve(): + total_a = self.get_balance(self.token_a_id) + total_b = self.get_balance(self.token_b_id) + + reserve_a = total_a - self.credit_a.total + reserve_b = total_b - self.credit_b.total + + return (reserve_a, reserve_b) +``` + +# Reference-level explanation +[reference-level-explanation]: #reference-level-explanation + +## Blueprints + +For the PoC, blueprints will be hard-coded in the full node. + +Each blueprint will have a unique identifier that will be used by nano contracts. + +## Nano Contracts + +Nano contracts will be vertices in the DAG with the following attributes: + +- Blueprint unique identifier: hash(32 bytes) +- Data for the constructor: script(push only) + +### State + +State of nano contracts will be stored in disk by the full node. + +TODO Format in disk. +TODO How to update the state of a nano contract when a transaction is voided? + +## NanoContractTxIn & NanoContractTxOut + +Attributes: +- `amount` +- `nano_contract_id` +- `method(arguments)` + +We will need to refactor `TxInput` to add `get_value()` and `validate()` methods. + +## Execution of Transactions + +As the order of execution follows the accumulated weight, a transaction in the mempool can move from valid to invalid if a new transaction with higher accumulated weight arrives. + +TODO What happens when a transaction without first block has higher accumulated weight than one with first block (impossible?!) +TODO How to validate a transaction added to the mempool? +TODO Should we require at least one block before executing a transaction? + +## Examples + +### Group Betting + +Betting using data from the real world requires an Oracle. Imagine a predict-a-score game where any person can place a bet and the funds will be split among the winners. In this case, each participants will transfer funds to the Nano Contract. At the end of the game, an Oracle will provide the game results and the winners will be able to collect their prize. Such Nano Contract would have the following blueprint: + +```python +class BetItem: + total: Amount = 0 + bets: Dict[Address, Amount] = 0 + + def add_bet(self, address: Address, amount: Amount): + self.bets[address] += amount + self.total += amount + +class Bet(Blueprint): + bets: Dict[str, BetItem] + withdrawals: Dict[Address, Amount] + total: Amount + oracle_script: TxOutputScript + date_last_offer: Timestamp + result: str + token_id: TokenId + + @public + def make_a_bet(self, address: Address, amount: Amount, score: str) -> None: + """Make a bet.""" + if timestamp > date_last_offer: + self.fail('cannot place bets after 2021-01-01 17:00') + if txout.token_id != self.token_id: + self.fail('invalid token') + self.bets[score].add_bet(address, amount) + self.total += amount + + @public + def set_result(self, result: str) -> None: + """Set result. Used by the oracle.""" + assert checksig(self.oracle_script) + self.result = result + + @public + def withdraw(self, address: SignedAddress, amount: Amount) -> None: + """Withdraw funds after the result is given.""" + if not self.result: + self.fail('result not available yet') + if not address.validate(): + self.fail('proof of ownership failed') + allowed = self.get_max_withdrawal(address) + if amount > allowed: + self.fail('amount is greater than allowed') + self.withdrawals[address] += amount + + def get_winner_amount(self, address) -> Amount: + item = self.bets[self.result] + return item.bets[address] * item.total / self.total + + def get_max_withdrawal(self, address) -> Amount: + winner_amount = self.get_winner_amount(address) + return winner_amount - self.withdrawals[address] +``` + +### Liquidity Pool + +TODO How to allow slippage? Credit system where users will withdraw in a second transaction. +TODO How to handle multiple calls? Add an execution context? + +#### Swap Transaction + +Swap(20 TKB => 10 TKA) + +Slippage: Send extra TKB and get the remaining in credits. + +in: +- 10 TKA, `swap()` +- 20 TKB, regular txin + +out: +- 20 TKB, `swap()` +- 10 TKA, script p2pkh + +#### Add Liquidity + +AddLiquidity(20 TKB, 10 TKA) + +Slippage: Send extra TKB or TKA and get the remaining in credits. + +in: +- 10 TKA, regular txin +- 20 TKB, regular txin + +out: +- 10 TKA, `add_liquidity()` +- 20 TKB, `add_liquidity()` + +```python +class MyContext(ExecutionContext): + in: Amount + out: Amount + +class LiquidityPools(Blueprint): + token_a_id: TokenId + token_b_id: TokenId + + credit_a: Dict[Address, Amount] + credit_b: Dict[Address, Amount] + + @public + def swap(...): + total_a, total_b = self.reserve() + + if txin.token_id != self.token_a_id: + self.fail('not implemented') + + in_a = tx.balance(self.token_a_id) + out_b = tx.balance(self.token_b_id) + + used_b = in_a * total_b / total_a + if used_b > out_b: + self.fail('not implemented') + + self.credit_b[address] += out_b - used_b + + def add_liquidity(...): + pass + + def remove_liquidity(...): + pass + + def claim_fee(...): + pass + + def withdraw_credits(...): + pass + + def pair(...): + return (self.token_a_id, self.token_b_id) + + def token(): + pass + + def reserve(): + total_a = self.get_balance(self.token_a_id) + total_b = self.get_balance(self.token_b_id) + + reserve_a = total_a - self.credit_a.total + reserve_b = total_b - self.credit_b.total + + return (reserve_a, reserve_b) +``` + + + +# Drawbacks +[drawbacks]: #drawbacks + +# Rationale and alternatives +[rationale-and-alternatives]: #rationale-and-alternatives + +## Nano Contracts with UTXO only + +In the UTXO model, the P2SH Script is a clever approach to create Nano Contracts because they have an address associated to each script. The methods of the Nano Contracts would be leafs in a Merkle Tree and the hash of the root will be used to generate the P2SH Address. The Nano Contracts would have to be published off-chain and users would interact with a Nano Contract spending a utxo and putting the desired method (and merkle path) in the input. + +These are some disadvantages to this approach that made me abadon it: (i) each transaction will replicate the code to be executed, requiring more storage and bandwidth, (ii) if many users would like to interact with the same Nano Contract at the same time, their transactions might be in conflict and some of them will have to regenerate their transactions, (iii) the funds of a Nano Contract will be split among many utxos and the wallet will have to select the right utxos when building a new transaction, and (iv) there's no way to store extra pieces of information. + +# Prior art +[prior-art]: #prior-art + +# Unresolved questions +[unresolved-questions]: #unresolved-questions + +1) Will we allow Nano Contracts to call methods from other Nano Contracts? + +We can do it to allow Nano Contracts to exchange funds between them. For example: +``` +deposit(other_nano.method, self.address, 10 TKA) +withdraw(other_nano.method, self.address, 10 TKA) +``` + +2) How to name and represent addresses? + +Type of addresses: +- P2PKH +- P2SH +- Nano Contract's internal address + +Should we use an address or a TxOutput's script? +If we decide to use scripts, what's the meaning of a timelock in a deposit? + + +## Generation of random values + +Even though it might be useful, it is far from simple because all full-nodes must generate exactly the same value to be able to verify the transaction. As full-nodes must generate the same value, it might be a predictable value which is a deal breaker for many uses. + +One idea might be to use the hash of the first block to confirm the transaction as a random seed (e.g. `sha256d(block.hash)`). If the hashrate is big enough and decentralized, it might be accepted as random enough. This would prevent the full-node from executing some transactions before it gets confirmed by a block and requires further analysis of impact. + +## Hathor Virtual Machine + +- Conversa Jan + - Modelo de gas mas gratuito até certo valor. + - Começar built-in e depois abrir para o público enviar. + - Explicar sobre a questão do estado e dar exemplos em que uma tx é válida em T1 e inválida em T2. + - Talvez permitir loop mas com gas limitado. + +- All instructions must be O(1) and no loops are allowed. + - Example: `x in mylist` is not allowed because it is O(n). +- Maximum number of instructions per method? +- Should we charge a fee per instruction? +- Enumerate variables and associate each one with a register. + - Maximum number of registers? +- Flags + - N: Negative + - Z: Zero + - C: Carry or unsigned overflow + - V: Signed overflow + +### Instruction Set + +Maybe we should get inspiration in [Python's bytecode](https://docs.python.org/3/library/dis.html#bytecodes). + +1. `JMP`: Unconditional jump (always relative). +2. `JLE`: Jump if less than or equal. (`(Z == 1) || (N != V)`) +3. `JLT`: Jump if less than. (`N != V`) +4. `JGE`: Jump if greater than or equal. (`N == V`) +5. `JGT`: Jump if greater than. (`(Z == 0) && (N == V)`) +6. `JNE`: Jump if not equal. (`Z == 0`) +7. `JEQ`: Jump if equal. (`Z == 1`) +8. `CMP`: Compare two values and update the flags. + +### Examples + +- `a += 1` will be compiled to `INC reg[A]`, where `regA` is the register associated with variable `a`. +- `a = a + b` will be compiled to `ADD reg[A] reg[A] reg[B]`. + +``` +if a > 5: | CMP reg[A] const[5] + | JLE mark1 + a += 1 | INC reg[A] + | JMP mark2 +else: | mark1: + a -= 1 | DEC reg[A] + | mark2: +``` + +The assembler will translate the assembly code into object files. + +## Matching for Transactions + +- Regexp for transactions + - Index: `txin[0].token_idx == 0` + - Any: `txin[?].token_idx == 1` + - All: `txin[*].token_idx == 0` + - `txout[0].amount < 500` + - `txout[?].token_idx == 1` + - `tokens[1] == "HTR"` + - `txin.length == 2` + - `txout.length == 3` + - `tokens.length == 1` + +For example, `type == Transaction && tokens.length == 0 && txin.length == 1 && txout.length <= 2 && txin[*].authority == False && txout[*].authority == False`. + +## Overall Costs & Fees + +As Nano Contracts will not be turing complete and the method will be fast to process, its economics might be similar to custom tokens. In other words, no fees will be chaged to execute methods in Nano Contracts and the transaction's PoW will be the spam control mechanism. + +Will the network charge fees for transactions executing a nano contract? No. + + + +# Future possibilities +[future-possibilities]: #future-possibilities +