@notabene/verify-proof 1.0.0-preview.6 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bitcoin.d.ts +1 -11
- package/dist/eth.d.ts +3 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.modern.js +1 -1
- package/dist/index.modern.js.map +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/index.umd.js.map +1 -1
- package/dist/solana.d.ts +1 -1
- package/package.json +8 -11
- package/src/bitcoin.ts +115 -12
- package/src/eth.ts +24 -10
- package/src/index.ts +1 -1
- package/src/solana.ts +4 -7
- package/src/tests/bitcoin.test.ts +34 -68
- package/src/tests/eth.test.ts +3 -2
- package/src/tests/index.test.ts +57 -45
- package/src/tests/solana.test.ts +9 -8
package/dist/solana.d.ts
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
import { SignatureProof } from "@notabene/javascript-sdk
|
1
|
+
import { SignatureProof } from "@notabene/javascript-sdk";
|
2
2
|
export declare function verifySolanaSignature(proof: SignatureProof): Promise<SignatureProof>;
|
package/package.json
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
{
|
2
2
|
"name": "@notabene/verify-proof",
|
3
|
-
"version": "1.0.0
|
3
|
+
"version": "1.0.0",
|
4
4
|
"description": "Verify ownership proofs",
|
5
5
|
"source": "src/index.ts",
|
6
6
|
"type": "module",
|
7
7
|
"module": "dist/index.js",
|
8
|
+
"types": "dist/index.d.ts",
|
8
9
|
"main": "dist/index.cjs",
|
9
10
|
"author": "Pelle Braendgaard",
|
10
11
|
"license": "Apache-2.0",
|
@@ -22,25 +23,21 @@
|
|
22
23
|
"test": "vitest"
|
23
24
|
},
|
24
25
|
"devDependencies": {
|
25
|
-
"@types/bitcoinjs-lib": "3.4.0",
|
26
|
-
"@types/node": "^22.9.0",
|
27
|
-
"bitcoinjs-lib": "^3.2.0",
|
28
|
-
"buffer": "^6.0.3",
|
29
|
-
"ecpair": "^3.0.0-rc.0",
|
30
26
|
"eslint": "^9.9.0",
|
31
27
|
"microbundle": "^0.15.1",
|
32
|
-
"
|
28
|
+
"ox": "^0.2.2",
|
33
29
|
"typescript": "^5.5.4",
|
30
|
+
"vite": "^5.4.11",
|
34
31
|
"vitest": "^2.0.5"
|
35
32
|
},
|
36
33
|
"dependencies": {
|
34
|
+
"@bitauth/libauth": "^3.0.0",
|
37
35
|
"@notabene/javascript-sdk": "^2.0.2",
|
38
|
-
"@solana/web3.js": "^1.95.4",
|
39
36
|
"@stablelib/base64": "^2.0.0",
|
37
|
+
"bech32": "^2.0.0",
|
40
38
|
"bigi": "^1.4.2",
|
41
|
-
"
|
42
|
-
"ox": "^0.1.4",
|
39
|
+
"bs58": "^6.0.0",
|
43
40
|
"tweetnacl": "^1.0.3",
|
44
|
-
"
|
41
|
+
"varuint-bitcoin": "^2.0.0"
|
45
42
|
}
|
46
43
|
}
|
package/src/bitcoin.ts
CHANGED
@@ -1,10 +1,24 @@
|
|
1
|
+
import { ProofStatus, SignatureProof } from "@notabene/javascript-sdk";
|
2
|
+
import { bech32 } from "bech32";
|
3
|
+
|
1
4
|
import {
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
5
|
+
secp256k1,
|
6
|
+
hash160,
|
7
|
+
hash256,
|
8
|
+
RecoveryId,
|
9
|
+
encodeBase58AddressFormat,
|
10
|
+
} from "@bitauth/libauth";
|
11
|
+
import { encode as encodeLength } from "varuint-bitcoin";
|
12
|
+
import { decode as decodeBase64 } from "@stablelib/base64";
|
13
|
+
|
14
|
+
enum SEGWIT_TYPES {
|
15
|
+
P2WPKH = "p2wpkh",
|
16
|
+
P2SH_P2WPKH = "p2sh(p2wpkh)",
|
17
|
+
}
|
18
|
+
|
19
|
+
const messagePrefix = "\u0018Bitcoin Signed Message:\n";
|
6
20
|
|
7
|
-
|
21
|
+
enum DerivationMode {
|
8
22
|
LEGACY = "Legacy",
|
9
23
|
NATIVE = "Native SegWit",
|
10
24
|
SEGWIT = "SegWit",
|
@@ -25,13 +39,7 @@ export async function verifyBTCSignature(
|
|
25
39
|
const segwit = [DerivationMode.SEGWIT, DerivationMode.NATIVE].includes(
|
26
40
|
getDerivationMode(address),
|
27
41
|
);
|
28
|
-
const verified =
|
29
|
-
proof.attestation,
|
30
|
-
address,
|
31
|
-
proof.proof,
|
32
|
-
undefined,
|
33
|
-
segwit,
|
34
|
-
);
|
42
|
+
const verified = verify(proof.attestation, address, proof.proof, segwit);
|
35
43
|
|
36
44
|
return {
|
37
45
|
...proof,
|
@@ -59,3 +67,98 @@ function getDerivationMode(address: string) {
|
|
59
67
|
);
|
60
68
|
}
|
61
69
|
}
|
70
|
+
|
71
|
+
type DecodedSignature = {
|
72
|
+
compressed: boolean;
|
73
|
+
segwitType?: SEGWIT_TYPES;
|
74
|
+
recovery: RecoveryId;
|
75
|
+
signature: Uint8Array;
|
76
|
+
};
|
77
|
+
|
78
|
+
function decodeSignature(proof: string): DecodedSignature {
|
79
|
+
const signature = decodeBase64(proof);
|
80
|
+
if (signature.length !== 65) throw new Error("Invalid signature length");
|
81
|
+
|
82
|
+
const flagByte = signature[0] - 27;
|
83
|
+
if (flagByte > 15 || flagByte < 0) {
|
84
|
+
throw new Error("Invalid signature parameter");
|
85
|
+
}
|
86
|
+
|
87
|
+
return {
|
88
|
+
compressed: !!(flagByte & 12),
|
89
|
+
segwitType: !(flagByte & 8)
|
90
|
+
? undefined
|
91
|
+
: !(flagByte & 4)
|
92
|
+
? SEGWIT_TYPES.P2SH_P2WPKH
|
93
|
+
: SEGWIT_TYPES.P2WPKH,
|
94
|
+
recovery: (flagByte & 3) as RecoveryId,
|
95
|
+
signature: signature.slice(1),
|
96
|
+
};
|
97
|
+
}
|
98
|
+
|
99
|
+
function verify(
|
100
|
+
attestation: string,
|
101
|
+
address: string,
|
102
|
+
proof: string,
|
103
|
+
checkSegwitAlways: boolean,
|
104
|
+
) {
|
105
|
+
const { compressed, segwitType, recovery, signature } =
|
106
|
+
decodeSignature(proof);
|
107
|
+
if (checkSegwitAlways && !compressed) {
|
108
|
+
throw new Error(
|
109
|
+
"checkSegwitAlways can only be used with a compressed pubkey signature flagbyte",
|
110
|
+
);
|
111
|
+
}
|
112
|
+
|
113
|
+
const hash = magicHash(attestation);
|
114
|
+
const publicKey: Uint8Array | string = compressed
|
115
|
+
? secp256k1.recoverPublicKeyCompressed(signature, recovery, hash)
|
116
|
+
: secp256k1.recoverPublicKeyUncompressed(signature, recovery, hash);
|
117
|
+
if (typeof publicKey === "string") throw new Error(publicKey);
|
118
|
+
const publicKeyHash = hash160(publicKey);
|
119
|
+
let actual: string = "";
|
120
|
+
|
121
|
+
if (segwitType) {
|
122
|
+
if (segwitType === SEGWIT_TYPES.P2SH_P2WPKH) {
|
123
|
+
actual = encodeBech32Address(publicKeyHash);
|
124
|
+
} else {
|
125
|
+
// parsed.segwitType === SEGWIT_TYPES.P2WPKH
|
126
|
+
// must be true since we only return null, P2SH_P2WPKH, or P2WPKH
|
127
|
+
// from the decodeSignature function.
|
128
|
+
actual = encodeBech32Address(publicKeyHash);
|
129
|
+
}
|
130
|
+
} else {
|
131
|
+
if (checkSegwitAlways) {
|
132
|
+
try {
|
133
|
+
actual = encodeBech32Address(publicKeyHash);
|
134
|
+
// if address is bech32 it is not p2sh
|
135
|
+
} catch (e) {
|
136
|
+
actual = encodeBech32Address(publicKeyHash);
|
137
|
+
// base58 can be p2pkh or p2sh-p2wpkh
|
138
|
+
}
|
139
|
+
} else {
|
140
|
+
actual = encodeBase58AddressFormat(0, publicKeyHash);
|
141
|
+
}
|
142
|
+
}
|
143
|
+
|
144
|
+
return actual === address;
|
145
|
+
}
|
146
|
+
|
147
|
+
function magicHash(attestation: string) {
|
148
|
+
const prefix = new TextEncoder().encode(messagePrefix);
|
149
|
+
const message = new TextEncoder().encode(attestation);
|
150
|
+
const length = encodeLength(message.length).buffer;
|
151
|
+
const buffer = new Uint8Array(
|
152
|
+
prefix.length + length.byteLength + message.length,
|
153
|
+
);
|
154
|
+
buffer.set(prefix);
|
155
|
+
buffer.set(new Uint8Array(length), prefix.length);
|
156
|
+
buffer.set(message, prefix.length + length.byteLength);
|
157
|
+
return hash256(buffer);
|
158
|
+
}
|
159
|
+
|
160
|
+
function encodeBech32Address(publicKeyHash: Uint8Array): string {
|
161
|
+
const bwords = bech32.toWords(publicKeyHash);
|
162
|
+
bwords.unshift(0);
|
163
|
+
return bech32.encode("bc", bwords);
|
164
|
+
}
|
package/src/eth.ts
CHANGED
@@ -1,8 +1,22 @@
|
|
1
|
-
import {
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
1
|
+
import { ProofStatus, SignatureProof } from "@notabene/javascript-sdk";
|
2
|
+
|
3
|
+
import { Secp256k1, Hex, PersonalMessage, Signature, Address } from "ox";
|
4
|
+
|
5
|
+
export function verifyEIP191(
|
6
|
+
address: Hex.Hex,
|
7
|
+
message: string,
|
8
|
+
proof: Hex.Hex,
|
9
|
+
): boolean {
|
10
|
+
try {
|
11
|
+
const payload = PersonalMessage.getSignPayload(Hex.fromString(message));
|
12
|
+
const signature = Signature.fromHex(proof);
|
13
|
+
const publicKey = Secp256k1.recoverPublicKey({ payload, signature });
|
14
|
+
const recovered = Address.fromPublicKey(publicKey);
|
15
|
+
return recovered.toString() === address.toString();
|
16
|
+
} catch (error) {
|
17
|
+
return false;
|
18
|
+
}
|
19
|
+
}
|
6
20
|
|
7
21
|
export async function verifyPersonalSignEIP191(
|
8
22
|
proof: SignatureProof,
|
@@ -10,11 +24,11 @@ export async function verifyPersonalSignEIP191(
|
|
10
24
|
const [ns, _, address] = proof.address.split(/:/);
|
11
25
|
if (ns !== "eip155") return { ...proof, status: ProofStatus.FAILED };
|
12
26
|
|
13
|
-
const verified =
|
14
|
-
address
|
15
|
-
|
16
|
-
|
17
|
-
|
27
|
+
const verified = verifyEIP191(
|
28
|
+
address as Hex.Hex,
|
29
|
+
proof.attestation,
|
30
|
+
proof.proof as Hex.Hex,
|
31
|
+
);
|
18
32
|
return {
|
19
33
|
...proof,
|
20
34
|
status: verified ? ProofStatus.VERIFIED : ProofStatus.FAILED,
|
package/src/index.ts
CHANGED
@@ -5,7 +5,7 @@ import {
|
|
5
5
|
ScreenshotProof,
|
6
6
|
ProofTypes,
|
7
7
|
ProofStatus,
|
8
|
-
} from "@notabene/javascript-sdk
|
8
|
+
} from "@notabene/javascript-sdk";
|
9
9
|
import { verifyBTCSignature } from "./bitcoin";
|
10
10
|
import { verifyPersonalSignEIP191 } from "./eth";
|
11
11
|
import { verifySolanaSignature } from "./solana";
|
package/src/solana.ts
CHANGED
@@ -1,10 +1,7 @@
|
|
1
|
-
import { PublicKey } from "@solana/web3.js";
|
2
1
|
import nacl from "tweetnacl";
|
3
|
-
import {
|
4
|
-
ProofStatus,
|
5
|
-
SignatureProof,
|
6
|
-
} from "@notabene/javascript-sdk/src/types";
|
2
|
+
import { ProofStatus, SignatureProof } from "@notabene/javascript-sdk";
|
7
3
|
import { decode as decodeBase64 } from "@stablelib/base64";
|
4
|
+
import bs58 from "bs58";
|
8
5
|
|
9
6
|
export async function verifySolanaSignature(
|
10
7
|
proof: SignatureProof,
|
@@ -12,13 +9,13 @@ export async function verifySolanaSignature(
|
|
12
9
|
const [ns, _, address] = proof.address.split(/:/);
|
13
10
|
if (ns !== "solana") return { ...proof, status: ProofStatus.FAILED };
|
14
11
|
try {
|
15
|
-
const publicKey =
|
12
|
+
const publicKey = bs58.decode(address);
|
16
13
|
const messageBytes = new TextEncoder().encode(proof.attestation);
|
17
14
|
const signatureBytes = decodeBase64(proof.proof);
|
18
15
|
const verified = nacl.sign.detached.verify(
|
19
16
|
messageBytes,
|
20
17
|
signatureBytes,
|
21
|
-
publicKey
|
18
|
+
publicKey,
|
22
19
|
);
|
23
20
|
|
24
21
|
return {
|
@@ -1,95 +1,61 @@
|
|
1
|
-
import { describe, it, expect
|
2
|
-
import * as bitcoin from "bitcoinjs-lib";
|
3
|
-
import * as bitcoinMessage from "bitcoinjs-message";
|
4
|
-
import { Buffer } from "node:buffer";
|
1
|
+
import { describe, it, expect } from "vitest";
|
5
2
|
|
6
3
|
import { verifyBTCSignature } from "../bitcoin";
|
7
4
|
import {
|
8
5
|
type SignatureProof,
|
9
6
|
ProofStatus,
|
10
7
|
ProofTypes,
|
11
|
-
} from "@notabene/javascript-sdk
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
};
|
37
|
-
}
|
38
|
-
|
39
|
-
function signP2PKHMessage(): SignatureProof {
|
40
|
-
return signMessage((kp) => kp.getAddress());
|
41
|
-
}
|
42
|
-
|
43
|
-
function signSegWitMessage(): SignatureProof {
|
44
|
-
return signMessage((kp) =>
|
45
|
-
bitcoin.address.toBech32(
|
46
|
-
bitcoin.crypto.hash160(kp.getPublicKeyBuffer()),
|
47
|
-
0,
|
48
|
-
kp.network.bech32 || "bc",
|
49
|
-
),
|
50
|
-
);
|
51
|
-
}
|
8
|
+
} from "@notabene/javascript-sdk";
|
9
|
+
|
10
|
+
const segwitProof: SignatureProof = {
|
11
|
+
type: ProofTypes.BIP137,
|
12
|
+
address:
|
13
|
+
"bip122:000000000019d6689c085ae165831e93:bc1qnf4kpa62dwhpwm0stsas5yv0skatt3v9s040p8",
|
14
|
+
did: "did:pkh:bip122:000000000019d6689c085ae165831e93:bc1qnf4kpa62dwhpwm0stsas5yv0skatt3v9s040p8",
|
15
|
+
attestation: "This is an example of a signed message.",
|
16
|
+
proof:
|
17
|
+
"J796FDv8f8w3syiaMSGoL6SAwPLRf6t13S+fYNjYA9EnJy3T0jZOY1eHBaGTBufOuW78FVFSwXKyUnrEjYOT9EU=",
|
18
|
+
status: ProofStatus.PENDING,
|
19
|
+
wallet_provider: "BitMask",
|
20
|
+
};
|
21
|
+
|
22
|
+
const legacyProof: SignatureProof = {
|
23
|
+
type: ProofTypes.BIP137,
|
24
|
+
address:
|
25
|
+
"bip122:000000000019d6689c085ae165831e93:1F5VhMHukdnUES9kfXqzPzMeF1GPHKiF64",
|
26
|
+
did: "did:pkh:bip122:000000000019d6689c085ae165831e93:1F5VhMHukdnUES9kfXqzPzMeF1GPHKiF64",
|
27
|
+
attestation: "This is an example of a signed message.",
|
28
|
+
proof:
|
29
|
+
"H796FDv8f8w3syiaMSGoL6SAwPLRf6t13S+fYNjYA9EnJy3T0jZOY1eHBaGTBufOuW78FVFSwXKyUnrEjYOT9EU=",
|
30
|
+
status: ProofStatus.PENDING,
|
31
|
+
wallet_provider: "BitMask",
|
32
|
+
};
|
52
33
|
|
53
34
|
describe("verifyBTCSignature", () => {
|
54
35
|
it("handles native segwit addresses", async () => {
|
55
|
-
const
|
56
|
-
|
57
|
-
expect(result.status).toBe(ProofStatus.VERIFIED);
|
36
|
+
const result = await verifyBTCSignature(segwitProof);
|
37
|
+
expect(result).toEqual({ ...segwitProof, status: ProofStatus.VERIFIED });
|
58
38
|
});
|
59
39
|
|
60
40
|
it("verifies legacy bitcoin address signature", async () => {
|
61
|
-
const
|
62
|
-
|
63
|
-
expect(result.status).toBe(ProofStatus.VERIFIED);
|
41
|
+
const result = await verifyBTCSignature(legacyProof);
|
42
|
+
expect(result).toEqual({ ...legacyProof, status: ProofStatus.VERIFIED });
|
64
43
|
});
|
65
44
|
|
66
45
|
it("fails for invalid bitcoin signature", async () => {
|
67
|
-
const proof: SignatureProof = {
|
68
|
-
type: ProofTypes.BIP137,
|
69
|
-
did: "did:pkh:bip122:000000000019d6689c085ae165831e93:1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",
|
70
|
-
address:
|
71
|
-
"bip122:000000000019d6689c085ae165831e93:1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",
|
72
|
-
attestation: "test message",
|
73
|
-
proof: "invalid_signature",
|
74
|
-
status: ProofStatus.PENDING,
|
75
|
-
};
|
46
|
+
const proof: SignatureProof = { ...legacyProof, proof: "invalitd" };
|
76
47
|
|
77
48
|
const result = await verifyBTCSignature(proof);
|
78
|
-
expect(result
|
49
|
+
expect(result).toEqual({ ...proof, status: ProofStatus.FAILED });
|
79
50
|
});
|
80
51
|
|
81
52
|
it("returns failed proof for non-BIP122 address", async () => {
|
82
53
|
const proof: SignatureProof = {
|
83
|
-
|
84
|
-
status: ProofStatus.PENDING,
|
85
|
-
did: "did:example:123",
|
54
|
+
...legacyProof,
|
86
55
|
address: "eip155:1:0x1234567890123456789012345678901234567890",
|
87
|
-
proof: "0x1234567890",
|
88
|
-
attestation: "I own this address",
|
89
|
-
wallet_provider: "MetaMask",
|
90
56
|
};
|
91
57
|
|
92
58
|
const result = await verifyBTCSignature(proof);
|
93
|
-
expect(result
|
59
|
+
expect(result).toEqual({ ...proof, status: ProofStatus.FAILED });
|
94
60
|
});
|
95
61
|
});
|
package/src/tests/eth.test.ts
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
import { describe, it, expect
|
1
|
+
import { describe, it, expect } from "vitest";
|
2
2
|
import { verifyPersonalSignEIP191 } from "../eth";
|
3
3
|
import {
|
4
4
|
type SignatureProof,
|
5
5
|
ProofStatus,
|
6
6
|
ProofTypes,
|
7
|
-
} from "@notabene/javascript-sdk
|
7
|
+
} from "@notabene/javascript-sdk";
|
8
8
|
|
9
9
|
import { Hex, Address, PersonalMessage, Secp256k1, Signature } from "ox";
|
10
10
|
|
@@ -45,6 +45,7 @@ describe("verifyPersonalSignEIP191", () => {
|
|
45
45
|
const proof: SignatureProof = {
|
46
46
|
type: ProofTypes.EIP191,
|
47
47
|
did: `did:pkh:bip122:000000000019d6689c085ae165831e93:128Lkh3S7CkDTBZ8W7BbpsN3YYizJMp8p6`,
|
48
|
+
wallet_provider: "metamask",
|
48
49
|
address:
|
49
50
|
"bip122:000000000019d6689c085ae165831e93:128Lkh3S7CkDTBZ8W7BbpsN3YYizJMp8p6",
|
50
51
|
attestation: "test message",
|
package/src/tests/index.test.ts
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
import { describe, it, expect
|
1
|
+
import { describe, it, expect } from "vitest";
|
2
2
|
import { verifyProof } from "../index";
|
3
3
|
import {
|
4
4
|
type DeclarationProof,
|
@@ -6,13 +6,7 @@ import {
|
|
6
6
|
type SignatureProof,
|
7
7
|
ProofStatus,
|
8
8
|
ProofTypes,
|
9
|
-
} from "@notabene/javascript-sdk
|
10
|
-
import { verifyMessage } from "viem";
|
11
|
-
|
12
|
-
// Mock viem's verifyMessage function
|
13
|
-
vi.mock("viem", () => ({
|
14
|
-
verifyMessage: vi.fn(),
|
15
|
-
}));
|
9
|
+
} from "@notabene/javascript-sdk";
|
16
10
|
|
17
11
|
describe("verifyProof", () => {
|
18
12
|
it("should verify a confirmed checkbox proof", async () => {
|
@@ -87,68 +81,86 @@ describe("verifyProof", () => {
|
|
87
81
|
expect(result).toEqual(proof);
|
88
82
|
});
|
89
83
|
|
90
|
-
|
84
|
+
describe("eip191", () => {
|
91
85
|
const proof: SignatureProof = {
|
92
86
|
type: ProofTypes.EIP191,
|
87
|
+
address: "eip155:1:0x772f226b9cc93a2953dc5868864de72f90441caf",
|
88
|
+
did: "did:pkh:eip155:1:0x772f226b9cc93a2953dc5868864de72f90441caf",
|
89
|
+
attestation: "I agree",
|
90
|
+
proof:
|
91
|
+
"0xddd74ca17084728a167b6f99326b50a0ce4a1ce4f80a56da4a60b23b69637c624e6dfb2270de56caece9182200fe38791339cbf097586a4e06a9ef4bd298cc1800",
|
93
92
|
status: ProofStatus.PENDING,
|
94
|
-
did: "did:example:123",
|
95
|
-
address: "eip155:1:0x1234567890123456789012345678901234567890",
|
96
|
-
proof: "0x1234567890",
|
97
|
-
attestation: "I own this address",
|
98
93
|
wallet_provider: "MetaMask",
|
99
94
|
};
|
100
95
|
|
101
|
-
|
102
|
-
|
96
|
+
it("should verify proof", async () => {
|
97
|
+
const result = await verifyProof(proof);
|
98
|
+
expect(result.status).toBe(ProofStatus.VERIFIED);
|
99
|
+
});
|
103
100
|
|
104
|
-
|
101
|
+
it("should fail if not invalid proof", async () => {
|
102
|
+
const result = await verifyProof({
|
103
|
+
...proof,
|
104
|
+
proof: "0x",
|
105
|
+
} as SignatureProof);
|
105
106
|
|
106
|
-
|
107
|
-
expect(verifyMessage).toHaveBeenCalledWith({
|
108
|
-
address: "0x1234567890123456789012345678901234567890",
|
109
|
-
message: proof.attestation,
|
110
|
-
signature: proof.proof,
|
107
|
+
expect(result.status).toBe(ProofStatus.FAILED);
|
111
108
|
});
|
112
109
|
});
|
113
110
|
|
114
|
-
|
111
|
+
describe("solana", () => {
|
115
112
|
const proof: SignatureProof = {
|
116
|
-
type: ProofTypes.
|
113
|
+
type: ProofTypes.ED25519,
|
117
114
|
status: ProofStatus.PENDING,
|
118
|
-
did: "did:
|
119
|
-
address:
|
120
|
-
|
121
|
-
attestation: "
|
122
|
-
|
115
|
+
did: "did:pkh:solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:3GgW39JvdBVCAdiK1mafeBcBKXJq8mee5woVoDARRnPq",
|
116
|
+
address:
|
117
|
+
"solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:3GgW39JvdBVCAdiK1mafeBcBKXJq8mee5woVoDARRnPq",
|
118
|
+
attestation: "Test message",
|
119
|
+
proof:
|
120
|
+
"8dkeX3S+qisPkadV5QkmLWu2gbom/ljqlEwDAVuEV33HWZbSbPol+KV/zET4NLn8JBvSpu6JROJRg45WAIMPAQ==",
|
121
|
+
wallet_provider: "Phantom",
|
123
122
|
};
|
124
123
|
|
125
|
-
|
126
|
-
|
124
|
+
it("should verify proof", async () => {
|
125
|
+
const result = await verifyProof(proof);
|
126
|
+
expect(result.status).toBe(ProofStatus.VERIFIED);
|
127
|
+
});
|
127
128
|
|
128
|
-
|
129
|
+
it("should fail if not invalid proof", async () => {
|
130
|
+
const result = await verifyProof({
|
131
|
+
...proof,
|
132
|
+
proof: "0x",
|
133
|
+
} as SignatureProof);
|
129
134
|
|
130
|
-
|
131
|
-
expect(verifyMessage).toHaveBeenCalledWith({
|
132
|
-
address: "0x1234567890123456789012345678901234567890",
|
133
|
-
message: proof.attestation,
|
134
|
-
signature: proof.proof,
|
135
|
+
expect(result.status).toBe(ProofStatus.FAILED);
|
135
136
|
});
|
136
137
|
});
|
137
138
|
|
138
|
-
|
139
|
+
describe("bitcoin", () => {
|
139
140
|
const proof: SignatureProof = {
|
140
|
-
type: ProofTypes.
|
141
|
+
type: ProofTypes.BIP137,
|
141
142
|
status: ProofStatus.PENDING,
|
142
|
-
did: "did:example:123",
|
143
143
|
address:
|
144
|
-
"bip122:000000000019d6689c085ae165831e93:
|
145
|
-
|
146
|
-
attestation: "
|
147
|
-
|
144
|
+
"bip122:000000000019d6689c085ae165831e93:bc1qnf4kpa62dwhpwm0stsas5yv0skatt3v9s040p8",
|
145
|
+
did: "did:pkh:bip122:000000000019d6689c085ae165831e93:bc1qnf4kpa62dwhpwm0stsas5yv0skatt3v9s040p8",
|
146
|
+
attestation: "This is an example of a signed message.",
|
147
|
+
proof:
|
148
|
+
"J796FDv8f8w3syiaMSGoL6SAwPLRf6t13S+fYNjYA9EnJy3T0jZOY1eHBaGTBufOuW78FVFSwXKyUnrEjYOT9EU=",
|
149
|
+
wallet_provider: "Phantom",
|
148
150
|
};
|
149
151
|
|
150
|
-
|
152
|
+
it("should verify proof", async () => {
|
153
|
+
const result = await verifyProof(proof);
|
154
|
+
expect(result.status).toBe(ProofStatus.VERIFIED);
|
155
|
+
});
|
151
156
|
|
152
|
-
|
157
|
+
it("should fail if not invalid proof", async () => {
|
158
|
+
const result = await verifyProof({
|
159
|
+
...proof,
|
160
|
+
proof: "0x",
|
161
|
+
} as SignatureProof);
|
162
|
+
|
163
|
+
expect(result.status).toBe(ProofStatus.FAILED);
|
164
|
+
});
|
153
165
|
});
|
154
166
|
});
|
package/src/tests/solana.test.ts
CHANGED
@@ -1,40 +1,41 @@
|
|
1
1
|
import { describe, it, expect } from "vitest";
|
2
|
-
import
|
2
|
+
import bs58 from "bs58";
|
3
3
|
import nacl from "tweetnacl";
|
4
4
|
import {
|
5
5
|
ProofStatus,
|
6
6
|
ProofTypes,
|
7
7
|
SignatureProof,
|
8
|
-
} from "@notabene/javascript-sdk
|
8
|
+
} from "@notabene/javascript-sdk";
|
9
9
|
import { verifySolanaSignature } from "../solana";
|
10
10
|
|
11
11
|
describe("verifySolanaSignature", () => {
|
12
|
+
const keypair = nacl.sign.keyPair();
|
13
|
+
const address = bs58.encode(keypair.publicKey);
|
12
14
|
it("verifies valid Solana signature", async () => {
|
13
15
|
// Generate a test keypair
|
14
|
-
const keypair = Keypair.generate();
|
15
16
|
const message = "Test message";
|
16
17
|
const messageBytes = new TextEncoder().encode(message);
|
17
18
|
const signature = nacl.sign.detached(messageBytes, keypair.secretKey);
|
18
19
|
const proof: SignatureProof = {
|
19
20
|
type: ProofTypes.ED25519,
|
20
21
|
status: ProofStatus.PENDING,
|
21
|
-
did: `did:pkh:solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:${
|
22
|
-
address: `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:${
|
22
|
+
did: `did:pkh:solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:${address}`,
|
23
|
+
address: `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:${address}`,
|
23
24
|
attestation: message,
|
24
25
|
proof: Buffer.from(signature).toString("base64"),
|
25
26
|
wallet_provider: "Phantom",
|
26
27
|
};
|
28
|
+
|
27
29
|
const result = await verifySolanaSignature(proof);
|
28
30
|
expect(result.status).toBe(ProofStatus.VERIFIED);
|
29
31
|
});
|
30
32
|
|
31
33
|
it("fails for invalid signature", async () => {
|
32
|
-
const keypair = Keypair.generate();
|
33
34
|
const proof: SignatureProof = {
|
34
35
|
type: ProofTypes.ED25519,
|
35
36
|
status: ProofStatus.PENDING,
|
36
|
-
did: `did:pkh:solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:${
|
37
|
-
address: `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:${
|
37
|
+
did: `did:pkh:solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:${address}`,
|
38
|
+
address: `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:${address}`,
|
38
39
|
attestation: "Test message",
|
39
40
|
proof: "invalid_signature",
|
40
41
|
wallet_provider: "Phantom",
|