Skip to content

Commit

Permalink
Account new signatures finished
Browse files Browse the repository at this point in the history
  • Loading branch information
marcvelmer committed Oct 4, 2023
1 parent 7e47818 commit 734c079
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 58 deletions.
14 changes: 8 additions & 6 deletions src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ export class VocdoniSDKClient {
.then((address) => AnonymousService.calcSik(address, sik, password))
.then((calculatedSIK) => {
const registerSIKTx = AccountCore.generateRegisterSIKTransaction(electionId, calculatedSIK, censusProof);
return this.accountService.signTransaction(registerSIKTx, wallet);
return this.accountService.signTransaction(registerSIKTx.tx, registerSIKTx.message, wallet);
})
.then((signedTx) => this.chainService.submitTx(signedTx))
.then((hash) => this.chainService.waitForTransaction(hash));
Expand Down Expand Up @@ -334,9 +334,11 @@ export class VocdoniSDKClient {
* @param {Promise<{ tx: Uint8Array; metadata: string }>} promAccountData Account data promise in Tx form.
* @returns {Promise<AccountData>}
*/
private setAccountInfo(promAccountData: Promise<{ tx: Uint8Array; metadata: string }>): Promise<AccountData> {
private setAccountInfo(
promAccountData: Promise<{ tx: Uint8Array; metadata: string; message: string }>
): Promise<AccountData> {
const accountTx = promAccountData.then((setAccountInfoTx) =>
this.accountService.signTransaction(setAccountInfoTx.tx, this.wallet)
this.accountService.signTransaction(setAccountInfoTx.tx, setAccountInfoTx.message, this.wallet)
);

return Promise.all([promAccountData, accountTx])
Expand Down Expand Up @@ -402,13 +404,13 @@ export class VocdoniSDKClient {

return Promise.all([this.fetchAccountInfo(), settings.wallet.getAddress()])
.then(([accountData, fromAddress]) => {
const tx = AccountCore.generateTransferTransaction(
const transferTx = AccountCore.generateTransferTransaction(
accountData.nonce,
fromAddress,
settings.to,
settings.amount
);
return this.accountService.signTransaction(tx, settings.wallet);
return this.accountService.signTransaction(transferTx.tx, transferTx.message, settings.wallet);
})
.then((signedTx) => this.chainService.submitTx(signedTx))
.then((txHash) => this.chainService.waitForTransaction(txHash));
Expand All @@ -429,7 +431,7 @@ export class VocdoniSDKClient {
.then(([account, faucet]) => {
const faucetPackage = this.faucetService.parseFaucetPackage(faucet);
const collectFaucetTx = AccountCore.generateCollectFaucetTransaction(account, faucetPackage);
return this.accountService.signTransaction(collectFaucetTx, this.wallet);
return this.accountService.signTransaction(collectFaucetTx.tx, collectFaucetTx.message, this.wallet);
})
.then((signedTx) => this.chainService.submitTx(signedTx))
.then((hash) => this.chainService.waitForTransaction(hash))
Expand Down
59 changes: 49 additions & 10 deletions src/core/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { Account, AccountMetadata } from '../types';
import { TransactionCore } from './transaction';
import { strip0x } from '../util/common';
import { AccountData, CensusProof, FaucetPackage } from '../services';
import { TxMessage } from '../util/constants';

export abstract class AccountCore extends TransactionCore {
/**
Expand All @@ -30,7 +31,8 @@ export abstract class AccountCore extends TransactionCore {
cid: string,
faucetPackage: FaucetPackage,
sik: string
): { tx: Uint8Array; metadata: string } {
): { tx: Uint8Array; metadata: string; message: string } {
const message = TxMessage.SET_ACCOUNT.replace('{type}', 'CREATE_ACCOUNT');
const txData = this.prepareSetAccountData({
address,
nonce: 0,
Expand All @@ -39,14 +41,18 @@ export abstract class AccountCore extends TransactionCore {
faucetPackage,
sik,
});
return this.generateSetAccountTransaction(txData);
return {
message,
...this.generateSetAccountTransaction(txData),
};
}

public static generateUpdateAccountTransaction(
accountData: AccountData,
account: Account,
cid: string
): { tx: Uint8Array; metadata: string } {
): { tx: Uint8Array; metadata: string; message: string } {
const message = TxMessage.SET_ACCOUNT.replace('{type}', 'SET_ACCOUNT_INFO_URI');
const txData = this.prepareSetAccountData(
{
address: accountData.address,
Expand All @@ -56,7 +62,10 @@ export abstract class AccountCore extends TransactionCore {
},
false
);
return this.generateSetAccountTransaction(txData);
return {
message,
...this.generateSetAccountTransaction(txData),
};
}

private static generateSetAccountTransaction(txData: { metadata: string; accountData: object }): {
Expand All @@ -74,15 +83,29 @@ export abstract class AccountCore extends TransactionCore {
};
}

public static generateCollectFaucetTransaction(accountData: AccountData, faucetPackage: FaucetPackage): Uint8Array {
public static generateCollectFaucetTransaction(
accountData: AccountData,
faucetPackage: FaucetPackage
): { tx: Uint8Array; message: string } {
const txData = this.prepareCollectFaucetData(accountData, faucetPackage);
const message = TxMessage.COLLECT_FAUCET;
const collectFaucet = CollectFaucetTx.fromPartial(txData);
return Tx.encode({
const tx = Tx.encode({
payload: { $case: 'collectFaucet', collectFaucet },
}).finish();

return {
message,
tx,
};
}

public static generateRegisterSIKTransaction(electionId: string, sik: string, proof: CensusProof): Uint8Array {
public static generateRegisterSIKTransaction(
electionId: string,
sik: string,
proof: CensusProof
): { tx: Uint8Array; message: string } {
const message = TxMessage.REGISTER_SIK.replace('{sik}', sik);
const aProof = ProofArbo.fromPartial({
siblings: Uint8Array.from(Buffer.from(proof.proof, 'hex')),
type: ProofArbo_Type.POSEIDON,
Expand All @@ -98,12 +121,26 @@ export abstract class AccountCore extends TransactionCore {
}),
});

return Tx.encode({
const tx = Tx.encode({
payload: { $case: 'registerSIK', registerSIK },
}).finish();

return {
message,
tx,
};
}

public static generateTransferTransaction(nonce: number, from: string, to: string, amount): Uint8Array {
public static generateTransferTransaction(
nonce: number,
from: string,
to: string,
amount: number
): { tx: Uint8Array; message: string } {
const message = TxMessage.SEND_TOKENS.replace('{amount}', amount.toString()).replace(
'{to}',
strip0x(to).toLowerCase()
);
const sendTokens = SendTokensTx.fromPartial({
txtype: TxType.SEND_TOKENS,
nonce: nonce,
Expand All @@ -112,9 +149,11 @@ export abstract class AccountCore extends TransactionCore {
value: amount,
});

return Tx.encode({
const tx = Tx.encode({
payload: { $case: 'sendTokens', sendTokens },
}).finish();

return { tx, message };
}

private static prepareSetAccountData(
Expand Down
9 changes: 7 additions & 2 deletions src/core/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { SignedTx } from '@vocdoni/proto/vochain';
import { Buffer } from 'buffer';
import { strip0x } from '../util/common';
import { Signing } from '../util/signing';
import { keccak256 } from '@ethersproject/keccak256';

export abstract class TransactionCore {
/**
Expand All @@ -13,13 +14,17 @@ export abstract class TransactionCore {

public static async signTransaction(
tx: Uint8Array,
chainId: string,
payload: string,
walletOrSigner: Wallet | Signer
): Promise<string> {
return Signing.signTransaction(tx, chainId, walletOrSigner).then((hexSignature) => {
return Signing.signTransaction(payload, walletOrSigner).then((hexSignature) => {
const signature = new Uint8Array(Buffer.from(strip0x(hexSignature), 'hex'));
const signedTx = SignedTx.encode({ tx, signature }).finish();
return Buffer.from(signedTx).toString('base64');
});
}

public static hashTransaction(tx: Uint8Array): string {
return strip0x(keccak256(tx));
}
}
11 changes: 7 additions & 4 deletions src/services/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,13 @@ export class AccountService extends Service implements AccountServiceProperties
return AccountAPI.setInfo(this.url, tx, metadata).then((response) => response.txHash);
}

async signTransaction(tx: Uint8Array, walletOrSigner: Wallet | Signer): Promise<string> {
async signTransaction(tx: Uint8Array, message: string, walletOrSigner: Wallet | Signer): Promise<string> {
invariant(this.chainService, 'No chain service set');
return this.chainService
.fetchChainData()
.then((chainData) => AccountCore.signTransaction(tx, chainData.chainId, walletOrSigner));
return this.chainService.fetchChainData().then((chainData) => {
const payload = message
.replace('{hash}', AccountCore.hashTransaction(tx))
.replace('{chainId}', chainData.chainId);
return AccountCore.signTransaction(tx, payload, walletOrSigner);
});
}
}
11 changes: 9 additions & 2 deletions src/util/constants.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export const API_URL = {
dev: 'https://api-dev.vocdoni.net/v2',
dev: 'https://celoni.vocdoni.net/v2',
stg: 'https://api-stg.vocdoni.net/v2',
prod: 'https://api.vocdoni.net/v2',
};
Expand All @@ -17,7 +17,7 @@ export const CENSUS3_URL = {
};

export const FAUCET_URL = {
dev: 'https://faucet-azeno.vocdoni.net/faucet/vocdoni/dev',
dev: 'https://celoni.vocdoni.net/v2/faucet/dev',
stg: 'https://faucet-azeno.vocdoni.net/faucet/vocdoni/stage',
};

Expand All @@ -37,3 +37,10 @@ export const VOCDONI_SIK_PAYLOAD =
'Only accept this signature request if you fully trust the application. This request will not trigger a blockchain transaction or cost any gas fees.';

export const CENSUS_CHUNK_SIZE = 8192;

export enum TxMessage {
REGISTER_SIK = 'Signing a Vocdoni transaction of type REGISTER_SIK for secret identity key {sik}. The hash of the transaction is {hash} and the destination chainID is {chainId}}.',
SET_ACCOUNT = 'Signing a Vocdoni transaction of type SET_ACCOUNT/{type}. The hash of the transaction is {hash} and the destination chainID is {chainId}.',
COLLECT_FAUCET = 'Signing a Vocdoni transaction of type COLLECT_FAUCET. The hash of the transaction is {hash} and the destination chainID is {chainId}.',
SEND_TOKENS = 'Signing a Vocdoni transaction of type SEND_TOKENS for an amount of {amount} VOC tokens to destination address {to}. The hash of the transaction is {hash} and the destination chainID is {chainId}.',
}
38 changes: 4 additions & 34 deletions src/util/signing.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,7 @@
import { Wallet } from '@ethersproject/wallet';
import { strip0x } from './common';
import { keccak256 } from '@ethersproject/keccak256';
import { Signer } from '@ethersproject/abstract-signer';
import { JsonRpcSigner } from '@ethersproject/providers';

export function isWallet(wallet: Wallet | Signer) {
// @ts-ignore
return typeof wallet.publicKey !== undefined && typeof wallet.publicKey === 'string' && wallet.publicKey.length > 0;
}

export class Signing {
/**
* Cannot be constructed.
Expand All @@ -17,30 +10,15 @@ export class Signing {

/**
* Prefix and Sign a binary payload using the given Ethers wallet or signer.
* @param messageBytes
* @param chainId The ID of the Vocdoni blockchain deployment for which the message is intended to
* @param message
* @param walletOrSigner
*/
static signTransaction(messageBytes: Uint8Array, chainId: string, walletOrSigner: Wallet | Signer): Promise<string> {
static signTransaction(message: string, walletOrSigner: Wallet | Signer): Promise<string> {
if (!walletOrSigner) throw new Error('Invalid wallet/signer');
const digestedMessage = this.digestVocdoniTransaction(messageBytes, chainId);

return this.signRaw(digestedMessage, walletOrSigner);
}

static digestVocdoniTransaction(payload: string | Uint8Array, chainId: string): Uint8Array {
const prefix = 'Vocdoni signed transaction:\n' + chainId + '\n';

return this.digestVocdoniPayload(payload, prefix);
}
const digestedMessage = new TextEncoder().encode(message);

static digestVocdoniPayload(payload: string | Uint8Array, prefix: string): Uint8Array {
const encoder = new TextEncoder();

const payloadBytes = typeof payload === 'string' ? encoder.encode(payload) : payload;
const digestedPayload = strip0x(keccak256(payloadBytes));

return encoder.encode(prefix + digestedPayload);
return this.signRaw(digestedMessage, walletOrSigner);
}

/**
Expand All @@ -67,12 +45,4 @@ export class Signing {
walletOrSigner.provider.send('personal_sign', [new TextDecoder('utf-8').decode(request), address.toLowerCase()])
);
}

static uint8ArrayToArray(buff: Uint8Array): number[] {
const result = [];
for (let i = 0; i < buff.length; ++i) {
result.push(buff[i]);
}
return result;
}
}

0 comments on commit 734c079

Please sign in to comment.