Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor!: Remove wrapper type for message signatures #1735

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .github/MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- [Stacks Network](#stacks-network)
- [Impacts](#impacts)
- [Fetch Methods](#fetch-methods)
- [Reducing Wrapper Types](#reducing-wrapper-types)
- [StacksNodeApi](#stacksnodeapi)
- [StacksNetwork to StacksNodeApi](#stacksnetwork-to-stacksnodeapi)
- [Clarity Representation](#clarity-representation)
Expand Down Expand Up @@ -36,6 +37,7 @@

- The `@stacks/network` `new StacksNetwork()` objects were removed. Instead `@stacks/network` now exports the objects `STACKS_MAINNET`, `STACKS_TESNET`, and `STACKS_DEVNET`, which are static (and shouldn't be changed for most use-cases). [Read more...](#stacks-network)
- Most `fetch` (aka networking) methods were renamed to indicate they send HTTP requests. The new methods are named `fetchXyz` and are compatible with the old `Xyz` interfaces. [Read more...](#fetch-methods)
- Reducing wrapper types, which create annoyances for the developer, rather than being able to use values directly. [Read more...](#reducing-wrapper-types)
- The `ClarityType` enum was replaced by a human-readable version. The previous (wire format compatible) enum is still available as `ClarityWireType`. [Read more...](#clarity-representation)
- The previous post-conditions types and `create..` methods were replaced with a human-readable representation. [Read more...](#post-conditions)
- `StacksTransaction.serialize` and other `serializeXyz` methods were changed to return `string` (hex-encoded) instead of `Uint8Array`. Compatible `serializeXzyBytes` methods were added to ease the migration. [Read more...](#serialize-methods)
Expand Down Expand Up @@ -83,6 +85,15 @@ The following methods were renamed:
`broadcastTransaction` wasn't renamed to highlight the uniqueness of the method.
Namely, the node/API it is sent to will "broadcast" the transaction to the mempool.

### Reducing Wrapper Types

With this release we are aiming to reduce unnecessary "wrapper" types, which are used in the internals of the codebase, but shouldn't be pushed onto the user/developer.

This breaks the signatures of many functions:

- `signMessageHashRsv`, `signWithKey` now return the message signature as a `string` directly.
- `nextSignature`, `nextVerification`, `publicKeyFromSignatureVrs`, `publicKeyFromSignatureRsv` now take in the message signature as a `string`.

### StacksNodeApi

The new `StacksNodeApi` class lets you interact with a Stacks node or API.
Expand Down
8 changes: 4 additions & 4 deletions packages/bns/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ApiParam, IntegerType, intToBigInt, utf8ToBytes } from '@stacks/common';
import { ApiParam, IntegerType, PublicKey, intToBigInt, utf8ToBytes } from '@stacks/common';
import { StacksNetwork } from '@stacks/network';
import {
ClarityType,
Expand Down Expand Up @@ -55,7 +55,7 @@ export interface PriceFunction {
export interface BnsContractCallOptions {
functionName: string;
functionArgs: ClarityValue[];
publicKey: string;
publicKey: PublicKey;
network: StacksNetwork;
postConditions?: PostCondition[];
}
Expand Down Expand Up @@ -480,7 +480,7 @@ export interface PreorderNameOptions {
/** amount of STX to burn for the registration */
stxToBurn: IntegerType;
/** the private key to sign the transaction */
publicKey: string;
publicKey: PublicKey;
/** the Stacks blockchain network to use */
network: StacksNetwork;
}
Expand Down Expand Up @@ -541,7 +541,7 @@ export interface RegisterNameOptions {
fullyQualifiedName: string;
salt: string;
zonefile: string;
publicKey: string;
publicKey: PublicKey;
network: StacksNetwork;
}

Expand Down
4 changes: 3 additions & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
"prepublishOnly": "npm run test && NODE_ENV=production npm run build",
"start": "tsc -b tsconfig.build.json --watch --verbose",
"test": "jest",
"test:watch": "jest --watch --coverage=false"
"test:watch": "jest --watch --coverage=false",
"typecheck": "tsc --noEmit",
"typecheck:watch": "npm run typecheck -- --watch"
},
"dependencies": {
"@scure/bip32": "1.1.3",
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,7 @@ async function migrateSubdomains(network: CLINetworkAdapter, args: string[]): Pr
const sig = signWithKey(account.dataPrivateKey, hash);

// https://docs.stacks.co/build-apps/references/bns#subdomain-lifecycle
subDomainOp.signature = sig.data;
subDomainOp.signature = sig;

payload.subdomains_list.push(subDomainOp);
}
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/tests/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,7 @@ describe('Subdomain Migration', () => {
const hash = crypto.createHash('sha256').update(textToSign).digest('hex');
const sig = signWithKey(privateKey, hash);

subDomainOp.signature = sig.data; // Assign signature to subDomainOp
subDomainOp.signature = sig; // Assign signature to subDomainOp

// Verify that the generated signature is valid
const pubKey = publicKeyFromSignatureVrs(hash, sig);
Expand Down
2 changes: 1 addition & 1 deletion packages/stacking/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,7 @@ export function signPox4SignatureHash({
return signStructuredData({
...pox4SignatureMessage({ topic, poxAddress, rewardCycle, period, network, maxAmount, authId }),
privateKey,
}).data;
});
}

/**
Expand Down
20 changes: 9 additions & 11 deletions packages/transactions/src/authorization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
intToBigInt,
intToBytes,
PrivateKey,
PublicKey,
writeUInt16BE,
} from '@stacks/common';
import { BytesReader } from './BytesReader';
Expand Down Expand Up @@ -117,7 +118,7 @@ export function createSpendingCondition(

export function createSingleSigSpendingCondition(
hashMode: SingleSigHashMode,
pubKey: string,
pubKey: PublicKey,
nonce: IntegerType,
fee: IntegerType
): SingleSigSpendingCondition {
Expand Down Expand Up @@ -365,11 +366,8 @@ export function makeSigHashPreSign(
return txidFromData(hexToBytes(sigHash));
}

function makeSigHashPostSign(
curSigHash: string,
pubKey: PublicKeyWire,
signature: MessageSignatureWire
): string {
/** @internal */
function makeSigHashPostSign(curSigHash: string, pubKey: PublicKeyWire, signature: string): string {
// new hash combines the previous hash and all the new data this signature will add. This
// includes:
// * the public key compression flag
Expand All @@ -380,7 +378,7 @@ function makeSigHashPostSign(
? PubKeyEncoding.Compressed
: PubKeyEncoding.Uncompressed;

const sigHash = curSigHash + leftPadHex(pubKeyEncoding.toString(16)) + signature.data;
const sigHash = curSigHash + leftPadHex(pubKeyEncoding.toString(16)) + signature;

const sigHashBytes = hexToBytes(sigHash);
if (sigHashBytes.byteLength > hashLength) {
Expand All @@ -397,7 +395,7 @@ export function nextSignature(
nonce: IntegerType,
privateKey: PrivateKey
): {
nextSig: MessageSignatureWire;
nextSig: string;
nextSigHash: string;
} {
const sigHashPreSign = makeSigHashPreSign(curSigHash, authType, fee, nonce);
Expand All @@ -418,7 +416,7 @@ export function nextVerification(
fee: IntegerType,
nonce: IntegerType,
pubKeyEncoding: PubKeyEncoding,
signature: MessageSignatureWire
signature: string
) {
const sigHashPreSign = makeSigHashPreSign(initialSigHash, authType, fee, nonce);

Expand Down Expand Up @@ -465,7 +463,7 @@ function verifySingleSig(
condition.fee,
condition.nonce,
condition.keyEncoding,
condition.signature
condition.signature.data
);

// address version arg doesn't matter for signer hash generation
Expand Down Expand Up @@ -508,7 +506,7 @@ function verifyMultiSig(
condition.fee,
condition.nonce,
field.pubKeyEncoding,
field.contents
field.contents.data
);

if (isSequentialMultiSig(condition.hashMode)) {
Expand Down
54 changes: 30 additions & 24 deletions packages/transactions/src/builders.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ApiOpts, ApiParam, IntegerType } from '@stacks/common';
import { ApiOpts, ApiParam, IntegerType, PrivateKey, PublicKey } from '@stacks/common';
import {
STACKS_MAINNET,
STACKS_TESTNET,
Expand Down Expand Up @@ -28,8 +28,14 @@ import {
SingleSigHashMode,
} from './constants';
import { ClarityAbi, validateContractCall } from './contract-abi';
import { fetchFeeEstimate, fetchAbi, fetchNonce } from './fetch';
import { createStacksPublicKey, privateKeyToPublic, publicKeyToAddress } from './keys';
import { fetchAbi, fetchFeeEstimate, fetchNonce } from './fetch';
import {
createStacksPublicKey,
privateKeyToHex,
privateKeyToPublic,
publicKeyToAddress,
publicKeyToHex,
} from './keys';
import { postConditionToWire } from './postcondition';
import { PostCondition } from './postcondition-types';
import { TransactionSigner } from './signer';
Expand All @@ -56,7 +62,7 @@ export interface UnsignedMultiSigOptions {
/** The minimum required signatures N (in a N of M multi-sig) */
numSignatures: number;
/** The M public-keys (in a N of M multi-sig), which together form the address of the multi-sig account */
publicKeys: string[];
publicKeys: PublicKey[];
/**
* The `address` of the multi-sig account.
* - If NOT provided, the public-key order is taken AS IS.
Expand All @@ -69,7 +75,7 @@ export interface UnsignedMultiSigOptions {
}

export type SignedMultiSigOptions = UnsignedMultiSigOptions & {
signerKeys: string[];
signerKeys: PrivateKey[];
};

/**
Expand All @@ -95,11 +101,11 @@ export type TokenTransferOptions = {
} & ApiParam;

export interface UnsignedTokenTransferOptions extends TokenTransferOptions {
publicKey: string;
publicKey: PublicKey;
}

export interface SignedTokenTransferOptions extends TokenTransferOptions {
senderKey: string;
senderKey: PrivateKey;
}

export type UnsignedMultiSigTokenTransferOptions = TokenTransferOptions & UnsignedMultiSigOptions;
Expand Down Expand Up @@ -151,12 +157,12 @@ export async function makeUnsignedSTXTokenTransfer(

const publicKeys = options.address
? sortPublicKeysForAddress(
options.publicKeys,
options.publicKeys.map(publicKeyToHex),
options.numSignatures,
hashMode,
createAddress(options.address).hash160
)
: options.publicKeys;
: options.publicKeys.map(publicKeyToHex);

spendingCondition = createMultiSigSpendingCondition(
hashMode,
Expand Down Expand Up @@ -226,8 +232,8 @@ export async function makeSTXTokenTransfer(

mutatingSignAppendMultiSig(
transaction,
txOptions.publicKeys.slice(),
txOptions.signerKeys,
txOptions.publicKeys.map(publicKeyToHex).slice(),
txOptions.signerKeys.map(privateKeyToHex),
txOptions.address
);

Expand Down Expand Up @@ -262,11 +268,11 @@ export interface BaseContractDeployOptions {

export interface UnsignedContractDeployOptions extends BaseContractDeployOptions {
/** a hex string of the public key of the transaction sender */
publicKey: string;
publicKey: PublicKey;
}

export interface SignedContractDeployOptions extends BaseContractDeployOptions {
senderKey: string;
senderKey: PrivateKey;
}

/** @deprecated Use {@link SignedContractDeployOptions} or {@link UnsignedContractDeployOptions} instead. */
Expand Down Expand Up @@ -307,8 +313,8 @@ export async function makeContractDeploy(

mutatingSignAppendMultiSig(
transaction,
txOptions.publicKeys.slice(),
txOptions.signerKeys,
txOptions.publicKeys.map(publicKeyToHex).slice(),
txOptions.signerKeys.map(privateKeyToHex),
txOptions.address
);

Expand Down Expand Up @@ -357,12 +363,12 @@ export async function makeUnsignedContractDeploy(

const publicKeys = options.address
? sortPublicKeysForAddress(
options.publicKeys,
options.publicKeys.map(publicKeyToHex),
options.numSignatures,
hashMode,
createAddress(options.address).hash160
)
: options.publicKeys;
: options.publicKeys.map(publicKeyToHex);

spendingCondition = createMultiSigSpendingCondition(
hashMode,
Expand Down Expand Up @@ -440,11 +446,11 @@ export interface ContractCallOptions {
}

export interface UnsignedContractCallOptions extends ContractCallOptions {
publicKey: string;
publicKey: PrivateKey;
}

export interface SignedContractCallOptions extends ContractCallOptions {
senderKey: string;
senderKey: PublicKey;
}

export type UnsignedMultiSigContractCallOptions = ContractCallOptions & UnsignedMultiSigOptions;
Expand Down Expand Up @@ -514,12 +520,12 @@ export async function makeUnsignedContractCall(

const publicKeys = options.address
? sortPublicKeysForAddress(
options.publicKeys,
options.publicKeys.map(publicKeyToHex),
options.numSignatures,
hashMode,
createAddress(options.address).hash160
)
: options.publicKeys;
: options.publicKeys.map(publicKeyToHex);

spendingCondition = createMultiSigSpendingCondition(
hashMode,
Expand Down Expand Up @@ -594,8 +600,8 @@ export async function makeContractCall(

mutatingSignAppendMultiSig(
transaction,
txOptions.publicKeys.slice(),
txOptions.signerKeys,
txOptions.publicKeys.map(publicKeyToHex).slice(),
txOptions.signerKeys.map(privateKeyToHex),
txOptions.address
);

Expand All @@ -610,7 +616,7 @@ export interface SponsorOptionsOpts {
/** the origin-signed transaction */
transaction: StacksTransaction;
/** the sponsor's private key */
sponsorPrivateKey: string;
sponsorPrivateKey: PrivateKey;
/** the transaction fee amount to sponsor */
fee?: IntegerType;
/** the nonce of the sponsor account */
Expand Down
Loading
Loading