Hello everyone, here is a proposal to add a new system call in the core of koinos blockchain called get_contract_metadata. This system call is needed to improve the security of the tokens and NFTs.
Before digging into the details, let's take a look to Ethereum, how they handle the authorizations, and then let's come back to koinos and see the differences.
Ethereum
Let's say you call the contrat A. This contract calls the contract B. This one the contract C. And this final contract tries to make a transfer of tokens from your account to another address. In summary is A -> B -> C -> transfer wEth. What is the process to authorize this transaction? In ethereum there are 2 addresses:
tx.origin
: This is the external owned account (EOA), that is, the user that signs the transaction.msg.sender
: This is the address that triggers the transfer. It could be a contract account or again the tx.origin. In our example it is the Contract C, the address that triggered the transfer.
The ERC20 token contract only allows to move tokens from the msg.sender
account when using the transfer
function. That is, the transfer is done only if you make the transfer directly. But if you are tx.origin
and the contract in the middle is msg.sender
then your funds are safe because tx.origin
is not involved in this process.
But then, how the contract C can actually move the tokens from the user? the answer is the transferFrom
function. In order to add more options to make transfers in ethereum, they introduced "allowances". So first, you have to allow to a specific contract to spend your tokens. Then this contract can call the transferFrom
function to actually move the tokens.
This allowance has to be set in a previous transaction.
Koinos
Koinos introduced a new model for authorisations. When a transfer of koin is called, the contract does the following process:
If the caller
of the koin contract is the owner of the tokens then the transaction is accepted. This is more or less similar to the msg.sender
of ethereum, but there is a difference: The caller
of koinos is just for contracts, not external accounts. This means that if the user signs a transaction to call the koin contract directly, he is not considered as caller
. The caller is empty in this case.
But then how it works? When the caller is not authorized, the koin contract calls the checkAuthority(from)
system call, where from
is the account that owns the tokens.
This system call works in this way:
- If
from
is a smart contract, then the chain will call it to ask if it authorizes the transfer. Then the transfer is accepted only if this contract replies YES. The advantage is that this contract could contain any logic to determine if the transaction is accepted or not. - If
from
is not a smart contract, then the chain will check if the transaction was signed byfrom
. So, this is like the equivalent oftx.origin
in ethereum.
This last point has a risk, because this means that a contract in the middle can request to make a transfer and it will be accepted because the user signed the transaction in the first place.
Please note that in ethereum, the tx.origin
is not involved in the transaction to not affect his assets. However, in koinos it is taken into account and this can put in risk the assets of the user. Here is a summary of the different scenarios:
Action | Ethereum | Koinos |
---|---|---|
User calls the token contract directly | User is msg.sender . Transfer accepted | User is not caller . User signed the transaction. Transfer accepted |
User has a contract and this one calls the token contract | User is msg.sender . Transfer accepted | User is caller . Transfer accepted |
User calls contract C and this one calls the token contract | User is not msg.sender . Transfer accepted only if the user defined an allowance for C in a previous tx | User is not caller . User signed transaction. Transfer accepted ⚠️ |
User has a contract, calls contract C and this one calls the token contract | User is not msg.sender . Transfer accepted only if the user defined an allowance for C in a previous tx | User is not caller . Transfer accepted only if the contract of the user allows the operation. |
How to improve the security in koinos?
The principal problem is the involvement of the tx.origin
in the authorization. In other words, the problem is that the signature of the user is accepted without taking into account who is the caller
.
The solution is to do something similar to Ethereum (defining allowances), and at the same time trying to keep the good parts of the authorization model of koinos. So the proposal for the token contracts is this one:
// transfer from A to B
if (caller == A) {
// equivalent of msg.sender
isAuthorized = true;
} else if (caller has allowance) {
// equivalent of transferFrom
isAuthorized = true;
} else if (A has a contract) {
// novel authority function of koinos.
// no equivalent in ethereum
isAuthorized = ask_contract_A();
} else if (no caller && tx signer == A) {
// equivalent of msg.sender
isAuthorized = true;
} else {
isAuthorized = false;
}
This logic can not be implemented with the current tools in koinos because the checkAuthority
function is the unique one that can check if the user has a contract, but its problem is that it doesn't take into account the caller when checking the signature.
Koinos proposal - Part 1 - get_contract_metadata
Create a new system call to get the contract metadata of a specific address. Then it can be used to know if an account has a smart contract and or not.
Roamin already created the code for this change. You can check it in this pull request: https://github.com/koinos/koinos-contracts-as/pull/100
Koinos proposal - Part 2 - update token contracts
Once the first part is done, then we can rewrite the token contracts in order to introduce the new authorization logic described above.
As you can notice, the checkAuthority
system call is not used anymore. Instead of that, we define an internal function in the contract to handle this authorization, and it calls the new get_contract_metadata
system call to know if the user has a contract.
I already created a token contract for this change. You can check the implementation here https://github.com/joticajulian/koinos-contracts-as/blob/main/contracts/token/assembly/Token.ts
It still needs the adaptation of the new system call, but it already has the allowances. Koin DX is already using this token contract as reference.
The idea is to use it as standard, and update the KOIN and VHP contracts using this logic.
Polls to vote
These proposals can be voted here:
- New system call to get contract metadata: https://koinosbox.com/polls/1
- Update KOIN and VHP contracts: https://koinosbox.com/polls/2
Edit: I forgot that the authorize function can only be called in kernel mode, and not by normal contracts. This means that the part where the token contract calls the authorize function of the user is not applicable. Then it is better to update the check authority system call and use it in the token contracts. The update consist in protect the user by allowing the signature ONLY if the caller is empty.
With regarding to the get contract metadata system call it is not useful anymore for the token contracts. However, it can be useful for other use cases.