ox 1.7.1 → 1.7.4
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 +42 -0
- package/dist/core/Hash.js +1 -1
- package/dist/core/Hash.js.map +1 -1
- package/dist/tempo/KeyAuthorization.d.ts +2 -2
- package/dist/tempo/KeyAuthorization.d.ts.map +1 -1
- package/dist/tempo/MultisigConfig.d.ts +151 -83
- package/dist/tempo/MultisigConfig.d.ts.map +1 -1
- package/dist/tempo/MultisigConfig.js +164 -86
- package/dist/tempo/MultisigConfig.js.map +1 -1
- package/dist/tempo/MultisigOperation.d.ts +340 -0
- package/dist/tempo/MultisigOperation.d.ts.map +1 -0
- package/dist/tempo/MultisigOperation.js +839 -0
- package/dist/tempo/MultisigOperation.js.map +1 -0
- package/dist/tempo/MultisigSimulation.d.ts +132 -0
- package/dist/tempo/MultisigSimulation.d.ts.map +1 -0
- package/dist/tempo/MultisigSimulation.js +140 -0
- package/dist/tempo/MultisigSimulation.js.map +1 -0
- package/dist/tempo/RpcSchemaTempo.d.ts +45 -0
- package/dist/tempo/RpcSchemaTempo.d.ts.map +1 -1
- package/dist/tempo/SignatureEnvelope.d.ts +55 -86
- package/dist/tempo/SignatureEnvelope.d.ts.map +1 -1
- package/dist/tempo/SignatureEnvelope.js +93 -158
- package/dist/tempo/SignatureEnvelope.js.map +1 -1
- package/dist/tempo/TransactionRequest.d.ts +9 -17
- package/dist/tempo/TransactionRequest.d.ts.map +1 -1
- package/dist/tempo/TransactionRequest.js +7 -7
- package/dist/tempo/TransactionRequest.js.map +1 -1
- package/dist/tempo/TxEnvelopeTempo.d.ts +3 -2
- package/dist/tempo/TxEnvelopeTempo.d.ts.map +1 -1
- package/dist/tempo/TxEnvelopeTempo.js +3 -2
- package/dist/tempo/TxEnvelopeTempo.js.map +1 -1
- package/dist/tempo/index.d.ts +22 -2
- package/dist/tempo/index.d.ts.map +1 -1
- package/dist/tempo/index.js +22 -2
- package/dist/tempo/index.js.map +1 -1
- package/dist/zod/tempo/AuthorizationTempo.d.ts +60 -180
- package/dist/zod/tempo/AuthorizationTempo.d.ts.map +1 -1
- package/dist/zod/tempo/KeyAuthorization.d.ts +65 -118
- package/dist/zod/tempo/KeyAuthorization.d.ts.map +1 -1
- package/dist/zod/tempo/MultisigConfig.d.ts +31 -2
- package/dist/zod/tempo/MultisigConfig.d.ts.map +1 -1
- package/dist/zod/tempo/MultisigConfig.js +26 -3
- package/dist/zod/tempo/MultisigConfig.js.map +1 -1
- package/dist/zod/tempo/MultisigOperation.d.ts +158 -0
- package/dist/zod/tempo/MultisigOperation.d.ts.map +1 -0
- package/dist/zod/tempo/MultisigOperation.js +62 -0
- package/dist/zod/tempo/MultisigOperation.js.map +1 -0
- package/dist/zod/tempo/MultisigSimulation.d.ts +120 -0
- package/dist/zod/tempo/MultisigSimulation.d.ts.map +1 -0
- package/dist/zod/tempo/MultisigSimulation.js +78 -0
- package/dist/zod/tempo/MultisigSimulation.js.map +1 -0
- package/dist/zod/tempo/RpcSchemaTempo.d.ts +528 -144
- package/dist/zod/tempo/RpcSchemaTempo.d.ts.map +1 -1
- package/dist/zod/tempo/RpcSchemaTempo.js +47 -0
- package/dist/zod/tempo/RpcSchemaTempo.js.map +1 -1
- package/dist/zod/tempo/SignatureEnvelope.d.ts +18 -54
- package/dist/zod/tempo/SignatureEnvelope.d.ts.map +1 -1
- package/dist/zod/tempo/SignatureEnvelope.js +5 -22
- package/dist/zod/tempo/SignatureEnvelope.js.map +1 -1
- package/dist/zod/tempo/Transaction.d.ts +138 -348
- package/dist/zod/tempo/Transaction.d.ts.map +1 -1
- package/dist/zod/tempo/TransactionRequest.d.ts +286 -239
- package/dist/zod/tempo/TransactionRequest.d.ts.map +1 -1
- package/dist/zod/tempo/TransactionRequest.js +11 -22
- package/dist/zod/tempo/TransactionRequest.js.map +1 -1
- package/dist/zod/tempo/TxEnvelopeTempo.d.ts +60 -48
- package/dist/zod/tempo/TxEnvelopeTempo.d.ts.map +1 -1
- package/dist/zod/tempo/ZoneRpcAuthentication.d.ts +15 -12
- package/dist/zod/tempo/ZoneRpcAuthentication.d.ts.map +1 -1
- package/dist/zod/tempo/z.d.ts +2 -0
- package/dist/zod/tempo/z.d.ts.map +1 -1
- package/dist/zod/tempo/z.js +2 -0
- package/dist/zod/tempo/z.js.map +1 -1
- package/package.json +21 -1
- package/src/core/Hash.ts +1 -1
- package/src/core/_test/Hash.test.ts +15 -0
- package/src/tempo/AuthorizationTempo.test.ts +4 -0
- package/src/tempo/KeyAuthorization.test-d.ts +13 -12
- package/src/tempo/KeyAuthorization.test.ts +109 -3
- package/src/tempo/KeyAuthorization.ts +4 -4
- package/src/tempo/MultisigConfig.test-d.ts +52 -0
- package/src/tempo/MultisigConfig.test.ts +288 -227
- package/src/tempo/MultisigConfig.ts +275 -104
- package/src/tempo/MultisigOperation.test-d.ts +79 -0
- package/src/tempo/MultisigOperation.test.ts +1950 -0
- package/src/tempo/MultisigOperation.ts +1293 -0
- package/src/tempo/MultisigSimulation.test-d.ts +33 -0
- package/src/tempo/MultisigSimulation.test.ts +283 -0
- package/src/tempo/MultisigSimulation.ts +266 -0
- package/src/tempo/RpcSchemaTempo.test-d.ts +91 -0
- package/src/tempo/RpcSchemaTempo.ts +46 -0
- package/src/tempo/SignatureEnvelope.test-d.ts +33 -72
- package/src/tempo/SignatureEnvelope.test.ts +533 -500
- package/src/tempo/SignatureEnvelope.ts +171 -258
- package/src/tempo/Transaction.test.ts +61 -0
- package/src/tempo/TransactionRequest.test-d.ts +11 -0
- package/src/tempo/TransactionRequest.test.ts +214 -41
- package/src/tempo/TransactionRequest.ts +19 -20
- package/src/tempo/TxEnvelopeTempo.test-d.ts +7 -0
- package/src/tempo/TxEnvelopeTempo.test.ts +25 -0
- package/src/tempo/TxEnvelopeTempo.ts +5 -3
- package/src/tempo/index.ts +22 -2
- package/src/tempo/multisig.e2e.test.ts +164 -87
- package/src/version.ts +1 -1
- package/src/zod/tempo/MultisigConfig.ts +29 -3
- package/src/zod/tempo/MultisigOperation.ts +66 -0
- package/src/zod/tempo/MultisigSimulation.ts +98 -0
- package/src/zod/tempo/RpcSchemaTempo.ts +53 -0
- package/src/zod/tempo/SignatureEnvelope.ts +12 -54
- package/src/zod/tempo/TransactionRequest.ts +17 -26
- package/src/zod/tempo/_test/KeyAuthorization.test.ts +2 -4
- package/src/zod/tempo/_test/MultisigConfig.test-d.ts +13 -0
- package/src/zod/tempo/_test/MultisigConfig.test.ts +13 -2
- package/src/zod/tempo/_test/MultisigOperation.test-d.ts +27 -0
- package/src/zod/tempo/_test/MultisigOperation.test.ts +66 -0
- package/src/zod/tempo/_test/MultisigSimulation.test-d.ts +13 -0
- package/src/zod/tempo/_test/MultisigSimulation.test.ts +78 -0
- package/src/zod/tempo/_test/RpcSchemaTempo.test-d.ts +48 -0
- package/src/zod/tempo/_test/RpcSchemaTempo.test.ts +83 -0
- package/src/zod/tempo/_test/SignatureEnvelope.test-d.ts +1 -1
- package/src/zod/tempo/_test/SignatureEnvelope.test.ts +57 -120
- package/src/zod/tempo/_test/TransactionRequest.test.ts +43 -16
- package/src/zod/tempo/z.ts +2 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Address,
|
|
3
|
+
Hash,
|
|
3
4
|
Hex,
|
|
4
5
|
P256,
|
|
5
6
|
PublicKey,
|
|
@@ -67,6 +68,9 @@ const signature_keychain_webauthn = SignatureEnvelope.from({
|
|
|
67
68
|
version: 'v2',
|
|
68
69
|
})
|
|
69
70
|
|
|
71
|
+
const signature_multisig_rpc =
|
|
72
|
+
'0xf897949dba7f426b711d4893c11611eacf7cc334e7146bf83ba000000000000000000000000000000000000000000000000000000000000000008001d7d6947e5f4552091a69125d5dfcb7b8c2659029395bdf01f843b841869437e01f64bebeb78a8a6b30bfd3a993819c8cad82c807515d9b9e9b36f98535dfaa5eebc597715d05f6ce4927747f14fa4cd2acc717fdcd3877146437f8f41b' as const
|
|
73
|
+
|
|
70
74
|
describe('assert', () => {
|
|
71
75
|
describe('secp256k1', () => {
|
|
72
76
|
test('behavior: validates valid signature', () => {
|
|
@@ -1116,72 +1120,86 @@ describe('from', () => {
|
|
|
1116
1120
|
})
|
|
1117
1121
|
|
|
1118
1122
|
describe('multisig', () => {
|
|
1119
|
-
const
|
|
1120
|
-
threshold: 1,
|
|
1123
|
+
const config = MultisigConfig.from({
|
|
1121
1124
|
owners: [
|
|
1122
1125
|
{
|
|
1123
1126
|
owner: '0x1111111111111111111111111111111111111111',
|
|
1124
1127
|
weight: 1,
|
|
1125
1128
|
},
|
|
1126
1129
|
],
|
|
1130
|
+
threshold: 1,
|
|
1127
1131
|
})
|
|
1128
1132
|
|
|
1129
|
-
test('behavior: derives
|
|
1130
|
-
const envelope = SignatureEnvelope.from({
|
|
1131
|
-
initialConfig,
|
|
1132
|
-
signatures: [SignatureEnvelope.from(signature_secp256k1)],
|
|
1133
|
-
})
|
|
1134
|
-
|
|
1135
|
-
expect(envelope).toMatchObject({
|
|
1136
|
-
type: 'multisig',
|
|
1137
|
-
account: MultisigConfig.getAddress(initialConfig),
|
|
1138
|
-
})
|
|
1139
|
-
expect('initialConfig' in envelope).toBe(false)
|
|
1140
|
-
expect((envelope as SignatureEnvelope.Multisig).init).toBeUndefined()
|
|
1141
|
-
})
|
|
1142
|
-
|
|
1143
|
-
test('behavior: `init: true` opts into bootstrap (uses `initialConfig` as `init`)', () => {
|
|
1133
|
+
test('behavior: derives an initial account from config', () => {
|
|
1144
1134
|
const envelope = SignatureEnvelope.from({
|
|
1145
|
-
|
|
1135
|
+
config,
|
|
1146
1136
|
signatures: [SignatureEnvelope.from(signature_secp256k1)],
|
|
1147
|
-
init: true,
|
|
1148
1137
|
})
|
|
1149
1138
|
|
|
1150
|
-
expect(
|
|
1151
|
-
|
|
1139
|
+
expect(envelope).toMatchInlineSnapshot(
|
|
1140
|
+
{
|
|
1141
|
+
signatures: [
|
|
1142
|
+
{
|
|
1143
|
+
signature: {
|
|
1144
|
+
r: expect.anything(),
|
|
1145
|
+
s: expect.anything(),
|
|
1146
|
+
yParity: expect.any(Number),
|
|
1147
|
+
},
|
|
1148
|
+
},
|
|
1149
|
+
],
|
|
1150
|
+
},
|
|
1151
|
+
`
|
|
1152
|
+
{
|
|
1153
|
+
"account": "0x8820d1497eeaf4f68e00b2cfc00a2f3b1dbb00da",
|
|
1154
|
+
"config": {
|
|
1155
|
+
"owners": [
|
|
1156
|
+
{
|
|
1157
|
+
"owner": "0x1111111111111111111111111111111111111111",
|
|
1158
|
+
"weight": 1,
|
|
1159
|
+
},
|
|
1160
|
+
],
|
|
1161
|
+
"salt": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
1162
|
+
"threshold": 1,
|
|
1163
|
+
"version": 0n,
|
|
1164
|
+
},
|
|
1165
|
+
"signatures": [
|
|
1166
|
+
{
|
|
1167
|
+
"signature": {
|
|
1168
|
+
"r": Anything,
|
|
1169
|
+
"s": Anything,
|
|
1170
|
+
"yParity": Any<Number>,
|
|
1171
|
+
},
|
|
1172
|
+
"type": "secp256k1",
|
|
1173
|
+
},
|
|
1174
|
+
],
|
|
1175
|
+
"type": "multisig",
|
|
1176
|
+
}
|
|
1177
|
+
`,
|
|
1152
1178
|
)
|
|
1153
1179
|
})
|
|
1154
1180
|
|
|
1155
|
-
test('behavior:
|
|
1156
|
-
const otherConfig = MultisigConfig.from({
|
|
1157
|
-
threshold: 1,
|
|
1158
|
-
owners: [
|
|
1159
|
-
{
|
|
1160
|
-
owner: '0x2222222222222222222222222222222222222222',
|
|
1161
|
-
weight: 1,
|
|
1162
|
-
},
|
|
1163
|
-
],
|
|
1164
|
-
})
|
|
1181
|
+
test('behavior: accepts an explicit account for a current config', () => {
|
|
1165
1182
|
const envelope = SignatureEnvelope.from({
|
|
1166
|
-
|
|
1183
|
+
account: '0x2222222222222222222222222222222222222222',
|
|
1184
|
+
config: { ...config, version: 1 },
|
|
1167
1185
|
signatures: [SignatureEnvelope.from(signature_secp256k1)],
|
|
1168
|
-
init: otherConfig,
|
|
1169
1186
|
})
|
|
1170
1187
|
|
|
1171
|
-
expect(
|
|
1188
|
+
expect(envelope.account).toBe(
|
|
1189
|
+
'0x2222222222222222222222222222222222222222',
|
|
1190
|
+
)
|
|
1191
|
+
expect(envelope.config.version).toBe(1n)
|
|
1172
1192
|
})
|
|
1173
1193
|
|
|
1174
|
-
test('
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
account,
|
|
1184
|
-
})
|
|
1194
|
+
test('error: requires an account for a current config', () => {
|
|
1195
|
+
expect(() =>
|
|
1196
|
+
SignatureEnvelope.from({
|
|
1197
|
+
config: { ...config, version: 1n },
|
|
1198
|
+
signatures: [SignatureEnvelope.from(signature_secp256k1)],
|
|
1199
|
+
} as never),
|
|
1200
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
1201
|
+
`[MultisigConfig.InvalidConfigError: Invalid native multisig config: account address requires version zero.]`,
|
|
1202
|
+
)
|
|
1185
1203
|
})
|
|
1186
1204
|
})
|
|
1187
1205
|
})
|
|
@@ -1793,8 +1811,6 @@ describe('serialize', () => {
|
|
|
1793
1811
|
})
|
|
1794
1812
|
|
|
1795
1813
|
describe('sortMultisigApprovals', () => {
|
|
1796
|
-
// Build owner key pairs first so we can construct a real initial config
|
|
1797
|
-
// whose owner set matches the keys that produce the approvals below.
|
|
1798
1814
|
const ownerKeys = Array.from({ length: 3 }, () => {
|
|
1799
1815
|
const privateKey = Secp256k1.randomPrivateKey()
|
|
1800
1816
|
const address = Address.fromPublicKey(
|
|
@@ -1806,14 +1822,16 @@ describe('sortMultisigApprovals', () => {
|
|
|
1806
1822
|
Hex.toBigInt(a.address) < Hex.toBigInt(b.address) ? -1 : 1,
|
|
1807
1823
|
)
|
|
1808
1824
|
|
|
1809
|
-
const
|
|
1810
|
-
threshold: 2,
|
|
1825
|
+
const config = MultisigConfig.from({
|
|
1811
1826
|
owners: ascendingOwners.map((o) => ({ owner: o.address, weight: 1 })),
|
|
1827
|
+
threshold: 2,
|
|
1812
1828
|
})
|
|
1829
|
+
const account = MultisigConfig.getAddress(config)
|
|
1813
1830
|
const payload = `0x${'42'.repeat(32)}` as const
|
|
1814
1831
|
const digest = MultisigConfig.getSignPayload({
|
|
1832
|
+
account,
|
|
1833
|
+
config,
|
|
1815
1834
|
payload,
|
|
1816
|
-
initialConfig,
|
|
1817
1835
|
})
|
|
1818
1836
|
|
|
1819
1837
|
const owners = ownerKeys.map((owner) => ({
|
|
@@ -1829,7 +1847,8 @@ describe('sortMultisigApprovals', () => {
|
|
|
1829
1847
|
|
|
1830
1848
|
test('behavior: orders approvals ascending by recovered owner address', () => {
|
|
1831
1849
|
const ordered = SignatureEnvelope.sortMultisigApprovals({
|
|
1832
|
-
|
|
1850
|
+
account,
|
|
1851
|
+
config,
|
|
1833
1852
|
payload,
|
|
1834
1853
|
// Provide approvals in reverse of the canonical order.
|
|
1835
1854
|
signatures: [...ascending].reverse().map((owner) => owner.signature),
|
|
@@ -1841,7 +1860,8 @@ describe('sortMultisigApprovals', () => {
|
|
|
1841
1860
|
const signatures = ascending.map((owner) => owner.signature)
|
|
1842
1861
|
expect(
|
|
1843
1862
|
SignatureEnvelope.sortMultisigApprovals({
|
|
1844
|
-
|
|
1863
|
+
account,
|
|
1864
|
+
config,
|
|
1845
1865
|
payload,
|
|
1846
1866
|
signatures,
|
|
1847
1867
|
}),
|
|
@@ -1850,7 +1870,8 @@ describe('sortMultisigApprovals', () => {
|
|
|
1850
1870
|
|
|
1851
1871
|
test('behavior: recovered order matches the config owner order', () => {
|
|
1852
1872
|
const ordered = SignatureEnvelope.sortMultisigApprovals({
|
|
1853
|
-
|
|
1873
|
+
account,
|
|
1874
|
+
config,
|
|
1854
1875
|
payload,
|
|
1855
1876
|
signatures: owners.map((owner) => owner.signature),
|
|
1856
1877
|
})
|
|
@@ -1859,23 +1880,6 @@ describe('sortMultisigApprovals', () => {
|
|
|
1859
1880
|
)
|
|
1860
1881
|
expect(recovered).toEqual(ascending.map((owner) => owner.address))
|
|
1861
1882
|
})
|
|
1862
|
-
|
|
1863
|
-
test('behavior: `initialConfig` and `{ account }` produce identical ordering', () => {
|
|
1864
|
-
const account = MultisigConfig.getAddress(initialConfig)
|
|
1865
|
-
const signatures = owners.map((owner) => owner.signature)
|
|
1866
|
-
|
|
1867
|
-
const fromConfig = SignatureEnvelope.sortMultisigApprovals({
|
|
1868
|
-
initialConfig,
|
|
1869
|
-
payload,
|
|
1870
|
-
signatures,
|
|
1871
|
-
})
|
|
1872
|
-
const fromAccount = SignatureEnvelope.sortMultisigApprovals({
|
|
1873
|
-
account,
|
|
1874
|
-
payload,
|
|
1875
|
-
signatures,
|
|
1876
|
-
})
|
|
1877
|
-
expect(fromConfig).toEqual(fromAccount)
|
|
1878
|
-
})
|
|
1879
1883
|
})
|
|
1880
1884
|
|
|
1881
1885
|
describe('validate', () => {
|
|
@@ -2491,6 +2495,41 @@ describe('fromRpc', () => {
|
|
|
2491
2495
|
expect(envelope.keyId).toBe('0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c')
|
|
2492
2496
|
})
|
|
2493
2497
|
})
|
|
2498
|
+
describe('multisig', () => {
|
|
2499
|
+
test('behavior: converts an untagged serialized multisig signature', () => {
|
|
2500
|
+
expect(
|
|
2501
|
+
SignatureEnvelope.serialize(
|
|
2502
|
+
SignatureEnvelope.fromRpc(signature_multisig_rpc),
|
|
2503
|
+
),
|
|
2504
|
+
).toMatchInlineSnapshot(`"0x05${signature_multisig_rpc.slice(2)}"`)
|
|
2505
|
+
})
|
|
2506
|
+
|
|
2507
|
+
test('error: rejects a payload with the transaction type byte', () => {
|
|
2508
|
+
expect(() =>
|
|
2509
|
+
SignatureEnvelope.fromRpc(`0x05${signature_multisig_rpc.slice(2)}`),
|
|
2510
|
+
).toThrowError()
|
|
2511
|
+
})
|
|
2512
|
+
|
|
2513
|
+
test('error: rejects trailing bytes', () => {
|
|
2514
|
+
expect(() =>
|
|
2515
|
+
SignatureEnvelope.fromRpc(`${signature_multisig_rpc}00`),
|
|
2516
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
2517
|
+
`[Rlp.TrailingBytesError: RLP payload encodes a single item, but \`1\` trailing byte remains.]`,
|
|
2518
|
+
)
|
|
2519
|
+
})
|
|
2520
|
+
|
|
2521
|
+
test('error: rejects a structured multisig signature', () => {
|
|
2522
|
+
expect(() =>
|
|
2523
|
+
SignatureEnvelope.fromRpc({
|
|
2524
|
+
account: '0x9dba7f426b711d4893c11611eacf7cc334e7146b',
|
|
2525
|
+
config: {},
|
|
2526
|
+
signatures: [],
|
|
2527
|
+
} as never),
|
|
2528
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
2529
|
+
`[SignatureEnvelope.CoercionError: Unable to coerce value (\`{"account":"0x9dba7f426b711d4893c11611eacf7cc334e7146b","config":{},"signatures":[]}\`) to a valid signature envelope.]`,
|
|
2530
|
+
)
|
|
2531
|
+
})
|
|
2532
|
+
})
|
|
2494
2533
|
})
|
|
2495
2534
|
|
|
2496
2535
|
describe('toRpc', () => {
|
|
@@ -2588,6 +2627,7 @@ describe('toRpc', () => {
|
|
|
2588
2627
|
const rpc = SignatureEnvelope.toRpc(
|
|
2589
2628
|
signature_keychain_p256,
|
|
2590
2629
|
) as SignatureEnvelope.KeychainRpc
|
|
2630
|
+
if (typeof rpc.signature === 'string') throw new Error('unreachable')
|
|
2591
2631
|
|
|
2592
2632
|
expect(rpc.type).toBe('keychain')
|
|
2593
2633
|
expect(rpc.userAddress).toBe(signature_keychain_p256.userAddress)
|
|
@@ -2600,6 +2640,7 @@ describe('toRpc', () => {
|
|
|
2600
2640
|
const rpc = SignatureEnvelope.toRpc(
|
|
2601
2641
|
signature_keychain_webauthn,
|
|
2602
2642
|
) as SignatureEnvelope.KeychainRpc
|
|
2643
|
+
if (typeof rpc.signature === 'string') throw new Error('unreachable')
|
|
2603
2644
|
|
|
2604
2645
|
expect(rpc.type).toBe('keychain')
|
|
2605
2646
|
expect(rpc.userAddress).toBe(signature_keychain_webauthn.userAddress)
|
|
@@ -2643,6 +2684,18 @@ describe('toRpc', () => {
|
|
|
2643
2684
|
expect(rpc.keyId).toBeUndefined()
|
|
2644
2685
|
})
|
|
2645
2686
|
})
|
|
2687
|
+
|
|
2688
|
+
describe('multisig', () => {
|
|
2689
|
+
test('behavior: removes the transaction type byte', () => {
|
|
2690
|
+
const envelope = SignatureEnvelope.deserialize(
|
|
2691
|
+
`0x05${signature_multisig_rpc.slice(2)}`,
|
|
2692
|
+
)
|
|
2693
|
+
|
|
2694
|
+
expect(SignatureEnvelope.toRpc(envelope)).toMatchInlineSnapshot(
|
|
2695
|
+
`"${signature_multisig_rpc}"`,
|
|
2696
|
+
)
|
|
2697
|
+
})
|
|
2698
|
+
})
|
|
2646
2699
|
})
|
|
2647
2700
|
|
|
2648
2701
|
describe('roundtrip: toRpc <-> fromRpc', () => {
|
|
@@ -2880,146 +2933,329 @@ describe('CoercionError', () => {
|
|
|
2880
2933
|
})
|
|
2881
2934
|
|
|
2882
2935
|
describe('multisig', () => {
|
|
2883
|
-
const
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
const
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2936
|
+
const initial =
|
|
2937
|
+
'0x05f897949dba7f426b711d4893c11611eacf7cc334e7146bf83ba000000000000000000000000000000000000000000000000000000000000000008001d7d6947e5f4552091a69125d5dfcb7b8c2659029395bdf01f843b841869437e01f64bebeb78a8a6b30bfd3a993819c8cad82c807515d9b9e9b36f98535dfaa5eebc597715d05f6ce4927747f14fa4cd2acc717fdcd3877146437f8f41b' as const
|
|
2938
|
+
const current =
|
|
2939
|
+
'0x05f897949dba7f426b711d4893c11611eacf7cc334e7146bf83ba000000000000000000000000000000000000000000000000000000000000000000101d7d6942b5ad5c4795c026514f8317c7a215e218dccd6cf01f843b8414a0e5b5b4a90f08e6e8d676a73a22dc2cf022ffdcc9299512b5d9a6daf66e0504a395a2ef6f6c0f173910b875c45d01aa66d928e56ba6f49bbc8186f80268bf51b' as const
|
|
2940
|
+
const nested =
|
|
2941
|
+
'0x05f8f0949969e2243075b27a8eab61009c59b77ab13b83f6f83ba033333333333333333333333333333333333333333333333333333333333333338001d7d6944f9f5b162a7464bcc260ce44d7ae0d935f9c583701f89cb89a05f897944f9f5b162a7464bcc260ce44d7ae0d935f9c5837f83ba022222222222222222222222222222222222222222222222222222222222222228001d7d6946813eb9362372eef6200f3b1dbc3f819671cba6901f843b841032aa6f3ea7b0b7069720d0f3891983c493d149326c5c957d864ed7371b8475e3e95325079b24491f4b6d68920e4b3bacd7c6094df6ed88b8674864ab559c8fb1b' as const
|
|
2942
|
+
|
|
2943
|
+
test('example: decodes and re-encodes the frozen initial signature', () => {
|
|
2944
|
+
const envelope = SignatureEnvelope.deserialize(initial)
|
|
2945
|
+
|
|
2946
|
+
expect(envelope).toMatchInlineSnapshot(`
|
|
2947
|
+
{
|
|
2948
|
+
"account": "0x9dba7f426b711d4893c11611eacf7cc334e7146b",
|
|
2949
|
+
"config": {
|
|
2950
|
+
"owners": [
|
|
2951
|
+
{
|
|
2952
|
+
"owner": "0x7e5f4552091a69125d5dfcb7b8c2659029395bdf",
|
|
2953
|
+
"weight": 1,
|
|
2954
|
+
},
|
|
2955
|
+
],
|
|
2956
|
+
"salt": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
2957
|
+
"threshold": 1,
|
|
2958
|
+
"version": 0n,
|
|
2959
|
+
},
|
|
2960
|
+
"signatures": [
|
|
2961
|
+
{
|
|
2962
|
+
"signature": {
|
|
2963
|
+
"r": "0x869437e01f64bebeb78a8a6b30bfd3a993819c8cad82c807515d9b9e9b36f985",
|
|
2964
|
+
"s": "0x35dfaa5eebc597715d05f6ce4927747f14fa4cd2acc717fdcd3877146437f8f4",
|
|
2965
|
+
"yParity": 0,
|
|
2966
|
+
},
|
|
2967
|
+
"type": "secp256k1",
|
|
2968
|
+
},
|
|
2969
|
+
],
|
|
2970
|
+
"type": "multisig",
|
|
2971
|
+
}
|
|
2972
|
+
`)
|
|
2973
|
+
expect(SignatureEnvelope.serialize(envelope)).toBe(initial)
|
|
2891
2974
|
})
|
|
2892
2975
|
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2976
|
+
test('example: decodes and re-encodes the frozen current signature', () => {
|
|
2977
|
+
const envelope = SignatureEnvelope.deserialize(current)
|
|
2978
|
+
|
|
2979
|
+
expect(envelope).toMatchInlineSnapshot(`
|
|
2980
|
+
{
|
|
2981
|
+
"account": "0x9dba7f426b711d4893c11611eacf7cc334e7146b",
|
|
2982
|
+
"config": {
|
|
2983
|
+
"owners": [
|
|
2984
|
+
{
|
|
2985
|
+
"owner": "0x2b5ad5c4795c026514f8317c7a215e218dccd6cf",
|
|
2986
|
+
"weight": 1,
|
|
2987
|
+
},
|
|
2988
|
+
],
|
|
2989
|
+
"salt": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
2990
|
+
"threshold": 1,
|
|
2991
|
+
"version": 1n,
|
|
2992
|
+
},
|
|
2993
|
+
"signatures": [
|
|
2994
|
+
{
|
|
2995
|
+
"signature": {
|
|
2996
|
+
"r": "0x4a0e5b5b4a90f08e6e8d676a73a22dc2cf022ffdcc9299512b5d9a6daf66e050",
|
|
2997
|
+
"s": "0x4a395a2ef6f6c0f173910b875c45d01aa66d928e56ba6f49bbc8186f80268bf5",
|
|
2998
|
+
"yParity": 0,
|
|
2999
|
+
},
|
|
3000
|
+
"type": "secp256k1",
|
|
3001
|
+
},
|
|
3002
|
+
],
|
|
3003
|
+
"type": "multisig",
|
|
3004
|
+
}
|
|
3005
|
+
`)
|
|
3006
|
+
expect(SignatureEnvelope.serialize(envelope)).toBe(current)
|
|
2897
3007
|
})
|
|
2898
3008
|
|
|
2899
|
-
test('
|
|
2900
|
-
const
|
|
2901
|
-
|
|
3009
|
+
test('example: decodes and re-encodes the frozen nested signature', () => {
|
|
3010
|
+
const envelope = SignatureEnvelope.deserialize(nested)
|
|
3011
|
+
|
|
3012
|
+
expect(envelope).toMatchInlineSnapshot(`
|
|
3013
|
+
{
|
|
3014
|
+
"account": "0x9969e2243075b27a8eab61009c59b77ab13b83f6",
|
|
3015
|
+
"config": {
|
|
3016
|
+
"owners": [
|
|
3017
|
+
{
|
|
3018
|
+
"owner": "0x4f9f5b162a7464bcc260ce44d7ae0d935f9c5837",
|
|
3019
|
+
"weight": 1,
|
|
3020
|
+
},
|
|
3021
|
+
],
|
|
3022
|
+
"salt": "0x3333333333333333333333333333333333333333333333333333333333333333",
|
|
3023
|
+
"threshold": 1,
|
|
3024
|
+
"version": 0n,
|
|
3025
|
+
},
|
|
3026
|
+
"signatures": [
|
|
3027
|
+
{
|
|
3028
|
+
"account": "0x4f9f5b162a7464bcc260ce44d7ae0d935f9c5837",
|
|
3029
|
+
"config": {
|
|
3030
|
+
"owners": [
|
|
3031
|
+
{
|
|
3032
|
+
"owner": "0x6813eb9362372eef6200f3b1dbc3f819671cba69",
|
|
3033
|
+
"weight": 1,
|
|
3034
|
+
},
|
|
3035
|
+
],
|
|
3036
|
+
"salt": "0x2222222222222222222222222222222222222222222222222222222222222222",
|
|
3037
|
+
"threshold": 1,
|
|
3038
|
+
"version": 0n,
|
|
3039
|
+
},
|
|
3040
|
+
"signatures": [
|
|
3041
|
+
{
|
|
3042
|
+
"signature": {
|
|
3043
|
+
"r": "0x032aa6f3ea7b0b7069720d0f3891983c493d149326c5c957d864ed7371b8475e",
|
|
3044
|
+
"s": "0x3e95325079b24491f4b6d68920e4b3bacd7c6094df6ed88b8674864ab559c8fb",
|
|
3045
|
+
"yParity": 0,
|
|
3046
|
+
},
|
|
3047
|
+
"type": "secp256k1",
|
|
3048
|
+
},
|
|
3049
|
+
],
|
|
3050
|
+
"type": "multisig",
|
|
3051
|
+
},
|
|
3052
|
+
],
|
|
3053
|
+
"type": "multisig",
|
|
3054
|
+
}
|
|
3055
|
+
`)
|
|
3056
|
+
expect(SignatureEnvelope.serialize(envelope)).toBe(nested)
|
|
2902
3057
|
})
|
|
2903
3058
|
|
|
2904
|
-
test('
|
|
2905
|
-
const
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
)
|
|
3059
|
+
test('behavior: preserves exact 65-byte secp256k1 precedence', () => {
|
|
3060
|
+
const serialized = ('0x05' +
|
|
3061
|
+
'00'.repeat(31) +
|
|
3062
|
+
'00'.repeat(31) +
|
|
3063
|
+
'01' +
|
|
3064
|
+
'00') as Hex.Hex
|
|
3065
|
+
|
|
3066
|
+
expect(Hex.size(serialized)).toBe(65)
|
|
3067
|
+
expect(SignatureEnvelope.deserialize(serialized).type).toBe('secp256k1')
|
|
2913
3068
|
})
|
|
2914
3069
|
|
|
2915
|
-
test('
|
|
3070
|
+
test('behavior: serializes account, config, and approvals in wire order', () => {
|
|
3071
|
+
const envelope = SignatureEnvelope.deserialize(initial)
|
|
3072
|
+
if (envelope.type !== 'multisig') throw new Error('unreachable')
|
|
2916
3073
|
const serialized = SignatureEnvelope.serialize(envelope)
|
|
2917
|
-
|
|
3074
|
+
const decoded = Rlp.toHex(Hex.slice(serialized, 1))
|
|
3075
|
+
|
|
3076
|
+
expect(decoded).toMatchInlineSnapshot(`
|
|
3077
|
+
[
|
|
3078
|
+
"0x9dba7f426b711d4893c11611eacf7cc334e7146b",
|
|
3079
|
+
[
|
|
3080
|
+
"0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
3081
|
+
"0x",
|
|
3082
|
+
"0x01",
|
|
3083
|
+
[
|
|
3084
|
+
[
|
|
3085
|
+
"0x7e5f4552091a69125d5dfcb7b8c2659029395bdf",
|
|
3086
|
+
"0x01",
|
|
3087
|
+
],
|
|
3088
|
+
],
|
|
3089
|
+
],
|
|
3090
|
+
[
|
|
3091
|
+
"0x869437e01f64bebeb78a8a6b30bfd3a993819c8cad82c807515d9b9e9b36f98535dfaa5eebc597715d05f6ce4927747f14fa4cd2acc717fdcd3877146437f8f41b",
|
|
3092
|
+
],
|
|
3093
|
+
]
|
|
3094
|
+
`)
|
|
2918
3095
|
})
|
|
2919
3096
|
|
|
2920
|
-
test('
|
|
2921
|
-
const
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
}
|
|
2927
|
-
const legacy = Hex.concat(
|
|
2928
|
-
'0x05',
|
|
2929
|
-
Rlp.fromHex([account, [SignatureEnvelope.serialize(innerP256)], init]),
|
|
2930
|
-
)
|
|
2931
|
-
expect(() => SignatureEnvelope.deserialize(legacy)).toThrowError(
|
|
2932
|
-
SignatureEnvelope.InvalidSerializedError,
|
|
2933
|
-
)
|
|
2934
|
-
})
|
|
3097
|
+
test('behavior: round trips initial and current RPC signatures', () => {
|
|
3098
|
+
const initialEnvelope = SignatureEnvelope.deserialize(initial)
|
|
3099
|
+
const currentEnvelope = SignatureEnvelope.deserialize(current)
|
|
3100
|
+
const rpc = {
|
|
3101
|
+
current: SignatureEnvelope.toRpc(currentEnvelope),
|
|
3102
|
+
initial: SignatureEnvelope.toRpc(initialEnvelope),
|
|
3103
|
+
}
|
|
2935
3104
|
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
3105
|
+
expect(rpc).toMatchInlineSnapshot(`
|
|
3106
|
+
{
|
|
3107
|
+
"current": "0xf897949dba7f426b711d4893c11611eacf7cc334e7146bf83ba000000000000000000000000000000000000000000000000000000000000000000101d7d6942b5ad5c4795c026514f8317c7a215e218dccd6cf01f843b8414a0e5b5b4a90f08e6e8d676a73a22dc2cf022ffdcc9299512b5d9a6daf66e0504a395a2ef6f6c0f173910b875c45d01aa66d928e56ba6f49bbc8186f80268bf51b",
|
|
3108
|
+
"initial": "0xf897949dba7f426b711d4893c11611eacf7cc334e7146bf83ba000000000000000000000000000000000000000000000000000000000000000008001d7d6947e5f4552091a69125d5dfcb7b8c2659029395bdf01f843b841869437e01f64bebeb78a8a6b30bfd3a993819c8cad82c807515d9b9e9b36f98535dfaa5eebc597715d05f6ce4927747f14fa4cd2acc717fdcd3877146437f8f41b",
|
|
3109
|
+
}
|
|
3110
|
+
`)
|
|
3111
|
+
expect(SignatureEnvelope.fromRpc(rpc.initial)).toStrictEqual(
|
|
3112
|
+
initialEnvelope,
|
|
2940
3113
|
)
|
|
2941
|
-
expect(
|
|
2942
|
-
|
|
3114
|
+
expect(SignatureEnvelope.fromRpc(rpc.current)).toStrictEqual(
|
|
3115
|
+
currentEnvelope,
|
|
2943
3116
|
)
|
|
2944
3117
|
})
|
|
2945
3118
|
|
|
2946
|
-
test('
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
3119
|
+
test('behavior: derives the account only for an initial config', () => {
|
|
3120
|
+
const initialEnvelope = SignatureEnvelope.deserialize(initial)
|
|
3121
|
+
if (initialEnvelope.type !== 'multisig') throw new Error('unreachable')
|
|
3122
|
+
if (initialEnvelope.config.version !== 0n) throw new Error('unreachable')
|
|
3123
|
+
const derived = SignatureEnvelope.from({
|
|
3124
|
+
config: initialEnvelope.config as MultisigConfig.Config<0n>,
|
|
3125
|
+
signatures: initialEnvelope.signatures,
|
|
3126
|
+
})
|
|
3127
|
+
const currentEnvelope = SignatureEnvelope.deserialize(current)
|
|
3128
|
+
if (currentEnvelope.type !== 'multisig') throw new Error('unreachable')
|
|
3129
|
+
|
|
3130
|
+
expect(derived.account).toBe(initialEnvelope.account)
|
|
3131
|
+
expect(() =>
|
|
3132
|
+
SignatureEnvelope.from({
|
|
3133
|
+
config: currentEnvelope.config,
|
|
3134
|
+
signatures: currentEnvelope.signatures,
|
|
2957
3135
|
} as never),
|
|
2958
|
-
).
|
|
3136
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
3137
|
+
`[MultisigConfig.InvalidConfigError: Invalid native multisig config: account address requires version zero.]`,
|
|
3138
|
+
)
|
|
2959
3139
|
})
|
|
2960
3140
|
|
|
2961
|
-
test('
|
|
3141
|
+
test('behavior: extracts the multisig account', () => {
|
|
3142
|
+
const envelope = SignatureEnvelope.deserialize(initial)
|
|
3143
|
+
if (envelope.type !== 'multisig') throw new Error('unreachable')
|
|
3144
|
+
|
|
2962
3145
|
expect(
|
|
2963
3146
|
SignatureEnvelope.extractAddress({
|
|
2964
3147
|
payload: '0xdeadbeef',
|
|
2965
3148
|
signature: envelope,
|
|
2966
3149
|
}),
|
|
2967
|
-
).toBe(account)
|
|
2968
|
-
})
|
|
2969
|
-
|
|
2970
|
-
test('
|
|
2971
|
-
const
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
3150
|
+
).toBe(envelope.account)
|
|
3151
|
+
})
|
|
3152
|
+
|
|
3153
|
+
test('example: matches the frozen eight-approval boundary vector', () => {
|
|
3154
|
+
const config = MultisigConfig.from({
|
|
3155
|
+
owners: Array.from({ length: 8 }, (_, index) => ({
|
|
3156
|
+
owner:
|
|
3157
|
+
`0x${(index + 1).toString(16).padStart(40, '0')}` as `0x${string}`,
|
|
3158
|
+
weight: 1,
|
|
3159
|
+
})),
|
|
3160
|
+
salt: `0x${'66'.repeat(32)}`,
|
|
3161
|
+
threshold: 8,
|
|
3162
|
+
})
|
|
3163
|
+
const approval = SignatureEnvelope.from({
|
|
3164
|
+
r: '0x840cfc572845f5786e702984c2a582528cad4b49b2a10b9db1be7fca90058565',
|
|
3165
|
+
s: '0x25e7109ceb98168d95b09b18bbf6b685130e0562f233877d492b94eee0c5b6d1',
|
|
3166
|
+
yParity: 0,
|
|
3167
|
+
})
|
|
3168
|
+
const envelope = SignatureEnvelope.from({
|
|
3169
|
+
config,
|
|
3170
|
+
signatures: Array.from(
|
|
3171
|
+
{ length: MultisigConfig.maxSignatures },
|
|
3172
|
+
() => approval,
|
|
3173
|
+
),
|
|
2977
3174
|
})
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
expect(
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
3175
|
+
const serialized = SignatureEnvelope.serialize(envelope)
|
|
3176
|
+
|
|
3177
|
+
expect({
|
|
3178
|
+
hash: Hash.keccak256(serialized),
|
|
3179
|
+
length: Hex.size(serialized),
|
|
3180
|
+
signatureCount: envelope.signatures.length,
|
|
3181
|
+
}).toMatchInlineSnapshot(`
|
|
3182
|
+
{
|
|
3183
|
+
"hash": "0x1cb47fcf31fb1ebb3177ec1744eeda195e4d789920d1214af32baaa6150ad5e2",
|
|
3184
|
+
"length": 787,
|
|
3185
|
+
"signatureCount": 8,
|
|
3186
|
+
}
|
|
3187
|
+
`)
|
|
2984
3188
|
})
|
|
2985
3189
|
|
|
2986
|
-
test('
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
3190
|
+
test('behavior: accepts initial and current nested signatures', () => {
|
|
3191
|
+
const initialEnvelope = SignatureEnvelope.deserialize(initial)
|
|
3192
|
+
const currentEnvelope = SignatureEnvelope.deserialize(current)
|
|
3193
|
+
if (
|
|
3194
|
+
initialEnvelope.type !== 'multisig' ||
|
|
3195
|
+
currentEnvelope.type !== 'multisig'
|
|
3196
|
+
)
|
|
3197
|
+
throw new Error('unreachable')
|
|
3198
|
+
|
|
3199
|
+
for (const signature of [initialEnvelope, currentEnvelope]) {
|
|
3200
|
+
const config = MultisigConfig.from({
|
|
3201
|
+
owners: [{ owner: signature.account, weight: 1 }],
|
|
3202
|
+
salt: ('0x' + 'ff'.repeat(32)) as Hex.Hex,
|
|
3203
|
+
threshold: 1,
|
|
3204
|
+
version: 1n,
|
|
3205
|
+
})
|
|
3206
|
+
const envelope = SignatureEnvelope.from({
|
|
3207
|
+
account: '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
|
|
3208
|
+
config,
|
|
3209
|
+
signatures: [signature],
|
|
3210
|
+
})
|
|
3211
|
+
|
|
3212
|
+
expect(
|
|
3213
|
+
SignatureEnvelope.deserialize(SignatureEnvelope.serialize(envelope)),
|
|
3214
|
+
).toStrictEqual(envelope)
|
|
3215
|
+
}
|
|
2990
3216
|
})
|
|
2991
3217
|
|
|
2992
|
-
test('
|
|
3218
|
+
test('error: rejects an initial account mismatch', () => {
|
|
3219
|
+
const envelope = SignatureEnvelope.deserialize(initial)
|
|
3220
|
+
if (envelope.type !== 'multisig') throw new Error('unreachable')
|
|
3221
|
+
|
|
2993
3222
|
expect(() =>
|
|
2994
3223
|
SignatureEnvelope.assert({
|
|
2995
|
-
|
|
2996
|
-
account,
|
|
2997
|
-
signatures: [],
|
|
3224
|
+
...envelope,
|
|
3225
|
+
account: '0x1111111111111111111111111111111111111111',
|
|
2998
3226
|
}),
|
|
2999
3227
|
).toThrowErrorMatchingInlineSnapshot(
|
|
3000
|
-
`[SignatureEnvelope.InvalidMultisigApprovalError: Invalid native multisig owner approval: multisig
|
|
3228
|
+
`[SignatureEnvelope.InvalidMultisigApprovalError: Invalid native multisig owner approval: initial multisig config does not derive account.]`,
|
|
3001
3229
|
)
|
|
3002
3230
|
})
|
|
3003
3231
|
|
|
3004
|
-
test('
|
|
3232
|
+
test('error: rejects a self-owned current signature', () => {
|
|
3233
|
+
const envelope = SignatureEnvelope.deserialize(current)
|
|
3234
|
+
if (envelope.type !== 'multisig') throw new Error('unreachable')
|
|
3235
|
+
const account = envelope.config.owners[0]!.owner
|
|
3236
|
+
|
|
3005
3237
|
expect(() =>
|
|
3006
|
-
SignatureEnvelope.assert({
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
)
|
|
3238
|
+
SignatureEnvelope.assert({ ...envelope, account }),
|
|
3239
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
3240
|
+
`[SignatureEnvelope.InvalidMultisigApprovalError: Invalid native multisig owner approval: multisig account cannot be an owner.]`,
|
|
3241
|
+
)
|
|
3242
|
+
})
|
|
3243
|
+
|
|
3244
|
+
test('error: rejects empty and excess approvals', () => {
|
|
3245
|
+
const envelope = SignatureEnvelope.deserialize(current)
|
|
3246
|
+
if (envelope.type !== 'multisig') throw new Error('unreachable')
|
|
3015
3247
|
|
|
3248
|
+
expect(() =>
|
|
3249
|
+
SignatureEnvelope.assert({ ...envelope, signatures: [] }),
|
|
3250
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
3251
|
+
`[SignatureEnvelope.InvalidMultisigApprovalError: Invalid native multisig owner approval: multisig signatures cannot be empty.]`,
|
|
3252
|
+
)
|
|
3016
3253
|
expect(() =>
|
|
3017
3254
|
SignatureEnvelope.assert({
|
|
3018
|
-
|
|
3019
|
-
account,
|
|
3255
|
+
...envelope,
|
|
3020
3256
|
signatures: Array.from(
|
|
3021
3257
|
{ length: MultisigConfig.maxSignatures + 1 },
|
|
3022
|
-
() =>
|
|
3258
|
+
() => envelope.signatures[0]!,
|
|
3023
3259
|
),
|
|
3024
3260
|
}),
|
|
3025
3261
|
).toThrowErrorMatchingInlineSnapshot(
|
|
@@ -3027,7 +3263,23 @@ describe('multisig', () => {
|
|
|
3027
3263
|
)
|
|
3028
3264
|
})
|
|
3029
3265
|
|
|
3030
|
-
test('
|
|
3266
|
+
test('error: rejects keychain owner approvals', () => {
|
|
3267
|
+
const envelope = SignatureEnvelope.deserialize(current)
|
|
3268
|
+
if (envelope.type !== 'multisig') throw new Error('unreachable')
|
|
3269
|
+
|
|
3270
|
+
expect(() =>
|
|
3271
|
+
SignatureEnvelope.assert({
|
|
3272
|
+
...envelope,
|
|
3273
|
+
signatures: [signature_keychain_secp256k1],
|
|
3274
|
+
} as never),
|
|
3275
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
3276
|
+
`[SignatureEnvelope.InvalidMultisigApprovalError: Invalid native multisig owner approval: keychain owner approvals are not allowed.]`,
|
|
3277
|
+
)
|
|
3278
|
+
})
|
|
3279
|
+
|
|
3280
|
+
test('error: rejects oversized primitive approvals', () => {
|
|
3281
|
+
const envelope = SignatureEnvelope.deserialize(current)
|
|
3282
|
+
if (envelope.type !== 'multisig') throw new Error('unreachable')
|
|
3031
3283
|
const oversized = {
|
|
3032
3284
|
...signature_webauthn,
|
|
3033
3285
|
metadata: {
|
|
@@ -3035,354 +3287,135 @@ describe('multisig', () => {
|
|
|
3035
3287
|
clientDataJSON: JSON.stringify({ value: 'x'.repeat(2048) }),
|
|
3036
3288
|
},
|
|
3037
3289
|
}
|
|
3290
|
+
|
|
3038
3291
|
expect(() =>
|
|
3039
3292
|
SignatureEnvelope.assert({
|
|
3040
|
-
|
|
3041
|
-
account,
|
|
3293
|
+
...envelope,
|
|
3042
3294
|
signatures: [oversized],
|
|
3043
3295
|
}),
|
|
3044
3296
|
).toThrowErrorMatchingInlineSnapshot(
|
|
3045
3297
|
`[SignatureEnvelope.InvalidMultisigApprovalError: Invalid native multisig owner approval: multisig owner signature exceeds 2049 bytes.]`,
|
|
3046
3298
|
)
|
|
3047
|
-
|
|
3048
|
-
const serialized = Hex.concat(
|
|
3049
|
-
'0x05',
|
|
3050
|
-
Rlp.fromHex([account, [SignatureEnvelope.serialize(oversized)]]),
|
|
3051
|
-
)
|
|
3052
|
-
expect(() => SignatureEnvelope.deserialize(serialized)).toThrowError(
|
|
3053
|
-
SignatureEnvelope.InvalidSerializedError,
|
|
3054
|
-
)
|
|
3055
3299
|
})
|
|
3056
3300
|
|
|
3057
|
-
test('
|
|
3301
|
+
test('error: rejects nesting deeper than two nodes', () => {
|
|
3302
|
+
const envelope = SignatureEnvelope.deserialize(initial)
|
|
3303
|
+
if (envelope.type !== 'multisig') throw new Error('unreachable')
|
|
3304
|
+
const depth2 = SignatureEnvelope.from({
|
|
3305
|
+
...envelope,
|
|
3306
|
+
signatures: [envelope],
|
|
3307
|
+
})
|
|
3308
|
+
|
|
3058
3309
|
expect(() =>
|
|
3059
3310
|
SignatureEnvelope.assert({
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
} as never),
|
|
3311
|
+
...envelope,
|
|
3312
|
+
signatures: [depth2],
|
|
3313
|
+
}),
|
|
3064
3314
|
).toThrowErrorMatchingInlineSnapshot(
|
|
3065
|
-
`[SignatureEnvelope.InvalidMultisigApprovalError: Invalid native multisig owner approval:
|
|
3315
|
+
`[SignatureEnvelope.InvalidMultisigApprovalError: Invalid native multisig owner approval: multisig nesting depth exceeds 2.]`,
|
|
3066
3316
|
)
|
|
3067
3317
|
})
|
|
3068
3318
|
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
test('assert: accepts the maximum nesting depth', () => {
|
|
3088
|
-
const depth2 = SignatureEnvelope.from({
|
|
3089
|
-
type: 'multisig',
|
|
3090
|
-
account,
|
|
3091
|
-
signatures: [nested],
|
|
3092
|
-
})
|
|
3093
|
-
expect(() => SignatureEnvelope.assert(depth2)).not.toThrowError()
|
|
3094
|
-
})
|
|
3095
|
-
|
|
3096
|
-
test('accepts a nested approval above the primitive byte limit', () => {
|
|
3097
|
-
const primitive = SignatureEnvelope.from({
|
|
3098
|
-
...signature_webauthn,
|
|
3099
|
-
metadata: {
|
|
3100
|
-
...signature_webauthn.metadata,
|
|
3101
|
-
clientDataJSON: JSON.stringify({ value: 'x'.repeat(1_050) }),
|
|
3102
|
-
},
|
|
3103
|
-
signature: {
|
|
3104
|
-
r: signature_webauthn.signature.r,
|
|
3105
|
-
s: signature_webauthn.signature.s,
|
|
3106
|
-
},
|
|
3107
|
-
})
|
|
3108
|
-
const largeNested = SignatureEnvelope.from({
|
|
3109
|
-
type: 'multisig',
|
|
3110
|
-
account: nestedAccount,
|
|
3111
|
-
signatures: [primitive, primitive],
|
|
3112
|
-
})
|
|
3113
|
-
expect(Hex.size(SignatureEnvelope.serialize(primitive))).toBeLessThan(
|
|
3114
|
-
MultisigConfig.maxOwnerSignatureBytes,
|
|
3115
|
-
)
|
|
3116
|
-
expect(
|
|
3117
|
-
Hex.size(SignatureEnvelope.serialize(largeNested)),
|
|
3118
|
-
).toBeGreaterThan(MultisigConfig.maxOwnerSignatureBytes)
|
|
3119
|
-
|
|
3120
|
-
const parent = SignatureEnvelope.from({
|
|
3121
|
-
type: 'multisig',
|
|
3122
|
-
account,
|
|
3123
|
-
signatures: [largeNested],
|
|
3124
|
-
})
|
|
3125
|
-
expect(
|
|
3126
|
-
SignatureEnvelope.deserialize(SignatureEnvelope.serialize(parent)),
|
|
3127
|
-
).toEqual(parent)
|
|
3128
|
-
})
|
|
3129
|
-
|
|
3130
|
-
test('assert: rejects nesting deeper than the maximum', () => {
|
|
3131
|
-
const depth3 = SignatureEnvelope.from({
|
|
3132
|
-
type: 'multisig',
|
|
3133
|
-
account,
|
|
3134
|
-
signatures: [
|
|
3135
|
-
SignatureEnvelope.from({
|
|
3136
|
-
type: 'multisig',
|
|
3137
|
-
account: nestedAccount,
|
|
3138
|
-
signatures: [nested],
|
|
3139
|
-
}),
|
|
3140
|
-
],
|
|
3141
|
-
})
|
|
3142
|
-
expect(() =>
|
|
3143
|
-
SignatureEnvelope.assert(depth3),
|
|
3144
|
-
).toThrowErrorMatchingInlineSnapshot(
|
|
3145
|
-
`[SignatureEnvelope.InvalidMultisigApprovalError: Invalid native multisig owner approval: multisig nesting depth exceeds 2.]`,
|
|
3146
|
-
)
|
|
3147
|
-
})
|
|
3148
|
-
|
|
3149
|
-
test('deserialize: rejects nesting deeper than the maximum during decode', () => {
|
|
3150
|
-
const primitive = SignatureEnvelope.serialize(innerP256)
|
|
3151
|
-
const depth1 = Hex.concat(
|
|
3152
|
-
'0x05',
|
|
3153
|
-
Rlp.fromHex([nestedAccount, [primitive]]),
|
|
3154
|
-
)
|
|
3155
|
-
const depth2 = Hex.concat('0x05', Rlp.fromHex([nestedAccount, [depth1]]))
|
|
3156
|
-
const depth3 = Hex.concat('0x05', Rlp.fromHex([account, [depth2]]))
|
|
3157
|
-
|
|
3158
|
-
expect(() => SignatureEnvelope.deserialize(depth3)).toThrowError(
|
|
3159
|
-
SignatureEnvelope.InvalidSerializedError,
|
|
3160
|
-
)
|
|
3161
|
-
})
|
|
3162
|
-
|
|
3163
|
-
test('assert: rejects nested approvals carrying `init`', () => {
|
|
3164
|
-
expect(() =>
|
|
3165
|
-
SignatureEnvelope.assert({
|
|
3166
|
-
type: 'multisig',
|
|
3167
|
-
account,
|
|
3168
|
-
signatures: [
|
|
3169
|
-
{
|
|
3170
|
-
...nested,
|
|
3171
|
-
init: {
|
|
3172
|
-
threshold: 1,
|
|
3173
|
-
owners: [{ owner: nestedAccount, weight: 1 }],
|
|
3319
|
+
test.each([
|
|
3320
|
+
{
|
|
3321
|
+
name: 'two fields',
|
|
3322
|
+
value: Rlp.fromHex([
|
|
3323
|
+
'0x9dba7f426b711d4893c11611eacf7cc334e7146b',
|
|
3324
|
+
[Hex.slice(initial, -65)],
|
|
3325
|
+
]),
|
|
3326
|
+
},
|
|
3327
|
+
{
|
|
3328
|
+
name: 'trailing field',
|
|
3329
|
+
value: Rlp.fromHex([
|
|
3330
|
+
'0x9dba7f426b711d4893c11611eacf7cc334e7146b',
|
|
3331
|
+
MultisigConfig.toTuple(
|
|
3332
|
+
MultisigConfig.from({
|
|
3333
|
+
owners: [
|
|
3334
|
+
{
|
|
3335
|
+
owner: '0x7e5f4552091a69125d5dfcb7b8c2659029395bdf',
|
|
3336
|
+
weight: 1,
|
|
3174
3337
|
},
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
).toThrowErrorMatchingInlineSnapshot(
|
|
3179
|
-
`[SignatureEnvelope.InvalidMultisigApprovalError: Invalid native multisig owner approval: nested multisig owner approvals cannot carry \`init\`.]`,
|
|
3180
|
-
)
|
|
3181
|
-
})
|
|
3182
|
-
})
|
|
3183
|
-
|
|
3184
|
-
describe('init (bootstrap)', () => {
|
|
3185
|
-
const init = {
|
|
3186
|
-
salt: `0x${'00'.repeat(32)}` as const,
|
|
3187
|
-
threshold: 1,
|
|
3188
|
-
owners: [
|
|
3189
|
-
{
|
|
3190
|
-
owner: '0x1111111111111111111111111111111111111111' as const,
|
|
3191
|
-
weight: 1,
|
|
3192
|
-
},
|
|
3193
|
-
],
|
|
3194
|
-
}
|
|
3195
|
-
const bootstrapAccount = MultisigConfig.getAddress(init)
|
|
3196
|
-
|
|
3197
|
-
const bootstrapEnvelope = SignatureEnvelope.from({
|
|
3198
|
-
type: 'multisig',
|
|
3199
|
-
account: bootstrapAccount,
|
|
3200
|
-
signatures: [SignatureEnvelope.from(signature_secp256k1), innerP256],
|
|
3201
|
-
init,
|
|
3202
|
-
})
|
|
3203
|
-
|
|
3204
|
-
test('serialize/deserialize round-trip with init', () => {
|
|
3205
|
-
const serialized = SignatureEnvelope.serialize(bootstrapEnvelope)
|
|
3206
|
-
expect(SignatureEnvelope.deserialize(serialized)).toEqual(
|
|
3207
|
-
bootstrapEnvelope,
|
|
3208
|
-
)
|
|
3209
|
-
})
|
|
3210
|
-
|
|
3211
|
-
test('deserialize: validates bootstrap init semantics', () => {
|
|
3212
|
-
const invalid = Hex.concat(
|
|
3213
|
-
'0x05',
|
|
3214
|
-
Rlp.fromHex([
|
|
3215
|
-
[MultisigConfig.zeroSalt, '0x01', []],
|
|
3216
|
-
[SignatureEnvelope.serialize(innerP256)],
|
|
3217
|
-
]),
|
|
3218
|
-
)
|
|
3219
|
-
expect(() => SignatureEnvelope.deserialize(invalid)).toThrowError(
|
|
3220
|
-
MultisigConfig.InvalidConfigError,
|
|
3221
|
-
)
|
|
3222
|
-
})
|
|
3223
|
-
|
|
3224
|
-
test('deserialize: rejects noncanonical bootstrap field widths', () => {
|
|
3225
|
-
const signatures = [SignatureEnvelope.serialize(innerP256)]
|
|
3226
|
-
const shortSalt = Hex.concat(
|
|
3227
|
-
'0x05',
|
|
3228
|
-
Rlp.fromHex([
|
|
3229
|
-
[`0x${'00'.repeat(31)}`, '0x01', [[init.owners[0]!.owner, '0x01']]],
|
|
3230
|
-
signatures,
|
|
3231
|
-
]),
|
|
3232
|
-
)
|
|
3233
|
-
const wideThreshold = Hex.concat(
|
|
3234
|
-
'0x05',
|
|
3235
|
-
Rlp.fromHex([
|
|
3236
|
-
[init.salt, '0x0001', [[init.owners[0]!.owner, '0x01']]],
|
|
3237
|
-
signatures,
|
|
3238
|
-
]),
|
|
3239
|
-
)
|
|
3240
|
-
const wideWeight = Hex.concat(
|
|
3241
|
-
'0x05',
|
|
3242
|
-
Rlp.fromHex([
|
|
3243
|
-
[init.salt, '0x01', [[init.owners[0]!.owner, '0x0001']]],
|
|
3244
|
-
signatures,
|
|
3245
|
-
]),
|
|
3246
|
-
)
|
|
3247
|
-
|
|
3248
|
-
for (const serialized of [shortSalt, wideThreshold, wideWeight])
|
|
3249
|
-
expect(() => SignatureEnvelope.deserialize(serialized)).toThrowError(
|
|
3250
|
-
SignatureEnvelope.InvalidSerializedError,
|
|
3251
|
-
)
|
|
3252
|
-
})
|
|
3253
|
-
|
|
3254
|
-
test('serialize: wire shape is `0x05 || rlp([init, signatures])`', () => {
|
|
3255
|
-
const serialized = SignatureEnvelope.serialize(bootstrapEnvelope)
|
|
3256
|
-
const [address] = Rlp.toHex(Hex.slice(serialized, 1)) as readonly [
|
|
3257
|
-
MultisigConfig.Tuple,
|
|
3258
|
-
readonly Hex.Hex[],
|
|
3259
|
-
]
|
|
3260
|
-
expect(MultisigConfig.fromTuple(address)).toEqual(init)
|
|
3261
|
-
})
|
|
3262
|
-
|
|
3263
|
-
test('serialize/deserialize round-trip preserves non-zero salt', () => {
|
|
3264
|
-
const saltedInit = {
|
|
3265
|
-
...init,
|
|
3266
|
-
salt: `0x${'42'.repeat(32)}` as const,
|
|
3267
|
-
}
|
|
3268
|
-
const salted = SignatureEnvelope.from({
|
|
3269
|
-
type: 'multisig',
|
|
3270
|
-
account: MultisigConfig.getAddress(saltedInit),
|
|
3271
|
-
signatures: [SignatureEnvelope.from(signature_secp256k1), innerP256],
|
|
3272
|
-
init: saltedInit,
|
|
3273
|
-
})
|
|
3274
|
-
const serialized = SignatureEnvelope.serialize(salted)
|
|
3275
|
-
const deserialized = SignatureEnvelope.deserialize(
|
|
3276
|
-
serialized,
|
|
3277
|
-
) as SignatureEnvelope.Multisig
|
|
3278
|
-
expect(deserialized.init?.salt).toBe(`0x${'42'.repeat(32)}`)
|
|
3279
|
-
expect(deserialized).toEqual(salted)
|
|
3280
|
-
})
|
|
3281
|
-
|
|
3282
|
-
test('absent init has no `init` key after deserialize', () => {
|
|
3283
|
-
const serialized = SignatureEnvelope.serialize(envelope)
|
|
3284
|
-
const deserialized = SignatureEnvelope.deserialize(serialized)
|
|
3285
|
-
expect('init' in deserialized).toBe(false)
|
|
3286
|
-
})
|
|
3287
|
-
|
|
3288
|
-
test('init absent vs present produce different serializations', () => {
|
|
3289
|
-
expect(SignatureEnvelope.serialize(envelope)).not.toBe(
|
|
3290
|
-
SignatureEnvelope.serialize(bootstrapEnvelope),
|
|
3291
|
-
)
|
|
3292
|
-
})
|
|
3293
|
-
|
|
3294
|
-
test('toRpc/fromRpc round-trip with init', () => {
|
|
3295
|
-
const rpc = SignatureEnvelope.toRpc(
|
|
3296
|
-
bootstrapEnvelope,
|
|
3297
|
-
) as SignatureEnvelope.MultisigRpc
|
|
3298
|
-
expect(rpc.init).toEqual(init)
|
|
3299
|
-
expect(rpc.account).toBeUndefined()
|
|
3300
|
-
expect('type' in rpc).toBe(false)
|
|
3301
|
-
expect(SignatureEnvelope.fromRpc(rpc)).toEqual(bootstrapEnvelope)
|
|
3302
|
-
})
|
|
3303
|
-
|
|
3304
|
-
test('toRpc normalizes numberish init fields and the default salt', () => {
|
|
3305
|
-
const rpc = SignatureEnvelope.toRpc({
|
|
3306
|
-
...bootstrapEnvelope,
|
|
3307
|
-
init: {
|
|
3308
|
-
threshold: '0x1',
|
|
3309
|
-
owners: init.owners.map((owner) => ({
|
|
3310
|
-
...owner,
|
|
3311
|
-
weight: '0x1' as const,
|
|
3312
|
-
})),
|
|
3313
|
-
},
|
|
3314
|
-
} as SignatureEnvelope.Multisig<
|
|
3315
|
-
number | Hex.Hex
|
|
3316
|
-
>) as SignatureEnvelope.MultisigRpc
|
|
3317
|
-
expect(rpc.init).toEqual({
|
|
3318
|
-
salt: MultisigConfig.zeroSalt,
|
|
3319
|
-
threshold: 1,
|
|
3320
|
-
owners: init.owners,
|
|
3321
|
-
})
|
|
3322
|
-
})
|
|
3323
|
-
|
|
3324
|
-
test('toRpc encodes owner approvals as structured RPC envelopes', () => {
|
|
3325
|
-
const multisig = bootstrapEnvelope as SignatureEnvelope.Multisig
|
|
3326
|
-
const rpc = SignatureEnvelope.toRpc(
|
|
3327
|
-
multisig,
|
|
3328
|
-
) as SignatureEnvelope.MultisigRpc
|
|
3329
|
-
expect(rpc.signatures).toEqual(
|
|
3330
|
-
multisig.signatures.map((signature) =>
|
|
3331
|
-
SignatureEnvelope.toRpc(signature),
|
|
3338
|
+
],
|
|
3339
|
+
threshold: 1,
|
|
3340
|
+
}),
|
|
3332
3341
|
),
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3342
|
+
[Hex.slice(initial, -65)],
|
|
3343
|
+
'0x',
|
|
3344
|
+
]),
|
|
3345
|
+
},
|
|
3346
|
+
])('error: rejects malformed $name wire shape', ({ value }) => {
|
|
3347
|
+
expect(() =>
|
|
3348
|
+
SignatureEnvelope.deserialize(Hex.concat('0x05', value)),
|
|
3349
|
+
).toThrowError(SignatureEnvelope.InvalidSerializedError)
|
|
3350
|
+
})
|
|
3351
|
+
|
|
3352
|
+
test.each([
|
|
3353
|
+
{
|
|
3354
|
+
field: 'salt',
|
|
3355
|
+
value: [
|
|
3356
|
+
('0x' + '00'.repeat(31)) as Hex.Hex,
|
|
3357
|
+
'0x',
|
|
3358
|
+
'0x01',
|
|
3359
|
+
[['0x7e5f4552091a69125d5dfcb7b8c2659029395bdf', '0x01']],
|
|
3360
|
+
],
|
|
3361
|
+
},
|
|
3362
|
+
{
|
|
3363
|
+
field: 'oversized version',
|
|
3364
|
+
value: [
|
|
3365
|
+
('0x' + '00'.repeat(32)) as Hex.Hex,
|
|
3366
|
+
('0x' + '01'.repeat(9)) as Hex.Hex,
|
|
3367
|
+
'0x01',
|
|
3368
|
+
[['0x7e5f4552091a69125d5dfcb7b8c2659029395bdf', '0x01']],
|
|
3369
|
+
],
|
|
3370
|
+
},
|
|
3371
|
+
{
|
|
3372
|
+
field: 'zero version',
|
|
3373
|
+
value: [
|
|
3374
|
+
('0x' + '00'.repeat(32)) as Hex.Hex,
|
|
3375
|
+
'0x00',
|
|
3376
|
+
'0x01',
|
|
3377
|
+
[['0x7e5f4552091a69125d5dfcb7b8c2659029395bdf', '0x01']],
|
|
3378
|
+
],
|
|
3379
|
+
},
|
|
3380
|
+
{
|
|
3381
|
+
field: 'zero-prefixed version',
|
|
3382
|
+
value: [
|
|
3383
|
+
('0x' + '00'.repeat(32)) as Hex.Hex,
|
|
3384
|
+
'0x0001',
|
|
3385
|
+
'0x01',
|
|
3386
|
+
[['0x7e5f4552091a69125d5dfcb7b8c2659029395bdf', '0x01']],
|
|
3387
|
+
],
|
|
3388
|
+
},
|
|
3389
|
+
{
|
|
3390
|
+
field: 'threshold',
|
|
3391
|
+
value: [
|
|
3392
|
+
('0x' + '00'.repeat(32)) as Hex.Hex,
|
|
3393
|
+
'0x',
|
|
3394
|
+
'0x0001',
|
|
3395
|
+
[['0x7e5f4552091a69125d5dfcb7b8c2659029395bdf', '0x01']],
|
|
3396
|
+
],
|
|
3397
|
+
},
|
|
3398
|
+
{
|
|
3399
|
+
field: 'weight',
|
|
3400
|
+
value: [
|
|
3401
|
+
('0x' + '00'.repeat(32)) as Hex.Hex,
|
|
3402
|
+
'0x',
|
|
3403
|
+
'0x01',
|
|
3404
|
+
[['0x7e5f4552091a69125d5dfcb7b8c2659029395bdf', '0x0001']],
|
|
3405
|
+
],
|
|
3406
|
+
},
|
|
3407
|
+
])('error: rejects a noncanonical $field', ({ value }) => {
|
|
3408
|
+
const malformed = Hex.concat(
|
|
3409
|
+
'0x05',
|
|
3410
|
+
Rlp.fromHex([
|
|
3411
|
+
'0x9dba7f426b711d4893c11611eacf7cc334e7146b',
|
|
3412
|
+
value as never,
|
|
3413
|
+
[Hex.slice(initial, -65)],
|
|
3414
|
+
]),
|
|
3415
|
+
)
|
|
3374
3416
|
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
type: 'multisig',
|
|
3379
|
-
account,
|
|
3380
|
-
signatures: [innerP256],
|
|
3381
|
-
init,
|
|
3382
|
-
}),
|
|
3383
|
-
).toThrowErrorMatchingInlineSnapshot(
|
|
3384
|
-
`[SignatureEnvelope.InvalidMultisigApprovalError: Invalid native multisig owner approval: multisig init does not derive account.]`,
|
|
3385
|
-
)
|
|
3386
|
-
})
|
|
3417
|
+
expect(() => SignatureEnvelope.deserialize(malformed)).toThrowError(
|
|
3418
|
+
SignatureEnvelope.InvalidSerializedError,
|
|
3419
|
+
)
|
|
3387
3420
|
})
|
|
3388
3421
|
})
|