@calimero-network/mero-js 26.11.0 → 27.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/README.md +73 -288
- package/dist/cloud-v2/attestation.d.ts +91 -0
- package/dist/cloud-v2/attestation.js +135 -0
- package/dist/cloud-v2/capabilities.d.ts +30 -0
- package/dist/cloud-v2/capabilities.js +31 -0
- package/dist/cloud-v2/client.d.ts +426 -0
- package/dist/cloud-v2/client.js +646 -0
- package/dist/cloud-v2/errors.d.ts +98 -0
- package/dist/cloud-v2/errors.js +165 -0
- package/dist/cloud-v2/index.d.ts +10 -0
- package/dist/cloud-v2/index.js +5 -0
- package/dist/cloud-v2/passkey.d.ts +27 -0
- package/dist/cloud-v2/passkey.js +73 -0
- package/dist/crypto/internal.d.ts +3 -37
- package/dist/crypto/internal.js +3 -72
- package/dist/index.browser.mjs +4 -2
- package/dist/index.browser.mjs.map +4 -4
- package/dist/index.cjs +1750 -12772
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +3 -56
- package/dist/index.js +14 -75
- package/dist/index.mjs +1750 -12772
- package/dist/index.mjs.map +4 -4
- package/dist/sealed/index.d.ts +4 -4
- package/dist/sealed/index.js +2 -2
- package/dist/sealed/release.d.ts +21 -76
- package/dist/sealed/release.js +24 -108
- package/dist/sealed/sealed.d.ts +8 -56
- package/dist/sealed/sealed.js +11 -89
- package/dist/sealed/verify.d.ts +7 -13
- package/dist/sealed/verify.js +3 -7
- package/package.json +8 -13
- package/dist/account/account-admin.d.ts +0 -80
- package/dist/account/account-admin.js +0 -346
- package/dist/account/account.d.ts +0 -196
- package/dist/account/account.js +0 -269
- package/dist/account/application-id.d.ts +0 -50
- package/dist/account/application-id.js +0 -83
- package/dist/account/bootstrap-from-invitation.d.ts +0 -110
- package/dist/account/bootstrap-from-invitation.js +0 -80
- package/dist/account/create-context.d.ts +0 -103
- package/dist/account/create-context.js +0 -267
- package/dist/account/govern.d.ts +0 -24
- package/dist/account/govern.js +0 -50
- package/dist/account/index.d.ts +0 -26
- package/dist/account/index.js +0 -15
- package/dist/account/join-as-account.d.ts +0 -23
- package/dist/account/join-as-account.js +0 -34
- package/dist/account/join-with-node.d.ts +0 -115
- package/dist/account/join-with-node.js +0 -161
- package/dist/account/node-admin.d.ts +0 -20
- package/dist/account/node-admin.js +0 -33
- package/dist/account/relay-from-invitation.d.ts +0 -137
- package/dist/account/relay-from-invitation.js +0 -180
- package/dist/account/relay-login-error.d.ts +0 -32
- package/dist/account/relay-login-error.js +0 -34
- package/dist/account/session.d.ts +0 -266
- package/dist/account/session.js +0 -915
- package/dist/account/wordlist.d.ts +0 -16
- package/dist/account/wordlist.js +0 -273
- package/dist/admin-api/admin-client.d.ts +0 -688
- package/dist/admin-api/admin-client.js +0 -1230
- package/dist/admin-api/admin-factory.d.ts +0 -14
- package/dist/admin-api/admin-factory.js +0 -22
- package/dist/admin-api/admin-types.d.ts +0 -2046
- package/dist/admin-api/admin-types.js +0 -12
- package/dist/admin-api/index.d.ts +0 -4
- package/dist/admin-api/index.js +0 -5
- package/dist/admin-api/redeem-invitation.d.ts +0 -101
- package/dist/admin-api/redeem-invitation.js +0 -200
- package/dist/auth/index.d.ts +0 -34
- package/dist/auth/index.js +0 -67
- package/dist/auth-api/auth-client.d.ts +0 -31
- package/dist/auth-api/auth-client.js +0 -117
- package/dist/auth-api/auth-factory.d.ts +0 -7
- package/dist/auth-api/auth-factory.js +0 -41
- package/dist/auth-api/auth-types.d.ts +0 -130
- package/dist/auth-api/auth-types.js +0 -2
- package/dist/auth-api/index.d.ts +0 -3
- package/dist/auth-api/index.js +0 -4
- package/dist/capabilities.d.ts +0 -84
- package/dist/capabilities.js +0 -117
- package/dist/cloud/account-ownership.d.ts +0 -63
- package/dist/cloud/account-ownership.js +0 -87
- package/dist/cloud/cloud-client.d.ts +0 -949
- package/dist/cloud/cloud-client.js +0 -1068
- package/dist/cloud/connect.d.ts +0 -213
- package/dist/cloud/connect.js +0 -260
- package/dist/cloud/enrol-redirect.d.ts +0 -181
- package/dist/cloud/enrol-redirect.js +0 -244
- package/dist/cloud/index.d.ts +0 -12
- package/dist/cloud/index.js +0 -10
- package/dist/cloud/link-redirect.d.ts +0 -95
- package/dist/cloud/link-redirect.js +0 -121
- package/dist/cloud/link-state.d.ts +0 -36
- package/dist/cloud/link-state.js +0 -104
- package/dist/cloud/routing-proof.d.ts +0 -113
- package/dist/cloud/routing-proof.js +0 -96
- package/dist/device-cert/device-cert.d.ts +0 -189
- package/dist/device-cert/device-cert.js +0 -239
- package/dist/device-cert/device-scope.d.ts +0 -58
- package/dist/device-cert/device-scope.js +0 -76
- package/dist/device-cert/index.d.ts +0 -4
- package/dist/device-cert/index.js +0 -3
- package/dist/ephemeral/codec.d.ts +0 -4
- package/dist/ephemeral/codec.js +0 -12
- package/dist/ephemeral/index.d.ts +0 -57
- package/dist/ephemeral/index.js +0 -101
- package/dist/ephemeral/types.d.ts +0 -27
- package/dist/ephemeral/types.js +0 -1
- package/dist/errors/classify.d.ts +0 -65
- package/dist/errors/classify.js +0 -117
- package/dist/errors/index.d.ts +0 -2
- package/dist/errors/index.js +0 -1
- package/dist/events/group.d.ts +0 -95
- package/dist/events/group.js +0 -10
- package/dist/events/index.d.ts +0 -5
- package/dist/events/index.js +0 -2
- package/dist/events/sse.d.ts +0 -127
- package/dist/events/sse.js +0 -469
- package/dist/events/ws.d.ts +0 -57
- package/dist/events/ws.js +0 -212
- package/dist/http-client/api-response.d.ts +0 -15
- package/dist/http-client/api-response.js +0 -1
- package/dist/http-client/http-factory.d.ts +0 -71
- package/dist/http-client/http-factory.js +0 -68
- package/dist/http-client/http-types.d.ts +0 -87
- package/dist/http-client/http-types.js +0 -1
- package/dist/http-client/index.d.ts +0 -7
- package/dist/http-client/index.js +0 -11
- package/dist/http-client/path-segment.d.ts +0 -1
- package/dist/http-client/path-segment.js +0 -8
- package/dist/http-client/retry.d.ts +0 -5
- package/dist/http-client/retry.js +0 -82
- package/dist/http-client/signal-utils.d.ts +0 -2
- package/dist/http-client/signal-utils.js +0 -41
- package/dist/http-client/web-client.d.ts +0 -80
- package/dist/http-client/web-client.js +0 -557
- package/dist/invitation/index.d.ts +0 -2
- package/dist/invitation/index.js +0 -2
- package/dist/invitation/invitation.d.ts +0 -106
- package/dist/invitation/invitation.js +0 -104
- package/dist/login/index.d.ts +0 -4
- package/dist/login/index.js +0 -3
- package/dist/login/login.d.ts +0 -111
- package/dist/login/login.js +0 -110
- package/dist/login/session.d.ts +0 -124
- package/dist/login/session.js +0 -141
- package/dist/mero-js.d.ts +0 -231
- package/dist/mero-js.js +0 -469
- package/dist/namespace-op/index.d.ts +0 -2
- package/dist/namespace-op/index.js +0 -3
- package/dist/namespace-op/namespace-op.d.ts +0 -150
- package/dist/namespace-op/namespace-op.js +0 -191
- package/dist/nodeDiscovery.d.ts +0 -47
- package/dist/nodeDiscovery.js +0 -101
- package/dist/owner-op/index.d.ts +0 -2
- package/dist/owner-op/index.js +0 -3
- package/dist/owner-op/owner-op.d.ts +0 -119
- package/dist/owner-op/owner-op.js +0 -214
- package/dist/presence/relay-presence.d.ts +0 -56
- package/dist/presence/relay-presence.js +0 -107
- package/dist/presence/statement.d.ts +0 -12
- package/dist/presence/statement.js +0 -17
- package/dist/relay/index.d.ts +0 -4
- package/dist/relay/index.js +0 -2
- package/dist/relay/nonce-source.d.ts +0 -72
- package/dist/relay/nonce-source.js +0 -111
- package/dist/relay/relay-client.d.ts +0 -472
- package/dist/relay/relay-client.js +0 -518
- package/dist/relay-attestation/attest-node-key.d.ts +0 -66
- package/dist/relay-attestation/attest-node-key.js +0 -110
- package/dist/relay-attestation/index.d.ts +0 -1
- package/dist/relay-attestation/index.js +0 -1
- package/dist/request/caller-proof.d.ts +0 -43
- package/dist/request/caller-proof.js +0 -36
- package/dist/request/index.d.ts +0 -3
- package/dist/request/index.js +0 -3
- package/dist/request/proof-signer.d.ts +0 -49
- package/dist/request/proof-signer.js +0 -59
- package/dist/request/request-sig.d.ts +0 -99
- package/dist/request/request-sig.js +0 -95
- package/dist/request-proof/authorizer.d.ts +0 -41
- package/dist/request-proof/authorizer.js +0 -60
- package/dist/request-proof/index.d.ts +0 -4
- package/dist/request-proof/index.js +0 -2
- package/dist/request-proof/request-proof.d.ts +0 -53
- package/dist/request-proof/request-proof.js +0 -95
- package/dist/rpc/index.d.ts +0 -64
- package/dist/rpc/index.js +0 -78
- package/dist/signer/index.d.ts +0 -2
- package/dist/signer/index.js +0 -3
- package/dist/signer/signer.d.ts +0 -92
- package/dist/signer/signer.js +0 -147
- package/dist/token-store/index.d.ts +0 -27
- package/dist/token-store/index.js +0 -62
- package/dist/transport/index.d.ts +0 -6
- package/dist/transport/index.js +0 -3
- package/dist/transport/mero-client.d.ts +0 -294
- package/dist/transport/mero-client.js +0 -386
- package/dist/transport/relay-observer.d.ts +0 -152
- package/dist/transport/relay-observer.js +0 -173
- package/dist/transport/relay-transport.d.ts +0 -73
- package/dist/transport/relay-transport.js +0 -94
- package/dist/transport/types.d.ts +0 -100
- package/dist/transport/types.js +0 -32
- package/dist/types/api-response.d.ts +0 -15
- package/dist/types/api-response.js +0 -1
- package/dist/types/index.d.ts +0 -1
- package/dist/types/index.js +0 -1
- package/dist/warrant/creation-warrant.d.ts +0 -163
- package/dist/warrant/creation-warrant.js +0 -194
- package/dist/warrant/governance-op.d.ts +0 -202
- package/dist/warrant/governance-op.js +0 -355
- package/dist/warrant/governance-warrant.d.ts +0 -124
- package/dist/warrant/governance-warrant.js +0 -164
- package/dist/warrant/index.d.ts +0 -8
- package/dist/warrant/index.js +0 -5
- package/dist/warrant/warrant.d.ts +0 -188
- package/dist/warrant/warrant.js +0 -202
|
@@ -1,355 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Encode the governance ops a member may have a relay publish for them.
|
|
3
|
-
*
|
|
4
|
-
* A governance warrant commits to an op's **borsh bytes**, not to a description
|
|
5
|
-
* of it, so the client has to produce exactly the bytes the node decodes. These
|
|
6
|
-
* encoders cover the delegable ops (core's `GroupOp::delegable_form` and
|
|
7
|
-
* `RootOp::delegable_form`) that a keyholder app is likely to need. Every
|
|
8
|
-
* discriminant is a variant's position in core's enum, which is how borsh
|
|
9
|
-
* encodes a Rust enum; they are pinned against core's own vectors in the test.
|
|
10
|
-
*
|
|
11
|
-
* **Delegable form.** A removal, a leave and a cascade delete carry fields only
|
|
12
|
-
* the publisher can compute (post-state hashes, the enumerated subtree). The
|
|
13
|
-
* member signs them cleared, and these encoders only ever produce the cleared
|
|
14
|
-
* form, which is the one a relay accepts. The relay fills them in afterwards.
|
|
15
|
-
*
|
|
16
|
-
* Sources in core, for the next person to check a discriminant:
|
|
17
|
-
* `crates/governance-types/src/lib.rs` (`GroupOp`, `RootOp`),
|
|
18
|
-
* `crates/primitives/src/context.rs` (`GroupMemberRole`),
|
|
19
|
-
* `crates/account/src/namespace_id.rs` (`founded_namespace_id`), and the vectors in
|
|
20
|
-
* `crates/governance-types/src/tests.rs` (`delegated_governance_op_vectors_are_stable`).
|
|
21
|
-
*/
|
|
22
|
-
import { CAPABILITIES, hasCap } from '../capabilities.js';
|
|
23
|
-
import { concat, domainHash, fromHex, fromHexUnsized, hex, u32le } from '../crypto/internal.js';
|
|
24
|
-
/**
|
|
25
|
-
* `GroupMemberRole` discriminants. The two TEE roles (3 and 4) are minted only
|
|
26
|
-
* by attestation admission and never by a member's op, so they are not offered.
|
|
27
|
-
*/
|
|
28
|
-
const ROLE = {
|
|
29
|
-
Admin: 0,
|
|
30
|
-
Member: 1,
|
|
31
|
-
ReadOnly: 2,
|
|
32
|
-
};
|
|
33
|
-
/** `GroupOp` discriminants, by position in core's enum. */
|
|
34
|
-
const GROUP_OP = {
|
|
35
|
-
MemberAdded: 1,
|
|
36
|
-
MemberRemoved: 2,
|
|
37
|
-
MemberLeft: 3,
|
|
38
|
-
MemberRoleSet: 4,
|
|
39
|
-
MemberCapabilitySet: 5,
|
|
40
|
-
DefaultCapabilitiesSet: 6,
|
|
41
|
-
TargetApplicationSet: 7,
|
|
42
|
-
ContextDetached: 9,
|
|
43
|
-
SubgroupVisibilitySet: 10,
|
|
44
|
-
GroupMetadataSet: 11,
|
|
45
|
-
MemberMetadataSet: 12,
|
|
46
|
-
ContextMetadataSet: 13,
|
|
47
|
-
ContextCapabilityGranted: 16,
|
|
48
|
-
ContextCapabilityRevoked: 17,
|
|
49
|
-
};
|
|
50
|
-
/** `RootOp` discriminants, by position in core's enum. */
|
|
51
|
-
const ROOT_OP = {
|
|
52
|
-
GroupCreated: 0,
|
|
53
|
-
GroupReparented: 1,
|
|
54
|
-
GroupDeleted: 2,
|
|
55
|
-
MemberJoinedOpen: 7,
|
|
56
|
-
NamespaceCreatedV2: 9,
|
|
57
|
-
};
|
|
58
|
-
/** core's `NAMESPACE_ID_DOMAIN`, which a founded namespace id is hashed under. */
|
|
59
|
-
const NAMESPACE_ID_DOMAIN = new TextEncoder().encode('calimero.namespace.id.v1');
|
|
60
|
-
/** A post-state hash left for the relay to compute: 32 zero bytes. */
|
|
61
|
-
const CLEARED_HASH = new Uint8Array(32);
|
|
62
|
-
/** An empty borsh `Vec`: a zero `u32` count. */
|
|
63
|
-
const EMPTY_VEC = u32le(0);
|
|
64
|
-
function role(value) {
|
|
65
|
-
const byte = ROLE[value];
|
|
66
|
-
if (byte === undefined) {
|
|
67
|
-
throw new Error(`role must be Admin, Member or ReadOnly, got ${String(value)}`);
|
|
68
|
-
}
|
|
69
|
-
return new Uint8Array([byte]);
|
|
70
|
-
}
|
|
71
|
-
function tag(byte) {
|
|
72
|
-
return new Uint8Array([byte]);
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* Add `member` (an account, hex) to the group with `role`.
|
|
76
|
-
*
|
|
77
|
-
* Applied as the author: adding a `Member` needs `MANAGE_MEMBERS` or admin,
|
|
78
|
-
* adding an `Admin` needs admin.
|
|
79
|
-
*/
|
|
80
|
-
export function memberAddedOp(member, memberRole) {
|
|
81
|
-
return {
|
|
82
|
-
kind: 'group',
|
|
83
|
-
bytes: concat(tag(GROUP_OP.MemberAdded), fromHex(member, 'member', 32), role(memberRole)),
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* Remove `member` (an account, hex) from the group, in delegable form: both
|
|
88
|
-
* post-state claims cleared, for the relay to compute from its own view.
|
|
89
|
-
*/
|
|
90
|
-
export function memberRemovedOp(member) {
|
|
91
|
-
return {
|
|
92
|
-
kind: 'group',
|
|
93
|
-
bytes: concat(tag(GROUP_OP.MemberRemoved), fromHex(member, 'member', 32), CLEARED_HASH, EMPTY_VEC),
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* The author leaves the group, in delegable form. The node applies it only when
|
|
98
|
-
* `member` is the author's own account.
|
|
99
|
-
*/
|
|
100
|
-
export function memberLeftOp(member) {
|
|
101
|
-
return {
|
|
102
|
-
kind: 'group',
|
|
103
|
-
bytes: concat(tag(GROUP_OP.MemberLeft), fromHex(member, 'member', 32), CLEARED_HASH, EMPTY_VEC),
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
/** Set `member`'s role (an account, hex). */
|
|
107
|
-
export function memberRoleSetOp(member, memberRole) {
|
|
108
|
-
return {
|
|
109
|
-
kind: 'group',
|
|
110
|
-
bytes: concat(tag(GROUP_OP.MemberRoleSet), fromHex(member, 'member', 32), role(memberRole)),
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
/** Domain of core's `created_subgroup_id`. */
|
|
114
|
-
const SUBGROUP_ID_DOMAIN = new TextEncoder().encode('calimero.subgroup.id.v1');
|
|
115
|
-
/**
|
|
116
|
-
* The id `admin` derives for a subgroup under `parentId` with birth visibility
|
|
117
|
-
* `restricted` and `salt` (all hex), as core's `created_subgroup_id` does:
|
|
118
|
-
* `domain_hash("calimero.subgroup.id.v1", [admin, parentId, [restricted], salt])`.
|
|
119
|
-
*
|
|
120
|
-
* A subgroup's id is bound to its create, so nobody but its creator can name
|
|
121
|
-
* it: a node refuses a `GroupCreated` whose fields do not reproduce its id.
|
|
122
|
-
* That is what stops a second member racing a concurrent create for an id it
|
|
123
|
-
* saw gossiped.
|
|
124
|
-
*/
|
|
125
|
-
export async function createdSubgroupId(admin, parentId, restricted, salt) {
|
|
126
|
-
return hex(await domainHash(SUBGROUP_ID_DOMAIN, [
|
|
127
|
-
fromHex(admin, 'admin', 32),
|
|
128
|
-
fromHex(parentId, 'parentId', 32),
|
|
129
|
-
new Uint8Array([restricted ? 1 : 0]),
|
|
130
|
-
fromHex(salt, 'salt', 32),
|
|
131
|
-
]));
|
|
132
|
-
}
|
|
133
|
-
/**
|
|
134
|
-
* Set the group's default capability mask (`MemberCapabilities` bits, a `u32`),
|
|
135
|
-
* the capabilities every member holds without a per-member grant. Needs admin.
|
|
136
|
-
*
|
|
137
|
-
* The relay refuses any change to `CAN_AUTHOR_ON_BEHALF` (bit 9) through this
|
|
138
|
-
* path, and no namespace starts with it in its default mask, so a mask that
|
|
139
|
-
* sets it is refused here rather than burning a nonce on a certain 403.
|
|
140
|
-
*/
|
|
141
|
-
/** A borsh `String`: a u32 byte length, then the UTF-8 bytes. */
|
|
142
|
-
function borshString(value, what) {
|
|
143
|
-
if (value.length === 0)
|
|
144
|
-
throw new Error(`${what} must not be empty`);
|
|
145
|
-
const bytes = new TextEncoder().encode(value);
|
|
146
|
-
return concat(u32le(bytes.length), bytes);
|
|
147
|
-
}
|
|
148
|
-
/**
|
|
149
|
-
* Choose the application a group runs (`GroupOp::TargetApplicationSet`), in
|
|
150
|
-
* its delegable form: `bytecode_id` is left for the relay to fill (32 zero
|
|
151
|
-
* bytes), resolved from `package@version` in the registry. The member signs
|
|
152
|
-
* the application id, package and version, so the relay can pick no other
|
|
153
|
-
* build; the application id binds package and signer, and every node checks
|
|
154
|
-
* the bundle's signature.
|
|
155
|
-
*
|
|
156
|
-
* Through a relay this is accepted only as a group's FIRST application, on a
|
|
157
|
-
* group that targets none yet (a namespace an account founded). Moving an
|
|
158
|
-
* existing target is an upgrade, which never goes through a relay.
|
|
159
|
-
*/
|
|
160
|
-
export function targetApplicationSetOp(input) {
|
|
161
|
-
return {
|
|
162
|
-
kind: 'group',
|
|
163
|
-
bytes: concat(tag(GROUP_OP.TargetApplicationSet), CLEARED_HASH, fromHex(input.applicationId, 'applicationId', 32), borshString(input.package, 'package'), borshString(input.version, 'version')),
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
export function defaultCapabilitiesSetOp(capabilities) {
|
|
167
|
-
if (!Number.isInteger(capabilities) || capabilities < 0 || capabilities > 4294967295) {
|
|
168
|
-
throw new Error(`capabilities must be a u32 bit mask, got ${String(capabilities)}`);
|
|
169
|
-
}
|
|
170
|
-
// Never changed through a relay.
|
|
171
|
-
if (hasCap(capabilities, CAPABILITIES.CAN_AUTHOR_ON_BEHALF)) {
|
|
172
|
-
throw new Error('capabilities may not include CAN_AUTHOR_ON_BEHALF (512): a relay never carries a change to it');
|
|
173
|
-
}
|
|
174
|
-
return {
|
|
175
|
-
kind: 'group',
|
|
176
|
-
bytes: concat(tag(GROUP_OP.DefaultCapabilitiesSet), u32le(capabilities)),
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
/**
|
|
180
|
-
* Create a subgroup under `parentId`, a root op published on the namespace.
|
|
181
|
-
*
|
|
182
|
-
* Needs `CAN_CREATE_SUBGROUP` or admin on the parent; the author, not the
|
|
183
|
-
* relay, becomes the new subgroup's admin.
|
|
184
|
-
*/
|
|
185
|
-
export function groupCreatedOp(input) {
|
|
186
|
-
return {
|
|
187
|
-
kind: 'root',
|
|
188
|
-
bytes: concat(tag(ROOT_OP.GroupCreated), fromHex(input.groupId, 'groupId', 32), fromHex(input.parentId, 'parentId', 32), tag(input.restricted ? 1 : 0), fromHex(input.admin, 'admin', 32), fromHex(input.salt, 'salt', 32)),
|
|
189
|
-
};
|
|
190
|
-
}
|
|
191
|
-
/**
|
|
192
|
-
* Create a subgroup under `parentId`: draws a fresh salt (unless one is given),
|
|
193
|
-
* derives the id with {@link createdSubgroupId}, and encodes the op.
|
|
194
|
-
*/
|
|
195
|
-
export async function subgroupCreation(input) {
|
|
196
|
-
const salt = input.salt ?? hex(crypto.getRandomValues(new Uint8Array(32)));
|
|
197
|
-
const groupId = await createdSubgroupId(input.admin, input.parentId, input.restricted, salt);
|
|
198
|
-
return { groupId, salt, op: groupCreatedOp({ ...input, groupId, salt }) };
|
|
199
|
-
}
|
|
200
|
-
/** Move `childGroupId` under `newParentId`, a root op. */
|
|
201
|
-
export function groupReparentedOp(childGroupId, newParentId) {
|
|
202
|
-
return {
|
|
203
|
-
kind: 'root',
|
|
204
|
-
bytes: concat(tag(ROOT_OP.GroupReparented), fromHex(childGroupId, 'childGroupId', 32), fromHex(newParentId, 'newParentId', 32)),
|
|
205
|
-
};
|
|
206
|
-
}
|
|
207
|
-
/**
|
|
208
|
-
* Delete `rootGroupId` and its subtree, in delegable form: both cascade lists
|
|
209
|
-
* empty, for the relay to enumerate.
|
|
210
|
-
*/
|
|
211
|
-
export function groupDeletedOp(rootGroupId) {
|
|
212
|
-
return {
|
|
213
|
-
kind: 'root',
|
|
214
|
-
bytes: concat(tag(ROOT_OP.GroupDeleted), fromHex(rootGroupId, 'rootGroupId', 32), EMPTY_VEC, EMPTY_VEC),
|
|
215
|
-
};
|
|
216
|
-
}
|
|
217
|
-
/**
|
|
218
|
-
* The id of the namespace `founder` founds with `salt`: core's
|
|
219
|
-
* `founded_namespace_id`, `domain_hash("calimero.namespace.id.v1", [founder, salt])`.
|
|
220
|
-
*
|
|
221
|
-
* Anyone holding the pair can recompute it, and only the founder's account
|
|
222
|
-
* hashes to it, which is why a relay founding a namespace for a member cannot
|
|
223
|
-
* choose the id: the member computes it and signs it.
|
|
224
|
-
*/
|
|
225
|
-
export async function foundedNamespaceId(founder, salt) {
|
|
226
|
-
return hex(await domainHash(NAMESPACE_ID_DOMAIN, [
|
|
227
|
-
fromHex(founder, 'founder', 32),
|
|
228
|
-
fromHex(salt, 'salt', 32),
|
|
229
|
-
]));
|
|
230
|
-
}
|
|
231
|
-
/**
|
|
232
|
-
* Found a namespace: core's `RootOp::NamespaceCreatedV2` genesis, a root op
|
|
233
|
-
* published on the new namespace's own log.
|
|
234
|
-
*
|
|
235
|
-
* Its delegable form is the op itself, nothing cleared, so the author signs the
|
|
236
|
-
* exact genesis. Borsh is `[9] || founder || credential || salt`: the credential
|
|
237
|
-
* is a nested struct, not a length-prefixed byte string, so it is written
|
|
238
|
-
* inline. The warrant's scope must be {@link foundedNamespaceId}`(founder, salt)`.
|
|
239
|
-
*/
|
|
240
|
-
export function namespaceCreatedOp(input) {
|
|
241
|
-
const credential = fromHexUnsized(input.credential, 'credential');
|
|
242
|
-
if (credential.length === 0) {
|
|
243
|
-
throw new Error('credential must be the founder\'s AccountProof<DeviceCert>, got nothing');
|
|
244
|
-
}
|
|
245
|
-
return {
|
|
246
|
-
kind: 'root',
|
|
247
|
-
bytes: concat(tag(ROOT_OP.NamespaceCreatedV2), fromHex(input.founder, 'founder', 32), credential, fromHex(input.salt, 'salt', 32)),
|
|
248
|
-
};
|
|
249
|
-
}
|
|
250
|
-
// ---------------------------------------------------------------------------
|
|
251
|
-
// The rest of the delegable set. Layouts are pinned against core's
|
|
252
|
-
// `delegable_governance_op_vectors_for_non_rust_encoders_are_stable`
|
|
253
|
-
// (crates/governance-types/src/tests.rs, core#4272).
|
|
254
|
-
/** A borsh `String`: a u32 byte length, then the UTF-8 bytes. */
|
|
255
|
-
function str(value) {
|
|
256
|
-
const bytes = new TextEncoder().encode(value);
|
|
257
|
-
return concat(u32le(bytes.length), bytes);
|
|
258
|
-
}
|
|
259
|
-
/** A borsh `Option<String>`: 0, or 1 then the string. */
|
|
260
|
-
function optionalStr(value) {
|
|
261
|
-
return value === undefined ? tag(0) : concat(tag(1), str(value));
|
|
262
|
-
}
|
|
263
|
-
/**
|
|
264
|
-
* A borsh `BTreeMap<String, String>`: a u32 count, then each entry in the
|
|
265
|
-
* map's own order, which is by key BYTES (not JS string order).
|
|
266
|
-
*/
|
|
267
|
-
function stringMap(data = {}) {
|
|
268
|
-
const enc = new TextEncoder();
|
|
269
|
-
const entries = Object.entries(data)
|
|
270
|
-
.map(([k, v]) => [enc.encode(k), v, k])
|
|
271
|
-
.sort(([a], [b]) => {
|
|
272
|
-
for (let i = 0; i < Math.min(a.length, b.length); i++)
|
|
273
|
-
if (a[i] !== b[i])
|
|
274
|
-
return a[i] - b[i];
|
|
275
|
-
return a.length - b.length;
|
|
276
|
-
});
|
|
277
|
-
return concat(u32le(entries.length), ...entries.flatMap(([, v, k]) => [str(k), str(v)]));
|
|
278
|
-
}
|
|
279
|
-
/** Validate a u32 capability mask a relay may carry: never bit 9. */
|
|
280
|
-
function relayableMask(capabilities, what) {
|
|
281
|
-
if (!Number.isInteger(capabilities) || capabilities < 0 || capabilities > 4294967295) {
|
|
282
|
-
throw new Error(`${what} must be a u32 bit mask, got ${String(capabilities)}`);
|
|
283
|
-
}
|
|
284
|
-
if (hasCap(capabilities, CAPABILITIES.CAN_AUTHOR_ON_BEHALF)) {
|
|
285
|
-
throw new Error(`${what} may not include CAN_AUTHOR_ON_BEHALF (512): a relay never carries a change to it`);
|
|
286
|
-
}
|
|
287
|
-
return capabilities;
|
|
288
|
-
}
|
|
289
|
-
/** Set `member`'s capabilities in the group (`GroupOp::MemberCapabilitySet`). Bit 9 is refused. */
|
|
290
|
-
export function memberCapabilitySetOp(member, capabilities) {
|
|
291
|
-
return {
|
|
292
|
-
kind: 'group',
|
|
293
|
-
bytes: concat(tag(GROUP_OP.MemberCapabilitySet), fromHex(member, 'member', 32), u32le(relayableMask(capabilities, 'capabilities'))),
|
|
294
|
-
};
|
|
295
|
-
}
|
|
296
|
-
/** Detach a context from the group (`GroupOp::ContextDetached`). */
|
|
297
|
-
export function contextDetachedOp(contextId) {
|
|
298
|
-
return { kind: 'group', bytes: concat(tag(GROUP_OP.ContextDetached), fromHex(contextId, 'contextId', 32)) };
|
|
299
|
-
}
|
|
300
|
-
/** Make a subgroup open (members of the parent may join it themselves) or restricted. */
|
|
301
|
-
export function subgroupVisibilitySetOp(mode) {
|
|
302
|
-
if (mode !== 'open' && mode !== 'restricted')
|
|
303
|
-
throw new Error(`mode must be open or restricted, got ${String(mode)}`);
|
|
304
|
-
return { kind: 'group', bytes: concat(tag(GROUP_OP.SubgroupVisibilitySet), tag(mode === 'open' ? 0 : 1)) };
|
|
305
|
-
}
|
|
306
|
-
/** Name the group, with optional key/value data (`GroupOp::GroupMetadataSet`). */
|
|
307
|
-
export function groupMetadataSetOp(input = {}) {
|
|
308
|
-
return { kind: 'group', bytes: concat(tag(GROUP_OP.GroupMetadataSet), optionalStr(input.name), stringMap(input.data)) };
|
|
309
|
-
}
|
|
310
|
-
/** Name a member in the group (`GroupOp::MemberMetadataSet`). */
|
|
311
|
-
export function memberMetadataSetOp(member, input = {}) {
|
|
312
|
-
return {
|
|
313
|
-
kind: 'group',
|
|
314
|
-
bytes: concat(tag(GROUP_OP.MemberMetadataSet), fromHex(member, 'member', 32), optionalStr(input.name), stringMap(input.data)),
|
|
315
|
-
};
|
|
316
|
-
}
|
|
317
|
-
/** Name a context in the group (`GroupOp::ContextMetadataSet`). */
|
|
318
|
-
export function contextMetadataSetOp(contextId, input = {}) {
|
|
319
|
-
return {
|
|
320
|
-
kind: 'group',
|
|
321
|
-
bytes: concat(tag(GROUP_OP.ContextMetadataSet), fromHex(contextId, 'contextId', 32), optionalStr(input.name), stringMap(input.data)),
|
|
322
|
-
};
|
|
323
|
-
}
|
|
324
|
-
function contextCapability(capability) {
|
|
325
|
-
if (!Number.isInteger(capability) || capability < 1 || capability > 0xff) {
|
|
326
|
-
throw new Error(`capability must be a non-zero u8, got ${String(capability)}`);
|
|
327
|
-
}
|
|
328
|
-
return tag(capability);
|
|
329
|
-
}
|
|
330
|
-
/** Grant `member` a per-context capability (`GroupOp::ContextCapabilityGranted`). */
|
|
331
|
-
export function contextCapabilityGrantedOp(contextId, member, capability) {
|
|
332
|
-
return {
|
|
333
|
-
kind: 'group',
|
|
334
|
-
bytes: concat(tag(GROUP_OP.ContextCapabilityGranted), fromHex(contextId, 'contextId', 32), fromHex(member, 'member', 32), contextCapability(capability)),
|
|
335
|
-
};
|
|
336
|
-
}
|
|
337
|
-
/** Revoke a per-context capability from `member` (`GroupOp::ContextCapabilityRevoked`). */
|
|
338
|
-
export function contextCapabilityRevokedOp(contextId, member, capability) {
|
|
339
|
-
return {
|
|
340
|
-
kind: 'group',
|
|
341
|
-
bytes: concat(tag(GROUP_OP.ContextCapabilityRevoked), fromHex(contextId, 'contextId', 32), fromHex(member, 'member', 32), contextCapability(capability)),
|
|
342
|
-
};
|
|
343
|
-
}
|
|
344
|
-
/**
|
|
345
|
-
* Join an open subgroup yourself (`RootOp::MemberJoinedOpen`), a root op posted
|
|
346
|
-
* to the namespace. `member` must be the author; `credential` is the author's
|
|
347
|
-
* own `AccountProof<DeviceCert>`, hex, carried as-is. The author's
|
|
348
|
-
* `CAN_JOIN_OPEN_SUBGROUPS` and the open inheritance path are checked as usual.
|
|
349
|
-
*/
|
|
350
|
-
export function memberJoinedOpenOp(input) {
|
|
351
|
-
return {
|
|
352
|
-
kind: 'root',
|
|
353
|
-
bytes: concat(tag(ROOT_OP.MemberJoinedOpen), fromHex(input.member, 'member', 32), fromHex(input.groupId, 'groupId', 32), fromHexUnsized(input.credential, 'credential')),
|
|
354
|
-
};
|
|
355
|
-
}
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Mint a governance warrant: the author's consent for one relay to publish one
|
|
3
|
-
* governance op in the author's name, once.
|
|
4
|
-
*
|
|
5
|
-
* The intent warrant lets an account with no node write to a context and the
|
|
6
|
-
* creation warrant lets it create one. This covers the governance around them:
|
|
7
|
-
* adding the other person to a DM, creating a channel's subgroup, changing a
|
|
8
|
-
* role. The relay wraps the op in `OnBehalf` and publishes it; every peer applies
|
|
9
|
-
* the inner op **as the author**, so the author's own authority decides
|
|
10
|
-
* (`MANAGE_MEMBERS` to add a member, `CAN_CREATE_SUBGROUP` to create a subgroup,
|
|
11
|
-
* and so on) and the relay needs only standing to act for members.
|
|
12
|
-
*
|
|
13
|
-
* **It commits to the op's bytes.** The warrant carries
|
|
14
|
-
* `H(kind || op bytes)` over the op's delegable form (see `governance-op.ts`), so
|
|
15
|
-
* what the op does, to whom and where is signed byte for byte and the relay
|
|
16
|
-
* chooses nothing about it. `kind` is inside the commitment, so a group op can
|
|
17
|
-
* never be presented as a root op whose bytes happen to coincide.
|
|
18
|
-
*
|
|
19
|
-
* **`scope` is where the op is published**: the group itself for a group op,
|
|
20
|
-
* the namespace for a root op.
|
|
21
|
-
*
|
|
22
|
-
* Same primitives and the same two-widths trap as `warrant.ts`: counts inside
|
|
23
|
-
* the encoding are borsh `u32`, counts in the signing preimage are `u64`.
|
|
24
|
-
*
|
|
25
|
-
* **The byte contract is pinned in core**, at
|
|
26
|
-
* `crates/account/src/tests/governance_wire_fixture.rs`, and this module's test
|
|
27
|
-
* asserts the same vectors. A drift would surface at a relay as a 400.
|
|
28
|
-
*/
|
|
29
|
-
import { type Signer } from '../signer/signer.js';
|
|
30
|
-
import type { GovernanceOp, GovernanceOpKind } from './governance-op.js';
|
|
31
|
-
/** What a governance warrant authorises. */
|
|
32
|
-
export interface GovernanceWarrantInput {
|
|
33
|
-
/**
|
|
34
|
-
* Where the op is published, hex (32 bytes): the group for a group op, the
|
|
35
|
-
* namespace for a root op. The relay refuses a warrant presented anywhere else.
|
|
36
|
-
*/
|
|
37
|
-
scope: string;
|
|
38
|
-
/** The op, from one of the encoders in `governance-op.ts`. Its kind is signed over. */
|
|
39
|
-
op: GovernanceOp;
|
|
40
|
-
/** The author's account, hex: whose authority the op is applied under. */
|
|
41
|
-
authorAccount: string;
|
|
42
|
-
/** The relay's account, hex: the subject its authorship grant is held by. */
|
|
43
|
-
executor: string;
|
|
44
|
-
/** The one signing key of `executor` that may publish it, hex: discovery's `executorKey`. */
|
|
45
|
-
executorKey: string;
|
|
46
|
-
/**
|
|
47
|
-
* Monotonic per author **device**, spent in a per-group ledger. The same
|
|
48
|
-
* nonce source `RelayClient` uses for every other warrant is correct.
|
|
49
|
-
*/
|
|
50
|
-
nonce: number | bigint;
|
|
51
|
-
/** Unix seconds after which the relay must refuse it. */
|
|
52
|
-
notAfter: number | bigint;
|
|
53
|
-
/** Account-log heads the author saw, each hex (32 bytes). At most 64. */
|
|
54
|
-
accountHeads?: string[];
|
|
55
|
-
/** Governance heads the author's view descended from, each hex (32 bytes). At most 64. */
|
|
56
|
-
governanceFloor?: string[];
|
|
57
|
-
/**
|
|
58
|
-
* The author device's ed25519 signing secret, hex (32 bytes). Never sent.
|
|
59
|
-
*
|
|
60
|
-
* Mutually exclusive with {@link GovernanceWarrantInput.signer}: exactly one is required.
|
|
61
|
-
* Prefer `signer` in a browser, since a secret held as a string is readable
|
|
62
|
-
* by anything on the origin.
|
|
63
|
-
*/
|
|
64
|
-
deviceSecret?: string;
|
|
65
|
-
/**
|
|
66
|
-
* The author device's signer, in place of {@link GovernanceWarrantInput.deviceSecret}: for a
|
|
67
|
-
* key that cannot be exported to hex (a non-extractable WebCrypto key, a
|
|
68
|
-
* passkey, a hardware or remote signer). Its `publicKey` becomes the
|
|
69
|
-
* warrant's `author_device_key`, and it signs the 32-byte preimage as is.
|
|
70
|
-
*/
|
|
71
|
-
signer?: Signer;
|
|
72
|
-
}
|
|
73
|
-
/** A governance warrant's fields, decoded from the wire encoding. */
|
|
74
|
-
export interface GovernanceWarrantFields {
|
|
75
|
-
scope: string;
|
|
76
|
-
kind: GovernanceOpKind;
|
|
77
|
-
authorAccount: string;
|
|
78
|
-
/** The author device key: the signer's public key. */
|
|
79
|
-
deviceKey: string;
|
|
80
|
-
executor: string;
|
|
81
|
-
executorKey: string;
|
|
82
|
-
opHash: string;
|
|
83
|
-
accountHeads: string[];
|
|
84
|
-
governanceFloor: string[];
|
|
85
|
-
nonce: bigint;
|
|
86
|
-
notAfter: bigint;
|
|
87
|
-
signature: string;
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* The commitment a governance warrant carries in place of the op:
|
|
91
|
-
* `H(kind || op bytes)` under its own domain.
|
|
92
|
-
*/
|
|
93
|
-
export declare function governanceOpHash(op: GovernanceOp): Promise<Uint8Array>;
|
|
94
|
-
/** The already-decoded fields the signature covers. Internal. */
|
|
95
|
-
export interface GovernancePreimageParts {
|
|
96
|
-
scope: Uint8Array;
|
|
97
|
-
kind: GovernanceOpKind;
|
|
98
|
-
authorAccount: Uint8Array;
|
|
99
|
-
deviceKey: Uint8Array;
|
|
100
|
-
executor: Uint8Array;
|
|
101
|
-
executorKey: Uint8Array;
|
|
102
|
-
opHash: Uint8Array;
|
|
103
|
-
accountHeads: Uint8Array[];
|
|
104
|
-
governanceFloor: Uint8Array[];
|
|
105
|
-
nonce: number | bigint;
|
|
106
|
-
notAfter: number | bigint;
|
|
107
|
-
}
|
|
108
|
-
/**
|
|
109
|
-
* The bytes the device key signs. Exported for the conformance test only, not
|
|
110
|
-
* from the package root.
|
|
111
|
-
*/
|
|
112
|
-
export declare function governanceWarrantPreimage(p: GovernancePreimageParts): Promise<Uint8Array>;
|
|
113
|
-
/**
|
|
114
|
-
* Sign a governance warrant and return it hex-encoded (borsh
|
|
115
|
-
* `GovernanceWarrant`), ready for the relay alongside the op's own bytes.
|
|
116
|
-
*/
|
|
117
|
-
export declare function signGovernanceWarrant(input: GovernanceWarrantInput): Promise<string>;
|
|
118
|
-
/**
|
|
119
|
-
* Decode a hex governance warrant back into its fields.
|
|
120
|
-
*
|
|
121
|
-
* Refuses truncated or trailing bytes and an unknown kind. Does not verify the
|
|
122
|
-
* signature.
|
|
123
|
-
*/
|
|
124
|
-
export declare function parseGovernanceWarrant(warrant: string): GovernanceWarrantFields;
|
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Mint a governance warrant: the author's consent for one relay to publish one
|
|
3
|
-
* governance op in the author's name, once.
|
|
4
|
-
*
|
|
5
|
-
* The intent warrant lets an account with no node write to a context and the
|
|
6
|
-
* creation warrant lets it create one. This covers the governance around them:
|
|
7
|
-
* adding the other person to a DM, creating a channel's subgroup, changing a
|
|
8
|
-
* role. The relay wraps the op in `OnBehalf` and publishes it; every peer applies
|
|
9
|
-
* the inner op **as the author**, so the author's own authority decides
|
|
10
|
-
* (`MANAGE_MEMBERS` to add a member, `CAN_CREATE_SUBGROUP` to create a subgroup,
|
|
11
|
-
* and so on) and the relay needs only standing to act for members.
|
|
12
|
-
*
|
|
13
|
-
* **It commits to the op's bytes.** The warrant carries
|
|
14
|
-
* `H(kind || op bytes)` over the op's delegable form (see `governance-op.ts`), so
|
|
15
|
-
* what the op does, to whom and where is signed byte for byte and the relay
|
|
16
|
-
* chooses nothing about it. `kind` is inside the commitment, so a group op can
|
|
17
|
-
* never be presented as a root op whose bytes happen to coincide.
|
|
18
|
-
*
|
|
19
|
-
* **`scope` is where the op is published**: the group itself for a group op,
|
|
20
|
-
* the namespace for a root op.
|
|
21
|
-
*
|
|
22
|
-
* Same primitives and the same two-widths trap as `warrant.ts`: counts inside
|
|
23
|
-
* the encoding are borsh `u32`, counts in the signing preimage are `u64`.
|
|
24
|
-
*
|
|
25
|
-
* **The byte contract is pinned in core**, at
|
|
26
|
-
* `crates/account/src/tests/governance_wire_fixture.rs`, and this module's test
|
|
27
|
-
* asserts the same vectors. A drift would surface at a relay as a 400.
|
|
28
|
-
*/
|
|
29
|
-
import { concat, domainHash, fromHex, hex, u32le, u64le } from '../crypto/internal.js';
|
|
30
|
-
import { resolveSigner } from '../signer/signer.js';
|
|
31
|
-
import { checkedSignature, citedHeads } from './warrant.js';
|
|
32
|
-
const SIGN_DOMAIN = new TextEncoder().encode('calimero.governance-warrant.v1');
|
|
33
|
-
const OP_DOMAIN = new TextEncoder().encode('calimero.governance-warrant.op.v1');
|
|
34
|
-
/** core's `GovernanceOpKind` discriminants. */
|
|
35
|
-
const KIND_BYTE = { group: 0, root: 1 };
|
|
36
|
-
/**
|
|
37
|
-
* The commitment a governance warrant carries in place of the op:
|
|
38
|
-
* `H(kind || op bytes)` under its own domain.
|
|
39
|
-
*/
|
|
40
|
-
export async function governanceOpHash(op) {
|
|
41
|
-
return domainHash(OP_DOMAIN, [new Uint8Array([kindByte(op.kind)]), op.bytes]);
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* The bytes the device key signs. Exported for the conformance test only, not
|
|
45
|
-
* from the package root.
|
|
46
|
-
*/
|
|
47
|
-
export async function governanceWarrantPreimage(p) {
|
|
48
|
-
// u64 counts HERE; u32 on the wire. The counts keep the two adjacent head
|
|
49
|
-
// lists from being relabelled into each other.
|
|
50
|
-
return domainHash(SIGN_DOMAIN, [
|
|
51
|
-
p.scope,
|
|
52
|
-
new Uint8Array([kindByte(p.kind)]),
|
|
53
|
-
p.authorAccount,
|
|
54
|
-
p.deviceKey,
|
|
55
|
-
p.executor,
|
|
56
|
-
p.executorKey,
|
|
57
|
-
p.opHash,
|
|
58
|
-
u64le(p.accountHeads.length),
|
|
59
|
-
...p.accountHeads,
|
|
60
|
-
u64le(p.governanceFloor.length),
|
|
61
|
-
...p.governanceFloor,
|
|
62
|
-
u64le(p.nonce),
|
|
63
|
-
u64le(p.notAfter),
|
|
64
|
-
]);
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Sign a governance warrant and return it hex-encoded (borsh
|
|
68
|
-
* `GovernanceWarrant`), ready for the relay alongside the op's own bytes.
|
|
69
|
-
*/
|
|
70
|
-
export async function signGovernanceWarrant(input) {
|
|
71
|
-
const scope = fromHex(input.scope, 'scope', 32);
|
|
72
|
-
const authorAccount = fromHex(input.authorAccount, 'authorAccount', 32);
|
|
73
|
-
const executor = fromHex(input.executor, 'executor', 32);
|
|
74
|
-
const executorKey = fromHex(input.executorKey, 'executorKey', 32);
|
|
75
|
-
const accountHeads = citedHeads(input.accountHeads, 'accountHeads');
|
|
76
|
-
const governanceFloor = citedHeads(input.governanceFloor, 'governanceFloor');
|
|
77
|
-
const kind = kindByte(input.op.kind);
|
|
78
|
-
const signer = await resolveSigner(input.deviceSecret, input.signer, 'deviceSecret');
|
|
79
|
-
const deviceKey = fromHex(signer.publicKey, 'signer.publicKey', 32);
|
|
80
|
-
const opHash = await governanceOpHash(input.op);
|
|
81
|
-
const preimage = await governanceWarrantPreimage({
|
|
82
|
-
scope,
|
|
83
|
-
kind: input.op.kind,
|
|
84
|
-
authorAccount,
|
|
85
|
-
deviceKey,
|
|
86
|
-
executor,
|
|
87
|
-
executorKey,
|
|
88
|
-
opHash,
|
|
89
|
-
accountHeads,
|
|
90
|
-
governanceFloor,
|
|
91
|
-
nonce: input.nonce,
|
|
92
|
-
notAfter: input.notAfter,
|
|
93
|
-
});
|
|
94
|
-
const signature = checkedSignature(await signer.sign(preimage));
|
|
95
|
-
return hex(concat(scope, new Uint8Array([kind]), authorAccount, deviceKey, executor, executorKey, opHash, u32le(accountHeads.length), ...accountHeads, u32le(governanceFloor.length), ...governanceFloor, u64le(input.nonce), u64le(input.notAfter), signature));
|
|
96
|
-
}
|
|
97
|
-
/**
|
|
98
|
-
* Decode a hex governance warrant back into its fields.
|
|
99
|
-
*
|
|
100
|
-
* Refuses truncated or trailing bytes and an unknown kind. Does not verify the
|
|
101
|
-
* signature.
|
|
102
|
-
*/
|
|
103
|
-
export function parseGovernanceWarrant(warrant) {
|
|
104
|
-
const clean = warrant.trim();
|
|
105
|
-
if (!/^[0-9a-fA-F]*$/.test(clean) || clean.length % 2 !== 0) {
|
|
106
|
-
throw new Error('governance warrant must be an even number of hex characters');
|
|
107
|
-
}
|
|
108
|
-
const bytes = new Uint8Array(clean.length / 2);
|
|
109
|
-
for (let i = 0; i < bytes.length; i += 1) {
|
|
110
|
-
bytes[i] = Number.parseInt(clean.slice(i * 2, i * 2 + 2), 16);
|
|
111
|
-
}
|
|
112
|
-
const view = new DataView(bytes.buffer);
|
|
113
|
-
let o = 0;
|
|
114
|
-
const take = (n, what) => {
|
|
115
|
-
if (o + n > bytes.length) {
|
|
116
|
-
throw new Error(`governance warrant ends inside ${what} (${bytes.length} bytes)`);
|
|
117
|
-
}
|
|
118
|
-
const out = bytes.subarray(o, o + n);
|
|
119
|
-
o += n;
|
|
120
|
-
return out;
|
|
121
|
-
};
|
|
122
|
-
const u32 = (what) => view.getUint32(take(4, what).byteOffset, true);
|
|
123
|
-
const u64 = (what) => view.getBigUint64(take(8, what).byteOffset, true);
|
|
124
|
-
const heads = (what) => {
|
|
125
|
-
const count = u32(what);
|
|
126
|
-
if (count > 64) {
|
|
127
|
-
throw new Error(`${what} cites ${count} heads, over the 64 a node accepts`);
|
|
128
|
-
}
|
|
129
|
-
return Array.from({ length: count }, () => hex(take(32, what)));
|
|
130
|
-
};
|
|
131
|
-
const kind = () => {
|
|
132
|
-
const byte = take(1, 'kind')[0];
|
|
133
|
-
if (byte === 0)
|
|
134
|
-
return 'group';
|
|
135
|
-
if (byte === 1)
|
|
136
|
-
return 'root';
|
|
137
|
-
throw new Error(`governance warrant has kind ${byte}, expected 0 (group) or 1 (root)`);
|
|
138
|
-
};
|
|
139
|
-
const fields = {
|
|
140
|
-
scope: hex(take(32, 'scope')),
|
|
141
|
-
kind: kind(),
|
|
142
|
-
authorAccount: hex(take(32, 'authorAccount')),
|
|
143
|
-
deviceKey: hex(take(32, 'deviceKey')),
|
|
144
|
-
executor: hex(take(32, 'executor')),
|
|
145
|
-
executorKey: hex(take(32, 'executorKey')),
|
|
146
|
-
opHash: hex(take(32, 'opHash')),
|
|
147
|
-
accountHeads: heads('accountHeads'),
|
|
148
|
-
governanceFloor: heads('governanceFloor'),
|
|
149
|
-
nonce: u64('nonce'),
|
|
150
|
-
notAfter: u64('notAfter'),
|
|
151
|
-
signature: hex(take(64, 'signature')),
|
|
152
|
-
};
|
|
153
|
-
if (o !== bytes.length) {
|
|
154
|
-
throw new Error(`governance warrant is ${bytes.length} bytes but its fields account for ${o}`);
|
|
155
|
-
}
|
|
156
|
-
return fields;
|
|
157
|
-
}
|
|
158
|
-
function kindByte(kind) {
|
|
159
|
-
const byte = KIND_BYTE[kind];
|
|
160
|
-
if (byte === undefined) {
|
|
161
|
-
throw new Error(`op kind must be 'group' or 'root', got ${String(kind)}`);
|
|
162
|
-
}
|
|
163
|
-
return byte;
|
|
164
|
-
}
|
package/dist/warrant/index.d.ts
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
export { signWarrant, intentHash } from './warrant.js';
|
|
2
|
-
export type { WarrantInput } from './warrant.js';
|
|
3
|
-
export { signCreationWarrant, parseCreationWarrant, creationInitHash, } from './creation-warrant.js';
|
|
4
|
-
export type { CreationWarrantInput, CreationWarrantFields, SignedCreationWarrant, } from './creation-warrant.js';
|
|
5
|
-
export { signGovernanceWarrant, parseGovernanceWarrant, governanceOpHash, } from './governance-warrant.js';
|
|
6
|
-
export type { GovernanceWarrantInput, GovernanceWarrantFields, } from './governance-warrant.js';
|
|
7
|
-
export { memberAddedOp, memberRemovedOp, memberLeftOp, memberRoleSetOp, groupCreatedOp, createdSubgroupId, subgroupCreation, groupReparentedOp, groupDeletedOp, memberCapabilitySetOp, contextDetachedOp, subgroupVisibilitySetOp, groupMetadataSetOp, memberMetadataSetOp, contextMetadataSetOp, contextCapabilityGrantedOp, contextCapabilityRevokedOp, memberJoinedOpenOp, namespaceCreatedOp, defaultCapabilitiesSetOp, targetApplicationSetOp, foundedNamespaceId, } from './governance-op.js';
|
|
8
|
-
export type { GovernanceOp, GovernanceOpKind, GovernanceMemberRole, GroupCreatedInput, SubgroupCreation, NamespaceCreatedInput, } from './governance-op.js';
|
package/dist/warrant/index.js
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
// Warrant signing — the author's half of delegated authorship
|
|
2
|
-
export { signWarrant, intentHash } from './warrant.js';
|
|
3
|
-
export { signCreationWarrant, parseCreationWarrant, creationInitHash, } from './creation-warrant.js';
|
|
4
|
-
export { signGovernanceWarrant, parseGovernanceWarrant, governanceOpHash, } from './governance-warrant.js';
|
|
5
|
-
export { memberAddedOp, memberRemovedOp, memberLeftOp, memberRoleSetOp, groupCreatedOp, createdSubgroupId, subgroupCreation, groupReparentedOp, groupDeletedOp, memberCapabilitySetOp, contextDetachedOp, subgroupVisibilitySetOp, groupMetadataSetOp, memberMetadataSetOp, contextMetadataSetOp, contextCapabilityGrantedOp, contextCapabilityRevokedOp, memberJoinedOpenOp, namespaceCreatedOp, defaultCapabilitiesSetOp, targetApplicationSetOp, foundedNamespaceId, } from './governance-op.js';
|