Skip to content

Commit

Permalink
refactor!: change Authorization from a class to functional interface
Browse files Browse the repository at this point in the history
  • Loading branch information
reedrosenbluth committed Sep 7, 2021
1 parent 0ada72a commit 327a153
Show file tree
Hide file tree
Showing 6 changed files with 168 additions and 203 deletions.
268 changes: 111 additions & 157 deletions packages/transactions/src/authorization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,7 @@ import {
} from './keys';

import { BufferReader } from './bufferReader';
import { DeserializationError, SerializationError, SigningError } from './errors';

abstract class Deserializable {
abstract serialize(): Buffer;
abstract deserialize(bufferReader: BufferReader): void;
static deserialize<T extends Deserializable>(this: new () => T, bufferReader: BufferReader): T {
const message = new this();
message.deserialize(bufferReader);
return message;
}
}
import { DeserializationError, SigningError } from './errors';

export interface MessageSignature {
readonly type: StacksMessageType.MessageSignature;
Expand Down Expand Up @@ -503,177 +493,141 @@ function verifySingleSig(
return nextSigHash;
}

export class Authorization extends Deserializable {
authType?: AuthType;
spendingCondition?: SpendingCondition;
sponsorSpendingCondition?: SpendingCondition;

constructor(
authType?: AuthType,
spendingConditions?: SpendingConditionOpts,
sponsorSpendingCondition?: SpendingConditionOpts
) {
super();
this.authType = authType;
if (spendingConditions) {
this.spendingCondition = {
...spendingConditions,
nonce: intToBigInt(spendingConditions.nonce, false),
fee: intToBigInt(spendingConditions.fee, false),
};
}
if (sponsorSpendingCondition) {
this.sponsorSpendingCondition = {
...sponsorSpendingCondition,
nonce: intToBigInt(sponsorSpendingCondition.nonce, false),
fee: intToBigInt(sponsorSpendingCondition.fee, false),
};
}
}
export type Authorization = StandardAuthorization | SponsoredAuthorization;

intoInitialSighashAuth(): Authorization {
if (this.spendingCondition) {
switch (this.authType) {
case AuthType.Standard:
return new Authorization(AuthType.Standard, clearCondition(this.spendingCondition));
case AuthType.Sponsored:
return new Authorization(
AuthType.Sponsored,
clearCondition(this.spendingCondition),
newInitialSigHash()
);
default:
throw new SigningError('Unexpected authorization type for signing');
}
}
export interface StandardAuthorization {
authType: AuthType.Standard;
spendingCondition: SpendingCondition;
}

throw new Error('Authorization missing SpendingCondition');
}
export interface SponsoredAuthorization {
authType: AuthType.Sponsored;
spendingCondition: SpendingCondition;
sponsorSpendingCondition: SpendingCondition;
}

setFee(amount: IntegerType) {
switch (this.authType) {
case AuthType.Standard:
this.spendingCondition!.fee = intToBigInt(amount, false);
break;
case AuthType.Sponsored:
this.sponsorSpendingCondition!.fee = intToBigInt(amount, false);
break;
}
export function createStandardAuth(spendingCondition: SpendingCondition): StandardAuthorization {
return {
authType: AuthType.Standard,
spendingCondition,
};
}

export function createSponsoredAuth(
spendingCondition: SpendingCondition,
sponsorSpendingCondition?: SpendingCondition
): Authorization {
if (!sponsorSpendingCondition) {
sponsorSpendingCondition = createSingleSigSpendingCondition(
AddressHashMode.SerializeP2PKH,
'0'.repeat(66),
0,
0
);
}

getFee(): bigint {
switch (this.authType) {
return {
authType: AuthType.Sponsored,
spendingCondition,
sponsorSpendingCondition
};
}

export function intoInitialSighashAuth(auth: Authorization): Authorization {
if (auth.spendingCondition) {
switch (auth.authType) {
case AuthType.Standard:
return this.spendingCondition!.fee;
return createStandardAuth(clearCondition(auth.spendingCondition));
case AuthType.Sponsored:
return this.sponsorSpendingCondition!.fee;
return createSponsoredAuth(
clearCondition(auth.spendingCondition),
newInitialSigHash()
);
default:
return BigInt(0);
throw new SigningError('Unexpected authorization type for signing');
}
}

setNonce(nonce: IntegerType) {
this.spendingCondition!.nonce = intToBigInt(nonce, false);
}
throw new Error('Authorization missing SpendingCondition');
}

setSponsorNonce(nonce: IntegerType) {
this.sponsorSpendingCondition!.nonce = intToBigInt(nonce, false);
export function verifyOrigin(auth: Authorization, initialSigHash: string): string {
switch (auth.authType) {
case AuthType.Standard:
return verify(auth.spendingCondition, initialSigHash, AuthType.Standard);
case AuthType.Sponsored:
return verify(auth.spendingCondition, initialSigHash, AuthType.Standard);
default:
throw new SigningError('Invalid origin auth type');
}
}

setSponsor(sponsorSpendingCondition: SpendingConditionOpts) {
this.sponsorSpendingCondition = {
...sponsorSpendingCondition,
nonce: intToBigInt(sponsorSpendingCondition.nonce, false),
fee: intToBigInt(sponsorSpendingCondition.fee, false),
};
export function setFee(auth: Authorization, amount: IntegerType) {
switch (auth.authType) {
case AuthType.Standard:
auth.spendingCondition.fee = intToBigInt(amount, false);
break;
case AuthType.Sponsored:
auth.sponsorSpendingCondition.fee = intToBigInt(amount, false);
break;
}
}

verifyOrigin(initialSigHash: string): string {
switch (this.authType) {
case AuthType.Standard:
return verify(this.spendingCondition!, initialSigHash, AuthType.Standard);
case AuthType.Sponsored:
return verify(this.spendingCondition!, initialSigHash, AuthType.Standard);
default:
throw new SigningError('Invalid origin auth type');
}
export function getFee(auth: Authorization): bigint {
switch (auth.authType) {
case AuthType.Standard:
return auth.spendingCondition.fee;
case AuthType.Sponsored:
return auth.sponsorSpendingCondition.fee;
}
}

serialize(): Buffer {
const bufferArray: BufferArray = new BufferArray();
if (this.authType === undefined) {
throw new SerializationError('"authType" is undefined');
}
bufferArray.appendByte(this.authType);
export function setNonce(auth: Authorization, nonce: IntegerType) {
auth.spendingCondition.nonce = intToBigInt(nonce, false);
}

switch (this.authType) {
case AuthType.Standard:
if (this.spendingCondition === undefined) {
throw new SerializationError('"spendingCondition" is undefined');
}
bufferArray.push(serializeSpendingCondition(this.spendingCondition));
break;
case AuthType.Sponsored:
if (this.spendingCondition === undefined) {
throw new SerializationError('"spendingCondition" is undefined');
}
if (this.sponsorSpendingCondition === undefined) {
throw new SerializationError('"spendingCondition" is undefined');
}
bufferArray.push(serializeSpendingCondition(this.spendingCondition));
bufferArray.push(serializeSpendingCondition(this.sponsorSpendingCondition));
break;
default:
throw new SerializationError(
`Unexpected transaction AuthType while serializing: ${JSON.stringify(this.authType)}`
);
}
export function setSponsorNonce(auth: SponsoredAuthorization, nonce: IntegerType) {
auth.sponsorSpendingCondition.nonce = intToBigInt(nonce, false);
}

return bufferArray.concatBuffer();
}
export function setSponsor(auth: SponsoredAuthorization, sponsorSpendingCondition: SpendingConditionOpts) {
auth.sponsorSpendingCondition = {
...sponsorSpendingCondition,
nonce: intToBigInt(sponsorSpendingCondition.nonce, false),
fee: intToBigInt(sponsorSpendingCondition.fee, false),
};
}

deserialize(bufferReader: BufferReader) {
this.authType = bufferReader.readUInt8Enum(AuthType, n => {
throw new DeserializationError(`Could not parse ${n} as AuthType`);
});
export function serializeAuthorization(auth: Authorization): Buffer {
const bufferArray: BufferArray = new BufferArray();
bufferArray.appendByte(auth.authType);

switch (this.authType) {
case AuthType.Standard:
this.spendingCondition = deserializeSpendingCondition(bufferReader);
break;
case AuthType.Sponsored:
this.spendingCondition = deserializeSpendingCondition(bufferReader);
this.sponsorSpendingCondition = deserializeSpendingCondition(bufferReader);
break;
// throw new DeserializationError('Not yet implemented: deserializing sponsored transactions');
default:
throw new DeserializationError(
`Unexpected transaction AuthType while deserializing: ${JSON.stringify(this.authType)}`
);
}
switch (auth.authType) {
case AuthType.Standard:
bufferArray.push(serializeSpendingCondition(auth.spendingCondition));
break;
case AuthType.Sponsored:
bufferArray.push(serializeSpendingCondition(auth.spendingCondition));
bufferArray.push(serializeSpendingCondition(auth.sponsorSpendingCondition));
break;
}
}

export class StandardAuthorization extends Authorization {
constructor(spendingCondition: SpendingConditionOpts) {
super(AuthType.Standard, spendingCondition);
}
return bufferArray.concatBuffer();
}

export class SponsoredAuthorization extends Authorization {
constructor(
originSpendingCondition: SpendingConditionOpts,
sponsorSpendingCondition?: SpendingConditionOpts
) {
let sponsorSC = sponsorSpendingCondition;
if (!sponsorSC) {
sponsorSC = createSingleSigSpendingCondition(
AddressHashMode.SerializeP2PKH,
'0'.repeat(66),
0,
0
);
}
super(AuthType.Sponsored, originSpendingCondition, sponsorSC);
export function deserializeAuthorization(bufferReader: BufferReader) {
let authType = bufferReader.readUInt8Enum(AuthType, n => {
throw new DeserializationError(`Could not parse ${n} as AuthType`);
});

let spendingCondition;
switch (authType) {
case AuthType.Standard:
spendingCondition = deserializeSpendingCondition(bufferReader);
return createStandardAuth(spendingCondition);
case AuthType.Sponsored:
spendingCondition = deserializeSpendingCondition(bufferReader);
const sponsorSpendingCondition = deserializeSpendingCondition(bufferReader);
return createSponsoredAuth(spendingCondition, sponsorSpendingCondition);
}
}
16 changes: 8 additions & 8 deletions packages/transactions/src/builders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import {
} from './payload';

import {
StandardAuthorization,
SponsoredAuthorization,
createSingleSigSpendingCondition,
createMultiSigSpendingCondition,
createSponsoredAuth,
createStandardAuth,
} from './authorization';

import {
Expand Down Expand Up @@ -517,9 +517,9 @@ export async function makeUnsignedSTXTokenTransfer(
}

if (options.sponsored) {
authorization = new SponsoredAuthorization(spendingCondition);
authorization = createSponsoredAuth(spendingCondition);
} else {
authorization = new StandardAuthorization(spendingCondition);
authorization = createStandardAuth(spendingCondition);
}

const postConditions: PostCondition[] = [];
Expand Down Expand Up @@ -717,9 +717,9 @@ export async function makeContractDeploy(
);

if (options.sponsored) {
authorization = new SponsoredAuthorization(spendingCondition);
authorization = createSponsoredAuth(spendingCondition);
} else {
authorization = new StandardAuthorization(spendingCondition);
authorization = createStandardAuth(spendingCondition);
}

const postConditions: PostCondition[] = [];
Expand Down Expand Up @@ -930,9 +930,9 @@ export async function makeUnsignedContractCall(
}

if (options.sponsored) {
authorization = new SponsoredAuthorization(spendingCondition);
authorization = createSponsoredAuth(spendingCondition);
} else {
authorization = new StandardAuthorization(spendingCondition);
authorization = createStandardAuth(spendingCondition);
}

const postConditions: PostCondition[] = [];
Expand Down
4 changes: 2 additions & 2 deletions packages/transactions/src/signer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,8 @@ export class TransactionSigner {
if (this.transaction.auth === undefined) {
throw new SigningError('"transaction.auth" is undefined');
}
if (this.transaction.auth.sponsorSpendingCondition === undefined) {
throw new SigningError('"transaction.auth.spendingCondition" is undefined');
if (this.transaction.auth.authType !== AuthType.Sponsored) {
throw new SigningError('"transaction.auth.authType" is not AuthType.Sponsored');
}

const nextSighash = this.transaction.signNextSponsor(this.sigHash, privateKey);
Expand Down
Loading

0 comments on commit 327a153

Please sign in to comment.