@uvrn/adapter 1.3.0 → 1.4.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/README.md +5 -6
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.js +3 -2
- package/dist/src/signer.d.ts +17 -12
- package/dist/src/signer.js +148 -15
- package/dist/src/wrapper.d.ts +3 -6
- package/dist/src/wrapper.js +6 -7
- package/dist/tests/integration.test.js +5 -8
- package/dist/tests/wrapper.test.js +11 -10
- package/package.json +31 -15
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @uvrn/adapter
|
|
2
2
|
|
|
3
|
-
UVRN DRVC3 envelope adapter — wraps Delta Engine receipts in DRVC3 envelopes with EIP-191 signatures. Use this when you need to attach issuer identity and signing to core receipts without changing their deterministic hash. **Release:** 1.
|
|
3
|
+
UVRN DRVC3 envelope adapter — wraps Delta Engine receipts in DRVC3 envelopes with EIP-191 signatures. Use this when you need to attach issuer identity and signing to core receipts without changing their deterministic hash. **Release:** 1.4.0.
|
|
4
4
|
|
|
5
5
|
**Disclaimer:** UVRN is in Alpha testing. The engine measures whether your sources agree with each other — not whether they’re correct. Final trust of output rests with the user. Use at your own risk. Have fun.
|
|
6
6
|
|
|
@@ -27,19 +27,18 @@ npm install @uvrn/core @uvrn/adapter
|
|
|
27
27
|
## Usage
|
|
28
28
|
|
|
29
29
|
1. Obtain a **DeltaReceipt** from `@uvrn/core` (e.g. `runDeltaEngine(bundle)`).
|
|
30
|
-
2. Use **wrapInDRVC3** with
|
|
30
|
+
2. Use **wrapInDRVC3** with a signer private key (hex string) and options to produce a DRVC3 envelope.
|
|
31
31
|
3. Use **validateDRVC3** / **extractDeltaReceipt** to validate envelopes and read back the core receipt.
|
|
32
32
|
|
|
33
33
|
```typescript
|
|
34
34
|
import { runDeltaEngine } from '@uvrn/core';
|
|
35
35
|
import { wrapInDRVC3, validateDRVC3, extractDeltaReceipt } from '@uvrn/adapter';
|
|
36
|
-
import { Wallet } from 'ethers';
|
|
37
36
|
|
|
38
37
|
const bundle = { /* ... DeltaBundle ... */ };
|
|
39
38
|
const receipt = runDeltaEngine(bundle);
|
|
40
39
|
|
|
41
|
-
const
|
|
42
|
-
const drvc3 = await wrapInDRVC3(receipt,
|
|
40
|
+
const privateKeyHex = process.env.SIGNER_PRIVATE_KEY!; // 64 hex chars, optional 0x prefix
|
|
41
|
+
const drvc3 = await wrapInDRVC3(receipt, privateKeyHex, {
|
|
43
42
|
issuer: 'my-service',
|
|
44
43
|
event: 'delta-reconciliation',
|
|
45
44
|
});
|
|
@@ -69,7 +68,7 @@ You can set these options when wrapping a receipt (no code changes to this packa
|
|
|
69
68
|
Example:
|
|
70
69
|
|
|
71
70
|
```typescript
|
|
72
|
-
const drvc3 = await wrapInDRVC3(receipt,
|
|
71
|
+
const drvc3 = await wrapInDRVC3(receipt, privateKeyHex, {
|
|
73
72
|
issuer: 'acme-corp',
|
|
74
73
|
event: 'price-attestation',
|
|
75
74
|
certificate: 'Acme Receipt v1',
|
package/dist/src/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* UVRN Adapter
|
|
3
3
|
* Layer 2 - Wraps Delta Engine Core receipts in DRVC3 envelopes
|
|
4
4
|
*/
|
|
5
5
|
export * from './types';
|
|
6
6
|
export { wrapInDRVC3, extractDeltaReceipt } from './wrapper';
|
|
7
|
-
export { signHash, recoverSigner, verifySignature } from './signer';
|
|
7
|
+
export { signHash, recoverSigner, verifySignature, privateKeyToAddress } from './signer';
|
|
8
8
|
export { validateDRVC3, isDRVC3Receipt, verifyDRVC3Integrity } from './validator';
|
package/dist/src/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* UVRN Adapter
|
|
4
4
|
* Layer 2 - Wraps Delta Engine Core receipts in DRVC3 envelopes
|
|
5
5
|
*/
|
|
6
6
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
@@ -18,7 +18,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
18
18
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
19
19
|
};
|
|
20
20
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
-
exports.verifyDRVC3Integrity = exports.isDRVC3Receipt = exports.validateDRVC3 = exports.verifySignature = exports.recoverSigner = exports.signHash = exports.extractDeltaReceipt = exports.wrapInDRVC3 = void 0;
|
|
21
|
+
exports.verifyDRVC3Integrity = exports.isDRVC3Receipt = exports.validateDRVC3 = exports.privateKeyToAddress = exports.verifySignature = exports.recoverSigner = exports.signHash = exports.extractDeltaReceipt = exports.wrapInDRVC3 = void 0;
|
|
22
22
|
// Types
|
|
23
23
|
__exportStar(require("./types"), exports);
|
|
24
24
|
// Core wrapper
|
|
@@ -30,6 +30,7 @@ var signer_1 = require("./signer");
|
|
|
30
30
|
Object.defineProperty(exports, "signHash", { enumerable: true, get: function () { return signer_1.signHash; } });
|
|
31
31
|
Object.defineProperty(exports, "recoverSigner", { enumerable: true, get: function () { return signer_1.recoverSigner; } });
|
|
32
32
|
Object.defineProperty(exports, "verifySignature", { enumerable: true, get: function () { return signer_1.verifySignature; } });
|
|
33
|
+
Object.defineProperty(exports, "privateKeyToAddress", { enumerable: true, get: function () { return signer_1.privateKeyToAddress; } });
|
|
33
34
|
// Validation (validateDRVC3 is schema-only; use verifyDRVC3Integrity for full integrity)
|
|
34
35
|
var validator_1 = require("./validator");
|
|
35
36
|
Object.defineProperty(exports, "validateDRVC3", { enumerable: true, get: function () { return validator_1.validateDRVC3; } });
|
package/dist/src/signer.d.ts
CHANGED
|
@@ -1,24 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* EIP-191 Signing Utilities
|
|
3
|
-
* Provides Ethereum personal message signing for DRVC3 receipts
|
|
3
|
+
* Provides Ethereum personal message signing for DRVC3 receipts using @noble/hashes and @noble/secp256k1
|
|
4
4
|
*/
|
|
5
|
-
import { Wallet, HDNodeWallet } from 'ethers';
|
|
6
5
|
/**
|
|
7
|
-
* Signs a hash using EIP-191 (Ethereum Signed Message)
|
|
8
|
-
* @param hash - The hex string hash to sign
|
|
9
|
-
* @param
|
|
10
|
-
* @returns Promise resolving to the signature hex string
|
|
6
|
+
* Signs a hash using EIP-191 (Ethereum Signed Message).
|
|
7
|
+
* @param hash - The hex string hash (or message) to sign; treated as UTF-8 for EIP-191 prefixing
|
|
8
|
+
* @param privateKeyHex - Raw hex private key (64 hex chars, optional 0x prefix)
|
|
9
|
+
* @returns Promise resolving to the signature hex string (0x-prefixed, 65 bytes)
|
|
11
10
|
*/
|
|
12
|
-
export declare function signHash(hash: string,
|
|
11
|
+
export declare function signHash(hash: string, privateKeyHex: string): Promise<string>;
|
|
13
12
|
/**
|
|
14
|
-
* Recovers the signer address from a hash and signature
|
|
15
|
-
* @param hash - The original hash that was signed
|
|
16
|
-
* @param signature - The EIP-191 signature
|
|
17
|
-
* @returns The signer's Ethereum address
|
|
13
|
+
* Recovers the signer address from a hash and EIP-191 signature.
|
|
14
|
+
* @param hash - The original hash (message) that was signed
|
|
15
|
+
* @param signature - The EIP-191 signature (0x-prefixed 65-byte hex)
|
|
16
|
+
* @returns The signer's Ethereum address (EIP-55 checksummed)
|
|
18
17
|
*/
|
|
19
18
|
export declare function recoverSigner(hash: string, signature: string): string;
|
|
20
19
|
/**
|
|
21
|
-
*
|
|
20
|
+
* Derives the Ethereum address (EIP-55) for a given private key.
|
|
21
|
+
* @param privateKeyHex - Raw hex private key (64 hex chars, optional 0x prefix)
|
|
22
|
+
* @returns The address (checksummed)
|
|
23
|
+
*/
|
|
24
|
+
export declare function privateKeyToAddress(privateKeyHex: string): string;
|
|
25
|
+
/**
|
|
26
|
+
* Verifies that a signature was created by the expected signer.
|
|
22
27
|
* @param hash - The original hash that was signed
|
|
23
28
|
* @param signature - The EIP-191 signature
|
|
24
29
|
* @param expectedAddress - The expected signer address
|
package/dist/src/signer.js
CHANGED
|
@@ -1,34 +1,167 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
3
|
* EIP-191 Signing Utilities
|
|
4
|
-
* Provides Ethereum personal message signing for DRVC3 receipts
|
|
4
|
+
* Provides Ethereum personal message signing for DRVC3 receipts using @noble/hashes and @noble/secp256k1
|
|
5
5
|
*/
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
+
}) : function(o, v) {
|
|
20
|
+
o["default"] = v;
|
|
21
|
+
});
|
|
22
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
23
|
+
var ownKeys = function(o) {
|
|
24
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
25
|
+
var ar = [];
|
|
26
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
27
|
+
return ar;
|
|
28
|
+
};
|
|
29
|
+
return ownKeys(o);
|
|
30
|
+
};
|
|
31
|
+
return function (mod) {
|
|
32
|
+
if (mod && mod.__esModule) return mod;
|
|
33
|
+
var result = {};
|
|
34
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
35
|
+
__setModuleDefault(result, mod);
|
|
36
|
+
return result;
|
|
37
|
+
};
|
|
38
|
+
})();
|
|
6
39
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
40
|
exports.signHash = signHash;
|
|
8
41
|
exports.recoverSigner = recoverSigner;
|
|
42
|
+
exports.privateKeyToAddress = privateKeyToAddress;
|
|
9
43
|
exports.verifySignature = verifySignature;
|
|
10
|
-
const
|
|
44
|
+
const sha3_1 = require("@noble/hashes/sha3");
|
|
45
|
+
const utils_1 = require("@noble/hashes/utils");
|
|
46
|
+
const secp = __importStar(require("@noble/secp256k1"));
|
|
47
|
+
const EIP191_PREFIX = '\x19Ethereum Signed Message:\n';
|
|
11
48
|
/**
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* @param wallet - ethers Wallet instance with private key
|
|
15
|
-
* @returns Promise resolving to the signature hex string
|
|
49
|
+
* Build EIP-191 prefixed message and return its keccak256 hash (what gets signed).
|
|
50
|
+
* Message is treated as UTF-8 (matches ethers signMessage behavior for string input).
|
|
16
51
|
*/
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
52
|
+
function eip191MessageHash(message) {
|
|
53
|
+
const messageBytes = (0, utils_1.utf8ToBytes)(message);
|
|
54
|
+
const prefix = (0, utils_1.utf8ToBytes)(EIP191_PREFIX + messageBytes.length);
|
|
55
|
+
const prefixed = (0, utils_1.concatBytes)(prefix, messageBytes);
|
|
56
|
+
return (0, sha3_1.keccak_256)(prefixed);
|
|
20
57
|
}
|
|
21
58
|
/**
|
|
22
|
-
*
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
59
|
+
* Normalize hex private key to 32-byte Uint8Array (strip 0x, require 64 hex chars).
|
|
60
|
+
*/
|
|
61
|
+
function privateKeyToBytes(privateKeyHex) {
|
|
62
|
+
const hex = privateKeyHex.startsWith('0x') ? privateKeyHex.slice(2) : privateKeyHex;
|
|
63
|
+
if (hex.length !== 64 || !/^[0-9a-fA-F]+$/.test(hex)) {
|
|
64
|
+
throw new Error('Invalid private key: expected 64 hex chars (with or without 0x)');
|
|
65
|
+
}
|
|
66
|
+
return (0, utils_1.hexToBytes)(hex);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Serialize signature to 65-byte Ethereum format: r (32) || s (32) || v (1), v = recovery + 27.
|
|
70
|
+
*/
|
|
71
|
+
function signatureToEthereumFormat(sig) {
|
|
72
|
+
const compact = sig.toBytes();
|
|
73
|
+
const v = sig.recovery + 27;
|
|
74
|
+
const out = new Uint8Array(65);
|
|
75
|
+
out.set(compact, 0);
|
|
76
|
+
out[64] = v;
|
|
77
|
+
return '0x' + (0, utils_1.bytesToHex)(out);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Parse 0x-prefixed 65-byte signature into r, s, recovery for recovery.
|
|
81
|
+
*/
|
|
82
|
+
function parseSignature(signature) {
|
|
83
|
+
const hex = signature.startsWith('0x') ? signature.slice(2) : signature;
|
|
84
|
+
if (hex.length !== 130) {
|
|
85
|
+
throw new Error('Invalid signature: expected 65 bytes (130 hex chars)');
|
|
86
|
+
}
|
|
87
|
+
const bytes = (0, utils_1.hexToBytes)(hex);
|
|
88
|
+
const r = bytes.slice(0, 32);
|
|
89
|
+
const s = bytes.slice(32, 64);
|
|
90
|
+
const v = bytes[64];
|
|
91
|
+
if (v !== 27 && v !== 28) {
|
|
92
|
+
throw new Error('Invalid signature: v must be 27 or 28');
|
|
93
|
+
}
|
|
94
|
+
const recovery = v - 27;
|
|
95
|
+
return { r, s, recovery };
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Ethereum address from uncompressed public key: keccak256(pubkey[1:65]), last 20 bytes.
|
|
99
|
+
*/
|
|
100
|
+
function publicKeyToAddress(pubKey) {
|
|
101
|
+
if (pubKey.length !== 65) {
|
|
102
|
+
throw new Error('Expected 65-byte uncompressed public key');
|
|
103
|
+
}
|
|
104
|
+
const hash = (0, sha3_1.keccak_256)(pubKey.slice(1));
|
|
105
|
+
const addressBytes = hash.slice(-20);
|
|
106
|
+
return toChecksumAddress('0x' + (0, utils_1.bytesToHex)(addressBytes));
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* EIP-55 checksum: keccak256(lowercase address hex), then uppercase hex chars where hash nibble >= 8.
|
|
110
|
+
*/
|
|
111
|
+
function toChecksumAddress(address) {
|
|
112
|
+
const hex = address.startsWith('0x') ? address.slice(2).toLowerCase() : address.toLowerCase();
|
|
113
|
+
if (hex.length !== 40) {
|
|
114
|
+
throw new Error('Invalid address length');
|
|
115
|
+
}
|
|
116
|
+
const hash = (0, sha3_1.keccak_256)((0, utils_1.utf8ToBytes)(hex));
|
|
117
|
+
let result = '0x';
|
|
118
|
+
for (let i = 0; i < 40; i++) {
|
|
119
|
+
const nibble = (hash[Math.floor(i / 2)] >> (4 * (1 - (i % 2)))) & 0xf;
|
|
120
|
+
result += nibble >= 8 ? hex[i].toUpperCase() : hex[i];
|
|
121
|
+
}
|
|
122
|
+
return result;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Signs a hash using EIP-191 (Ethereum Signed Message).
|
|
126
|
+
* @param hash - The hex string hash (or message) to sign; treated as UTF-8 for EIP-191 prefixing
|
|
127
|
+
* @param privateKeyHex - Raw hex private key (64 hex chars, optional 0x prefix)
|
|
128
|
+
* @returns Promise resolving to the signature hex string (0x-prefixed, 65 bytes)
|
|
129
|
+
*/
|
|
130
|
+
async function signHash(hash, privateKeyHex) {
|
|
131
|
+
const messageHash = eip191MessageHash(hash);
|
|
132
|
+
const privKey = privateKeyToBytes(privateKeyHex);
|
|
133
|
+
const sig = await secp.signAsync(messageHash, privKey);
|
|
134
|
+
return signatureToEthereumFormat(sig);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Recovers the signer address from a hash and EIP-191 signature.
|
|
138
|
+
* @param hash - The original hash (message) that was signed
|
|
139
|
+
* @param signature - The EIP-191 signature (0x-prefixed 65-byte hex)
|
|
140
|
+
* @returns The signer's Ethereum address (EIP-55 checksummed)
|
|
26
141
|
*/
|
|
27
142
|
function recoverSigner(hash, signature) {
|
|
28
|
-
|
|
143
|
+
const messageHash = eip191MessageHash(hash);
|
|
144
|
+
const { r, s, recovery } = parseSignature(signature);
|
|
145
|
+
// Reconstruct RecoveredSignature for noble: we need a Signature-like with recovery.
|
|
146
|
+
// noble's recoverPublicKey(sig, msg) expects sig to have r, s, recovery. Build from compact bytes.
|
|
147
|
+
const compact = (0, utils_1.concatBytes)(r, s);
|
|
148
|
+
const sig = secp.Signature.fromBytes(compact).addRecoveryBit(recovery);
|
|
149
|
+
const pubKey = sig.recoverPublicKey(messageHash);
|
|
150
|
+
const pubKeyBytes = pubKey.toBytes(false);
|
|
151
|
+
return publicKeyToAddress(pubKeyBytes);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Derives the Ethereum address (EIP-55) for a given private key.
|
|
155
|
+
* @param privateKeyHex - Raw hex private key (64 hex chars, optional 0x prefix)
|
|
156
|
+
* @returns The address (checksummed)
|
|
157
|
+
*/
|
|
158
|
+
function privateKeyToAddress(privateKeyHex) {
|
|
159
|
+
const privKey = privateKeyToBytes(privateKeyHex);
|
|
160
|
+
const pubKey = secp.getPublicKey(privKey, false);
|
|
161
|
+
return publicKeyToAddress(pubKey);
|
|
29
162
|
}
|
|
30
163
|
/**
|
|
31
|
-
* Verifies that a signature was created by the expected signer
|
|
164
|
+
* Verifies that a signature was created by the expected signer.
|
|
32
165
|
* @param hash - The original hash that was signed
|
|
33
166
|
* @param signature - The EIP-191 signature
|
|
34
167
|
* @param expectedAddress - The expected signer address
|
package/dist/src/wrapper.d.ts
CHANGED
|
@@ -7,13 +7,12 @@
|
|
|
7
7
|
* and do NOT affect determinism or replay.
|
|
8
8
|
*/
|
|
9
9
|
import { DeltaReceipt } from '@uvrn/core';
|
|
10
|
-
import { Wallet, HDNodeWallet } from 'ethers';
|
|
11
10
|
import { DRVC3Receipt, WrapOptions } from './types';
|
|
12
11
|
/**
|
|
13
12
|
* Wraps a DeltaReceipt in a DRVC3 envelope
|
|
14
13
|
*
|
|
15
14
|
* @param deltaReceipt - Layer 1 DeltaReceipt (deterministic, hash-covered)
|
|
16
|
-
* @param
|
|
15
|
+
* @param signerPrivateKeyHex - Raw hex private key (64 hex chars, optional 0x prefix) for EIP-191 signing
|
|
17
16
|
* @param options - Envelope options (issuer, event, etc.)
|
|
18
17
|
* @returns Promise resolving to complete DRVC3 receipt
|
|
19
18
|
*
|
|
@@ -21,17 +20,15 @@ import { DRVC3Receipt, WrapOptions } from './types';
|
|
|
21
20
|
* ```typescript
|
|
22
21
|
* import { runDeltaEngine } from '@uvrn/core';
|
|
23
22
|
* import { wrapInDRVC3 } from '@uvrn/adapter';
|
|
24
|
-
* import { Wallet } from 'ethers';
|
|
25
23
|
*
|
|
26
24
|
* const deltaReceipt = runDeltaEngine(bundle);
|
|
27
|
-
* const
|
|
28
|
-
* const drvc3 = await wrapInDRVC3(deltaReceipt, wallet, {
|
|
25
|
+
* const drvc3 = await wrapInDRVC3(deltaReceipt, process.env.SIGNER_PRIVATE_KEY!, {
|
|
29
26
|
* issuer: 'uvrn',
|
|
30
27
|
* event: 'delta-reconciliation'
|
|
31
28
|
* });
|
|
32
29
|
* ```
|
|
33
30
|
*/
|
|
34
|
-
export declare function wrapInDRVC3(deltaReceipt: DeltaReceipt,
|
|
31
|
+
export declare function wrapInDRVC3(deltaReceipt: DeltaReceipt, signerPrivateKeyHex: string, options: WrapOptions): Promise<DRVC3Receipt>;
|
|
35
32
|
/**
|
|
36
33
|
* Extracts the original DeltaReceipt from a DRVC3 envelope.
|
|
37
34
|
* Does not verify signature or receipt hash; for untrusted input use verifyDRVC3Integrity() first, then extract.
|
package/dist/src/wrapper.js
CHANGED
|
@@ -16,7 +16,7 @@ const signer_1 = require("./signer");
|
|
|
16
16
|
* Wraps a DeltaReceipt in a DRVC3 envelope
|
|
17
17
|
*
|
|
18
18
|
* @param deltaReceipt - Layer 1 DeltaReceipt (deterministic, hash-covered)
|
|
19
|
-
* @param
|
|
19
|
+
* @param signerPrivateKeyHex - Raw hex private key (64 hex chars, optional 0x prefix) for EIP-191 signing
|
|
20
20
|
* @param options - Envelope options (issuer, event, etc.)
|
|
21
21
|
* @returns Promise resolving to complete DRVC3 receipt
|
|
22
22
|
*
|
|
@@ -24,17 +24,15 @@ const signer_1 = require("./signer");
|
|
|
24
24
|
* ```typescript
|
|
25
25
|
* import { runDeltaEngine } from '@uvrn/core';
|
|
26
26
|
* import { wrapInDRVC3 } from '@uvrn/adapter';
|
|
27
|
-
* import { Wallet } from 'ethers';
|
|
28
27
|
*
|
|
29
28
|
* const deltaReceipt = runDeltaEngine(bundle);
|
|
30
|
-
* const
|
|
31
|
-
* const drvc3 = await wrapInDRVC3(deltaReceipt, wallet, {
|
|
29
|
+
* const drvc3 = await wrapInDRVC3(deltaReceipt, process.env.SIGNER_PRIVATE_KEY!, {
|
|
32
30
|
* issuer: 'uvrn',
|
|
33
31
|
* event: 'delta-reconciliation'
|
|
34
32
|
* });
|
|
35
33
|
* ```
|
|
36
34
|
*/
|
|
37
|
-
async function wrapInDRVC3(deltaReceipt,
|
|
35
|
+
async function wrapInDRVC3(deltaReceipt, signerPrivateKeyHex, options) {
|
|
38
36
|
// 1. Verify receipt integrity before attesting (do not sign tampered payloads)
|
|
39
37
|
const coreVerify = (0, core_1.verifyReceipt)(deltaReceipt);
|
|
40
38
|
if (!coreVerify.verified) {
|
|
@@ -46,7 +44,8 @@ async function wrapInDRVC3(deltaReceipt, signer, options) {
|
|
|
46
44
|
// 3. Read hash from DeltaReceipt (HASH DOMAIN - read-only from Layer 1)
|
|
47
45
|
const hash = deltaReceipt.hash;
|
|
48
46
|
// 4. Sign hash with EIP-191 (ENVELOPE METADATA - certifies "who" and "when")
|
|
49
|
-
const signature = await (0, signer_1.signHash)(hash,
|
|
47
|
+
const signature = await (0, signer_1.signHash)(hash, signerPrivateKeyHex);
|
|
48
|
+
const signer_address = (0, signer_1.privateKeyToAddress)(signerPrivateKeyHex);
|
|
50
49
|
// 5. Construct DRVC3 envelope
|
|
51
50
|
const drvc3 = {
|
|
52
51
|
receipt_id,
|
|
@@ -58,7 +57,7 @@ async function wrapInDRVC3(deltaReceipt, signer, options) {
|
|
|
58
57
|
hash,
|
|
59
58
|
signature_method: 'eip191',
|
|
60
59
|
signature,
|
|
61
|
-
signer_address
|
|
60
|
+
signer_address
|
|
62
61
|
},
|
|
63
62
|
validation: {
|
|
64
63
|
v_score: deltaReceipt.deltaFinal,
|
|
@@ -4,14 +4,11 @@
|
|
|
4
4
|
* Tests the full flow from DeltaBundle → DeltaReceipt → DRVC3Receipt
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
const ethers_1 = require("ethers");
|
|
8
7
|
const core_1 = require("@uvrn/core");
|
|
9
8
|
const src_1 = require("../src");
|
|
9
|
+
// Fixed test private key (deterministic)
|
|
10
|
+
const TEST_PRIVATE_KEY_HEX = '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80';
|
|
10
11
|
describe('Integration: Layer 1 → Layer 2', () => {
|
|
11
|
-
let testWallet;
|
|
12
|
-
beforeAll(() => {
|
|
13
|
-
testWallet = ethers_1.Wallet.createRandom();
|
|
14
|
-
});
|
|
15
12
|
it('should complete full flow: bundle → receipt → DRVC3', async () => {
|
|
16
13
|
// 1. Create a DeltaBundle (input to Layer 1)
|
|
17
14
|
const bundle = {
|
|
@@ -47,7 +44,7 @@ describe('Integration: Layer 1 → Layer 2', () => {
|
|
|
47
44
|
const verifyResult = (0, core_1.verifyReceipt)(deltaReceipt);
|
|
48
45
|
expect(verifyResult.verified).toBe(true);
|
|
49
46
|
// 3. Wrap in DRVC3 (Layer 2)
|
|
50
|
-
const drvc3 = await (0, src_1.wrapInDRVC3)(deltaReceipt,
|
|
47
|
+
const drvc3 = await (0, src_1.wrapInDRVC3)(deltaReceipt, TEST_PRIVATE_KEY_HEX, {
|
|
51
48
|
issuer: 'uvrn-integration',
|
|
52
49
|
event: 'delta-reconciliation',
|
|
53
50
|
tags: ['#uvrn', '#integration-test']
|
|
@@ -87,11 +84,11 @@ describe('Integration: Layer 1 → Layer 2', () => {
|
|
|
87
84
|
const receipt = (0, core_1.runDeltaEngine)(bundle);
|
|
88
85
|
const originalHash = receipt.hash;
|
|
89
86
|
// Wrap multiple times - hash should always match
|
|
90
|
-
const drvc3_1 = await (0, src_1.wrapInDRVC3)(receipt,
|
|
87
|
+
const drvc3_1 = await (0, src_1.wrapInDRVC3)(receipt, TEST_PRIVATE_KEY_HEX, {
|
|
91
88
|
issuer: 'test',
|
|
92
89
|
event: 'test'
|
|
93
90
|
});
|
|
94
|
-
const drvc3_2 = await (0, src_1.wrapInDRVC3)(receipt,
|
|
91
|
+
const drvc3_2 = await (0, src_1.wrapInDRVC3)(receipt, TEST_PRIVATE_KEY_HEX, {
|
|
95
92
|
issuer: 'test',
|
|
96
93
|
event: 'test'
|
|
97
94
|
});
|
|
@@ -4,16 +4,17 @@
|
|
|
4
4
|
* Tests the wrapInDRVC3 function and schema validation
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
const ethers_1 = require("ethers");
|
|
8
7
|
const core_1 = require("@uvrn/core");
|
|
9
8
|
const wrapper_1 = require("../src/wrapper");
|
|
10
9
|
const validator_1 = require("../src/validator");
|
|
11
10
|
const signer_1 = require("../src/signer");
|
|
11
|
+
// Fixed test private key (deterministic); address derived via privateKeyToAddress
|
|
12
|
+
const TEST_PRIVATE_KEY_HEX = '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80';
|
|
12
13
|
describe('DRVC3 Wrapper', () => {
|
|
13
14
|
let mockDeltaReceipt;
|
|
14
|
-
let
|
|
15
|
+
let expectedAddress;
|
|
15
16
|
beforeAll(() => {
|
|
16
|
-
|
|
17
|
+
expectedAddress = (0, signer_1.privateKeyToAddress)(TEST_PRIVATE_KEY_HEX);
|
|
17
18
|
const bundle = {
|
|
18
19
|
bundleId: 'test-bundle-001',
|
|
19
20
|
claim: 'Test claim',
|
|
@@ -26,7 +27,7 @@ describe('DRVC3 Wrapper', () => {
|
|
|
26
27
|
mockDeltaReceipt = (0, core_1.runDeltaEngine)(bundle);
|
|
27
28
|
});
|
|
28
29
|
it('should wrap DeltaReceipt in valid DRVC3 envelope', async () => {
|
|
29
|
-
const drvc3 = await (0, wrapper_1.wrapInDRVC3)(mockDeltaReceipt,
|
|
30
|
+
const drvc3 = await (0, wrapper_1.wrapInDRVC3)(mockDeltaReceipt, TEST_PRIVATE_KEY_HEX, {
|
|
30
31
|
issuer: 'uvrn-test',
|
|
31
32
|
event: 'delta-reconciliation'
|
|
32
33
|
});
|
|
@@ -42,13 +43,13 @@ describe('DRVC3 Wrapper', () => {
|
|
|
42
43
|
expect(drvc3.integrity.hash).toBe(mockDeltaReceipt.hash);
|
|
43
44
|
expect(drvc3.integrity.signature_method).toBe('eip191');
|
|
44
45
|
expect(drvc3.integrity.signature).toBeDefined();
|
|
45
|
-
expect(drvc3.integrity.signer_address).toBe(
|
|
46
|
+
expect(drvc3.integrity.signer_address).toBe(expectedAddress);
|
|
46
47
|
// Check validation block
|
|
47
48
|
expect(drvc3.validation.v_score).toBe(mockDeltaReceipt.deltaFinal);
|
|
48
49
|
expect(drvc3.validation.checks.delta_receipt).toEqual(mockDeltaReceipt);
|
|
49
50
|
});
|
|
50
51
|
it('should produce schema-valid DRVC3 receipt', async () => {
|
|
51
|
-
const drvc3 = await (0, wrapper_1.wrapInDRVC3)(mockDeltaReceipt,
|
|
52
|
+
const drvc3 = await (0, wrapper_1.wrapInDRVC3)(mockDeltaReceipt, TEST_PRIVATE_KEY_HEX, {
|
|
52
53
|
issuer: 'uvrn',
|
|
53
54
|
event: 'test'
|
|
54
55
|
});
|
|
@@ -57,7 +58,7 @@ describe('DRVC3 Wrapper', () => {
|
|
|
57
58
|
expect(result.errors).toBeUndefined();
|
|
58
59
|
});
|
|
59
60
|
it('should include optional fields when provided', async () => {
|
|
60
|
-
const drvc3 = await (0, wrapper_1.wrapInDRVC3)(mockDeltaReceipt,
|
|
61
|
+
const drvc3 = await (0, wrapper_1.wrapInDRVC3)(mockDeltaReceipt, TEST_PRIVATE_KEY_HEX, {
|
|
61
62
|
issuer: 'uvrn',
|
|
62
63
|
event: 'delta-reconciliation',
|
|
63
64
|
blockState: 'blocked',
|
|
@@ -71,16 +72,16 @@ describe('DRVC3 Wrapper', () => {
|
|
|
71
72
|
expect(drvc3.tags).toEqual(['#uvrn', '#receipt', '#test']);
|
|
72
73
|
});
|
|
73
74
|
it('should create verifiable signature', async () => {
|
|
74
|
-
const drvc3 = await (0, wrapper_1.wrapInDRVC3)(mockDeltaReceipt,
|
|
75
|
+
const drvc3 = await (0, wrapper_1.wrapInDRVC3)(mockDeltaReceipt, TEST_PRIVATE_KEY_HEX, {
|
|
75
76
|
issuer: 'uvrn',
|
|
76
77
|
event: 'test'
|
|
77
78
|
});
|
|
78
79
|
// Recover signer from signature
|
|
79
80
|
const recoveredAddress = (0, signer_1.recoverSigner)(drvc3.integrity.hash, drvc3.integrity.signature);
|
|
80
|
-
expect(recoveredAddress.toLowerCase()).toBe(
|
|
81
|
+
expect(recoveredAddress.toLowerCase()).toBe(expectedAddress.toLowerCase());
|
|
81
82
|
});
|
|
82
83
|
it('should extract original DeltaReceipt from envelope', async () => {
|
|
83
|
-
const drvc3 = await (0, wrapper_1.wrapInDRVC3)(mockDeltaReceipt,
|
|
84
|
+
const drvc3 = await (0, wrapper_1.wrapInDRVC3)(mockDeltaReceipt, TEST_PRIVATE_KEY_HEX, {
|
|
84
85
|
issuer: 'uvrn',
|
|
85
86
|
event: 'test'
|
|
86
87
|
});
|
package/package.json
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uvrn/adapter",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"publishConfig": {
|
|
3
|
+
"version": "1.4.0",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"access": "public"
|
|
6
|
+
},
|
|
5
7
|
"description": "UVRN DRVC3 envelope adapter — wraps core receipts with EIP-191 signatures",
|
|
6
8
|
"main": "dist/index.js",
|
|
7
9
|
"types": "dist/index.d.ts",
|
|
8
|
-
"keywords": [
|
|
10
|
+
"keywords": [
|
|
11
|
+
"uvrn",
|
|
12
|
+
"drvc3",
|
|
13
|
+
"eip191",
|
|
14
|
+
"receipts"
|
|
15
|
+
],
|
|
9
16
|
"license": "MIT",
|
|
10
17
|
"repository": {
|
|
11
18
|
"type": "git",
|
|
@@ -13,25 +20,34 @@
|
|
|
13
20
|
"directory": "uvrn-adapter"
|
|
14
21
|
},
|
|
15
22
|
"homepage": "https://github.com/UVRN-org/uvrn-packages#readme",
|
|
16
|
-
"scripts": {
|
|
17
|
-
"build": "tsc",
|
|
18
|
-
"test": "jest",
|
|
19
|
-
"lint": "eslint src/**/*.ts",
|
|
20
|
-
"clean": "rm -rf dist"
|
|
21
|
-
},
|
|
22
23
|
"dependencies": {
|
|
23
|
-
"@
|
|
24
|
-
"
|
|
24
|
+
"@noble/hashes": "^1.4.0",
|
|
25
|
+
"@noble/secp256k1": "^2.0.0",
|
|
25
26
|
"ajv": "^8.12.0",
|
|
26
|
-
"ajv-formats": "^2.1.1"
|
|
27
|
+
"ajv-formats": "^2.1.1",
|
|
28
|
+
"@uvrn/core": "^1.4.0"
|
|
27
29
|
},
|
|
28
30
|
"devDependencies": {
|
|
31
|
+
"@babel/core": "^7.24.0",
|
|
32
|
+
"@babel/preset-env": "^7.24.0",
|
|
29
33
|
"@types/jest": "^29.5.0",
|
|
30
34
|
"@types/node": "^20.0.0",
|
|
35
|
+
"babel-jest": "^29.7.0",
|
|
31
36
|
"jest": "^29.5.0",
|
|
32
37
|
"ts-jest": "^29.1.0",
|
|
33
38
|
"typescript": "^5.3.0"
|
|
34
39
|
},
|
|
35
|
-
"engines": {
|
|
36
|
-
|
|
37
|
-
}
|
|
40
|
+
"engines": {
|
|
41
|
+
"node": ">=18.0.0"
|
|
42
|
+
},
|
|
43
|
+
"files": [
|
|
44
|
+
"dist",
|
|
45
|
+
"README.md"
|
|
46
|
+
],
|
|
47
|
+
"scripts": {
|
|
48
|
+
"build": "tsc",
|
|
49
|
+
"test": "jest",
|
|
50
|
+
"lint": "eslint src/**/*.ts",
|
|
51
|
+
"clean": "rm -rf dist"
|
|
52
|
+
}
|
|
53
|
+
}
|