Skip to content

Commit

Permalink
feat!: remove wrapped private key type
Browse files Browse the repository at this point in the history
BREAKING CHANGE:
Remove wrapped `StacksPrivateKey` type in favor of simple type alias for string/Uint8Array.
  • Loading branch information
janniks committed Mar 11, 2024
1 parent 4596778 commit d6b76aa
Show file tree
Hide file tree
Showing 17 changed files with 271 additions and 308 deletions.
2 changes: 2 additions & 0 deletions packages/common/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ export const DEVNET_URL = 'http://localhost:3999';

export const GAIA_URL = 'https://hub.blockstack.org';

// todo: deduplicate magic variables

/** @ignore internal */
export const PRIVATE_KEY_COMPRESSED_LENGTH = 33;

Expand Down
11 changes: 6 additions & 5 deletions packages/encryption/src/keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
import base58 from 'bs58';
import { hashRipemd160 } from './hashRipemd160';
import { hashSha256Sync } from './sha2Hash';
import { PrivateKey } from '../../transactions/src';

const BITCOIN_PUBKEYHASH = 0x00;

Expand Down Expand Up @@ -96,7 +97,7 @@ export function publicKeyToBtcAddress(
* @ignore
* @returns a compressed public key
*/
export function getPublicKeyFromPrivate(privateKey: string | Uint8Array): string {
export function getPublicKeyFromPrivate(privateKey: PrivateKey): string {
const privateKeyBytes = privateKeyToBytes(privateKey);
// for backwards compatibility we always return a compressed public key, regardless of private key mode
return bytesToHex(nobleGetPublicKey(privateKeyBytes.slice(0, 32), true));
Expand All @@ -105,23 +106,23 @@ export function getPublicKeyFromPrivate(privateKey: string | Uint8Array): string
/**
* @ignore
*/
export function ecSign(messageHash: Uint8Array, hexPrivateKey: string | Uint8Array) {
return signSync(messageHash, privateKeyToBytes(hexPrivateKey).slice(0, 32), {
export function ecSign(messageHash: Uint8Array, privateKey: PrivateKey) {
return signSync(messageHash, privateKeyToBytes(privateKey).slice(0, 32), {
der: false,
});
}

/**
* @ignore
*/
export function isValidPrivateKey(privateKey: string | Uint8Array): boolean {
export function isValidPrivateKey(privateKey: PrivateKey): boolean {
return utils.isValidPrivateKey(privateKeyToBytes(privateKey));
}

/**
* @ignore
*/
export function compressPrivateKey(privateKey: string | Uint8Array): Uint8Array {
export function compressPrivateKey(privateKey: PrivateKey): Uint8Array {
const privateKeyBytes = privateKeyToBytes(privateKey);

return privateKeyBytes.length == PRIVATE_KEY_COMPRESSED_LENGTH
Expand Down
44 changes: 21 additions & 23 deletions packages/transactions/src/authorization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,22 @@ import {
StacksMessageType,
} from './constants';

import { cloneDeep, leftPadHex, txidFromData } from './utils';
import { BytesReader } from './bytesReader';
import { MessageSignature } from './common';
import { DeserializationError, SigningError, VerificationError } from './errors';
import {
createStacksPublicKey,
PrivateKey,
privateKeyToPublic,
publicKeyFromSignatureVrs,
publicKeyIsCompressed,
signWithKey,
StacksPublicKey,
} from './keys';
import {
TransactionAuthField,
serializeMessageSignature,
deserializeMessageSignature,
serializeMessageSignature,
TransactionAuthField,
} from './signature';
import {
addressFromPublicKeys,
Expand All @@ -30,20 +41,7 @@ import {
deserializeLPList,
serializeLPList,
} from './types';

import {
createStacksPublicKey,
getPublicKey,
isCompressed,
publicKeyFromSignatureVrs,
signWithKey,
StacksPrivateKey,
StacksPublicKey,
} from './keys';

import { MessageSignature } from './common';
import { DeserializationError, SigningError, VerificationError } from './errors';
import { BytesReader } from './bytesReader';
import { cloneDeep, leftPadHex, txidFromData } from './utils';

export function emptyMessageSignature(): MessageSignature {
return {
Expand Down Expand Up @@ -133,7 +131,7 @@ export function createSingleSigSpendingCondition(
1,
[createStacksPublicKey(pubKey)]
).hash160;
const keyEncoding = isCompressed(createStacksPublicKey(pubKey))
const keyEncoding = publicKeyIsCompressed(pubKey)
? PubKeyEncoding.Compressed
: PubKeyEncoding.Uncompressed;

Expand Down Expand Up @@ -276,7 +274,7 @@ export function deserializeMultiSigSpendingCondition(
for (const field of fields) {
switch (field.contents.type) {
case StacksMessageType.PublicKey:
if (!isCompressed(field.contents)) haveUncompressed = true;
if (!publicKeyIsCompressed(field.contents.data)) haveUncompressed = true;
break;
case StacksMessageType.MessageSignature:
if (field.pubKeyEncoding === PubKeyEncoding.Uncompressed) haveUncompressed = true;
Expand Down Expand Up @@ -363,7 +361,7 @@ function makeSigHashPostSign(
// * the signature
const hashLength = 32 + 1 + RECOVERABLE_ECDSA_SIG_LENGTH_BYTES;

const pubKeyEncoding = isCompressed(pubKey)
const pubKeyEncoding = publicKeyIsCompressed(pubKey.data)
? PubKeyEncoding.Compressed
: PubKeyEncoding.Uncompressed;

Expand All @@ -382,15 +380,15 @@ export function nextSignature(
authType: AuthType,
fee: IntegerType,
nonce: IntegerType,
privateKey: StacksPrivateKey
privateKey: PrivateKey
): {
nextSig: MessageSignature;
nextSigHash: string;
} {
const sigHashPreSign = makeSigHashPreSign(curSigHash, authType, fee, nonce);

const signature = signWithKey(privateKey, sigHashPreSign);
const publicKey = getPublicKey(privateKey);
const publicKey = createStacksPublicKey(privateKeyToPublic(privateKey));
const nextSigHash = makeSigHashPostSign(sigHashPreSign, publicKey, signature);

return {
Expand Down Expand Up @@ -491,7 +489,7 @@ function verifyMultiSig(

switch (field.contents.type) {
case StacksMessageType.PublicKey:
if (!isCompressed(field.contents)) haveUncompressed = true;
if (!publicKeyIsCompressed(field.contents.data)) haveUncompressed = true;
foundPubKey = field.contents;
break;
case StacksMessageType.MessageSignature:
Expand Down
76 changes: 35 additions & 41 deletions packages/transactions/src/builders.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
import { ApiOpts, ApiParam, bytesToHex, hexToBytes, IntegerType } from '@stacks/common';
import { ApiOpts, ApiParam, IntegerType } from '@stacks/common';
import {
STACKS_MAINNET,
STACKS_TESTNET,
StacksNetwork,
StacksNetworkName,
TransactionVersion,
networkFrom,
whenTransactionVersion,
} from '@stacks/network';
import { c32address } from 'c32check';
import {
createSingleSigSpendingCondition,
Expand All @@ -21,14 +30,7 @@ import {
} from './constants';
import { ClarityAbi, validateContractCall } from './contract-abi';
import { estimateFee, getAbi, getNonce } from './fetch';
import {
createStacksPrivateKey,
getPublicKey,
pubKeyfromPrivKey,
publicKeyFromBytes,
publicKeyToAddress,
publicKeyToString,
} from './keys';
import { createStacksPublicKey, privateKeyToPublic, publicKeyToAddress } from './keys';
import {
createContractCallPayload,
createSmartContractPayload,
Expand All @@ -41,26 +43,17 @@ import {
} from './postcondition';
import {
AssetInfo,
createContractPrincipal,
createStandardPrincipal,
FungiblePostCondition,
NonFungiblePostCondition,
PostCondition,
STXPostCondition,
createContractPrincipal,
createStandardPrincipal,
} from './postcondition-types';
import { TransactionSigner } from './signer';
import { StacksTransaction } from './transaction';
import { createLPList } from './types';
import { defaultApiFromNetwork, omit } from './utils';
import {
networkFrom,
STACKS_MAINNET,
STACKS_TESTNET,
StacksNetworkName,
TransactionVersion,
whenTransactionVersion,
} from '@stacks/network';
import { StacksNetwork } from '@stacks/network';

export interface MultiSigOptions {
numSignatures: number;
Expand Down Expand Up @@ -185,11 +178,11 @@ export async function makeSTXTokenTransfer(
): Promise<StacksTransaction> {
if ('senderKey' in txOptions) {
// txOptions is SignedTokenTransferOptions
const publicKey = publicKeyToString(getPublicKey(createStacksPrivateKey(txOptions.senderKey)));
const publicKey = privateKeyToPublic(txOptions.senderKey);
const options = omit(txOptions, 'senderKey');
const transaction = await makeUnsignedSTXTokenTransfer({ publicKey, ...options });

const privKey = createStacksPrivateKey(txOptions.senderKey);
const privKey = txOptions.senderKey;
const signer = new TransactionSigner(transaction);
signer.signOrigin(privKey);

Expand All @@ -202,13 +195,13 @@ export async function makeSTXTokenTransfer(
const signer = new TransactionSigner(transaction);
let pubKeys = txOptions.publicKeys;
for (const key of txOptions.signerKeys) {
const pubKey = pubKeyfromPrivKey(key);
pubKeys = pubKeys.filter(pk => pk !== bytesToHex(pubKey.data));
signer.signOrigin(createStacksPrivateKey(key));
const pubKey = privateKeyToPublic(key);
pubKeys = pubKeys.filter(pk => pk !== pubKey);
signer.signOrigin(key);
}

for (const key of pubKeys) {
signer.appendOrigin(publicKeyFromBytes(hexToBytes(key)));
signer.appendOrigin(createStacksPublicKey(key));
}

return transaction;
Expand Down Expand Up @@ -280,11 +273,12 @@ export async function makeContractDeploy(
): Promise<StacksTransaction> {
if ('senderKey' in txOptions) {
// txOptions is SignedContractDeployOptions
const publicKey = publicKeyToString(getPublicKey(createStacksPrivateKey(txOptions.senderKey)));

const publicKey = privateKeyToPublic(txOptions.senderKey);
const options = omit(txOptions, 'senderKey');
const transaction = await makeUnsignedContractDeploy({ publicKey, ...options });

const privKey = createStacksPrivateKey(txOptions.senderKey);
const privKey = txOptions.senderKey;
const signer = new TransactionSigner(transaction);
signer.signOrigin(privKey);

Expand All @@ -297,13 +291,13 @@ export async function makeContractDeploy(
const signer = new TransactionSigner(transaction);
let pubKeys = txOptions.publicKeys;
for (const key of txOptions.signerKeys) {
const pubKey = pubKeyfromPrivKey(key);
pubKeys = pubKeys.filter(pk => pk !== bytesToHex(pubKey.data));
signer.signOrigin(createStacksPrivateKey(key));
const pubKey = privateKeyToPublic(key);
pubKeys = pubKeys.filter(pk => pk !== pubKey);
signer.signOrigin(key);
}

for (const key of pubKeys) {
signer.appendOrigin(publicKeyFromBytes(hexToBytes(key)));
signer.appendOrigin(createStacksPublicKey(key));
}

return transaction;
Expand Down Expand Up @@ -522,11 +516,11 @@ export async function makeContractCall(
txOptions: SignedContractCallOptions | SignedMultiSigContractCallOptions
): Promise<StacksTransaction> {
if ('senderKey' in txOptions) {
const publicKey = publicKeyToString(getPublicKey(createStacksPrivateKey(txOptions.senderKey)));
const publicKey = privateKeyToPublic(txOptions.senderKey);
const options = omit(txOptions, 'senderKey');
const transaction = await makeUnsignedContractCall({ publicKey, ...options });

const privKey = createStacksPrivateKey(txOptions.senderKey);
const privKey = txOptions.senderKey;
const signer = new TransactionSigner(transaction);
signer.signOrigin(privKey);

Expand All @@ -538,13 +532,13 @@ export async function makeContractCall(
const signer = new TransactionSigner(transaction);
let pubKeys = txOptions.publicKeys;
for (const key of txOptions.signerKeys) {
const pubKey = pubKeyfromPrivKey(key);
pubKeys = pubKeys.filter(pk => pk !== bytesToHex(pubKey.data));
signer.signOrigin(createStacksPrivateKey(key));
const pubKey = privateKeyToPublic(key);
pubKeys = pubKeys.filter(pk => pk !== pubKey);
signer.signOrigin(key);
}

for (const key of pubKeys) {
signer.appendOrigin(publicKeyFromBytes(hexToBytes(key)));
signer.appendOrigin(createStacksPublicKey(key));
}

return transaction;
Expand Down Expand Up @@ -744,7 +738,7 @@ export async function sponsorTransaction(
const options = Object.assign(defaultOptions, sponsorOptions);
options.api = defaultApiFromNetwork(options.network, sponsorOptions.api);

const sponsorPubKey = pubKeyfromPrivKey(options.sponsorPrivateKey);
const sponsorPubKey = privateKeyToPublic(options.sponsorPrivateKey);

if (sponsorOptions.fee == null) {
let txFee: bigint | number = 0;
Expand Down Expand Up @@ -778,14 +772,14 @@ export async function sponsorTransaction(

const sponsorSpendingCondition = createSingleSigSpendingCondition(
options.sponsorAddressHashmode,
publicKeyToString(sponsorPubKey),
sponsorPubKey,
options.sponsorNonce,
options.fee
);

options.transaction.setSponsor(sponsorSpendingCondition);

const privKey = createStacksPrivateKey(options.sponsorPrivateKey);
const privKey = options.sponsorPrivateKey;
const signer = TransactionSigner.createSponsorSigner(
options.transaction,
sponsorSpendingCondition
Expand Down
Loading

0 comments on commit d6b76aa

Please sign in to comment.