Skip to content

Commit

Permalink
Only checking necessary wallet signature.
Browse files Browse the repository at this point in the history
Made from/to optional
  • Loading branch information
acedward committed Jan 4, 2024
1 parent b305ffa commit bc1f5a7
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 46 deletions.
87 changes: 65 additions & 22 deletions packages/engine/paima-sm/src/delegate-wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { PoolClient } from 'pg';

import { PaimaParser } from '@paima/concise';
import { ENV, doLog } from '@paima/utils';
import type { IVerify } from '@paima/crypto';
import { CryptoManager } from '@paima/crypto';
import type { IGetAddressFromAddressResult } from '@paima/db';
import {
Expand Down Expand Up @@ -31,7 +32,7 @@ type ParsedDelegateWalletCommand =
command: 'migrate';
args: { from: string; to: string; from_signature: string; to_signature: string };
}
| { command: 'cancelDelegations'; args: { to: string; to_signature: string } };
| { command: 'cancelDelegations'; args: { to_signature: string } };

// Delegate Wallet manages cache cleanup.
enableManualCache();
Expand All @@ -44,26 +45,25 @@ export class DelegateWallet {
private static readonly SEP = ':';

private static readonly delegationGrammar = `
delegate = &wd|from|to|from_signature|to_signature
migrate = &wm|from|to|from_signature|to_signature
cancelDelegations = &wc|to|to_signature
delegate = &wd|from?|to?|from_signature|to_signature
migrate = &wm|from?|to?|from_signature|to_signature
cancelDelegations = &wc|to_signature
`;

private static readonly parserCommands = {
delegate: {
from: PaimaParser.WalletAddress(),
to: PaimaParser.WalletAddress(),
from: PaimaParser.OptionalParser('', PaimaParser.WalletAddress()),
to: PaimaParser.OptionalParser('', PaimaParser.WalletAddress()),
from_signature: PaimaParser.NCharsParser(0, 1024),
to_signature: PaimaParser.NCharsParser(0, 1024),
},
migrate: {
from: PaimaParser.WalletAddress(),
to: PaimaParser.WalletAddress(),
from: PaimaParser.OptionalParser('', PaimaParser.WalletAddress()),
to: PaimaParser.OptionalParser('', PaimaParser.WalletAddress()),
from_signature: PaimaParser.NCharsParser(0, 1024),
to_signature: PaimaParser.NCharsParser(0, 1024),
},
cancelDelegations: {
to: PaimaParser.WalletAddress(),
to_signature: PaimaParser.NCharsParser(0, 1024),
},
};
Expand All @@ -82,26 +82,52 @@ export class DelegateWallet {
}${internalMessage.toLocaleLowerCase()}${DelegateWallet.SEP}${ENV.CONTRACT_ADDRESS}`;
}

private validateSender(to: string, from: string, realAddress: string): void {
if (to === from) throw new Error('Cannot delegate self');
const isSelf = to === realAddress || from === realAddress;
if (!isSelf) throw new Error('Either to or from must bt the sender address');
}

// Regex must match all possible wallets for the network.
private static readonly WALLET_VALIDATORS: { regex: RegExp; manager: () => IVerify }[] = [
{
// EVM 0xeEacBe169AD0EB650E8130fc918e2FDE0d8548b3
regex: /^0x[a-fA-F0-9]{40}$/,
manager: () => CryptoManager.Evm(new Web3()),
},
{
// Cardano addr1qxd9et27q0funwzturr3sex4zdammc7wdeelpg2ph5pt3dyzmyra5yewwvce7chndkth9g7q2u8n33puue4guh3c6lvqtkfq0t
regex: /^addr1[a-zA-Z0-9]+$/,
manager: () => CryptoManager.Cardano(),
},
{
// Algorand EZ6MRYCRVUQ4A6SCWKID6ND4C6ZNVVGBDJDXAEYX6EBCYMHLLUMOPEKU2U
regex: /^[A-Z2-7]{58}$/,
manager: () => CryptoManager.Algorand(),
},
{
// Polkadot 5FbQJpZnbEFkwwfagwyL5NWwtJhZ9GpdHcKhmWhPUYrJRnap
regex: /^[1-9A-HJ-NP-Za-km-z]{47,48}$/,
manager: () => CryptoManager.Polkadot(),
},
];

/* Verify Signature with all possible wallets. */
private async verifySignature(
walletAddress: string,
message: string,
signature: string
): Promise<boolean> {
if (!walletAddress || !signature) throw new Error('No Signature');
const wallets = [
CryptoManager.Evm(new Web3()),
CryptoManager.Algorand(),
CryptoManager.Cardano(),
CryptoManager.Polkadot(),
];
for (const wallet of wallets) {

for (const validator of DelegateWallet.WALLET_VALIDATORS) {
try {
if (await wallet.verifySignature(walletAddress, message, signature)) {
if (!walletAddress.match(validator.regex)) continue;
if (await validator.manager().verifySignature(walletAddress, message, signature)) {
return true;
}
} catch {
// do nothing.
// do nothing, some validators throw errors if the signature is invalid
}
}
throw new Error(`Invalid Signature for ${walletAddress} : ${message}`);
Expand Down Expand Up @@ -260,7 +286,11 @@ export class DelegateWallet {
* If returns TRUE this is was an intend as internal command,
* and the paima-concise command should not passed into the game STF.
*/
public async process(command: string): Promise<boolean> {
public async process(
realAddress: string,
userAddress: string,
command: string
): Promise<boolean> {
try {
if (!command.startsWith(DelegateWallet.INTERNAL_COMMAND_PREFIX)) return false;
const parsed: ParsedDelegateWalletCommand = DelegateWallet.parser.start(
Expand All @@ -269,29 +299,42 @@ export class DelegateWallet {
switch (parsed.command) {
case 'delegate':
{
const { from, to, from_signature, to_signature } = parsed.args;
const from = parsed.args.from || realAddress;
const to = parsed.args.to || realAddress;
this.validateSender(to, from, realAddress);

const { from_signature, to_signature } = parsed.args;
await Promise.all([
this.verifySignature(from, this.generateMessage(to), from_signature),
this.verifySignature(to, this.generateMessage(from), to_signature),
]);
await this.cmdDelegate(from.toLocaleLowerCase(), to.toLocaleLowerCase());
doLog(`Delegate Wallet ${from.substring(0, 8)}... -> ${to.substring(0, 8)}...`);
}
break;
case 'migrate':
{
const { from, to, from_signature, to_signature } = parsed.args;
const from = parsed.args.from || realAddress;
const to = parsed.args.to || realAddress;
this.validateSender(to, from, realAddress);

const { from_signature, to_signature } = parsed.args;
await Promise.all([
this.verifySignature(from, this.generateMessage(to), from_signature),
this.verifySignature(to, this.generateMessage(from), to_signature),
]);
await this.cmdMigrate(from.toLocaleLowerCase(), to.toLocaleLowerCase());

doLog(`Migrate Wallet ${from.substring(0, 8)}... -> ${to.substring(0, 8)}...`);
}
break;
case 'cancelDelegations':
{
const { to, to_signature } = parsed.args;
const to = realAddress;
const { to_signature } = parsed.args;
await this.verifySignature(to, this.generateMessage(), to_signature);
await this.cmdCancelDelegations(to.toLocaleLowerCase());
doLog(`Cancel Delegate 'to' ${to.substring(0, 8)}...`);
}
break;
default:
Expand Down
12 changes: 8 additions & 4 deletions packages/engine/paima-sm/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -343,12 +343,16 @@ async function processUserInputs(
// Check if internal Concise Command
// Internal Concise Commands are prefixed with an ampersand (&)
//
// delegate = &wd|from|to|from_signature|to_signature
// migrate = &wm|from|to|from_signature|to_signature
// cancelDelegate = &wc|to|to_signature
// delegate = &wd|from?|to?|from_signature|to_signature
// migrate = &wm|from?|to?|from_signature|to_signature
// cancelDelegate = &wc|to_signature
const delegateWallet = new DelegateWallet(DBConn);
if (inputData.inputData.startsWith(DelegateWallet.INTERNAL_COMMAND_PREFIX)) {
await delegateWallet.process(inputData.inputData);
await delegateWallet.process(
inputData.realAddress,
inputData.userAddress,
inputData.inputData
);
} else {
// If wallet does not exist in address table: create it.
if (inputData.userId === NO_USER_ID) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* delegate = &wd|from|to|from_signature|to_signature
* migrate = &wm|from|to|from_signature|to_signature
* cancelDelegations = &wc|to|to_signature
* delegate = &wd|from?|to?|from_signature|to_signature
* migrate = &wm|from?|to?|from_signature|to_signature
* cancelDelegations = &wc|to_signature
*/

import { builder } from '@paima/concise';
Expand All @@ -22,18 +22,18 @@ import { paimaEndpoints } from '../index.js';
import assertNever from 'assert-never';

export async function walletConnect(
from: string,
to: string,
from: string | null,
to: string | null,
from_signature: string,
to_signature: string
): Promise<SuccessfulResult<PostDataResponse> | FailedResult> {
const errorFxn = buildEndpointErrorFxn('wallet-connect');
const errorFxn = buildEndpointErrorFxn('delegate-wallet');

// walletConnect = &wc|from|to|from_signature|to_signature
// delegate = &wd|from?|to?|from_signature|to_signature
const conciseBuilder = builder.initialize();
conciseBuilder.setPrefix('&wd');
conciseBuilder.addValue({ value: from });
conciseBuilder.addValue({ value: to });
conciseBuilder.addValue({ value: from || '' });
conciseBuilder.addValue({ value: to || '' });
conciseBuilder.addValue({ value: from_signature });
conciseBuilder.addValue({ value: to_signature });
try {
Expand All @@ -50,18 +50,18 @@ export async function walletConnect(
}

export async function walletConnectMigrate(
from: string,
to: string,
from: string | null,
to: string | null,
from_signature: string,
to_signature: string
): Promise<SuccessfulResult<PostDataResponse> | FailedResult> {
const errorFxn = buildEndpointErrorFxn('wallet-connect');
const errorFxn = buildEndpointErrorFxn('delegate-wallet-migrate');

// migrate = &wm|from|to|from_signature|to_signature
// migrate = &wm|from?|to?|from_signature|to_signature
const conciseBuilder = builder.initialize();
conciseBuilder.setPrefix('&wm');
conciseBuilder.addValue({ value: from });
conciseBuilder.addValue({ value: to });
conciseBuilder.addValue({ value: from || '' });
conciseBuilder.addValue({ value: to || '' });
conciseBuilder.addValue({ value: from_signature });
conciseBuilder.addValue({ value: to_signature });
try {
Expand All @@ -78,15 +78,13 @@ export async function walletConnectMigrate(
}

export async function walletConnectCancelDelegations(
to: string,
to_signature: string
): Promise<SuccessfulResult<PostDataResponse> | FailedResult> {
const errorFxn = buildEndpointErrorFxn('wallet-cancel-delegations');
const errorFxn = buildEndpointErrorFxn('delegate-wallet-cancel');

// walletConnect = &wc|to|to_signature
// walletConnect = &wc|to_signature
const conciseBuilder = builder.initialize();
conciseBuilder.setPrefix('&wc');
conciseBuilder.addValue({ value: to });
conciseBuilder.addValue({ value: to_signature });
try {
const result = await postConciseData(conciseBuilder.build(), errorFxn);
Expand Down
2 changes: 1 addition & 1 deletion packages/paima-sdk/paima-mw-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ const paimaEndpoints = {
...utilityEndpoints,
};

export * from './wallet-connect/index.js';
export * from './delegate-wallet/index.js';
export type * from './errors.js';
// Only for use in game-specific middleware:
export * from './types.js';
Expand Down
3 changes: 3 additions & 0 deletions packages/paima-sdk/paima-utils/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,11 @@ export type TransactionTemplate = {
export type NonceString = string;

export interface SubmittedData {
// Address of the wallet that submitted the data.
realAddress: WalletAddress;
// Mapped address to main wallet.
userAddress: WalletAddress;
// Fixed User ID
userId: number;
inputData: InputDataString;
inputNonce: NonceString;
Expand Down

0 comments on commit bc1f5a7

Please sign in to comment.