Skip to content

Commit

Permalink
Merge 186e064 into cf022c9
Browse files Browse the repository at this point in the history
  • Loading branch information
fboucquez committed Jun 7, 2021
2 parents cf022c9 + 186e064 commit 54f4a9d
Show file tree
Hide file tree
Showing 13 changed files with 1,740 additions and 1,394 deletions.
2 changes: 1 addition & 1 deletion cmds/enrollRewardProgram-testnet.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
#!/bin/bash
set -e
symbol-bootstrap enrollRewardProgram -t target/testnet-supernode --useKnownRestGateways --password 1111 $1 $2 $3
symbol-bootstrap enrollRewardProgram -t target/testnet-supernode --maxFee 1000000 --useKnownRestGateways --password 1111 $1 $2 $3
3 changes: 3 additions & 0 deletions docs/enrollRewardProgram.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ OPTIONS
-u, --url=url [default: http://localhost:3000] the network url
--ledger If --ledger is provided, the command will connect and sign the transactions using a
Ledger device.
--maxFee=maxFee the max fee used when announcing (absolute). The node min multiplier will be used if
it is not provided.
Expand Down
2 changes: 1 addition & 1 deletion docs/help.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ OPTIONS
--all see all commands in CLI
```

_See code: [@oclif/plugin-help](https://github.com/oclif/plugin-help/blob/v3.1.0/src/commands/help.ts)_
_See code: [@oclif/plugin-help](https://github.com/oclif/plugin-help/blob/v3.2.2/src/commands/help.ts)_
3 changes: 3 additions & 0 deletions docs/link.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ OPTIONS
-u, --url=url [default: http://localhost:3000] the network url
--ledger If --ledger is provided, the command will connect and sign the transactions using a
Ledger device.
--maxFee=maxFee the max fee used when announcing (absolute). The node min multiplier will be used if
it is not provided.
Expand Down
2,241 changes: 1,164 additions & 1,077 deletions package-lock.json

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
},
"bugs": "https://github.com/nemtech/symbol-bootstrap/issues",
"dependencies": {
"@ledgerhq/hw-transport": "^5.51.1",
"@ledgerhq/hw-transport-node-hid": "^5.51.1",
"@oclif/command": "^1.7.0",
"@oclif/config": "^1.16.0",
"@oclif/plugin-autocomplete": "^0.3.0",
Expand All @@ -23,6 +25,7 @@
"semver": "^7.3.5",
"shx": "^0.3.2",
"sshpk": "^1.16.1",
"symbol-ledger-typescript": "0.0.1-alpha-202106071807",
"symbol-sdk": "^1.0.1",
"tslib": "^1.13.0",
"utf8": "^2.1.2",
Expand All @@ -36,6 +39,8 @@
"@types/handlebars": "^4.1.0",
"@types/inquirer": "^7.3.1",
"@types/js-yaml": "^3.12.5",
"@types/ledgerhq__hw-transport": "^4.21.3",
"@types/ledgerhq__hw-transport-node-hid": "^4.22.2",
"@types/lodash": "^4.14.165",
"@types/memorystream": "^0.3.0",
"@types/mocha": "^8.2.2",
Expand Down
666 changes: 386 additions & 280 deletions src/service/AnnounceService.ts

Large diffs are not rendered by default.

14 changes: 6 additions & 8 deletions src/service/LinkService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export type LinkParams = {
unlink: boolean;
useKnownRestGateways: boolean;
ready?: boolean;
ledger?: boolean;
customPreset?: string;
};

Expand Down Expand Up @@ -81,15 +82,12 @@ export class LinkService implements TransactionFactory {
const customPreset = this.configLoader.loadCustomPreset(this.params.customPreset, this.params.password);
logger.info(`${this.params.unlink ? 'Unlinking' : 'Linking'} nodes`);

await new AnnounceService().announce(
this.params.url,
this.params.maxFee,
this.params.useKnownRestGateways,
this.params.ready,
this.configLoader.mergePresets(presetData, customPreset),
await new AnnounceService().announce({
...this.params,
addresses,
this,
);
presetData: this.configLoader.mergePresets(presetData, customPreset),
transactionFactory: this,
});
}

async createTransactions({
Expand Down
16 changes: 7 additions & 9 deletions src/service/RewardProgramService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export type RewardProgramParams = {
maxFee?: number;
useKnownRestGateways: boolean;
ready?: boolean;
ledger?: boolean;
customPreset?: string;
};

Expand Down Expand Up @@ -80,16 +81,13 @@ export class RewardProgramService implements TransactionFactory {
logger.warn('This network does not have a reward program controller public key. Nodes cannot be registered.');
return;
}
await new AnnounceService().announce(
this.params.url,
this.params.maxFee,
this.params.useKnownRestGateways,
this.params.ready,
this.configLoader.mergePresets(presetData, customPreset),
await new AnnounceService().announce({
...this.params,
addresses,
this,
'1M+',
);
presetData: this.configLoader.mergePresets(presetData, customPreset),
transactionFactory: this,
tokenAmount: '1M+',
});
}

async createTransactions({
Expand Down
127 changes: 127 additions & 0 deletions src/service/SigningUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*
* Copyright 2021 NEM
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { LedgerNetworkType, SymbolLedger } from 'symbol-ledger-typescript/lib';
import { Account, Address, AggregateTransaction, Convert, KeyPair, NetworkType, SignedTransaction, Transaction, UInt64 } from 'symbol-sdk';
import { LogType } from '../logger';
import Logger from '../logger/Logger';
import LoggerFactory from '../logger/LoggerFactory';

const logger: Logger = LoggerFactory.getLogger(LogType.System);

/**
* An account that knows how to asynchronously sign and co-sign transactions
*/
export interface SigningAccount {
readonly publicKey: string;
readonly address: Address;
cosignTransaction(transaction: Transaction, transactionHash: string): Promise<string>;
signTransaction(transaction: Transaction, generationHash: string): Promise<SignedTransaction>;
}

/**
* Basic signing account that adapts the sdk's account.
*/
export class PrivateKeyAccount implements SigningAccount {
public readonly address: Address;
public readonly publicKey: string;
constructor(private readonly account: Account) {
this.address = account.address;
this.publicKey = account.publicAccount.publicKey;
}

async cosignTransaction(transaction: Transaction, transactionHash: string): Promise<string> {
const keyPairEncoded = KeyPair.createKeyPairFromPrivateKeyString(this.account.privateKey);
return Convert.uint8ToHex(KeyPair.sign(keyPairEncoded, Convert.hexToUint8(transactionHash)));
}

async signTransaction(transaction: Transaction, generationHash: string): Promise<SignedTransaction> {
return this.account.sign(transaction, generationHash);
}
}

/**
* Ledger account that can sign by connecting to the device.
*/
export class LedgerAccount implements SigningAccount {
public readonly address: Address;

constructor(
private readonly ledger: SymbolLedger,
networkType: LedgerNetworkType,
private readonly path: string,
public readonly publicKey: string,
private readonly isOptinSymbolWallet: boolean,
) {
this.address = Address.createFromPublicKey(publicKey, networkType as number as NetworkType);
}

async cosignTransaction(transaction: Transaction, transactionHash: string): Promise<string> {
logger.info(`Co-signing transaction ${transactionHash} with Ledger account ${this.address.plain()}. Check your device!`);
return await this.ledger.signCosignatureTransaction(
this.path,
transaction,
transactionHash,
this.publicKey,
this.isOptinSymbolWallet,
);
}

async signTransaction(transaction: Transaction, generationHash: string): Promise<SignedTransaction> {
logger.info(`Signing transaction with Ledger account ${this.address.plain()}. Check your device!`);
const { payload } = await this.ledger.signTransaction(this.path, transaction, generationHash, this.publicKey, false);
const generationHashBytes = Array.from(Convert.hexToUint8(generationHash));
return new SignedTransaction(
payload,
Transaction.createTransactionHash(payload, generationHashBytes),
this.publicKey,
transaction.type,
transaction.networkType,
);
}
}

/**
* Utility object to sign transactions asynchronously
*/
export class SigningUtils {
public static async signTransactionWithCosignatories(
initiatorAccount: SigningAccount,
transaction: AggregateTransaction,
cosignatories: SigningAccount[],
generationHash: string,
): Promise<SignedTransaction> {
const signedTransaction = await initiatorAccount.signTransaction(transaction, generationHash);
let signedPayload = signedTransaction.payload;
for (const cosigner of cosignatories) {
const signature = await cosigner.cosignTransaction(transaction, signedTransaction.hash);
Convert.validateHexString(signature, 128, 'Cosignature is not valid hex!');
signedPayload += UInt64.fromUint(0).toHex() + cosigner.publicKey + signature;
}
// Calculate new size
const size = `00000000${(signedPayload.length / 2).toString(16)}`;
const formatedSize = size.substr(size.length - 8, size.length);
const littleEndianSize =
formatedSize.substr(6, 2) + formatedSize.substr(4, 2) + formatedSize.substr(2, 2) + formatedSize.substr(0, 2);
signedPayload = littleEndianSize + signedPayload.substr(8, signedPayload.length - 8);
return new SignedTransaction(
signedPayload,
signedTransaction.hash,
initiatorAccount.publicKey,
transaction.type,
transaction.networkType,
);
}
}
1 change: 1 addition & 0 deletions src/service/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export * from './PortService';
export * from './ReportService';
export * from './RewardProgramService';
export * from './RunService';
export * from './SigningUtils';
export * from './SshpkService';
export * from './VerifyService';
export * from './VotingService';
36 changes: 28 additions & 8 deletions test/service/script.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,26 +23,28 @@ import {
Deadline,
LinkAction,
MultisigAccountModificationTransaction,
PublicAccount,
RawMessage,
RepositoryFactoryHttp,
TransactionGroup,
TransactionService,
TransferTransaction,
UInt64,
} from 'symbol-sdk';
import { BootstrapUtils } from '../../src/service';
import { AnnounceService, BootstrapUtils, PrivateKeyAccount, SigningUtils } from '../../src/service';

// const url = 'http://localhost:3000';
// const multisigPrivateKey = '96258E6EDBFA77D4138A56D03A7A38B89F1BC7721B532B91A43704AE87BBCC87';
// const cosignatory1PrivateKey = 'B1AA7A73D3FCB6054BC7A968C289EFDAF57BCBDC1756E0F3F58696C03F201BFD';
// const cosignatory2PrivateKey = 'AECAA68E6C12C88D28067612AA71B70AA9DA9549CEB3B54D897B711A63E93B71';
// const cosignatory3PrivateKey = '2515356696EB926299ACD3BA872A404BB6D8AD35F9F86015F280858D60C16600';

const url = 'http://api-01.ap-northeast-1.testnet.symboldev.network:3000';
const url = 'http://ngl-dual-501.testnet.symboldev.network:3000';
const multisigPrivateKey = 'CA82E7ADAF7AB729A5462A1BD5AA78632390634904A64EB1BB22295E2E1A1BDD';
const cosignatory1PrivateKey = 'AAAAB232742BB4AB3A1368BD4615E4E6D0224AB71A016BAF8520A332C9778737';
const cosignatory2PrivateKey = 'BBBEE976890916E54FA825D26BDD0235F5EB5B6A143C199AB0AE5EE9328E08CE';
const cosignatory3PrivateKey = 'CCCEE976890916E54FA825D26BDD0235F5EB5B6A143C199AB0AE5EE9328E08EE';
const ledgerPublicKey = '638bb9ccad2f9c4e4f14215c91b3fe8e4623291926860f19196d3f89709a5772';

const example = async () => {
const repositoryFactory = new RepositoryFactoryHttp(url);
Expand Down Expand Up @@ -70,12 +72,17 @@ const example = async () => {
const cosignatory3 = Account.createFromPrivateKey(cosignatory3PrivateKey, networkType);
console.log(`cosignatory3 ${cosignatory3.address.plain()}`);

const ledger4 = PublicAccount.createFromPublicKey(ledgerPublicKey, networkType);
console.log(`ledger4 ${ledger4.address.plain()}`);

const ledgerData = await AnnounceService.resolveLedgerData(networkType);

const sendModification = async () => {
const multisigAccountModificationTransaction = MultisigAccountModificationTransaction.create(
deadline,
2,
2,
[cosignatory1.address, cosignatory2.address, cosignatory3.address],
[cosignatory1.address, cosignatory2.address, cosignatory3.address, ledger4.address],
[],
networkType,
);
Expand All @@ -88,12 +95,19 @@ const example = async () => {
maxFee,
);

const signedTransaction = multisig.signTransactionWithCosignatories(
const ledgerAccount = ledgerData.accounts.find((l) => l.address.equals(ledger4.address));
if (!ledgerAccount) {
throw new Error('Ledger account not found!!');
}
const signedTransaction = await SigningUtils.signTransactionWithCosignatories(
new PrivateKeyAccount(multisig),
aggregateTransaction,
[cosignatory1, cosignatory2, cosignatory3],
[new PrivateKeyAccount(cosignatory1), new PrivateKeyAccount(cosignatory2), new PrivateKeyAccount(cosignatory3), ledgerAccount],
networkGenerationHash,
);
console.log('Announcing multisig ' + multisig.address.plain());
await service.announce(signedTransaction, listener).toPromise();
console.log(`Multisig ${multisig.address.plain()} confirmed`);
return signedTransaction;
};
// eslint-disable-next-line @typescript-eslint/no-unused-vars
Expand Down Expand Up @@ -212,10 +226,16 @@ const example = async () => {
} catch (e) {
console.log(e);
} finally {
ledgerData.ledger.close();
listener.close();
}
};

example().then(() => {
console.log('finish!');
});
example().then(
() => {
console.log('finish!');
},
(err) => {
console.error(err);
},
);
18 changes: 8 additions & 10 deletions test/supernode.yml
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
#privateKeySecurityMode: PROMPT_MAIN
nodeUseRemoteAccount: false
nodeUseRemoteAccount: true
logLevel: 'Debug'
nodes:
- voting: false
- voting: true
rewardProgram: 'supernode'
host: fboucquez-agent-symbollocal.ngrok.io
agentUrl: https://fboucquez-agent-symbollocal.ngrok.io
restGatewayUrl: http://fboucquez-rest-gateway-symbollocal.ngrok.io
mainPrivateKey: E095162875BB1D98CA5E0941670E01C1B0DBDF86DF7B3BEDA4A93635F8E51A03
transportPrivateKey: 415F253ABF0FB2DFD39D7F409EFA2E88769873CAEB45617313B98657A1476A15
remotePrivateKey: 24B485712045FEDA5782ECFF1752F63D41C134371BB46AC68EE6DC9183CF5061
vrfPrivateKey: D34A231C860EB1B6E3A4F8366C8F0F6C7C2462A40263DCB67124FB76CDD4E368
agentPrivateKey: AAAA231C860EB1B6E3A4F8366C8F0F6C7C2462A40263DCB67124FB76CDD4E368
host: my-supernode
mainPrivateKey: CA82E7ADAF7AB729A5462A1BD5AA78632390634904A64EB1BB22295E2E1A1BDD
transportPrivateKey: A15F253ABF0FB2DFD39D7F409EFA2E88769873CAEB45617313B98657A1476A15
remotePrivateKey: B4B485712045FEDA5782ECFF1752F63D41C134371BB46AC68EE6DC9183CF5061
vrfPrivateKey: C34A231C860EB1B6E3A4F8366C8F0F6C7C2462A40263DCB67124FB76CDD4E368
agentPrivateKey: DAAA231C860EB1B6E3A4F8366C8F0F6C7C2462A40263DCB67124FB76CDD4E368

0 comments on commit 54f4a9d

Please sign in to comment.