@notabene/verify-proof 1.0.0-preview.7 → 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 +4 -12
- package/src/bitcoin.ts +4 -6
- package/src/eth.ts +24 -10
- package/src/index.ts +1 -1
- package/src/solana.ts +2 -4
- package/src/tests/bitcoin.test.ts +33 -71
- package/src/tests/eth.test.ts +1 -1
- package/src/tests/index.test.ts +57 -45
- package/src/tests/solana.test.ts +2 -1
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,6 +1,6 @@
|
|
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",
|
@@ -23,19 +23,12 @@
|
|
23
23
|
"test": "vitest"
|
24
24
|
},
|
25
25
|
"devDependencies": {
|
26
|
-
"@types/bitcoinjs-lib": "3.4.0",
|
27
|
-
"@types/node": "^22.9.0",
|
28
|
-
"bitcoinjs-lib": "^3.2.0",
|
29
|
-
"buffer": "^6.0.3",
|
30
|
-
"ecpair": "^3.0.0-rc.0",
|
31
26
|
"eslint": "^9.9.0",
|
32
27
|
"microbundle": "^0.15.1",
|
33
|
-
"
|
28
|
+
"ox": "^0.2.2",
|
34
29
|
"typescript": "^5.5.4",
|
35
30
|
"vite": "^5.4.11",
|
36
|
-
"vitest": "^2.0.5"
|
37
|
-
"bitcoinjs-message": "^2.2.0",
|
38
|
-
"ox": "^0.1.4"
|
31
|
+
"vitest": "^2.0.5"
|
39
32
|
},
|
40
33
|
"dependencies": {
|
41
34
|
"@bitauth/libauth": "^3.0.0",
|
@@ -45,7 +38,6 @@
|
|
45
38
|
"bigi": "^1.4.2",
|
46
39
|
"bs58": "^6.0.0",
|
47
40
|
"tweetnacl": "^1.0.3",
|
48
|
-
"varuint-bitcoin": "^2.0.0"
|
49
|
-
"viem": "^2.21.44"
|
41
|
+
"varuint-bitcoin": "^2.0.0"
|
50
42
|
}
|
51
43
|
}
|
package/src/bitcoin.ts
CHANGED
@@ -1,7 +1,4 @@
|
|
1
|
-
import {
|
2
|
-
ProofStatus,
|
3
|
-
SignatureProof,
|
4
|
-
} from "@notabene/javascript-sdk/src/types";
|
1
|
+
import { ProofStatus, SignatureProof } from "@notabene/javascript-sdk";
|
5
2
|
import { bech32 } from "bech32";
|
6
3
|
|
7
4
|
import {
|
@@ -21,7 +18,7 @@ enum SEGWIT_TYPES {
|
|
21
18
|
|
22
19
|
const messagePrefix = "\u0018Bitcoin Signed Message:\n";
|
23
20
|
|
24
|
-
|
21
|
+
enum DerivationMode {
|
25
22
|
LEGACY = "Legacy",
|
26
23
|
NATIVE = "Native SegWit",
|
27
24
|
SEGWIT = "SegWit",
|
@@ -77,6 +74,7 @@ type DecodedSignature = {
|
|
77
74
|
recovery: RecoveryId;
|
78
75
|
signature: Uint8Array;
|
79
76
|
};
|
77
|
+
|
80
78
|
function decodeSignature(proof: string): DecodedSignature {
|
81
79
|
const signature = decodeBase64(proof);
|
82
80
|
if (signature.length !== 65) throw new Error("Invalid signature length");
|
@@ -146,7 +144,7 @@ function verify(
|
|
146
144
|
return actual === address;
|
147
145
|
}
|
148
146
|
|
149
|
-
|
147
|
+
function magicHash(attestation: string) {
|
150
148
|
const prefix = new TextEncoder().encode(messagePrefix);
|
151
149
|
const message = new TextEncoder().encode(attestation);
|
152
150
|
const length = encodeLength(message.length).buffer;
|
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,8 @@
|
|
1
1
|
import nacl from "tweetnacl";
|
2
|
-
import {
|
3
|
-
ProofStatus,
|
4
|
-
SignatureProof,
|
5
|
-
} from "@notabene/javascript-sdk/src/types";
|
2
|
+
import { ProofStatus, SignatureProof } from "@notabene/javascript-sdk";
|
6
3
|
import { decode as decodeBase64 } from "@stablelib/base64";
|
7
4
|
import bs58 from "bs58";
|
5
|
+
|
8
6
|
export async function verifySolanaSignature(
|
9
7
|
proof: SignatureProof,
|
10
8
|
): Promise<SignatureProof> {
|
@@ -1,99 +1,61 @@
|
|
1
1
|
import { describe, it, expect } from "vitest";
|
2
|
-
import * as bitcoin from "bitcoinjs-lib";
|
3
|
-
import * as bitcoinMessage from "bitcoinjs-message";
|
4
|
-
import { Buffer } from "node:buffer";
|
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
|
-
proof,
|
37
|
-
status: ProofStatus.PENDING,
|
38
|
-
wallet_provider: "MetaMask",
|
39
|
-
};
|
40
|
-
}
|
41
|
-
|
42
|
-
function signP2PKHMessage(): SignatureProof {
|
43
|
-
return signMessage((kp) => kp.getAddress());
|
44
|
-
}
|
45
|
-
|
46
|
-
function signSegWitMessage(): SignatureProof {
|
47
|
-
return signMessage((kp) =>
|
48
|
-
bitcoin.address.toBech32(
|
49
|
-
bitcoin.crypto.hash160(kp.getPublicKeyBuffer()),
|
50
|
-
0,
|
51
|
-
kp.network.bech32 || "bc",
|
52
|
-
),
|
53
|
-
);
|
54
|
-
}
|
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
|
+
};
|
55
33
|
|
56
34
|
describe("verifyBTCSignature", () => {
|
57
35
|
it("handles native segwit addresses", async () => {
|
58
|
-
const
|
59
|
-
|
60
|
-
expect(result.status).toBe(ProofStatus.VERIFIED);
|
36
|
+
const result = await verifyBTCSignature(segwitProof);
|
37
|
+
expect(result).toEqual({ ...segwitProof, status: ProofStatus.VERIFIED });
|
61
38
|
});
|
62
39
|
|
63
40
|
it("verifies legacy bitcoin address signature", async () => {
|
64
|
-
const
|
65
|
-
|
66
|
-
expect(result.status).toBe(ProofStatus.VERIFIED);
|
41
|
+
const result = await verifyBTCSignature(legacyProof);
|
42
|
+
expect(result).toEqual({ ...legacyProof, status: ProofStatus.VERIFIED });
|
67
43
|
});
|
68
44
|
|
69
45
|
it("fails for invalid bitcoin signature", async () => {
|
70
|
-
const proof: SignatureProof = {
|
71
|
-
type: ProofTypes.BIP137,
|
72
|
-
did: "did:pkh:bip122:000000000019d6689c085ae165831e93:1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",
|
73
|
-
wallet_provider: "ledger",
|
74
|
-
address:
|
75
|
-
"bip122:000000000019d6689c085ae165831e93:1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",
|
76
|
-
attestation: "test message",
|
77
|
-
proof: "invalid_signature",
|
78
|
-
status: ProofStatus.PENDING,
|
79
|
-
};
|
46
|
+
const proof: SignatureProof = { ...legacyProof, proof: "invalitd" };
|
80
47
|
|
81
48
|
const result = await verifyBTCSignature(proof);
|
82
|
-
expect(result
|
49
|
+
expect(result).toEqual({ ...proof, status: ProofStatus.FAILED });
|
83
50
|
});
|
84
51
|
|
85
52
|
it("returns failed proof for non-BIP122 address", async () => {
|
86
53
|
const proof: SignatureProof = {
|
87
|
-
|
88
|
-
status: ProofStatus.PENDING,
|
89
|
-
did: "did:example:123",
|
54
|
+
...legacyProof,
|
90
55
|
address: "eip155:1:0x1234567890123456789012345678901234567890",
|
91
|
-
proof: "0x1234567890",
|
92
|
-
attestation: "I own this address",
|
93
|
-
wallet_provider: "MetaMask",
|
94
56
|
};
|
95
57
|
|
96
58
|
const result = await verifyBTCSignature(proof);
|
97
|
-
expect(result
|
59
|
+
expect(result).toEqual({ ...proof, status: ProofStatus.FAILED });
|
98
60
|
});
|
99
61
|
});
|
package/src/tests/eth.test.ts
CHANGED
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
@@ -5,7 +5,7 @@ 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", () => {
|
@@ -25,6 +25,7 @@ describe("verifySolanaSignature", () => {
|
|
25
25
|
proof: Buffer.from(signature).toString("base64"),
|
26
26
|
wallet_provider: "Phantom",
|
27
27
|
};
|
28
|
+
|
28
29
|
const result = await verifySolanaSignature(proof);
|
29
30
|
expect(result.status).toBe(ProofStatus.VERIFIED);
|
30
31
|
});
|