Skip to content

Commit 3cf38fa

Browse files
fix: improve WIF support (#1263)
* Remove networks * Additional tests * Fix packages/crypto-address-base58 * packages/crypto-address-keccak256 * Update crypto key pair * Generate identities in wif * Implement toPrivateKey * Test pair * Return compressed * Add crypto wif to key pair * Add WIFDecoder * Fix crypto-key-pair-ecdsa * Fix crypto-key-pair-bls12-381 * Remove decoder * Test crypto-address-keccak256 * Test crypto-address-base58 * Fix wif tests * style: resolve style guide violations [ci-lint-fix] * Fix deps * Lock file * Fix deps * Remove toPrivateKey
1 parent 07b0d04 commit 3cf38fa

30 files changed

Lines changed: 452 additions & 368 deletions

packages/crypto-address-base58/source/address.factory.test.ts

Lines changed: 23 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -10,32 +10,28 @@ import cryptoJson from "../../core/bin/config/devnet/core/crypto.json";
1010
import { describe } from "@mainsail/test-runner";
1111
import { AddressFactory } from "./address.factory";
1212

13-
const mnemonic = "this is a top secret passphrase";
14-
const wif = "SGq4xLgZKCGxs7bjmwnBrWcT4C1ADFEermj846KC97FSv1WFD1dA";
13+
const mnemonic = "this is a top secret mnemonic";
14+
const wif = "UfDzkBsi7xxjq491zm5tk7rCZ1EouBXsFUWaCvQWxAortbh1zq5T";
1515

16-
describe<{ app: Application }>("AddressFactory", ({ assert, beforeEach, it }) => {
16+
describe<{ app: Application; factory: AddressFactory }>("AddressFactory", ({ assert, beforeEach, it }) => {
1717
beforeEach(async (context) => {
1818
context.app = new Application();
1919
context.app.get<Contracts.Kernel.Repository>(Identifiers.Config.Repository).set("crypto", cryptoJson);
2020
await context.app.resolve(ValidationServiceProvider).register();
2121
await context.app.resolve(CryptoConfigServiceProvider).register();
2222

23-
context.app.get<Contracts.Crypto.Configuration>(Identifiers.Cryptography.Configuration).set("network.wif", 170);
24-
2523
await context.app.resolve<ECDSA>(ECDSA).register();
2624
await context.app.resolve<CryptoHashBcrypto>(CryptoHashBcrypto).register();
25+
context.factory = context.app.resolve(AddressFactory);
2726
});
2827

29-
it("should derive an address from an mnemonic", async (context) => {
30-
assert.is(
31-
await context.app.resolve(AddressFactory).fromMnemonic(mnemonic),
32-
"D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib",
33-
);
28+
it("#fromMnemonic - should derive an address from an mnemonic", async ({ factory }) => {
29+
assert.is(await factory.fromMnemonic(mnemonic), "DLsMhiUzAVEXBXDTY1NGNZteWz8SDvphfa");
3430
});
3531

36-
it("should derive an address from multi signature address", async (context) => {
32+
it("#fromMultiSignatureAsset - should derive an address from multi signature address", async ({ factory }) => {
3733
assert.is(
38-
await context.app.resolve(AddressFactory).fromMultiSignatureAsset({
34+
await factory.fromMultiSignatureAsset({
3935
min: 3,
4036
publicKeys: [
4137
"0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3",
@@ -47,55 +43,41 @@ describe<{ app: Application }>("AddressFactory", ({ assert, beforeEach, it }) =>
4743
);
4844
});
4945

50-
it("should derive an address from a public key", async (context) => {
46+
it("#fromPublicKey - should derive an address from a public key", async ({ factory }) => {
5147
assert.is(
52-
await context.app
53-
.resolve(AddressFactory)
54-
.fromPublicKey("034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed192"),
48+
await factory.fromPublicKey("034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed192"),
5549
"D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib",
5650
);
5751
});
5852

59-
it("should derive an address from wif", async (context) => {
60-
assert.is(await context.app.resolve(AddressFactory).fromWIF(wif), "D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib");
53+
it("#fromWIF - should derive an address from wif", async ({ factory }) => {
54+
assert.is(await factory.fromWIF(wif), "DLsMhiUzAVEXBXDTY1NGNZteWz8SDvphfa");
6155
});
6256

63-
it("should validate addresses", async (context) => {
64-
assert.true(await context.app.resolve(AddressFactory).validate("D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib"));
65-
assert.false(
66-
await context.app
67-
.resolve(AddressFactory)
68-
.validate("m0d1q05ypy7qw2hhqqz28rwetc6dauge6g6g65npy2qht5pjuheqwrse7gxkhwv"),
69-
);
57+
it("#validate - should validate addresses", async ({ factory }) => {
58+
assert.true(await factory.validate("DLsMhiUzAVEXBXDTY1NGNZteWz8SDvphfa"));
59+
assert.false(await factory.validate("m0d1q05ypy7qw2hhqqz28rwetc6dauge6g6g65npy2qht5pjuheqwrse7gxkhwv"));
7060
});
7161

72-
it("should convert between buffer", async (context) => {
73-
const addressFactory = context.app.resolve(AddressFactory);
74-
62+
it("#toBuffer & #fromBuffer - should convert between buffer", async ({ factory }) => {
7563
assert.equal(
76-
await addressFactory.fromBuffer(await addressFactory.toBuffer("D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib")),
77-
"D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib",
64+
await factory.fromBuffer(await factory.toBuffer("DLsMhiUzAVEXBXDTY1NGNZteWz8SDvphfa")),
65+
"DLsMhiUzAVEXBXDTY1NGNZteWz8SDvphfa",
7866
);
7967
});
8068

81-
it("should throw if pubKeyHash doesn't match", async (context) => {
82-
const addressFactory = context.app.resolve(AddressFactory);
83-
84-
context.app
85-
.get<Contracts.Crypto.Configuration>(Identifiers.Cryptography.Configuration)
86-
.set("network.pubKeyHash", 44);
69+
it("should throw if pubKeyHash doesn't match", async ({ factory, app }) => {
70+
app.get<Contracts.Crypto.Configuration>(Identifiers.Cryptography.Configuration).set("network.pubKeyHash", 44);
8771

8872
await assert.rejects(
89-
() => addressFactory.toBuffer("D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib"),
73+
() => factory.toBuffer("DLsMhiUzAVEXBXDTY1NGNZteWz8SDvphfa"),
9074
"Expected address network byte 44, but got 30.",
9175
);
9276
});
9377

94-
it("should throw invalid checksum", async (context) => {
95-
const addressFactory = context.app.resolve(AddressFactory);
96-
78+
it("#toBuffer - should throw invalid checksum", async ({ factory }) => {
9779
await assert.rejects(
98-
() => addressFactory.toBuffer("E61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib"),
80+
() => factory.toBuffer("E61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib"),
9981
"Invalid checksum for base58 string.",
10082
);
10183
});

packages/crypto-address-base58/source/schemas.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ describe<{
3434
}
3535
});
3636

37-
it("address - should be ok", ({ validator }) => {
37+
it("#legacyAddress - should be ok", ({ validator }) => {
3838
assert.undefined(validator.validate("legacyAddress", "a".repeat(length)).error);
3939

4040
const validChars = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
@@ -44,7 +44,7 @@ describe<{
4444
}
4545
});
4646

47-
it("address - should be ok for factory", async (context) => {
47+
it("#legacyAddress - should be ok for factory", async (context) => {
4848
await context.app.resolve<ECDSA>(ECDSA).register();
4949

5050
assert.undefined(
@@ -55,7 +55,7 @@ describe<{
5555
);
5656
});
5757

58-
it("address - should not be ok", ({ validator }) => {
58+
it("#legacyAddress - should not be ok", ({ validator }) => {
5959
assert.defined(validator.validate("legacyAddress", "a".repeat(length - 2)).error);
6060
assert.defined(validator.validate("legacyAddress", "a".repeat(length + 1)).error);
6161
assert.defined(validator.validate("legacyAddress", 123).error);
Lines changed: 57 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,63 @@
11
import { Identifiers } from "@mainsail/constants";
2-
import { Configuration } from "@mainsail/crypto-config";
32
import { ServiceProvider as ECDSA } from "@mainsail/crypto-key-pair-ecdsa";
43
import { Application } from "@mainsail/kernel";
4+
import { Contracts } from "@mainsail/contracts";
55
import { ServiceProvider as ValidationServiceProvider } from "@mainsail/validation";
6+
import { ServiceProvider as CryptoConfigServiceProvider } from "@mainsail/crypto-config";
67

78
import { describe } from "@mainsail/test-runner";
89
import { AddressFactory } from "./address.factory";
910

10-
const mnemonic =
11-
"program fragile industry scare sun visit race erase daughter empty anxiety cereal cycle hunt airport educate giggle picture sunset apart jewel similar pulp moment";
11+
import { wallets } from "../../crypto-wif/test/index.js";
12+
import cryptoJson from "../../core/bin/config/devnet/core/crypto.json";
1213

13-
const wif = "SDuW66dyGZ1zPZdN7ncEevbJdjaQTj9pT4LcmKzQ7eLFoyCXEdkx";
14-
15-
describe<{ app: Application }>("AddressFactory", ({ assert, beforeEach, it }) => {
14+
describe<{ app: Application; factory: AddressFactory }>("AddressFactory", ({ assert, beforeEach, it, each }) => {
1615
beforeEach(async (context) => {
1716
context.app = new Application();
18-
context.app.bind(Identifiers.Cryptography.Configuration).to(Configuration).inSingletonScope();
19-
17+
context.app.get<Contracts.Kernel.Repository>(Identifiers.Config.Repository).set("crypto", cryptoJson);
2018
await context.app.resolve(ValidationServiceProvider).register();
19+
await context.app.resolve(CryptoConfigServiceProvider).register();
20+
2121
await context.app.resolve<ECDSA>(ECDSA).register();
22+
23+
context.factory = context.app.resolve(AddressFactory);
2224
});
2325

24-
it("should derive an address from an mnemonic", async (context) => {
25-
assert.is(
26-
await context.app.resolve(AddressFactory).fromMnemonic(mnemonic),
27-
"0xC7C50f33278bDe272ffe23865fF9fBd0155a5175",
28-
);
26+
each(
27+
"#fromMnemonic - should derive an address from an mnemonic",
28+
async ({ context: { factory }, dataset: wallet }) => {
29+
assert.is(await factory.fromMnemonic(wallet.mnemonic), wallet.address);
30+
},
31+
wallets,
32+
);
33+
34+
each(
35+
"#fromPublicKey - should derive an address from a public key",
36+
async ({ context: { factory }, dataset: wallet }) => {
37+
assert.is(await factory.fromPublicKey(wallet.publicKey), wallet.address);
38+
},
39+
wallets,
40+
);
41+
42+
it("#fromPublicKey - should throw if public key doesn't have 65 chars", async ({ factory }) => {
43+
await assert.rejects(() => factory.fromPublicKey("0".repeat(66 * 2)), "Invalid uncompressed public key");
44+
});
45+
46+
it("#fromPublicKey - should throw if public key doesn't start with 0x04", async ({ factory }) => {
47+
await assert.rejects(() => factory.fromPublicKey("0".repeat(65 * 2)), "Invalid uncompressed public key");
2948
});
3049

31-
it("should derive an address from multi signature address", async (context) => {
50+
each(
51+
"#fromWIF - should derive an address from wif",
52+
async ({ context: { factory }, dataset: wallet }) => {
53+
assert.is(await factory.fromWIF(wallet.wif), wallet.address);
54+
},
55+
wallets,
56+
);
57+
58+
it("#fromMultiSignatureAsset - should derive an address from multi signature address", async ({ factory }) => {
3259
assert.is(
33-
await context.app.resolve(AddressFactory).fromMultiSignatureAsset({
60+
await factory.fromMultiSignatureAsset({
3461
min: 3,
3562
publicKeys: [
3663
"0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3",
@@ -42,49 +69,26 @@ describe<{ app: Application }>("AddressFactory", ({ assert, beforeEach, it }) =>
4269
);
4370
});
4471

45-
it("should derive an address from a public key", async (context) => {
46-
assert.is(
47-
await context.app
48-
.resolve(AddressFactory)
49-
.fromPublicKey("03e84093c072af70004a38dd95e34def119d2348d5261228175d032e5f2070e19f"),
50-
"0xC7C50f33278bDe272ffe23865fF9fBd0155a5175",
51-
);
52-
});
53-
54-
it("should throw if public key doesn't have 65 chars", async (context) => {
55-
await assert.rejects(
56-
() => context.app.resolve(AddressFactory).fromPublicKey("0".repeat(66 * 2)),
57-
"Invalid uncompressed public key",
58-
);
59-
});
72+
each(
73+
"#validate - should be valid",
74+
async ({ context: { factory }, dataset: address }) => {
75+
assert.true(await factory.validate(address));
76+
},
77+
["0xC7C50f33278bDe272ffe23865fF9fBd0155a5175", "0xC7C50f33278bDe272ffe23865fF9fBd0155a5175"].concat(
78+
wallets.map((wallet) => wallet.address),
79+
),
80+
);
6081

61-
it("should throw if public key doesn't start with 0x04", async (context) => {
62-
await assert.rejects(
63-
() => context.app.resolve(AddressFactory).fromPublicKey("0".repeat(65 * 2)),
64-
"Invalid uncompressed public key",
65-
);
66-
});
67-
68-
it("should derive an address from wif", async (context) => {
69-
assert.is(await context.app.resolve(AddressFactory).fromWIF(wif), "0xC7C50f33278bDe272ffe23865fF9fBd0155a5175");
70-
});
71-
72-
it("should validate addresses", async (context) => {
73-
assert.true(await context.app.resolve(AddressFactory).validate("0xC7C50f33278bDe272ffe23865fF9fBd0155a5175"));
74-
assert.true(await context.app.resolve(AddressFactory).validate("0xC7C50f33278bDe272ffe23865fF9fBd0155a5175"));
75-
assert.false(await context.app.resolve(AddressFactory).validate("0xC7C50f33278bde272ffe23865ff9fbd0155a5175"));
76-
assert.false(
77-
await context.app
78-
.resolve(AddressFactory)
79-
.validate("m0d1q05ypy7qw2hhqqz28rwetc6dauge6g6g65npy2qht5pjuheqwrse7gxkhwv"),
80-
);
82+
it("#validate - should be invalid", async ({ factory }) => {
83+
assert.false(await factory.validate("0xC7C50f33278bde272ffe23865ff9fbd0155a5175"));
84+
assert.false(await factory.validate("m0d1q05ypy7qw2hhqqz28rwetc6dauge6g6g65npy2qht5pjuheqwrse7gxkhwv"));
8185
});
8286

83-
it("should convert from and to buffer", async (context) => {
84-
const buffer = await context.app.resolve(AddressFactory).toBuffer("0xC7C50f33278bDe272ffe23865fF9fBd0155a5175");
87+
it("#toBuffer and #fromBuffer - should convert from and to buffer", async ({ factory }) => {
88+
const buffer = await factory.toBuffer("0xC7C50f33278bDe272ffe23865fF9fBd0155a5175");
8589
assert.equal(buffer.byteLength, 20);
8690

87-
const restored = await context.app.resolve(AddressFactory).fromBuffer(buffer);
91+
const restored = await factory.fromBuffer(buffer);
8892
assert.equal(restored, "0xC7C50f33278bDe272ffe23865fF9fBd0155a5175");
8993
});
9094
});

packages/crypto-address-keccak256/source/schemas.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ describe<{
3131
}
3232
});
3333

34-
it("address - should be ok", ({ validator }) => {
34+
it("#address - should be ok", ({ validator }) => {
3535
const prefix = "0x";
3636

3737
assert.undefined(validator.validate("address", prefix + "a".repeat(length - prefix.length)).error);
@@ -43,7 +43,7 @@ describe<{
4343
}
4444
});
4545

46-
it("address - should be ok for factory", async (context) => {
46+
it("#address - should be ok for factory", async (context) => {
4747
await context.app.resolve<ECDSA>(ECDSA).register();
4848

4949
assert.undefined(
@@ -54,7 +54,7 @@ describe<{
5454
);
5555
});
5656

57-
it("address - should not be ok", ({ validator }) => {
57+
it("#address - should not be ok", ({ validator }) => {
5858
const prefix = "0x";
5959
const invalidPrefix = "1x";
6060

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
11
import { Identifiers } from "@mainsail/constants";
2-
import { Configuration } from "@mainsail/crypto-config";
3-
import { ServiceProvider as ECDSA } from "@mainsail/crypto-key-pair-ecdsa";
4-
import { ServiceProvider as ValidationServiceProvider } from "@mainsail/validation";
52
import { ByteBuffer } from "@mainsail/utils";
63
import { Buffer } from "buffer";
74

@@ -10,33 +7,37 @@ import { describe } from "@mainsail/test-runner";
107
import { AddressFactory } from "./address.factory";
118
import { AddressSerializer } from "./serializer";
129

10+
import { wallets } from "../../crypto-wif/test/index.js";
11+
1312
describe<{
1413
app: Application;
1514
serializer: AddressSerializer;
1615
factory: AddressFactory;
17-
}>("AddressSerializer", ({ it, assert, beforeEach }) => {
16+
}>("AddressSerializer", ({ it, assert, beforeEach, each }) => {
1817
beforeEach(async (context) => {
1918
context.app = new Application();
20-
context.app.bind(Identifiers.Cryptography.Configuration).to(Configuration).inSingletonScope();
2119

22-
await context.app.resolve(ValidationServiceProvider).register();
23-
await context.app.resolve<ECDSA>(ECDSA).register();
20+
context.app.bind(Identifiers.Cryptography.Identity.KeyPair.Factory).toConstantValue({});
21+
context.app.bind(Identifiers.Cryptography.Identity.PublicKey.Factory).toConstantValue({});
2422

2523
context.serializer = context.app.resolve(AddressSerializer);
2624
context.factory = context.app.resolve(AddressFactory);
2725
});
2826

29-
it("should serialize and deserialize address", async ({ factory, serializer }) => {
30-
const address = "0xC7C50f33278bDe272ffe23865fF9fBd0155a5175";
31-
const buffer = await factory.toBuffer(address);
27+
each(
28+
"#serialize & #deserialize - should serialize and deserialize address",
29+
async ({ context: { factory, serializer }, dataset: wallet }) => {
30+
const buffer = await factory.toBuffer(wallet.address);
3231

33-
const byteBuffer = ByteBuffer.fromBuffer(Buffer.alloc(100));
32+
const byteBuffer = ByteBuffer.fromBuffer(Buffer.alloc(100));
3433

35-
serializer.serialize(byteBuffer, buffer);
36-
byteBuffer.reset();
34+
serializer.serialize(byteBuffer, buffer);
35+
byteBuffer.reset();
3736

38-
const readBuffer = serializer.deserialize(byteBuffer);
37+
const readBuffer = serializer.deserialize(byteBuffer);
3938

40-
assert.equal(await factory.fromBuffer(readBuffer), address);
41-
});
39+
assert.equal(await factory.fromBuffer(readBuffer), wallet.address);
40+
},
41+
wallets,
42+
);
4243
});

packages/crypto-key-pair-bls12-381/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,12 @@
2929
"@mainsail/kernel": "workspace:*",
3030
"@mainsail/utils": "workspace:*",
3131
"@scure/bip39": "2.0.1",
32-
"bls12-381-keygen": "0.2.4",
33-
"wif": "5.0.0"
32+
"bls12-381-keygen": "0.2.4"
3433
},
3534
"devDependencies": {
3635
"@mainsail/contracts": "workspace:*",
3736
"@mainsail/crypto-config": "workspace:*",
37+
"@mainsail/crypto-wif": "workspace:*",
3838
"@mainsail/test-runner": "workspace:*",
3939
"@mainsail/validation": "workspace:*",
4040
"bip39": "3.1.0",

0 commit comments

Comments
 (0)