Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Add transaction fee #25

Merged
merged 10 commits into from Sep 10, 2021
Merged

[WIP] Add transaction fee #25

merged 10 commits into from Sep 10, 2021

Conversation

qti3e
Copy link
Contributor

@qti3e qti3e commented Sep 2, 2021

This PR adds a constant transaction fee of 2B cycles for every transaction, keeps track of total volume of captured fees and handles the upgrade logic required for deployment.

@qti3e qti3e requested a review from kanej September 2, 2021 19:16
@kanej
Copy link
Contributor

kanej commented Sep 2, 2021

Can we get unit test coverage of each of the particular calls confirming that fees are taken and that they come through on the transactions.

xtc/src/fee.rs Outdated
@@ -0,0 +1,9 @@
/// Compute the fee that should be taken for a transaction based on the total amount
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the comment above kind of contradicts the implementation which doesn't take the amount into account :)

I think it is a good idea to use a fixed amount as you did, but maybe we can improve on it.
We could do something like this:
fee = min(max(2_000_000_000, amount * X%), 10_000_000_000)

so if someone transfers a larger amount that he pays more, but there is a cap of 10_000_000_000. This cap would encourage people who want to transfer let's say 10^16 cycles to use only 1 transaction instead of breaking it up to multiple smaller transactions.

regarding floating point calculations(* X%), web assembly is using an IEEE standard, so regardless of the architecture of the IC nodes, the result is the same on all nodes
https://webassembly.github.io/spec/core/syntax/types.html

@@ -194,10 +198,12 @@ async fn burn(args: BurnArguments) -> Result<TransactionId, BurnError> {
Ok(()) => {
let refunded = api::call::msg_cycles_refunded();
let cycles = args.amount - refunded;
let new_fee = compute_fee(cycles);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice logic, I was just thinking to make the variable names a little bit more descriptive for example:
fee => fee_deducted
new_fee = actual_fee

xtc/src/stats.rs Outdated
@@ -19,8 +33,9 @@ pub struct StatsData {

impl Default for StatsData {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmmm, wouldn't the following code do the same?

#[derive(Default)]
struct StatsData{...}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes! Thank you

let ledger = storage::get_mut::<Ledger>();

ledger
.withdraw(&user, args.amount)
.withdraw(&user, args.amount + fee)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a particular reason why the withdraw doesn't return the 'TransferError::InsufficientBalance'? I see this .map_err(|_| TransferError::InsufficientBalance) in almost all places when we call the withdraw method.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, because TransferError is not always what we return, look at cycles_wallet.rs file, there we want to have send error as a string.

But we should refactor it to send WithdrawError and take overflows into account in that level.

let ledger = storage::get_mut::<Ledger>();
ledger
.withdraw(&user, args.cycles)
.withdraw(&user, args.cycles + fee)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

☠️

this line looks innocent, but it is phrone to integer overflow and could result of Dank being hacked. If an attacker sends args.cycles = uint64.max - 2000, then args.cycles + fee will overflow and will be 0, so the attackers balance doesn't decrease and later he will receive uint64.max - 2000.

Of course it is unlikely that our canister have that many cycles on it (impossible right now thanks to the canister have a limit on the number of cycles). However we should never have integer overflows in our smart contracts. Rust have safe math functions they throw when integer overflow or underflow happens, I think we should use those.

#[derive(Deserialize, CandidType, Clone)]
pub struct StatsData {
supply: Nat,
fee: Nat,
Copy link

@ferencdg ferencdg Sep 3, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice that you thought about version, but should we change the candid file too? If I am not mistaken adding a new field is a compatible change. "Existing result types may be changed, but only to a subtype of the previous type."

I am actually curious if a client having an older version of the candid can still deserialize the result produced by the new service. Not sure if it is worth investigating now though, as we don't have many existing clients.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes we should change the candid

@ferencdg
Copy link

ferencdg commented Sep 3, 2021

we will need to think hard how we go live with this PR, as the current StatsData in the stable memory has a different format than the one this PR introduces

@kanej
Copy link
Contributor

kanej commented Sep 7, 2021

we will need to think hard how we go live with this PR, as the current StatsData in the stable memory has a different format than the one this PR introduces

Are these additions? Or a property renames/type changes?

@ferencdg
Copy link

ferencdg commented Sep 7, 2021

we will need to think hard how we go live with this PR, as the current StatsData in the stable memory has a different format than the one this PR introduces

Are these additions? Or a property renames/type changes?

Fee was added to the StatsData and StatsData is stored in stable memory. We should at least test in dev to see if the new code will be able to initialize StatsData from stable memory which holds the older version of StatsData.

I know there are backword compatibility guarantees with the candid interface, but not sure if there is any guarantee with regards to stable memory conversion. If depends if they serialize stucts in a descreptive way or just serialize the raw data.

@qti3e
Copy link
Contributor Author

qti3e commented Sep 7, 2021

@ferencdg The upgrade from the stable memory is taken into account in the code. For every version we keep the data structure used in the previous version, usually append a V0 to the name. The current canister that is deployed will write the data of the format StableStorageV0 into the stable storage once the pre_upgrade is called on it. Then the canister is stopped. And the new WASM is installed, now the new WASM will read StableStorageV0 from the stable storage and convert it to a StableStorage and restore the state of the heap. Now upon the next upgrade the data for StableStorage is written to the stable storage and the next version will read it, and either load it directly to heap or perform a conversion if needed.

@coveralls
Copy link

coveralls commented Sep 10, 2021

Pull Request Test Coverage Report for Build 1221943498

  • 168 of 200 (84.0%) changed or added relevant lines in 7 files are covered.
  • 1 unchanged line in 1 file lost coverage.
  • Overall coverage increased (+14.7%) to 58.536%

Changes Missing Coverage Covered Lines Changed/Added Lines %
xtc/src/history.rs 2 3 66.67%
xtc/src/upgrade.rs 0 2 0.0%
xtc/src/cycles_wallet.rs 19 22 86.36%
xtc/src/ledger.rs 18 23 78.26%
xtc/src/stats.rs 6 27 22.22%
Files with Coverage Reduction New Missed Lines %
xtc/src/upgrade.rs 1 0%
Totals Coverage Status
Change from base Build 1218284012: 14.7%
Covered Lines: 1615
Relevant Lines: 2759

💛 - Coveralls

xtc/src/cycles_wallet.rs Outdated Show resolved Hide resolved
xtc/src/ledger.rs Outdated Show resolved Hide resolved
xtc/src/ledger.rs Outdated Show resolved Hide resolved
@qti3e qti3e changed the title Add transaction fee [WIP] Add transaction fee Sep 10, 2021
@kanej kanej merged commit ce9dcf8 into main Sep 10, 2021
@kanej kanej deleted the feat/fee branch September 10, 2021 16:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants