Skip to content

Commit

Permalink
Merge pull request #3132 from dusk-network/w3sper-stake-nonce-3131
Browse files Browse the repository at this point in the history
 w3sper: Update the SDK to match Stake contract changes
  • Loading branch information
ZER0 authored Dec 6, 2024
2 parents 2560a5d + de78faf commit e67917b
Show file tree
Hide file tree
Showing 9 changed files with 634 additions and 405 deletions.
26 changes: 13 additions & 13 deletions w3sper.js/src/bookkeeper.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
// Copyright (c) DUSK NETWORK. All rights reserved.

import * as ProtocolDriver from "../src/protocol-driver/mod.js";
import { ProfileGenerator, Profile } from "./profile.js";
import { Profile, ProfileGenerator } from "./profile.js";

import {
Transfer,
UnshieldTransfer,
ShieldTransfer,
StakeTransfer,
Transfer,
UnshieldTransfer,
UnstakeTransfer,
WithdrawStakeRewardTransfer,
} from "../src/transaction.js";
Expand All @@ -25,14 +25,9 @@ class BookEntry {
}

get info() {
const entry = this;
return {
balance(type) {
return entry.bookkeeper.balance(entry.profile[type]);
},
stake() {
return entry.bookkeeper.stakeInfo(entry.profile.account);
},
balance: (type) => this.bookkeeper.balance(this.profile[type]),
stake: () => this.bookkeeper.stakeInfo(this.profile.account),
};
}

Expand All @@ -52,13 +47,17 @@ class BookEntry {
return new StakeTransfer(this).amount(amount);
}

unstake() {
return new UnstakeTransfer(this);
unstake(amount) {
return new UnstakeTransfer(this).amount(amount);
}

withdraw(amount) {
return new WithdrawStakeRewardTransfer(this).amount(amount);
}

topup(amount) {
return new StakeTransfer(this, { topup: true }).amount(amount);
}
}

export class Bookkeeper {
Expand All @@ -73,12 +72,13 @@ export class Bookkeeper {
switch (type) {
case "account":
return await this.#treasury.account(identifier);
case "address":
case "address": {
const notes = await this.#treasury.address(identifier);
const seed = await ProfileGenerator.seedFrom(identifier);
const index = +identifier;

return ProtocolDriver.balance(seed, index, notes);
}
}
}

Expand Down
20 changes: 8 additions & 12 deletions w3sper.js/src/network/syncer/account.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,25 +29,22 @@ class StakeAmount {
}

/**
* Holds information about a user's stake, including amount, reward,
* and a nonce to prevent repeat attacks. Also tracks faults.
* Holds information about a user's stake, including amount, reward
* and tracks faults.
*/
class StakeInfo {
/** @type {StakeAmount|null} */
amount;
/** @type {bigint} */
reward;
/** @type {bigint} */
nonce;
/** @type {number} */
faults;
/** @type {number} */
hardFaults;

constructor() {
this.amount = null;
this.reward = 0n;
this.nonce = 0n;
this.faults = 0;
this.hardFaults = 0;
}
Expand Down Expand Up @@ -77,9 +74,8 @@ class StakeInfo {
}

stakeInfo.reward = view.getBigUint64(40, true);
stakeInfo.nonce = view.getBigUint64(48, true);
stakeInfo.faults = view.getUint8(56);
stakeInfo.hardFaults = view.getUint8(57);
stakeInfo.faults = view.getUint8(48);
stakeInfo.hardFaults = view.getUint8(49);

return Object.freeze(stakeInfo);
}
Expand Down Expand Up @@ -155,8 +151,8 @@ export class AccountSyncer extends EventTarget {
async balances(profiles) {
const balances = await accountsIntoRaw(profiles).then((rawUsers) =>
rawUsers.map((user) =>
this.#network.contracts.transferContract.call.account(user),
),
this.#network.contracts.transferContract.call.account(user)
)
);

return Promise.all(balances)
Expand All @@ -174,8 +170,8 @@ export class AccountSyncer extends EventTarget {
async stakes(profiles) {
const stakes = await accountsIntoRaw(profiles).then((rawUsers) =>
rawUsers.map((user) =>
this.#network.contracts.stakeContract.call.get_stake(user),
),
this.#network.contracts.stakeContract.call.get_stake(user)
)
);

return Promise.all(stakes)
Expand Down
6 changes: 0 additions & 6 deletions w3sper.js/src/protocol-driver/mod.js
Original file line number Diff line number Diff line change
Expand Up @@ -843,11 +843,6 @@ export const stake = async (info) =>
ptr.nonce = await malloc(8);
await memcpy(ptr.nonce, nonce);

const stake_nonce = new Uint8Array(8);
new DataView(stake_nonce.buffer).setBigUint64(0, info.stake_nonce, true);
ptr.stake_nonce = await malloc(8);
await memcpy(ptr.stake_nonce, stake_nonce);

let tx = await malloc(4);
let hash = await malloc(64);

Expand All @@ -859,7 +854,6 @@ export const stake = async (info) =>
ptr.gas_price,
ptr.nonce,
info.chainId,
ptr.stake_nonce,
tx,
hash,
);
Expand Down
73 changes: 49 additions & 24 deletions w3sper.js/src/transaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export const TRANSFER =

import { AddressSyncer } from "./network/syncer/address.js";
import * as ProtocolDriver from "./protocol-driver/mod.js";
import { ProfileGenerator, Profile } from "./profile.js";
import { Profile, ProfileGenerator } from "./profile.js";
import * as base58 from "./encoders/b58.js";
import { Gas } from "./gas.js";

Expand Down Expand Up @@ -52,7 +52,7 @@ export class Transfer extends BasicTransfer {

to(value) {
let builder;
let identifier = String(value);
const identifier = String(value);
switch (ProfileGenerator.typeOf(identifier)) {
case "account":
builder = new AccountTransfer(this.bookentry);
Expand Down Expand Up @@ -117,7 +117,7 @@ class AccountTransfer extends Transfer {

nonce += 1n;

let [buffer, hash] = await ProtocolDriver.moonlight({
const [buffer, hash] = await ProtocolDriver.moonlight({
sender,
receiver,
transfer_value,
Expand Down Expand Up @@ -183,7 +183,7 @@ class AddressTransfer extends Transfer {
const { chainId } = await network.node.info;

// Create the unproven transaction
let [tx, circuits] = await ProtocolDriver.phoenix({
const [tx, circuits] = await ProtocolDriver.phoenix({
sender,
receiver,
inputs,
Expand Down Expand Up @@ -245,7 +245,7 @@ export class UnshieldTransfer extends BasicTransfer {
const { chainId } = await network.node.info;

// Create the unproven transaction
let [tx, circuits] = await ProtocolDriver.unshield({
const [tx, circuits] = await ProtocolDriver.unshield({
profile,
inputs,
openings,
Expand Down Expand Up @@ -279,7 +279,7 @@ export class ShieldTransfer extends BasicTransfer {
async build(network) {
const { attributes } = this;
const { amount: allocate_value, gas } = attributes;
const { profile, bookkeeper } = this.bookentry;
const { profile } = this.bookentry;

// Get the chain id from the network
const { chainId } = await network.node.info;
Expand All @@ -289,7 +289,7 @@ export class ShieldTransfer extends BasicTransfer {

nonce += 1n;

let [buffer, hash] = await ProtocolDriver.shield({
const [buffer, hash] = await ProtocolDriver.shield({
profile,
allocate_value,
gas_limit: gas.limit,
Expand All @@ -307,35 +307,45 @@ export class ShieldTransfer extends BasicTransfer {
}

export class StakeTransfer extends BasicTransfer {
constructor(from) {
constructor(from, options = {}) {
super(from);
this[_attributes].topup = Boolean(options.topup) || false;
}

async build(network) {
const { attributes } = this;
const { amount: stake_value, gas } = attributes;
const { amount: stake_value, gas, topup: isTopup } = attributes;
const { profile, bookkeeper } = this.bookentry;

const minimumStake = await bookkeeper.minimumStake;

if (stake_value < minimumStake) {
throw new Error(`Stake value must be greater than ${minimumStake}`);
if (!isTopup && stake_value < minimumStake) {
throw new RangeError(
`Stake amount must be greater or equal than ${minimumStake}`,
);
}

// Get the chain id from the network
const { chainId } = await network.node.info;

// Obtain the nonces
// Obtain the infos
let { nonce } = await this.bookentry.info.balance("account");
let { nonce: stake_nonce } = await this.bookentry.info.stake();
const stakeInfo = await this.bookentry.info.stake();
const hasStake = stakeInfo.amount !== null;

if (hasStake && !isTopup) {
throw new Error(
"Stake already exists. Use `topup` to add to the current stake",
);
} else if (!hasStake && isTopup) {
throw new Error("No stake to topup. Use `stake` to create a new stake");
}

nonce += 1n;
stake_nonce += 1n;

let [buffer, hash] = await ProtocolDriver.stake({
const [buffer, hash] = await ProtocolDriver.stake({
profile,
stake_value,
stake_nonce,
gas_limit: gas.limit,
gas_price: gas.price,
nonce,
Expand All @@ -357,7 +367,7 @@ export class UnstakeTransfer extends BasicTransfer {

async build(network) {
const { attributes } = this;
const { gas } = attributes;
const { gas, amount: unstake_amount } = attributes;
const { profile } = this.bookentry;

// Get the chain id from the network
Expand All @@ -367,13 +377,28 @@ export class UnstakeTransfer extends BasicTransfer {
let { nonce } = await this.bookentry.info.balance("account");

// Obtain the staked amount
let { amount } = await this.bookentry.info.stake();
const { amount } = await this.bookentry.info.stake();

const minimumStake = await this.bookentry.bookkeeper.minimumStake;

nonce += 1n;

let [buffer, hash] = await ProtocolDriver.unstake({
const unstake_value =
typeof unstake_amount === "bigint" && unstake_amount < amount.total
? unstake_amount
: amount.total;

const remainingStake = amount.total - unstake_value;

if (remainingStake > 0n && remainingStake < minimumStake) {
throw new RangeError(
`Remaining stake must be greater or equal than ${minimumStake}`,
);
}

const [buffer, hash] = await ProtocolDriver.unstake({
profile,
unstake_value: amount.total,
unstake_value,
gas_limit: gas.limit,
gas_price: gas.price,
nonce,
Expand Down Expand Up @@ -405,23 +430,23 @@ export class WithdrawStakeRewardTransfer extends BasicTransfer {
let { nonce } = await this.bookentry.info.balance("account");

// Obtain the staked amount
let { reward } = await this.bookentry.info.stake();
const { reward } = await this.bookentry.info.stake();

if (!reward) {
throw new Error(`No stake available to withdraw the reward from`);
} else if (reward_amount > reward) {
throw new Error(
throw new RangeError(
`The withdrawn reward amount must be less or equal to ${reward}`,
);
} else if (!reward_amount) {
throw new Error(
throw new RangeError(
`Can't withdraw an empty reward amount. I mean, you could, but it would be pointless.`,
);
}

nonce += 1n;

let [buffer, hash] = await ProtocolDriver.withdraw({
const [buffer, hash] = await ProtocolDriver.withdraw({
profile,
reward_amount,
gas_limit: gas.limit,
Expand Down
2 changes: 1 addition & 1 deletion w3sper.js/tests/assets/genesis.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[[stake]]
address = 'oCqYsUMRqpRn2kSabH52Gt6FQCwH5JXj5MtRdYVtjMSJ73AFvdbPf98p3gz98fQwNy9ZBiDem6m9BivzURKFSKLYWP3N9JahSPZs9PnZ996P18rTGAjQTNFsxtbrKx79yWu'
amount = 1_000_000_000_000
amount = 2_000_000_000_000

[[moonlight_account]]
address = 'oCqYsUMRqpRn2kSabH52Gt6FQCwH5JXj5MtRdYVtjMSJ73AFvdbPf98p3gz98fQwNy9ZBiDem6m9BivzURKFSKLYWP3N9JahSPZs9PnZ996P18rTGAjQTNFsxtbrKx79yWu'
Expand Down
2 changes: 1 addition & 1 deletion w3sper.js/tests/harness.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ const mergeMap = (dest, source, lookup) => {
};

export {
test,
assert,
test,
} from "http://rawcdn.githack.com/mio-mini/test-harness/0.1.1/mod.js";

import { Bookmark } from "@dusk/w3sper";
Expand Down
Loading

0 comments on commit e67917b

Please sign in to comment.