Skip to content

Commit 21eaed1

Browse files
committed
refactor(account-lib): use Signature type
TICKET: COIN-4724
1 parent fecedf6 commit 21eaed1

File tree

14 files changed

+283
-80
lines changed

14 files changed

+283
-80
lines changed

modules/abstract-eth/src/lib/messages/eip191/eip191MessageBuilder.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import { EIP191Message } from './eip191Message';
22
import { BaseCoin as CoinConfig } from '@bitgo/statics';
3-
import { BaseMessageBuilder, BroadcastableMessage, IMessage, MessageStandardType } from '@bitgo/sdk-core';
3+
import {
4+
BaseMessageBuilder,
5+
BroadcastableMessage,
6+
deserializeSignatures,
7+
IMessage,
8+
MessageStandardType,
9+
} from '@bitgo/sdk-core';
410

511
/**
612
* Builder for EIP-191 messages
@@ -48,14 +54,14 @@ export class Eip191MessageBuilder extends BaseMessageBuilder {
4854
* @returns The parsed message
4955
*/
5056
public async fromBroadcastFormat(broadcastMessage: BroadcastableMessage): Promise<IMessage> {
51-
const { type, payload, signatures, signers, metadata } = broadcastMessage;
57+
const { type, payload, serializedSignatures, signers, metadata } = broadcastMessage;
5258
if (type !== MessageStandardType.EIP191) {
5359
throw new Error(`Invalid message type, expected ${MessageStandardType.EIP191}`);
5460
}
5561
return new EIP191Message({
5662
coinConfig: this.coinConfig,
5763
payload,
58-
signatures,
64+
signatures: deserializeSignatures(serializedSignatures),
5965
signers,
6066
metadata: {
6167
...metadata,

modules/abstract-eth/test/unit/messages/eip191/eip191Message.ts

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import 'should';
22
import sinon from 'sinon';
3-
import { MessageStandardType } from '@bitgo/sdk-core';
3+
import { MessageStandardType, serializeSignatures } from '@bitgo/sdk-core';
44
import { fixtures } from '../fixtures';
55
import { EIP191Message } from '../../../../src';
66

@@ -84,17 +84,33 @@ describe('EIP191 Message', () => {
8484
message.getSigners().should.containEql(fixtures.eip191.signer);
8585

8686
// Test adding new ones
87-
message.addSignature('new-signature');
87+
message.addSignature({
88+
publicKey: { pub: 'pub1' },
89+
signature: Buffer.from('new-signature'),
90+
});
8891
message.addSigner('new-signer');
8992

90-
message.getSignatures().should.containEql('new-signature');
93+
message.getSignatures().should.containEql({
94+
publicKey: { pub: 'pub1' },
95+
signature: Buffer.from('new-signature'),
96+
});
9197
message.getSigners().should.containEql('new-signer');
9298

9399
// Test replacing all
94-
message.setSignatures(['replaced-signature']);
100+
message.setSignatures([
101+
{
102+
publicKey: { pub: 'pub2' },
103+
signature: Buffer.from('replaced-signature'),
104+
},
105+
]);
95106
message.setSigners(['replaced-signer']);
96107

97-
message.getSignatures().should.deepEqual(['replaced-signature']);
108+
message.getSignatures().should.deepEqual([
109+
{
110+
publicKey: { pub: 'pub2' },
111+
signature: Buffer.from('replaced-signature'),
112+
},
113+
]);
98114
message.getSigners().should.deepEqual(['replaced-signer']);
99115
});
100116

@@ -121,9 +137,10 @@ describe('EIP191 Message', () => {
121137

122138
const broadcastFormat = await message.toBroadcastFormat();
123139

140+
const expectedSerializedSignatures = serializeSignatures([fixtures.eip191.signature]);
124141
broadcastFormat.type.should.equal(MessageStandardType.EIP191);
125142
broadcastFormat.payload.should.equal(fixtures.messages.validMessage);
126-
broadcastFormat.signatures.should.deepEqual([fixtures.eip191.signature]);
143+
broadcastFormat.serializedSignatures.should.deepEqual(expectedSerializedSignatures);
127144
broadcastFormat.signers.should.deepEqual([fixtures.eip191.signer]);
128145
broadcastFormat.metadata!.should.deepEqual(fixtures.eip191.metadata);
129146
broadcastFormat.signablePayload!.should.equal('test-signable-payload');
@@ -164,10 +181,11 @@ describe('EIP191 Message', () => {
164181

165182
const broadcastString = await message.toBroadcastString();
166183
const parsedBroadcast = JSON.parse(broadcastString);
184+
const expectedSerializedSignatures = serializeSignatures([fixtures.eip191.signature]);
167185

168186
parsedBroadcast.type.should.equal(MessageStandardType.EIP191);
169187
parsedBroadcast.payload.should.equal(fixtures.messages.validMessage);
170-
parsedBroadcast.signatures.should.deepEqual([fixtures.eip191.signature]);
188+
parsedBroadcast.serializedSignatures.should.deepEqual(expectedSerializedSignatures);
171189
parsedBroadcast.signers.should.deepEqual([fixtures.eip191.signer]);
172190
parsedBroadcast.metadata.should.deepEqual(fixtures.eip191.metadata);
173191
});

modules/abstract-eth/test/unit/messages/eip191/eip191MessageBuilder.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import 'should';
22
import sinon from 'sinon';
3-
import { MessageStandardType } from '@bitgo/sdk-core';
3+
import { BroadcastableMessage, MessageStandardType, serializeSignatures } from '@bitgo/sdk-core';
44
import { fixtures } from '../fixtures';
55
import { EIP191Message, Eip191MessageBuilder } from '../../../../src';
66

@@ -79,10 +79,10 @@ describe('EIP191 Message Builder', () => {
7979
it('should reconstruct a message from broadcast format', async () => {
8080
const builder = new Eip191MessageBuilder(fixtures.coin);
8181

82-
const broadcastMessage = {
82+
const broadcastMessage: BroadcastableMessage = {
8383
type: MessageStandardType.EIP191,
8484
payload: fixtures.messages.validMessage,
85-
signatures: [fixtures.eip191.signature],
85+
serializedSignatures: serializeSignatures([fixtures.eip191.signature]),
8686
signers: [fixtures.eip191.signer],
8787
metadata: fixtures.eip191.metadata,
8888
};
@@ -101,10 +101,10 @@ describe('EIP191 Message Builder', () => {
101101
it('should throw an error for incorrect message type', async () => {
102102
const builder = new Eip191MessageBuilder(fixtures.coin);
103103

104-
const broadcastMessage = {
104+
const broadcastMessage: BroadcastableMessage = {
105105
type: MessageStandardType.UNKNOWN,
106106
payload: fixtures.messages.validMessage,
107-
signatures: [fixtures.eip191.signature],
107+
serializedSignatures: serializeSignatures([fixtures.eip191.signature]),
108108
signers: [fixtures.eip191.signer],
109109
metadata: {},
110110
};

modules/abstract-eth/test/unit/messages/fixtures.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,13 @@ export const fixtures = {
1313
},
1414
eip191: {
1515
validSignablePayload: '0x19457468657265756d205369676e6564204d6573736167653a0d48656c6c6f2c20776f726c6421',
16-
signature:
17-
'0x5d99b6f7f6d1f73d1a26497f2b1c89b24c0993913f86e9a2d02cd69887d9c94f3c880358579d811b21dd1b7fd9bb01c1d81d10e69f0384e675c32b39643be8921b',
16+
signature: {
17+
publicKey: { pub: '0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf' },
18+
signature: Buffer.from(
19+
'5d99b6f7f6d1f73d1a26497f2b1c89b24c0993913f86e9a2d02cd69887d9c94f3c880358579d811b21dd1b7fd9bb01c1d81d10e69f0384e675c32b39643be8921b',
20+
'hex'
21+
),
22+
},
1823
signer: '0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf',
1924
metadata: {
2025
encoding: 'utf8',

modules/sdk-core/src/account-lib/baseCoin/iface.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,33 @@ export type Signature = {
3535
signature: Buffer;
3636
};
3737

38+
export type SerializedSignature = {
39+
publicKey: string;
40+
signature: string;
41+
};
42+
43+
export function serializeSignature(signature: Signature): SerializedSignature {
44+
return {
45+
publicKey: signature.publicKey.pub,
46+
signature: signature.signature.toString('base64'),
47+
};
48+
}
49+
50+
export function serializeSignatures(signatures: Signature[]): SerializedSignature[] {
51+
return signatures.map(serializeSignature);
52+
}
53+
54+
export function deserializeSignature(serialized: SerializedSignature): Signature {
55+
return {
56+
publicKey: { pub: serialized.publicKey },
57+
signature: Buffer.from(serialized.signature, 'base64'),
58+
};
59+
}
60+
61+
export function deserializeSignatures(serialized: SerializedSignature[]): Signature[] {
62+
return serialized.map(deserializeSignature);
63+
}
64+
3865
export type KeyPairOptions = Seed | PrivateKey | PublicKey;
3966

4067
export type BaseBuilder = BaseTransactionBuilder | BaseTransactionBuilderFactory;

modules/sdk-core/src/account-lib/baseCoin/messages/baseMessage.ts

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,13 @@ import {
77
MessageStandardType,
88
} from '../../../bitgo';
99
import { IMessage } from './iface';
10+
import { serializeSignatures, Signature } from '../iface';
1011

1112
export abstract class BaseMessage implements IMessage {
1213
protected coinConfig: Readonly<CoinConfig>;
1314
protected type: MessageStandardType;
1415
protected payload: MessagePayload;
15-
protected signatures: string[] = [];
16+
protected signatures: Signature[] = [];
1617
protected signers: string[] = [];
1718
protected signablePayload?: string | Buffer;
1819
protected metadata?: MessageMetadata;
@@ -30,7 +31,11 @@ export abstract class BaseMessage implements IMessage {
3031
this.metadata = options.metadata || {};
3132

3233
if (options.signatures) {
33-
this.signatures = [...options.signatures];
34+
// Deep copy each Signature object, including the Buffer
35+
this.signatures = options.signatures.map((sig) => ({
36+
publicKey: { ...sig.publicKey },
37+
signature: Buffer.from(sig.signature),
38+
}));
3439
}
3540
if (options.signers) {
3641
this.signers = [...options.signers];
@@ -86,23 +91,23 @@ export abstract class BaseMessage implements IMessage {
8691
/**
8792
* Gets all signatures associated with this message
8893
*/
89-
getSignatures(): string[] {
94+
getSignatures(): Signature[] {
9095
return [...this.signatures];
9196
}
9297

9398
/**
9499
* Sets signatures for this message
95100
* @param signatures Array of signatures to set
96101
*/
97-
setSignatures(signatures: string[]): void {
102+
setSignatures(signatures: Signature[]): void {
98103
this.signatures = [...signatures];
99104
}
100105

101106
/**
102107
* Adds a signature to this message
103108
* @param signature The signature to add
104109
*/
105-
addSignature(signature: string): void {
110+
addSignature(signature: Signature): void {
106111
this.signatures.push(signature);
107112
}
108113

@@ -121,20 +126,27 @@ export abstract class BaseMessage implements IMessage {
121126
if (this.signatures.length === 0) {
122127
throw new Error('No signatures available for broadcast. Call setSignatures or addSignature first.');
123128
}
124-
125129
if (this.signers.length === 0) {
126130
throw new Error('No signers available for broadcast. Call setSigners or addSigner first.');
127131
}
132+
let signablePayload: string | undefined;
133+
if (this.signablePayload) {
134+
if (Buffer.isBuffer(this.signablePayload)) {
135+
signablePayload = this.signablePayload.toString('base64');
136+
} else {
137+
signablePayload = Buffer.from(String(this.signablePayload)).toString('base64');
138+
}
139+
}
128140

129141
return {
130142
type: this.type,
131143
payload: this.payload,
132-
signatures: this.signatures,
133-
signers: this.signers,
144+
serializedSignatures: serializeSignatures(this.signatures),
145+
signers: [...this.signers],
134146
metadata: {
135147
...(this.metadata ? JSON.parse(JSON.stringify(this.metadata)) : {}), // deep copy to avoid mutation
136148
},
137-
signablePayload: this.signablePayload,
149+
signablePayload,
138150
};
139151
}
140152

modules/sdk-core/src/account-lib/baseCoin/messages/baseMessageBuilder.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { BroadcastableMessage, MessagePayload, MessageStandardType } from '../../../bitgo';
22
import { BaseCoin as CoinConfig } from '@bitgo/statics';
33
import { IMessage, IMessageBuilder } from './iface';
4+
import { Signature } from '../iface';
45

56
/**
67
* Base Message Builder
@@ -9,7 +10,7 @@ export abstract class BaseMessageBuilder implements IMessageBuilder {
910
protected coinConfig: Readonly<CoinConfig>;
1011
protected payload: MessagePayload = '';
1112
protected type: MessageStandardType;
12-
protected signatures: string[] = [];
13+
protected signatures: Signature[] = [];
1314
protected signers: string[] = [];
1415
protected metadata?: Record<string, unknown> = {};
1516
protected digest?: string;
@@ -72,17 +73,21 @@ export abstract class BaseMessageBuilder implements IMessageBuilder {
7273
return this;
7374
}
7475

75-
public getSignatures(): string[] {
76+
public getSignatures(): Signature[] {
7677
return this.signatures;
7778
}
7879

79-
public setSignatures(value: string[]): IMessageBuilder {
80-
this.signatures = value;
80+
public setSignatures(signatures: Signature[]): IMessageBuilder {
81+
this.signatures = signatures;
8182
return this;
8283
}
8384

84-
public addSignature(signature: string): IMessageBuilder {
85-
if (!this.signatures.includes(signature)) {
85+
public addSignature(signature: Signature): IMessageBuilder {
86+
if (
87+
!this.signatures.some(
88+
(sig) => sig.publicKey.pub === signature.publicKey.pub && sig.signature.equals(signature.signature)
89+
)
90+
) {
8691
this.signatures.push(signature);
8792
}
8893
return this;

modules/sdk-core/src/account-lib/baseCoin/messages/iface.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { BroadcastableMessage, MessageMetadata, MessagePayload, MessageStandardType } from '../../../bitgo';
2+
import { Signature } from '../iface';
23

34
/**
45
* Represents a built message that can be signed
@@ -39,19 +40,19 @@ export interface IMessage {
3940
/**
4041
* Gets all signatures associated with this message
4142
*/
42-
getSignatures(): string[];
43+
getSignatures(): Signature[];
4344

4445
/**
4546
* Sets signatures for this message
4647
* @param signatures Array of signatures to set
4748
*/
48-
setSignatures(signatures: string[]): void;
49+
setSignatures(signatures: Signature[]): void;
4950

5051
/**
5152
* Adds a signature to this message
5253
* @param signature The signature to add
5354
*/
54-
addSignature(signature: string): void;
55+
addSignature(signature: Signature): void;
5556

5657
/**
5758
* Returns the payload that should be signed
@@ -98,14 +99,14 @@ export interface IMessageBuilder {
9899
* @param signatures The signatures to add
99100
* @returns The builder instance for chaining
100101
*/
101-
setSignatures(signatures: string[]): IMessageBuilder;
102+
setSignatures(signatures: Signature[]): IMessageBuilder;
102103

103104
/**
104105
* Adds a signature to the message
105106
* @param signature The signature to add
106107
* @returns The builder instance for chaining
107108
*/
108-
addSignature(signature: string): IMessageBuilder;
109+
addSignature(signature: Signature): IMessageBuilder;
109110

110111
/**
111112
* Sets the signers for the message

modules/sdk-core/src/bitgo/utils/messageTypes.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import { BaseCoin as CoinConfig } from '@bitgo/statics';
2+
import { SerializedSignature, Signature } from '../../account-lib';
23

34
/**
45
* Supported message signing standard types
56
*/
67
export enum MessageStandardType {
78
UNKNOWN = 'UNKNOWN',
89
EIP191 = 'EIP191',
10+
CIP8 = 'CIP8',
911
}
1012

1113
export type MessagePayload = string;
@@ -17,10 +19,10 @@ export type MessageMetadata = Record<string, unknown>;
1719
export interface BroadcastableMessage {
1820
type: MessageStandardType;
1921
payload: MessagePayload;
20-
signatures: string[];
22+
serializedSignatures: SerializedSignature[];
2123
signers: string[]; // list of addresses or public keys of the signers
2224
metadata?: MessageMetadata;
23-
signablePayload?: string | Buffer;
25+
signablePayload?: string;
2426
}
2527

2628
/**
@@ -32,6 +34,6 @@ export interface MessageOptions {
3234
type?: MessageStandardType;
3335
signablePayload?: string | Buffer;
3436
metadata?: MessageMetadata;
35-
signatures?: string[];
37+
signatures?: Signature[];
3638
signers?: string[];
3739
}

0 commit comments

Comments
 (0)