@notabene/verify-proof 1.0.0-preview.6 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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",
|