ox 0.14.37 → 0.14.39
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/CHANGELOG.md +29 -0
- package/_cjs/tempo/MultisigConfig.js +71 -15
- package/_cjs/tempo/MultisigConfig.js.map +1 -1
- package/_cjs/tempo/MultisigOperation.js +241 -19
- package/_cjs/tempo/MultisigOperation.js.map +1 -1
- package/_cjs/tempo/SignatureEnvelope.js +71 -108
- package/_cjs/tempo/SignatureEnvelope.js.map +1 -1
- package/_cjs/version.js +1 -1
- package/_esm/tempo/MultisigConfig.js +140 -71
- package/_esm/tempo/MultisigConfig.js.map +1 -1
- package/_esm/tempo/MultisigOperation.js +331 -19
- package/_esm/tempo/MultisigOperation.js.map +1 -1
- package/_esm/tempo/SignatureEnvelope.js +84 -143
- package/_esm/tempo/SignatureEnvelope.js.map +1 -1
- package/_esm/tempo/index.js +2 -2
- package/_esm/version.js +1 -1
- package/_types/tempo/KeyAuthorization.d.ts +1 -1
- package/_types/tempo/KeyAuthorization.d.ts.map +1 -1
- package/_types/tempo/MultisigConfig.d.ts +137 -73
- package/_types/tempo/MultisigConfig.d.ts.map +1 -1
- package/_types/tempo/MultisigOperation.d.ts +169 -1
- package/_types/tempo/MultisigOperation.d.ts.map +1 -1
- package/_types/tempo/SignatureEnvelope.d.ts +81 -75
- package/_types/tempo/SignatureEnvelope.d.ts.map +1 -1
- package/_types/tempo/index.d.ts +2 -2
- package/_types/version.d.ts +1 -1
- package/package.json +6 -1
- package/tempo/KeyAuthorization.test-d.ts +12 -0
- package/tempo/KeyAuthorization.test.ts +54 -0
- package/tempo/KeyAuthorization.ts +1 -1
- package/tempo/MultisigConfig.test-d/package.json +6 -0
- package/tempo/MultisigConfig.test-d.ts +52 -0
- package/tempo/MultisigConfig.test.ts +291 -263
- package/tempo/MultisigConfig.ts +250 -95
- package/tempo/MultisigOperation.test-d.ts +25 -0
- package/tempo/MultisigOperation.test.ts +754 -22
- package/tempo/MultisigOperation.ts +548 -17
- package/tempo/SignatureEnvelope.test-d.ts +57 -40
- package/tempo/SignatureEnvelope.test.ts +523 -480
- package/tempo/SignatureEnvelope.ts +250 -257
- package/tempo/index.ts +2 -2
- package/tempo/multisig.e2e.test.ts +113 -86
- package/version.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# ox
|
|
2
2
|
|
|
3
|
+
## 0.14.39
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#412](https://github.com/wevm/ox/pull/412) [`200d9a7`](https://github.com/wevm/ox/commit/200d9a7832b676880d296b5b03fe1dde548b9f37) Thanks [@jxom](https://github.com/jxom)! - Changed native multisig configuration and signature APIs to require complete TIP-1061 witnesses with a `version`.
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
const config = MultisigConfig.from({
|
|
11
|
+
owners,
|
|
12
|
+
threshold: 2,
|
|
13
|
+
version: 1,
|
|
14
|
+
});
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## 0.14.38
|
|
18
|
+
|
|
19
|
+
### Patch Changes
|
|
20
|
+
|
|
21
|
+
- [#409](https://github.com/wevm/ox/pull/409) [`8f25490`](https://github.com/wevm/ox/commit/8f254906699e008e3d70ca172f27afda1a8e269a) Thanks [@jxom](https://github.com/jxom)! - Added multisig operation hashing, approval selection, and transaction serialization helpers.
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import { MultisigOperation } from "ox/tempo";
|
|
25
|
+
|
|
26
|
+
const selection = await MultisigOperation.selectApprovals(options);
|
|
27
|
+
const transaction = MultisigOperation.serializeTransaction(operation, {
|
|
28
|
+
approvals: selection.selectedApprovals,
|
|
29
|
+
});
|
|
30
|
+
```
|
|
31
|
+
|
|
3
32
|
## 0.14.37
|
|
4
33
|
|
|
5
34
|
### Patch Changes
|
|
@@ -1,30 +1,36 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.InvalidConfigError = exports.zeroSalt = exports.signatureTypeByte = exports.
|
|
3
|
+
exports.InvalidConfigError = exports.zeroSalt = exports.signatureTypeByte = exports.maxVersion = exports.maxThreshold = exports.maxSignatures = exports.maxOwners = exports.maxOwnerSignatureBytes = exports.maxNestingDepth = void 0;
|
|
4
4
|
exports.assert = assert;
|
|
5
5
|
exports.from = from;
|
|
6
|
+
exports.fromRpc = fromRpc;
|
|
6
7
|
exports.fromTuple = fromTuple;
|
|
7
8
|
exports.getAddress = getAddress;
|
|
9
|
+
exports.getCommitment = getCommitment;
|
|
8
10
|
exports.getSignPayload = getSignPayload;
|
|
11
|
+
exports.toRpc = toRpc;
|
|
9
12
|
exports.toTuple = toTuple;
|
|
10
13
|
exports.validate = validate;
|
|
11
14
|
const Address = require("../core/Address.js");
|
|
12
15
|
const Errors = require("../core/Errors.js");
|
|
13
16
|
const Hash = require("../core/Hash.js");
|
|
14
17
|
const Hex = require("../core/Hex.js");
|
|
15
|
-
exports.maxOwners = 48;
|
|
16
|
-
exports.maxThreshold = 0xff;
|
|
17
|
-
exports.maxSignatures = 8;
|
|
18
18
|
exports.maxNestingDepth = 2;
|
|
19
19
|
exports.maxOwnerSignatureBytes = 2049;
|
|
20
|
+
exports.maxOwners = 48;
|
|
21
|
+
exports.maxSignatures = 8;
|
|
22
|
+
exports.maxThreshold = 0xff;
|
|
23
|
+
exports.maxVersion = 2n ** 64n - 1n;
|
|
20
24
|
exports.signatureTypeByte = '0x05';
|
|
21
25
|
exports.zeroSalt = `0x${'00'.repeat(32)}`;
|
|
22
26
|
const accountDomain = 'tempo:multisig:account';
|
|
27
|
+
const configDomain = 'tempo:multisig:config';
|
|
23
28
|
const signatureDomain = 'tempo:multisig:signature';
|
|
24
29
|
function assert(config) {
|
|
25
|
-
const { salt, threshold,
|
|
30
|
+
const { owners, salt, threshold, version = 0n } = config;
|
|
26
31
|
if (typeof salt !== 'undefined' && Hex.size(salt) !== 32)
|
|
27
32
|
throw new InvalidConfigError({ reason: 'salt must be 32 bytes' });
|
|
33
|
+
assertVersion(version);
|
|
28
34
|
if (owners.length === 0)
|
|
29
35
|
throw new InvalidConfigError({ reason: 'owners cannot be empty' });
|
|
30
36
|
if (owners.length > exports.maxOwners)
|
|
@@ -75,20 +81,27 @@ function assert(config) {
|
|
|
75
81
|
});
|
|
76
82
|
}
|
|
77
83
|
function from(config) {
|
|
84
|
+
const version = config.version ?? 0n;
|
|
85
|
+
assertVersion(version);
|
|
78
86
|
const owners = [...config.owners].sort((a, b) => Hex.toBigInt(a.owner) < Hex.toBigInt(b.owner) ? -1 : 1);
|
|
79
87
|
const normalized = {
|
|
88
|
+
owners,
|
|
80
89
|
salt: config.salt ? Hex.padLeft(config.salt, 32) : exports.zeroSalt,
|
|
81
90
|
threshold: config.threshold,
|
|
82
|
-
|
|
91
|
+
version: BigInt(version),
|
|
83
92
|
};
|
|
84
93
|
assert(normalized);
|
|
85
94
|
return normalized;
|
|
86
95
|
}
|
|
96
|
+
function fromRpc(config) {
|
|
97
|
+
return from({
|
|
98
|
+
...config,
|
|
99
|
+
version: Hex.toBigInt(config.version),
|
|
100
|
+
});
|
|
101
|
+
}
|
|
87
102
|
function fromTuple(tuple) {
|
|
88
|
-
const [salt, threshold, owners] = tuple;
|
|
103
|
+
const [salt, version, threshold, owners] = tuple;
|
|
89
104
|
return {
|
|
90
|
-
salt: salt && salt !== '0x' ? Hex.padLeft(salt, 32) : exports.zeroSalt,
|
|
91
|
-
threshold: threshold === '0x' ? 0 : Hex.toNumber(threshold),
|
|
92
105
|
owners: owners.map((owner) => {
|
|
93
106
|
const [ownerAddress, weight] = owner;
|
|
94
107
|
return {
|
|
@@ -96,10 +109,17 @@ function fromTuple(tuple) {
|
|
|
96
109
|
weight: !weight || weight === '0x' ? 0 : Hex.toNumber(weight),
|
|
97
110
|
};
|
|
98
111
|
}),
|
|
112
|
+
salt: salt && salt !== '0x' ? Hex.padLeft(salt, 32) : exports.zeroSalt,
|
|
113
|
+
threshold: threshold === '0x' ? 0 : Hex.toNumber(threshold),
|
|
114
|
+
version: version === '0x' ? 0n : Hex.toBigInt(version),
|
|
99
115
|
};
|
|
100
116
|
}
|
|
101
117
|
function getAddress(config) {
|
|
102
118
|
assert(config);
|
|
119
|
+
if (BigInt(config.version ?? 0) !== 0n)
|
|
120
|
+
throw new InvalidConfigError({
|
|
121
|
+
reason: 'account address requires version zero',
|
|
122
|
+
});
|
|
103
123
|
const hash = Hash.keccak256(Hex.concat(Hex.fromString(accountDomain), Hex.padLeft(config.salt ?? exports.zeroSalt, 32), Hex.fromNumber(config.threshold, { size: 1 }), Hex.fromNumber(config.owners.length, { size: 1 }), ...config.owners.flatMap((owner) => [
|
|
104
124
|
owner.owner,
|
|
105
125
|
Hex.fromNumber(owner.weight, { size: 1 }),
|
|
@@ -107,20 +127,47 @@ function getAddress(config) {
|
|
|
107
127
|
const account = Address.from(Hex.slice(hash, 12, 32));
|
|
108
128
|
if (Hex.toBigInt(account) === 0n)
|
|
109
129
|
throw new InvalidConfigError({ reason: 'derived account cannot be zero' });
|
|
130
|
+
if (config.owners.some((owner) => Address.isEqual(owner.owner, account)))
|
|
131
|
+
throw new InvalidConfigError({
|
|
132
|
+
reason: 'derived account cannot be an owner',
|
|
133
|
+
});
|
|
110
134
|
return account;
|
|
111
135
|
}
|
|
136
|
+
function getCommitment(config) {
|
|
137
|
+
assert(config);
|
|
138
|
+
return Hash.keccak256(Hex.concat(Hex.fromString(configDomain), Hex.padLeft(config.salt ?? exports.zeroSalt, 32), Hex.fromNumber(config.version ?? 0n, { size: 8 }), Hex.fromNumber(config.threshold, { size: 1 }), Hex.fromNumber(config.owners.length, { size: 1 }), ...config.owners.flatMap((owner) => [
|
|
139
|
+
owner.owner,
|
|
140
|
+
Hex.fromNumber(owner.weight, { size: 1 }),
|
|
141
|
+
])));
|
|
142
|
+
}
|
|
112
143
|
function getSignPayload(value) {
|
|
113
|
-
const {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
144
|
+
const { account, config, payload } = value;
|
|
145
|
+
assertVersion(config.version);
|
|
146
|
+
return Hash.keccak256(Hex.concat(Hex.fromString(signatureDomain), Hex.from(payload), account, Hex.fromNumber(config.version ?? 0n, { size: 8 })));
|
|
147
|
+
}
|
|
148
|
+
function toRpc(config) {
|
|
149
|
+
const value = from(config);
|
|
150
|
+
return {
|
|
151
|
+
owners: value.owners.map((owner) => ({
|
|
152
|
+
owner: owner.owner,
|
|
153
|
+
weight: Number(owner.weight),
|
|
154
|
+
})),
|
|
155
|
+
salt: value.salt,
|
|
156
|
+
threshold: Number(value.threshold),
|
|
157
|
+
version: Hex.fromNumber(value.version),
|
|
158
|
+
};
|
|
118
159
|
}
|
|
119
160
|
function toTuple(config) {
|
|
120
161
|
assert(config);
|
|
121
162
|
const owners = config.owners.map((owner) => [owner.owner, Hex.fromNumber(owner.weight)]);
|
|
122
163
|
const salt = config.salt ? Hex.padLeft(config.salt, 32) : exports.zeroSalt;
|
|
123
|
-
|
|
164
|
+
const version = BigInt(config.version ?? 0);
|
|
165
|
+
return [
|
|
166
|
+
salt,
|
|
167
|
+
version === 0n ? '0x' : Hex.fromNumber(version),
|
|
168
|
+
Hex.fromNumber(config.threshold),
|
|
169
|
+
owners,
|
|
170
|
+
];
|
|
124
171
|
}
|
|
125
172
|
function validate(config) {
|
|
126
173
|
try {
|
|
@@ -143,4 +190,13 @@ class InvalidConfigError extends Errors.BaseError {
|
|
|
143
190
|
}
|
|
144
191
|
}
|
|
145
192
|
exports.InvalidConfigError = InvalidConfigError;
|
|
193
|
+
function assertVersion(version) {
|
|
194
|
+
if ((typeof version !== 'bigint' && typeof version !== 'number') ||
|
|
195
|
+
(typeof version === 'number' && !Number.isSafeInteger(version)) ||
|
|
196
|
+
BigInt(version) < 0n ||
|
|
197
|
+
BigInt(version) > exports.maxVersion)
|
|
198
|
+
throw new InvalidConfigError({
|
|
199
|
+
reason: 'version must be an unsigned 64-bit integer',
|
|
200
|
+
});
|
|
201
|
+
}
|
|
146
202
|
//# sourceMappingURL=MultisigConfig.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MultisigConfig.js","sourceRoot":"","sources":["../../tempo/MultisigConfig.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"MultisigConfig.js","sourceRoot":"","sources":["../../tempo/MultisigConfig.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.InvalidOperationError = void 0;
|
|
3
|
+
exports.InvalidOperationError = exports.InvalidApprovalError = void 0;
|
|
4
|
+
exports.getHash = getHash;
|
|
5
|
+
exports.selectApprovals = selectApprovals;
|
|
6
|
+
exports.serializeTransaction = serializeTransaction;
|
|
4
7
|
exports.from = from;
|
|
5
8
|
exports.fromRpc = fromRpc;
|
|
6
9
|
exports.toRpc = toRpc;
|
|
@@ -13,22 +16,75 @@ const MultisigConfig = require("./MultisigConfig.js");
|
|
|
13
16
|
const SignatureEnvelope = require("./SignatureEnvelope.js");
|
|
14
17
|
const TxEnvelopeTempo = require("./TxEnvelopeTempo.js");
|
|
15
18
|
const maxConfigVersion = 2n ** 64n - 1n;
|
|
19
|
+
function getHash(options) {
|
|
20
|
+
const { account, configVersion } = options;
|
|
21
|
+
const payload = options.type === 'transaction'
|
|
22
|
+
? TxEnvelopeTempo.getSignPayload(TxEnvelopeTempo.deserialize(options.transaction))
|
|
23
|
+
: KeyAuthorization_.getSignPayload(KeyAuthorization_.deserialize(options.keyAuthorization));
|
|
24
|
+
return MultisigConfig.getSignPayload({
|
|
25
|
+
account,
|
|
26
|
+
config: { version: configVersion },
|
|
27
|
+
payload,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
async function selectApprovals(options) {
|
|
31
|
+
const { account, approvals, hash, resolveConfig } = options;
|
|
32
|
+
if (!Address.validate(account) || Hex.toBigInt(account) === 0n)
|
|
33
|
+
throw new InvalidApprovalError({ reason: 'account is invalid' });
|
|
34
|
+
if (!Hash.validate(hash))
|
|
35
|
+
throw new InvalidApprovalError({ reason: 'hash is invalid' });
|
|
36
|
+
return selectApprovals_internal({
|
|
37
|
+
account,
|
|
38
|
+
approvals,
|
|
39
|
+
config: MultisigConfig.from(options.config),
|
|
40
|
+
hash,
|
|
41
|
+
resolveConfig,
|
|
42
|
+
}, [account.toLowerCase()]);
|
|
43
|
+
}
|
|
44
|
+
function serializeTransaction(operation, options) {
|
|
45
|
+
const value = from(operation);
|
|
46
|
+
const envelope = TxEnvelopeTempo.deserialize(value.transaction);
|
|
47
|
+
const approvals = options.approvals.map((approval) => SignatureEnvelope.from(approval));
|
|
48
|
+
assertRetainedApprovals(value, approvals);
|
|
49
|
+
const signatures = SignatureEnvelope.sortMultisigApprovals({
|
|
50
|
+
account: value.account,
|
|
51
|
+
config: value.config,
|
|
52
|
+
payload: TxEnvelopeTempo.getSignPayload(envelope),
|
|
53
|
+
signatures: approvals,
|
|
54
|
+
});
|
|
55
|
+
const signature = SignatureEnvelope.from({
|
|
56
|
+
account: value.account,
|
|
57
|
+
config: value.config,
|
|
58
|
+
signatures,
|
|
59
|
+
});
|
|
60
|
+
return TxEnvelopeTempo.serialize(envelope, value.transaction.startsWith(TxEnvelopeTempo.feePayerMagic)
|
|
61
|
+
? {
|
|
62
|
+
format: 'feePayer',
|
|
63
|
+
sender: envelope.from,
|
|
64
|
+
signature,
|
|
65
|
+
}
|
|
66
|
+
: { signature });
|
|
67
|
+
}
|
|
16
68
|
function from(operation) {
|
|
17
69
|
try {
|
|
18
|
-
const config = MultisigConfig.from(
|
|
70
|
+
const config = MultisigConfig.from({
|
|
71
|
+
...operation.config,
|
|
72
|
+
version: operation.configVersion,
|
|
73
|
+
});
|
|
19
74
|
if (typeof config.threshold !== 'number' ||
|
|
20
75
|
config.owners.some((owner) => typeof owner.weight !== 'number'))
|
|
21
76
|
throw new InvalidOperationError({
|
|
22
77
|
reason: 'config threshold and owner weights must be numbers',
|
|
23
78
|
});
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
79
|
+
const value = { ...operation, config };
|
|
80
|
+
assertBase(value, config);
|
|
81
|
+
if (value.type === 'transaction')
|
|
82
|
+
assertTransaction(value);
|
|
83
|
+
else if (value.type === 'keyAuthorization')
|
|
84
|
+
assertKeyAuthorization(value, config);
|
|
29
85
|
else
|
|
30
86
|
throw new InvalidOperationError({ reason: 'unknown operation type' });
|
|
31
|
-
return
|
|
87
|
+
return value;
|
|
32
88
|
}
|
|
33
89
|
catch (cause) {
|
|
34
90
|
if (cause instanceof InvalidOperationError)
|
|
@@ -48,7 +104,11 @@ function fromRpc(operation) {
|
|
|
48
104
|
throw new InvalidOperationError({
|
|
49
105
|
reason: 'configVersion must use canonical quantity encoding',
|
|
50
106
|
});
|
|
51
|
-
return from({
|
|
107
|
+
return from({
|
|
108
|
+
...operation,
|
|
109
|
+
config: MultisigConfig.fromRpc(operation.config),
|
|
110
|
+
configVersion,
|
|
111
|
+
});
|
|
52
112
|
}
|
|
53
113
|
catch (cause) {
|
|
54
114
|
if (cause instanceof InvalidOperationError)
|
|
@@ -60,9 +120,146 @@ function toRpc(operation) {
|
|
|
60
120
|
const value = from(operation);
|
|
61
121
|
return {
|
|
62
122
|
...value,
|
|
123
|
+
config: MultisigConfig.toRpc(value.config),
|
|
63
124
|
configVersion: Hex.fromNumber(value.configVersion),
|
|
64
125
|
};
|
|
65
126
|
}
|
|
127
|
+
async function selectApprovals_internal(options, path) {
|
|
128
|
+
const owners = new Map(options.config.owners.map((owner) => [
|
|
129
|
+
owner.owner.toLowerCase(),
|
|
130
|
+
{ address: owner.owner, weight: Number(owner.weight) },
|
|
131
|
+
]));
|
|
132
|
+
const groups = new Map();
|
|
133
|
+
for (const serialized of options.approvals) {
|
|
134
|
+
const signature = SignatureEnvelope.from(serialized);
|
|
135
|
+
if (signature.type === 'keychain')
|
|
136
|
+
throw new InvalidApprovalError({
|
|
137
|
+
reason: 'keychain signatures cannot approve a multisig operation',
|
|
138
|
+
});
|
|
139
|
+
const address = signature.type === 'multisig'
|
|
140
|
+
? signature.account
|
|
141
|
+
: SignatureEnvelope.extractAddress({
|
|
142
|
+
payload: options.hash,
|
|
143
|
+
signature,
|
|
144
|
+
});
|
|
145
|
+
const owner = owners.get(address.toLowerCase());
|
|
146
|
+
if (!owner)
|
|
147
|
+
throw new InvalidApprovalError({
|
|
148
|
+
reason: `signature is from non-owner ${address}`,
|
|
149
|
+
});
|
|
150
|
+
const key = address.toLowerCase();
|
|
151
|
+
const group = groups.get(key);
|
|
152
|
+
if (group)
|
|
153
|
+
group.signatures.push(signature);
|
|
154
|
+
else
|
|
155
|
+
groups.set(key, {
|
|
156
|
+
address: owner.address,
|
|
157
|
+
signatures: [signature],
|
|
158
|
+
weight: owner.weight,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
const valid = [];
|
|
162
|
+
const retained = [];
|
|
163
|
+
for (const group of groups.values()) {
|
|
164
|
+
const nested = group.signatures.filter((signature) => signature.type === 'multisig');
|
|
165
|
+
if (nested.length > 0) {
|
|
166
|
+
if (nested.length !== group.signatures.length)
|
|
167
|
+
throw new InvalidApprovalError({
|
|
168
|
+
reason: `owner ${group.address} has conflicting signature types`,
|
|
169
|
+
});
|
|
170
|
+
if (path.length >= MultisigConfig.maxNestingDepth ||
|
|
171
|
+
path.includes(group.address.toLowerCase()))
|
|
172
|
+
throw new InvalidApprovalError({
|
|
173
|
+
reason: `nested multisig owner ${group.address} is invalid`,
|
|
174
|
+
});
|
|
175
|
+
if (!options.resolveConfig)
|
|
176
|
+
throw new InvalidApprovalError({
|
|
177
|
+
reason: `nested multisig owner ${group.address} requires a config resolver`,
|
|
178
|
+
});
|
|
179
|
+
const resolved = await options.resolveConfig({ account: group.address });
|
|
180
|
+
const config = MultisigConfig.from({
|
|
181
|
+
...resolved.config,
|
|
182
|
+
version: resolved.version,
|
|
183
|
+
});
|
|
184
|
+
const selected = await selectApprovals_internal({
|
|
185
|
+
account: group.address,
|
|
186
|
+
approvals: nested.flatMap((signature) => signature.signatures.map((approval) => SignatureEnvelope.serialize(approval))),
|
|
187
|
+
config,
|
|
188
|
+
hash: MultisigConfig.getSignPayload({
|
|
189
|
+
account: group.address,
|
|
190
|
+
config,
|
|
191
|
+
payload: options.hash,
|
|
192
|
+
}),
|
|
193
|
+
resolveConfig: options.resolveConfig,
|
|
194
|
+
}, [...path, group.address.toLowerCase()]);
|
|
195
|
+
retained.push({
|
|
196
|
+
address: group.address,
|
|
197
|
+
signature: SignatureEnvelope.serialize(SignatureEnvelope.from({
|
|
198
|
+
account: group.address,
|
|
199
|
+
config,
|
|
200
|
+
signatures: selected.approvals.map((approval) => SignatureEnvelope.from(approval)),
|
|
201
|
+
})),
|
|
202
|
+
});
|
|
203
|
+
if (selected.weight >= selected.threshold)
|
|
204
|
+
valid.push({
|
|
205
|
+
address: group.address,
|
|
206
|
+
signature: SignatureEnvelope.serialize(SignatureEnvelope.from({
|
|
207
|
+
account: group.address,
|
|
208
|
+
config,
|
|
209
|
+
signatures: selected.selectedApprovals.map((approval) => SignatureEnvelope.from(approval)),
|
|
210
|
+
})),
|
|
211
|
+
weight: group.weight,
|
|
212
|
+
});
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
const signatures = group.signatures.map((signature) => {
|
|
216
|
+
if (!SignatureEnvelope.verify(signature, {
|
|
217
|
+
address: group.address,
|
|
218
|
+
payload: options.hash,
|
|
219
|
+
}))
|
|
220
|
+
throw new InvalidApprovalError({
|
|
221
|
+
reason: `signature from owner ${group.address} is invalid`,
|
|
222
|
+
});
|
|
223
|
+
return SignatureEnvelope.serialize(signature);
|
|
224
|
+
});
|
|
225
|
+
const signature = signatures.sort(compareHex)[0];
|
|
226
|
+
valid.push({
|
|
227
|
+
address: group.address,
|
|
228
|
+
signature,
|
|
229
|
+
weight: group.weight,
|
|
230
|
+
});
|
|
231
|
+
retained.push({ address: group.address, signature });
|
|
232
|
+
}
|
|
233
|
+
const ranked = valid.sort((a, b) => b.weight - a.weight || compareApprovalAddress(a, b));
|
|
234
|
+
const selected = [];
|
|
235
|
+
let weight = 0;
|
|
236
|
+
for (const approval of ranked.slice(0, MultisigConfig.maxSignatures)) {
|
|
237
|
+
if (weight >= Number(options.config.threshold))
|
|
238
|
+
break;
|
|
239
|
+
selected.push(approval);
|
|
240
|
+
weight += approval.weight;
|
|
241
|
+
}
|
|
242
|
+
selected.sort(compareApprovalAddress);
|
|
243
|
+
return {
|
|
244
|
+
approvals: retained
|
|
245
|
+
.sort(compareApprovalAddress)
|
|
246
|
+
.map((approval) => approval.signature),
|
|
247
|
+
selectedApprovals: selected.map((approval) => approval.signature),
|
|
248
|
+
signatureCount: selected.length,
|
|
249
|
+
threshold: Number(options.config.threshold),
|
|
250
|
+
weight,
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
function compareApprovalAddress(a, b) {
|
|
254
|
+
const addressA = Hex.toBigInt(a.address);
|
|
255
|
+
const addressB = Hex.toBigInt(b.address);
|
|
256
|
+
return addressA < addressB ? -1 : addressA > addressB ? 1 : 0;
|
|
257
|
+
}
|
|
258
|
+
function compareHex(a, b) {
|
|
259
|
+
const hexA = a.toLowerCase();
|
|
260
|
+
const hexB = b.toLowerCase();
|
|
261
|
+
return hexA < hexB ? -1 : hexA > hexB ? 1 : 0;
|
|
262
|
+
}
|
|
66
263
|
function assertBase(operation, config) {
|
|
67
264
|
if (!Address.validate(operation.account))
|
|
68
265
|
throw new InvalidOperationError({ reason: 'account is invalid' });
|
|
@@ -117,7 +314,7 @@ function assertBase(operation, config) {
|
|
|
117
314
|
if (typeof approval !== 'string' ||
|
|
118
315
|
!Hex.validate(approval, { strict: true }))
|
|
119
316
|
throw new InvalidOperationError({ reason: 'approval is invalid' });
|
|
120
|
-
const signature = assertApproval(operation.account, approval);
|
|
317
|
+
const signature = assertApproval(operation.account, approval, config);
|
|
121
318
|
const address = SignatureEnvelope.extractAddress({
|
|
122
319
|
payload: operation.hash,
|
|
123
320
|
signature,
|
|
@@ -271,13 +468,9 @@ function assertKeyAuthorization(operation, config) {
|
|
|
271
468
|
reason: 'key authorization signatureCount does not match its signature',
|
|
272
469
|
});
|
|
273
470
|
assertSelectedApprovals(operation, signature.signatures, authorization);
|
|
274
|
-
if (
|
|
275
|
-
throw new InvalidOperationError({
|
|
276
|
-
reason: 'key authorization bootstrap state does not match',
|
|
277
|
-
});
|
|
278
|
-
if (signature.init && !sameConfig(signature.init, config))
|
|
471
|
+
if (!sameConfig(signature.config, config))
|
|
279
472
|
throw new InvalidOperationError({
|
|
280
|
-
reason: 'key authorization
|
|
473
|
+
reason: 'key authorization config does not match',
|
|
281
474
|
});
|
|
282
475
|
}
|
|
283
476
|
assertOperationHash(operation, KeyAuthorization_.getSignPayload(authorization));
|
|
@@ -287,10 +480,11 @@ function assertKeyAuthorization(operation, config) {
|
|
|
287
480
|
reason: 'keyAuthorization is not canonically serialized',
|
|
288
481
|
});
|
|
289
482
|
}
|
|
290
|
-
function assertApproval(account, serialized) {
|
|
483
|
+
function assertApproval(account, serialized, config) {
|
|
291
484
|
const approval = SignatureEnvelope.deserialize(serialized);
|
|
292
485
|
SignatureEnvelope.assert({
|
|
293
486
|
account,
|
|
487
|
+
config,
|
|
294
488
|
signatures: [approval],
|
|
295
489
|
type: 'multisig',
|
|
296
490
|
});
|
|
@@ -299,6 +493,17 @@ function assertApproval(account, serialized) {
|
|
|
299
493
|
throw new InvalidOperationError({ reason: 'approval is not canonical' });
|
|
300
494
|
return approval;
|
|
301
495
|
}
|
|
496
|
+
function assertRetainedApprovals(operation, selected) {
|
|
497
|
+
const retained = operation.approvals.map((approval) => SignatureEnvelope.deserialize(approval));
|
|
498
|
+
for (const approval of selected) {
|
|
499
|
+
const index = retained.findIndex((candidate) => includesApproval(candidate, approval));
|
|
500
|
+
if (index === -1)
|
|
501
|
+
throw new InvalidOperationError({
|
|
502
|
+
reason: 'transaction signature is not a retained approval',
|
|
503
|
+
});
|
|
504
|
+
retained.splice(index, 1);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
302
507
|
function assertSelectedApprovals(operation, selected, authorization) {
|
|
303
508
|
const retained = operation.approvals.map((approval) => SignatureEnvelope.deserialize(approval));
|
|
304
509
|
for (const approval of selected) {
|
|
@@ -311,8 +516,8 @@ function assertSelectedApprovals(operation, selected, authorization) {
|
|
|
311
516
|
}
|
|
312
517
|
const digest = MultisigConfig.getSignPayload({
|
|
313
518
|
account: operation.account,
|
|
519
|
+
config: operation.config,
|
|
314
520
|
payload: KeyAuthorization_.getSignPayload(authorization),
|
|
315
|
-
version: operation.configVersion,
|
|
316
521
|
});
|
|
317
522
|
const addresses = selected.map((signature) => SignatureEnvelope.extractAddress({ payload: digest, signature }));
|
|
318
523
|
for (let index = 1; index < addresses.length; index++) {
|
|
@@ -334,6 +539,8 @@ function includesApproval(retained, selected) {
|
|
|
334
539
|
SignatureEnvelope.serialize(selected).toLowerCase());
|
|
335
540
|
if (retained.account.toLowerCase() !== selected.account.toLowerCase())
|
|
336
541
|
return false;
|
|
542
|
+
if (!sameConfig(retained.config, selected.config))
|
|
543
|
+
return false;
|
|
337
544
|
let index = 0;
|
|
338
545
|
for (const approval of selected.signatures) {
|
|
339
546
|
while (index < retained.signatures.length &&
|
|
@@ -357,8 +564,8 @@ function isWeightReachable(weights, signatureCount, weight) {
|
|
|
357
564
|
function assertOperationHash(operation, payload) {
|
|
358
565
|
const hash = MultisigConfig.getSignPayload({
|
|
359
566
|
account: operation.account,
|
|
567
|
+
config: operation.config,
|
|
360
568
|
payload,
|
|
361
|
-
version: operation.configVersion,
|
|
362
569
|
});
|
|
363
570
|
if (hash.toLowerCase() !== operation.hash.toLowerCase())
|
|
364
571
|
throw new InvalidOperationError({
|
|
@@ -376,6 +583,7 @@ function sameConfig(a, b) {
|
|
|
376
583
|
const configB = MultisigConfig.from(b);
|
|
377
584
|
return (Hex.isEqual(configA.salt ?? MultisigConfig.zeroSalt, configB.salt ?? MultisigConfig.zeroSalt) &&
|
|
378
585
|
configA.threshold === configB.threshold &&
|
|
586
|
+
configA.version === configB.version &&
|
|
379
587
|
configA.owners.length === configB.owners.length &&
|
|
380
588
|
configA.owners.every((owner, index) => {
|
|
381
589
|
const other = configB.owners[index];
|
|
@@ -383,6 +591,20 @@ function sameConfig(a, b) {
|
|
|
383
591
|
owner.weight === other.weight);
|
|
384
592
|
}));
|
|
385
593
|
}
|
|
594
|
+
class InvalidApprovalError extends Errors.BaseError {
|
|
595
|
+
constructor(options = {}) {
|
|
596
|
+
super(options.reason
|
|
597
|
+
? `Invalid multisig approval: ${options.reason}.`
|
|
598
|
+
: 'Invalid multisig approval.', { cause: options.cause });
|
|
599
|
+
Object.defineProperty(this, "name", {
|
|
600
|
+
enumerable: true,
|
|
601
|
+
configurable: true,
|
|
602
|
+
writable: true,
|
|
603
|
+
value: 'MultisigOperation.InvalidApprovalError'
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
exports.InvalidApprovalError = InvalidApprovalError;
|
|
386
608
|
class InvalidOperationError extends Errors.BaseError {
|
|
387
609
|
constructor(options = {}) {
|
|
388
610
|
super(options.reason
|