Skip to content

Commit

Permalink
refactor(account)!: remove generateKeyPair
Browse files Browse the repository at this point in the history
BREAKING CHANGE: `generateKeyPair` removed
Use MemoryAccount::generate instead.
Optionally add `decode` if you need raw keys.
Obtain the secret key via MemoryAccount:secretKey.
  • Loading branch information
davidyuk committed Jul 3, 2024
1 parent d7f7b3c commit 18c6789
Show file tree
Hide file tree
Showing 14 changed files with 53 additions and 104 deletions.
28 changes: 12 additions & 16 deletions docs/quick-start.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,46 +10,42 @@ const {
AeSdk,
MemoryAccount,
Node,
CompilerHttp,
AE_AMOUNT_FORMATS,
generateKeyPair
AE_AMOUNT_FORMATS
} = require('@aeternity/aepp-sdk')
```

## 2. Create a Keypair for sender
## 2. Create a sender account

```js
const keypair = generateKeyPair()
console.log(`Secret key: ${keypair.secretKey}`)
console.log(`Public key: ${keypair.publicKey}`)
const sender = MemoryAccount.generate()
console.log('Sender address:', sender.address)
console.log('Sender secret key:', sender.secretKey)
```

## 3. Get some _AE_ using the Faucet
To receive some _AE_ you can use the [Faucet](https://faucet.aepps.com/). Just paste sender's publicKey, hit `Top UP` and you'll immediately get some test coins.
To receive some _AE_ you can use the [Faucet](https://faucet.aepps.com/). Just paste sender's address, hit `Top UP` and you'll immediately get some test coins.

## 4. Interact with the æternity blockchain
This example shows:

- how to create an instance of the SDK using the `Aesdk` class
- how to create an instance of the SDK using the `AeSdk` class
- how to spend (send) 1 AE from the account the SDK instance was initialized with to some other AE address

```js
const NODE_URL = 'https://testnet.aeternity.io'
const COMPILER_URL = 'https://v8.compiler.aepps.com' // required for contract interactions
// replace <SENDER_SECRET_KEY> with the generated secretKey from step 2
const senderAccount = new MemoryAccount('<SENDER_SECRET_KEY>');
const sender = new MemoryAccount('<SENDER_SECRET_KEY>');

(async function () {
const node = new Node(NODE_URL)
const aeSdk = new AeSdk({
onCompiler: new CompilerHttp(COMPILER_URL),
nodes: [{ name: 'testnet', instance: node }],
accounts: [senderAccount],
accounts: [sender],
})

// spend one AE
await aeSdk.spend(1, '<RECIPIENT_PUBLIC_KEY>', {
// replace <RECIPIENT_PUBLIC_KEY>, Ideally you use public key from Superhero Wallet you have created before
await aeSdk.spend(1, '<RECIPIENT_ADDRESS>', {
// replace <RECIPIENT_ADDRESS>, Ideally you use address from Superhero Wallet you have created before
denomination: AE_AMOUNT_FORMATS.AE
})
})()
Expand All @@ -60,4 +56,4 @@ Note:
- You may remove code from Step 2 as this serves only for one-time creation
- By default the `spend` function expects the amount to be spent in `aettos` (the smallest possible unit)
- Following the example snippet you would specify `AE` as denomination
- See [Testnet Explorer](https://explorer.testnet.aeternity.io/) and track your transactions
- See [Testnet Explorer](https://testnet.aescan.io/) and track your transactions
5 changes: 2 additions & 3 deletions examples/browser/wallet-iframe/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@

<script>
import {
MemoryAccount, generateKeyPair, AeSdkWallet, Node, CompilerHttp,
MemoryAccount, AeSdkWallet, Node, CompilerHttp,
BrowserWindowMessageConnection, METHODS, WALLET_TYPE, RPC_STATUS,
RpcConnectionDenyError, RpcRejectedByUserError, unpackTx, unpackDelegation,
} from '@aeternity/aepp-sdk';
Expand Down Expand Up @@ -199,8 +199,7 @@ export default {
}
static generate() {
// TODO: can inherit parent method after implementing https://github.com/aeternity/aepp-sdk-js/issues/1672
return new AccountMemoryProtected(generateKeyPair().secretKey);
return new AccountMemoryProtected(super().secretKey);
}
}
Expand Down
5 changes: 2 additions & 3 deletions examples/browser/wallet-web-extension/src/background.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import browser from 'webextension-polyfill';
import {
AeSdkWallet, CompilerHttp, Node, MemoryAccount, generateKeyPair, BrowserRuntimeConnection,
AeSdkWallet, CompilerHttp, Node, MemoryAccount, BrowserRuntimeConnection,
WALLET_TYPE, RpcConnectionDenyError, RpcRejectedByUserError, unpackTx, unpackDelegation,
} from '@aeternity/aepp-sdk';
import { TypeResolver, ContractByteArrayEncoder } from '@aeternity/aepp-calldata';
Expand Down Expand Up @@ -94,8 +94,7 @@ class AccountMemoryProtected extends MemoryAccount {
}

static generate() {
// TODO: can inherit parent method after implementing https://github.com/aeternity/aepp-sdk-js/issues/1672
return new AccountMemoryProtected(generateKeyPair().secretKey);
return new AccountMemoryProtected(super().secretKey);
}
}

Expand Down
1 change: 0 additions & 1 deletion examples/node/paying-for-contract-call-tx.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@

// ## 1. Specify imports
// You need to import `AeSdk`, `Node` and `MemoryAccount` classes from the SDK.
// Additionally you import the `generateKeyPair` utility function to generate a new keypair.
import {
AeSdk, Contract, CompilerHttp, Node, MemoryAccount, Tag,
} from '@aeternity/aepp-sdk';
Expand Down
1 change: 0 additions & 1 deletion examples/node/paying-for-spend-tx.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@

// ## 1. Specify imports
// You need to import `AeSdk`, `Node` and `MemoryAccount` classes from the SDK.
// Additionally you import the `generateKeyPair` utility function to generate a new keypair.
import {
AeSdk, Node, MemoryAccount, Tag,
} from '@aeternity/aepp-sdk';
Expand Down
5 changes: 3 additions & 2 deletions src/account/Memory.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import nacl from 'tweetnacl';
import AccountBase from './Base';
import {
sign, generateKeyPair, hash, messageToHash, messagePrefixLength,
sign, hash, messageToHash, messagePrefixLength,
} from '../utils/crypto';
import { ArgumentError } from '../utils/errors';
import {
Expand Down Expand Up @@ -45,7 +45,8 @@ export default class AccountMemory extends AccountBase {
* Generates a new AccountMemory using a random secret key
*/
static generate(): AccountMemory {
return new AccountMemory(generateKeyPair().secretKey);
const secretKey = encode(nacl.randomBytes(32), Encoding.AccountSecretKey);
return new AccountMemory(secretKey);
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
Expand Down
3 changes: 1 addition & 2 deletions src/index-browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ export {
export { InvalidTxError, sendTransaction } from './send-transaction';
export {
getAddressFromPriv, isAddressValid, genSalt, encodeUnsigned, hash, encodeContractAddress,
generateKeyPair, sign, verify, messageToHash, signMessage,
verifyMessage, isValidKeypair,
sign, verify, messageToHash, signMessage, verifyMessage, isValidKeypair,
} from './utils/crypto';
export {
signJwt, unpackJwt, verifyJwt, isJwt, ensureJwt,
Expand Down
29 changes: 0 additions & 29 deletions src/utils/crypto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,35 +100,6 @@ export function encodeContractAddress(
return encode(hash(binary), Encoding.ContractAddress);
}

/**
* Generate a random ED25519 keypair
* @param raw - Whether to return raw (binary) keys
* @returns Key pair
*/
export function generateKeyPair(raw: true): { publicKey: Buffer; secretKey: Buffer };
export function generateKeyPair(raw?: false): {
publicKey: Encoded.AccountAddress; secretKey: Encoded.AccountSecretKey;
};
export function generateKeyPair(raw = false): {
publicKey: Encoded.AccountAddress | Buffer;
secretKey: string | Buffer;
} {
const keyPair = nacl.sign.keyPair();
const publicBuffer = Buffer.from(keyPair.publicKey);
const secretBuffer = Buffer.from(keyPair.secretKey);

if (raw) {
return {
publicKey: publicBuffer,
secretKey: secretBuffer,
};
}
return {
publicKey: encode(publicBuffer, Encoding.AccountAddress),
secretKey: encode(secretBuffer.subarray(0, 32), Encoding.AccountSecretKey),
};
}

// SIGNATURES

/**
Expand Down
4 changes: 2 additions & 2 deletions test/environment/vue-cli-4-autorest/src/main.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// eslint-disable-next-line import/no-unresolved
import { AeSdk, Node, generateKeyPair } from '@aeternity/aepp-sdk';
import { AeSdk, Node, MemoryAccount } from '@aeternity/aepp-sdk';

const aeSdk = new AeSdk({
nodes: [{
Expand All @@ -9,7 +9,7 @@ const aeSdk = new AeSdk({
});

(async () => {
const balance = await aeSdk.getBalance(generateKeyPair().publicKey);
const balance = await aeSdk.getBalance(MemoryAccount.generate().address);
if (balance !== '0') console.error('Balance expected to be equal 0');
else console.log('`instanceof RestError` check works correctly');
})();
17 changes: 8 additions & 9 deletions test/integration/account-generalized.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { expect } from 'chai';
import { getSdk } from '.';
import {
AeSdk,
generateKeyPair,
genSalt,
MemoryAccount,
AccountGeneralized,
Expand Down Expand Up @@ -79,28 +78,28 @@ describe('Generalized Account', () => {
})).to.throw(ArgumentError, 'nonce should be equal 1 if GaAttachTx, got 2 instead');
});

const { publicKey } = generateKeyPair();
const recipient = MemoryAccount.generate().address;

it('Init MemoryAccount for GA and Spend using GA', async () => {
aeSdk.removeAccount(gaAccountAddress);
aeSdk.addAccount(new AccountGeneralized(gaAccountAddress), { select: true });

const callData = authContract._calldata.encode('BlindAuth', 'authorize', [genSalt()]);
await aeSdk.spend(10000, publicKey, { authData: { callData } });
await aeSdk.spend(10000, publicKey, { authData: { sourceCode, args: [genSalt()] } });
const balanceAfter = await aeSdk.getBalance(publicKey);
await aeSdk.spend(10000, recipient, { authData: { callData } });
await aeSdk.spend(10000, recipient, { authData: { sourceCode, args: [genSalt()] } });
const balanceAfter = await aeSdk.getBalance(recipient);
balanceAfter.should.be.equal('20000');
});

it('throws error if gasLimit exceeds the maximum value', async () => {
const authData = { sourceCode, args: [genSalt()], gasLimit: 50001 };
await expect(aeSdk.spend(10000, publicKey, { authData }))
await expect(aeSdk.spend(10000, recipient, { authData }))
.to.be.rejectedWith('Gas limit 50001 must be less or equal to 50000');
});

it('buildAuthTxHash generates a proper hash', async () => {
const { rawTx } = await aeSdk
.spend(10000, publicKey, { authData: { sourceCode, args: [genSalt()] } });
.spend(10000, recipient, { authData: { sourceCode, args: [genSalt()] } });

expect(new Uint8Array(await aeSdk.buildAuthTxHashByGaMetaTx(rawTx))).to.be
.eql((await authContract.getTxHash()).decodedResult);
Expand All @@ -117,7 +116,7 @@ describe('Generalized Account', () => {
let spendTx;
const fee = 1e15;
const gasPrice = MIN_GAS_PRICE + 1;
const { rawTx } = await aeSdk.spend(10000, publicKey, {
const { rawTx } = await aeSdk.spend(10000, recipient, {
authData: async (tx) => {
spendTx = tx;
return {
Expand All @@ -139,7 +138,7 @@ describe('Generalized Account', () => {

it('fails trying to send GaMeta using basic account', async () => {
const options = {
onAccount: new AccountGeneralized(publicKey),
onAccount: new AccountGeneralized(recipient),
authData: { callData: 'cb_KxFs8lcLG2+HEPb2FOjjZ2DqRd4=' },
} as const;
await expect(aeSdk.spend(1, gaAccountAddress, options))
Expand Down
4 changes: 2 additions & 2 deletions test/integration/aens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
assertNotNull, ensureEqual, randomName, randomString,
} from '../utils';
import {
AeSdk, Name, generateKeyPair, buildContractId, computeBidFee, ensureName, produceNameId, Contract,
AeSdk, Name, MemoryAccount, buildContractId, computeBidFee, ensureName, produceNameId, Contract,
AensPointerContextError, encode, decode, Encoding, ContractMethodsBase,
IllegalArgumentError, Tag, unpackTx, buildTxHash,
} from '../../src';
Expand Down Expand Up @@ -219,7 +219,7 @@ describe('Aens', () => {
expect((await contract.getArg(42, { callStatic: false })).decodedResult).to.be.equal(42n);
});

const address = generateKeyPair().publicKey;
const { address } = MemoryAccount.generate();
let pointers: Parameters<Name['update']>[0];
let pointersNode: Array<{ key: string; id: typeof pointers[string] }>;

Expand Down
14 changes: 7 additions & 7 deletions test/integration/chain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import { expect } from 'chai';
import { stub } from 'sinon';
import { getSdk, timeoutBlock } from '.';
import {
generateKeyPair, AeSdk, Tag, MemoryAccount, Encoded, Node, Contract,
AeSdk, Tag, MemoryAccount, Encoded, Node, Contract,
} from '../../src';
import { assertNotNull, bindRequestCounter } from '../utils';

describe('Node Chain', () => {
let aeSdk: AeSdk;
let aeSdkWithoutAccount: AeSdk;
const { publicKey } = generateKeyPair();
const recipient = MemoryAccount.generate().address;

before(async () => {
aeSdk = await getSdk();
Expand Down Expand Up @@ -66,7 +66,7 @@ describe('Node Chain', () => {

it('Can verify transaction from broadcast error', async () => {
const error = await aeSdk
.spend(0, publicKey, { ttl: 1, absoluteTtl: true, verify: false })
.spend(0, recipient, { ttl: 1, absoluteTtl: true, verify: false })
.catch((e) => e);
expect(await error.verifyTx()).to.have.lengthOf(1);
});
Expand Down Expand Up @@ -101,7 +101,7 @@ describe('Node Chain', () => {
tag: Tag.SpendTx,
amount: 1,
senderId: aeSdk.address,
recipientId: publicKey,
recipientId: recipient,
});
const signed = await aeSdk.signTransaction(tx);
const { txHash } = await aeSdk.api.postTransaction({ tx: signed });
Expand All @@ -122,19 +122,19 @@ describe('Node Chain', () => {

await aeSdk.getHeight({ cached: false });
getCount = bindRequestCounter(aeSdk.api);
hash = (await aeSdk.spend(100, publicKey, { waitMined: false, verify: false })).hash;
hash = (await aeSdk.spend(100, recipient, { waitMined: false, verify: false })).hash;
expect(getCount()).to.be.equal(2); // nonce, post tx
await aeSdk.poll(hash);

await aeSdk.getHeight({ cached: false });
getCount = bindRequestCounter(aeSdk.api);
hash = (await aeSdk.spend(100, publicKey, { waitMined: false, verify: false })).hash;
hash = (await aeSdk.spend(100, recipient, { waitMined: false, verify: false })).hash;
expect(getCount()).to.be.equal(2); // nonce, post tx
await aeSdk.poll(hash);

await aeSdk.getHeight({ cached: false });
getCount = bindRequestCounter(aeSdk.api);
hash = (await aeSdk.spend(100, publicKey, { waitMined: false })).hash;
hash = (await aeSdk.spend(100, recipient, { waitMined: false })).hash;
expect(getCount()).to.be.equal(6); // nonce, validator(acc, recipient, height, status), post tx
await aeSdk.poll(hash);
});
Expand Down
17 changes: 7 additions & 10 deletions test/integration/rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import {
MESSAGE_DIRECTION,
METHODS,
RPC_STATUS,
generateKeyPair,
verify,
NoWalletConnectedError,
UnAuthorizedAccountError,
Expand Down Expand Up @@ -98,7 +97,7 @@ describe('Aepp<->Wallet', () => {
});

describe('New RPC Wallet-AEPP: AEPP node', () => {
const keypair = generateKeyPair();
const { address } = MemoryAccount.generate();
let aepp: AeSdkAepp;
let wallet: AeSdkWallet;

Expand Down Expand Up @@ -230,9 +229,8 @@ describe('Aepp<->Wallet', () => {
});

it('Try to use `onAccount` for not existent account', () => {
const { publicKey } = generateKeyPair();
expect(() => { aepp.spend(100, publicKey, { onAccount: publicKey }); })
.to.throw(UnAuthorizedAccountError, `You do not have access to account ${publicKey}`);
expect(() => { aepp.spend(100, address, { onAccount: address }); })
.to.throw(UnAuthorizedAccountError, `You do not have access to account ${address}`);
});

it('aepp accepts key pairs in onAccount', async () => {
Expand Down Expand Up @@ -299,8 +297,8 @@ describe('Aepp<->Wallet', () => {
.callsFake(() => { throw new Error('test'); });
const tx = await aepp.buildTx({
tag: Tag.SpendTx,
senderId: keypair.publicKey,
recipientId: keypair.publicKey,
senderId: address,
recipientId: address,
amount: 0,
});
await expect(aepp.signTransaction(tx))
Expand All @@ -326,14 +324,13 @@ describe('Aepp<->Wallet', () => {
});

it('Try to sign using unpermited account', async () => {
const { publicKey: pub } = generateKeyPair();
assertNotNull(aepp.rpcClient);
await expect(aepp.rpcClient.request(METHODS.sign, {
tx: 'tx_+NkLAfhCuECIIeWttRUiZ32uriBdmM1t+dCg90KuG2ABxOiuXqzpAul6uTWvsyfx3EFJDah6trudrityh+6XSX3mkPEimhgGuJH4jzIBoQELtO15J/l7UeG8teE0DRIzWyorEsi8UiHWPEvLOdQeYYgbwW1nTsgAAKEB6bv2BOYRtUYKOzmZ6Xcbb2BBfXPOfFUZ4S9+EnoSJcqIG8FtZ07IAACIAWNFeF2KAAAKAIYSMJzlQADAoDBrIcoop8JfZ4HOD9p3nDTiNthj7jjl+ArdHwEMUrvQgitwOr/v3Q==',
onAccount: pub,
onAccount: address,
returnSigned: true,
networkId,
})).to.be.eventually.rejectedWith(`You are not subscribed for account ${pub}`)
})).to.be.eventually.rejectedWith(`You are not subscribed for account ${address}`)
.with.property('code', 11);
});

Expand Down
Loading

0 comments on commit 18c6789

Please sign in to comment.