Skip to content

Commit

Permalink
Trezor wallet Catalyst voting registration
Browse files Browse the repository at this point in the history
  • Loading branch information
yushih committed May 12, 2021
1 parent 4ff86b7 commit bdc0b8a
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 61 deletions.
34 changes: 30 additions & 4 deletions packages/yoroi-extension/app/api/ada/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -374,12 +374,24 @@ export type CreateDelegationTxRequest = {|
valueInAccount: MultiToken,
|};

export type CreateVotingRegTxRequest = {|
type CreateVotingRegTxRequestCommon = {|
publicDeriver: IPublicDeriver<ConceptualWallet> & IGetAllUtxos & IHasUtxoChains,
absSlotNumber: BigNumber,
metadata: RustModule.WalletV4.GeneralTransactionMetadata,
|};

export type CreateVotingRegTxRequest = {|
...CreateVotingRegTxRequestCommon,
normalWallet: {|
metadata: RustModule.WalletV4.GeneralTransactionMetadata,
|}
|} | {|
...CreateVotingRegTxRequestCommon,
trezorTWallet: {|
votingPublicKey: string,
|}
|};


export type CreateDelegationTxResponse = {|
signTxRequest: HaskellShelleyTxSignRequest,
totalAmountToDelegate: MultiToken,
Expand Down Expand Up @@ -849,7 +861,6 @@ export default class AdaApi {
Number.parseInt(config.ChainNetworkId, 10),
);
Logger.debug(`${nameof(AdaApi)}::${nameof(this.createTrezorSignTxData)} success: ` + stringifyData(trezorSignTxPayload));

return {
trezorSignTxPayload,
};
Expand Down Expand Up @@ -1400,7 +1411,16 @@ export default class AdaApi {
if (changeAddr == null) {
throw new Error(`${nameof(this.createVotingRegTx)} no internal addresses left. Should never happen`);
}
const trxMetadata = RustModule.WalletV4.TransactionMetadata.new(request.metadata);
let trxMetadata;
if (request.trezorTWallet) {
trxMetadata = undefined;
} else {
// Mnemonic wallet
trxMetadata = RustModule.WalletV4.TransactionMetadata.new(
request.normalWallet.metadata
);
}

const unsignedTx = shelleyNewAdaUnsignedTx(
[],
{
Expand Down Expand Up @@ -1431,6 +1451,12 @@ export default class AdaApi {
neededHashes: new Set(),
wits: new Set(),
},
trezorTCatalystRegistrationTxSignData:
request.trezorTWallet &&
{
publicVotingKey: request.trezorTWallet.publicVotingKey,
nonce: request.absSlotNumber,
},
});
} catch (error) {
Logger.error(`${nameof(AdaApi)}::${nameof(this.createVotingRegTx)} error: ` + stringifyError(error));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ implements ISignRequest<RustModule.WalletV4.TransactionBuilder> {
neededHashes: Set<string>, // StakeCredential
wits: Set<string>, // Vkeywitness
|};
trezorTCatalystRegistrationTxSignData: void | {|
votingPublicKey: string,
|};

constructor(data: {|
senderUtxos: Array<CardanoAddressedUtxo>,
Expand All @@ -56,13 +59,18 @@ implements ISignRequest<RustModule.WalletV4.TransactionBuilder> {
neededHashes: Set<string>, // StakeCredential
wits: Set<string>, // Vkeywitness
|},
trezorTCatalystRegistrationTxSignData: void | {|
votingPublicKey: string,
|};
|}) {
this.senderUtxos = data.senderUtxos;
this.unsignedTx = data.unsignedTx;
this.changeAddr = data.changeAddr;
this.metadata = data.metadata;
this.networkSettingSnapshot = data.networkSettingSnapshot;
this.neededStakingKeyHashes = data.neededStakingKeyHashes;
this.trezorTCatalystRegistrationTxSignData =
data.trezorTCatalystRegistrationTxSignData;
}

txId(): string {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,20 @@ export async function createTrezorSignTxPayload(
metadata: Buffer.from(metadata.to_bytes()).toString('hex')
};

if (signRequest.trezorTCatalystRegistrationTxSignData) {
request.auxiliaryData = {
catalystRegistrationParameters: {
votingPublicKey: signRequest.trezorTCatalystRegistrationTxSignData.votingPublicKey,
stakingPath: getStakingKeyPath(),
rewardAddressParameters: {
addressType: ADDRESS_TYPE.Reward,
stakingPath: getStakingKeyPath(),
},
nonce: signRequest.trezorTCatalystRegistrationTxSignData.nonce,
},
};
}

return request;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ import Voting from '../../../components/wallet/voting/Voting';
import VotingRegistrationDialogContainer from '../dialogs/voting/VotingRegistrationDialogContainer';
import type { GeneratedData as VotingRegistrationDialogContainerData } from '../dialogs/voting/VotingRegistrationDialogContainer';
import { handleExternalLinkClick } from '../../../utils/routing';
import { WalletTypeOption, } from '../../../api/ada/lib/storage/models/ConceptualWallet/interfaces';
import UnsupportedWallet from '../UnsupportedWallet';
import { PublicDeriver } from '../../../api/ada/lib/storage/models/PublicDeriver/index';
import LoadingSpinner from '../../../components/widgets/LoadingSpinner';
import VerticallyCenteredLayout from '../../../components/layout/VerticallyCenteredLayout';
Expand Down Expand Up @@ -85,13 +83,6 @@ export default class VotingPage extends Component<Props> {
} = this.generated.stores;
let activeDialog = null;

if(selected == null){
throw new Error(`${nameof(VotingPage)} no wallet selected`);
}
if (selected.getParent().getWalletType() === WalletTypeOption.HARDWARE_WALLET) {
return <UnsupportedWallet />;
}

const balance = this.generated.balance;
if (balance == null) {
return (
Expand Down
133 changes: 85 additions & 48 deletions packages/yoroi-extension/app/stores/ada/VotingStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
isLedgerNanoWallet,
isTrezorTWallet,
} from '../../api/ada/lib/storage/models/ConceptualWallet/index';
import { WalletTypeOption } from '../../api/ada/lib/storage/models/ConceptualWallet/interfaces';
import { genOwnStakingKey } from '../../api/ada/index';
import { RustModule } from '../../api/ada/lib/cardanoCrypto/rustLoader';
import type { StepStateEnum } from '../../components/widgets/ProgressSteps';
Expand Down Expand Up @@ -128,9 +129,24 @@ export default class VotingStore extends Store<StoresMap, ActionsMap> {
this.progressInfo.stepState = StepState.LOAD;
};

@action _submitConfirm: void => void = () => {
this.progressInfo.currentStep = ProgressStep.REGISTER;
this.progressInfo.stepState = StepState.LOAD;
@action _submitConfirm: void => void = async () => {
const selected = this.stores.wallets.selected;
if (!selected) {
throw new Error(`${nameof(this._submitConfirm)} no public deriver. Should never happen`);
}
let nextStep;
if (
selected.getParent().getWalletType() === WalletTypeOption.HARDWARE_WALLET
) {
await this.actions.ada.voting.createTransaction.trigger();
nextStep = ProgressStep.TRANSACTION;
} else {
nextStep = ProgressStep.REGISTER;
}
runInAction(() => {
this.progressInfo.currentStep = nextStep;
this.progressInfo.stepState = StepState.LOAD;
})
};

@action _submitConfirmError: void => void = () => {
Expand Down Expand Up @@ -167,61 +183,83 @@ export default class VotingStore extends Store<StoresMap, ActionsMap> {
this.progressInfo.stepState = StepState.ERROR;
};

// we need password for transaction building to sign the voting key with stake key
// as part of metadata
// For mnemonic wallet, we need password for transaction building to sign
// the voting key with stake key as part of metadata.
@action
_createTransaction: string => Promise<void> = async spendingPassword => {
_createTransaction: ?string => Promise<void> = async spendingPassword => {
this.progressInfo.stepState = StepState.PROCESS;
const publicDeriver = this.stores.wallets.selected;
if (!publicDeriver) {
return;
}

const withSigning = asGetSigningKey(publicDeriver);
if (withSigning == null) {
const withUtxos = asGetAllUtxos(publicDeriver);
if (withUtxos == null) {
throw new Error(`${nameof(this._createTransaction)} missing utxo functionality`);
}

const network = withUtxos.getParent().getNetworkInfo();
if (!isCardanoHaskell(network)) {
throw new Error(
`${nameof(this._createTransaction)} public deriver missing signing functionality.`
`${nameof(VotingStore)}::${nameof(this._createTransaction)} network not supported`
);
}
const withStakingKey = asGetAllAccounting(withSigning);
if (withStakingKey == null) {
throw new Error(`${nameof(this._createTransaction)} missing staking key functionality`);

const withHasUtxoChains = asHasUtxoChains(withUtxos);
if (withHasUtxoChains == null) {
throw new Error(`${nameof(this._createTransaction)} missing chains functionality`);
}
const stakingKey = await genOwnStakingKey({
publicDeriver: withStakingKey,
password: spendingPassword,
});

const fullConfig = getCardanoHaskellBaseConfig(
publicDeriver.getParent().getNetworkInfo()
);
const config = fullConfig.reduce((acc, next) => Object.assign(acc, next), {});
const rewardAddress = RustModule.WalletV4.RewardAddress.new(
Number.parseInt(config.ChainNetworkId, 10),
RustModule.WalletV4.StakeCredential.from_keyhash(stakingKey.to_public().hash()),

const timeToSlot = await genTimeToSlot(fullConfig);
const absSlotNumber = new BigNumber(
timeToSlot({
// use server time for TTL if connected to server
time: this.stores.serverConnectionStore.serverTime ?? new Date(),
}).slot
);

const withUtxos = asGetAllUtxos(publicDeriver);
if (withUtxos == null) {
throw new Error(`${nameof(this._createTransaction)} missing utxo functionality`);
if(this.catalystPrivateKey === undefined){
throw new Error(`${nameof(this._createTransaction)} should never happen`);
}

const network = withUtxos.getParent().getNetworkInfo();
if (isCardanoHaskell(network)) {
const withHasUtxoChains = asHasUtxoChains(withUtxos);
if (withHasUtxoChains == null) {
throw new Error(`${nameof(this._createTransaction)} missing chains functionality`);
}
const timeToSlot = await genTimeToSlot(fullConfig);
const absSlotNumber = new BigNumber(
timeToSlot({
// use server time for TTL if connected to server
time: this.stores.serverConnectionStore.serverTime ?? new Date(),
}).slot
);
let votingRegTxPromise;

if (isTrezorTWallet(publicDeriver.getParent())) {
const votingPublicKey = `0x${Buffer.from(this.catalystPrivateKey.to_public().as_bytes()).toString('hex')}`;

if(this.catalystPrivateKey === undefined){
throw new Error(`${nameof(this._createTransaction)} should never happen`);
votingRegTxPromise = this.createVotingRegTx.execute({
publicDeriver: withHasUtxoChains,
absSlotNumber,
trezorTWallet: {
votingPublicKey
},
}).promise;
} else if (
publicDeriver.getParent().getWalletType() === WalletTypeOption.WEB_WALLET
) {
const withSigning = asGetSigningKey(publicDeriver);
if (withSigning == null) {
throw new Error(
`${nameof(this._createTransaction)} public deriver missing signing functionality.`
);
}
const withStakingKey = asGetAllAccounting(withSigning);
if (withStakingKey == null) {
throw new Error(`${nameof(this._createTransaction)} missing staking key functionality`);
}
const stakingKey = await genOwnStakingKey({
publicDeriver: withStakingKey,
password: spendingPassword,
});
const config = fullConfig.reduce((acc, next) => Object.assign(acc, next), {});
const rewardAddress = RustModule.WalletV4.RewardAddress.new(
Number.parseInt(config.ChainNetworkId, 10),
RustModule.WalletV4.StakeCredential.from_keyhash(stakingKey.to_public().hash()),
);

const trxMeta = generateRegistration({
stakePrivateKey: stakingKey,
Expand All @@ -233,21 +271,20 @@ export default class VotingStore extends Store<StoresMap, ActionsMap> {
}).slot,
});

const votingRegTxPromise = this.createVotingRegTx.execute({
votingRegTxPromise = this.createVotingRegTx.execute({
publicDeriver: withHasUtxoChains,
absSlotNumber,
metadata: trxMeta,
normalWallet: { metadata: trxMeta },
}).promise;
if (votingRegTxPromise == null) {
throw new Error(`${nameof(this._createTransaction)} should never happen`);
}
await votingRegTxPromise;
this.markStale(false);
} else {
throw new Error(
`${nameof(VotingStore)}::${nameof(this._createTransaction)} network not supported`
);
throw new Error(`${nameof(this._createTransaction)} unexpected wallet type`);
}

if (votingRegTxPromise == null) {
throw new Error(`${nameof(this._createTransaction)} should never happen`);
}
await votingRegTxPromise;
this.markStale(false);
};

@action
Expand Down

0 comments on commit bdc0b8a

Please sign in to comment.