ox 1.7.1 → 1.7.2
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 +15 -0
- package/dist/core/Hash.js +1 -1
- package/dist/core/Hash.js.map +1 -1
- package/dist/tempo/KeyAuthorization.d.ts +1 -1
- 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 +324 -0
- package/dist/tempo/MultisigOperation.d.ts.map +1 -0
- package/dist/tempo/MultisigOperation.js +809 -0
- package/dist/tempo/MultisigOperation.js.map +1 -0
- package/dist/tempo/MultisigWitness.d.ts +106 -0
- package/dist/tempo/MultisigWitness.d.ts.map +1 -0
- package/dist/tempo/MultisigWitness.js +118 -0
- package/dist/tempo/MultisigWitness.js.map +1 -0
- package/dist/tempo/RpcSchemaTempo.d.ts +35 -0
- package/dist/tempo/RpcSchemaTempo.d.ts.map +1 -1
- package/dist/tempo/SignatureEnvelope.d.ts +60 -80
- package/dist/tempo/SignatureEnvelope.d.ts.map +1 -1
- package/dist/tempo/SignatureEnvelope.js +87 -146
- 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 +110 -110
- package/dist/zod/tempo/AuthorizationTempo.d.ts.map +1 -1
- package/dist/zod/tempo/KeyAuthorization.d.ts +90 -83
- 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 +168 -0
- package/dist/zod/tempo/MultisigOperation.d.ts.map +1 -0
- package/dist/zod/tempo/MultisigOperation.js +65 -0
- package/dist/zod/tempo/MultisigOperation.js.map +1 -0
- package/dist/zod/tempo/MultisigWitness.d.ts +152 -0
- package/dist/zod/tempo/MultisigWitness.d.ts.map +1 -0
- package/dist/zod/tempo/MultisigWitness.js +76 -0
- package/dist/zod/tempo/MultisigWitness.js.map +1 -0
- package/dist/zod/tempo/RpcSchemaTempo.d.ts +607 -102
- package/dist/zod/tempo/RpcSchemaTempo.d.ts.map +1 -1
- package/dist/zod/tempo/RpcSchemaTempo.js +38 -0
- package/dist/zod/tempo/RpcSchemaTempo.js.map +1 -1
- package/dist/zod/tempo/SignatureEnvelope.d.ts +33 -33
- package/dist/zod/tempo/SignatureEnvelope.d.ts.map +1 -1
- package/dist/zod/tempo/SignatureEnvelope.js +9 -16
- package/dist/zod/tempo/SignatureEnvelope.js.map +1 -1
- package/dist/zod/tempo/Transaction.d.ts +228 -222
- package/dist/zod/tempo/Transaction.d.ts.map +1 -1
- package/dist/zod/tempo/TransactionRequest.d.ts +379 -176
- 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/KeyAuthorization.test-d.ts +13 -1
- package/src/tempo/KeyAuthorization.test.ts +65 -0
- package/src/tempo/KeyAuthorization.ts +1 -1
- 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 +77 -0
- package/src/tempo/MultisigOperation.test.ts +1883 -0
- package/src/tempo/MultisigOperation.ts +1254 -0
- package/src/tempo/MultisigWitness.test-d.ts +42 -0
- package/src/tempo/MultisigWitness.test.ts +288 -0
- package/src/tempo/MultisigWitness.ts +210 -0
- package/src/tempo/RpcSchemaTempo.test-d.ts +56 -0
- package/src/tempo/RpcSchemaTempo.ts +37 -0
- package/src/tempo/SignatureEnvelope.test-d.ts +63 -46
- package/src/tempo/SignatureEnvelope.test.ts +523 -500
- package/src/tempo/SignatureEnvelope.ts +172 -242
- package/src/tempo/TransactionRequest.test-d.ts +11 -0
- package/src/tempo/TransactionRequest.test.ts +285 -33
- package/src/tempo/TransactionRequest.ts +15 -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 +113 -86
- package/src/version.ts +1 -1
- package/src/zod/tempo/MultisigConfig.ts +29 -3
- package/src/zod/tempo/MultisigOperation.ts +69 -0
- package/src/zod/tempo/MultisigWitness.ts +101 -0
- package/src/zod/tempo/RpcSchemaTempo.ts +43 -0
- package/src/zod/tempo/SignatureEnvelope.ts +20 -38
- package/src/zod/tempo/TransactionRequest.ts +17 -26
- package/src/zod/tempo/_test/KeyAuthorization.test.ts +11 -0
- 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 +68 -0
- package/src/zod/tempo/_test/MultisigWitness.test-d.ts +13 -0
- package/src/zod/tempo/_test/MultisigWitness.test.ts +149 -0
- package/src/zod/tempo/_test/RpcSchemaTempo.test-d.ts +31 -0
- package/src/zod/tempo/_test/RpcSchemaTempo.test.ts +41 -0
- package/src/zod/tempo/_test/SignatureEnvelope.test-d.ts +1 -1
- package/src/zod/tempo/_test/SignatureEnvelope.test.ts +25 -34
- package/src/zod/tempo/_test/TransactionRequest.test.ts +176 -8
- 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,
|
|
@@ -1116,72 +1117,86 @@ describe('from', () => {
|
|
|
1116
1117
|
})
|
|
1117
1118
|
|
|
1118
1119
|
describe('multisig', () => {
|
|
1119
|
-
const
|
|
1120
|
-
threshold: 1,
|
|
1120
|
+
const config = MultisigConfig.from({
|
|
1121
1121
|
owners: [
|
|
1122
1122
|
{
|
|
1123
1123
|
owner: '0x1111111111111111111111111111111111111111',
|
|
1124
1124
|
weight: 1,
|
|
1125
1125
|
},
|
|
1126
1126
|
],
|
|
1127
|
+
threshold: 1,
|
|
1127
1128
|
})
|
|
1128
1129
|
|
|
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`)', () => {
|
|
1130
|
+
test('behavior: derives an initial account from config', () => {
|
|
1144
1131
|
const envelope = SignatureEnvelope.from({
|
|
1145
|
-
|
|
1132
|
+
config,
|
|
1146
1133
|
signatures: [SignatureEnvelope.from(signature_secp256k1)],
|
|
1147
|
-
init: true,
|
|
1148
1134
|
})
|
|
1149
1135
|
|
|
1150
|
-
expect(
|
|
1151
|
-
|
|
1136
|
+
expect(envelope).toMatchInlineSnapshot(
|
|
1137
|
+
{
|
|
1138
|
+
signatures: [
|
|
1139
|
+
{
|
|
1140
|
+
signature: {
|
|
1141
|
+
r: expect.anything(),
|
|
1142
|
+
s: expect.anything(),
|
|
1143
|
+
yParity: expect.any(Number),
|
|
1144
|
+
},
|
|
1145
|
+
},
|
|
1146
|
+
],
|
|
1147
|
+
},
|
|
1148
|
+
`
|
|
1149
|
+
{
|
|
1150
|
+
"account": "0x8820d1497eeaf4f68e00b2cfc00a2f3b1dbb00da",
|
|
1151
|
+
"config": {
|
|
1152
|
+
"owners": [
|
|
1153
|
+
{
|
|
1154
|
+
"owner": "0x1111111111111111111111111111111111111111",
|
|
1155
|
+
"weight": 1,
|
|
1156
|
+
},
|
|
1157
|
+
],
|
|
1158
|
+
"salt": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
1159
|
+
"threshold": 1,
|
|
1160
|
+
"version": 0n,
|
|
1161
|
+
},
|
|
1162
|
+
"signatures": [
|
|
1163
|
+
{
|
|
1164
|
+
"signature": {
|
|
1165
|
+
"r": Anything,
|
|
1166
|
+
"s": Anything,
|
|
1167
|
+
"yParity": Any<Number>,
|
|
1168
|
+
},
|
|
1169
|
+
"type": "secp256k1",
|
|
1170
|
+
},
|
|
1171
|
+
],
|
|
1172
|
+
"type": "multisig",
|
|
1173
|
+
}
|
|
1174
|
+
`,
|
|
1152
1175
|
)
|
|
1153
1176
|
})
|
|
1154
1177
|
|
|
1155
|
-
test('behavior:
|
|
1156
|
-
const otherConfig = MultisigConfig.from({
|
|
1157
|
-
threshold: 1,
|
|
1158
|
-
owners: [
|
|
1159
|
-
{
|
|
1160
|
-
owner: '0x2222222222222222222222222222222222222222',
|
|
1161
|
-
weight: 1,
|
|
1162
|
-
},
|
|
1163
|
-
],
|
|
1164
|
-
})
|
|
1178
|
+
test('behavior: accepts an explicit account for a current config', () => {
|
|
1165
1179
|
const envelope = SignatureEnvelope.from({
|
|
1166
|
-
|
|
1180
|
+
account: '0x2222222222222222222222222222222222222222',
|
|
1181
|
+
config: { ...config, version: 1 },
|
|
1167
1182
|
signatures: [SignatureEnvelope.from(signature_secp256k1)],
|
|
1168
|
-
init: otherConfig,
|
|
1169
1183
|
})
|
|
1170
1184
|
|
|
1171
|
-
expect(
|
|
1185
|
+
expect(envelope.account).toBe(
|
|
1186
|
+
'0x2222222222222222222222222222222222222222',
|
|
1187
|
+
)
|
|
1188
|
+
expect(envelope.config.version).toBe(1n)
|
|
1172
1189
|
})
|
|
1173
1190
|
|
|
1174
|
-
test('
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
account,
|
|
1184
|
-
})
|
|
1191
|
+
test('error: requires an account for a current config', () => {
|
|
1192
|
+
expect(() =>
|
|
1193
|
+
SignatureEnvelope.from({
|
|
1194
|
+
config: { ...config, version: 1n },
|
|
1195
|
+
signatures: [SignatureEnvelope.from(signature_secp256k1)],
|
|
1196
|
+
} as never),
|
|
1197
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
1198
|
+
`[MultisigConfig.InvalidConfigError: Invalid native multisig config: account address requires version zero.]`,
|
|
1199
|
+
)
|
|
1185
1200
|
})
|
|
1186
1201
|
})
|
|
1187
1202
|
})
|
|
@@ -1793,8 +1808,6 @@ describe('serialize', () => {
|
|
|
1793
1808
|
})
|
|
1794
1809
|
|
|
1795
1810
|
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
1811
|
const ownerKeys = Array.from({ length: 3 }, () => {
|
|
1799
1812
|
const privateKey = Secp256k1.randomPrivateKey()
|
|
1800
1813
|
const address = Address.fromPublicKey(
|
|
@@ -1806,14 +1819,16 @@ describe('sortMultisigApprovals', () => {
|
|
|
1806
1819
|
Hex.toBigInt(a.address) < Hex.toBigInt(b.address) ? -1 : 1,
|
|
1807
1820
|
)
|
|
1808
1821
|
|
|
1809
|
-
const
|
|
1810
|
-
threshold: 2,
|
|
1822
|
+
const config = MultisigConfig.from({
|
|
1811
1823
|
owners: ascendingOwners.map((o) => ({ owner: o.address, weight: 1 })),
|
|
1824
|
+
threshold: 2,
|
|
1812
1825
|
})
|
|
1826
|
+
const account = MultisigConfig.getAddress(config)
|
|
1813
1827
|
const payload = `0x${'42'.repeat(32)}` as const
|
|
1814
1828
|
const digest = MultisigConfig.getSignPayload({
|
|
1829
|
+
account,
|
|
1830
|
+
config,
|
|
1815
1831
|
payload,
|
|
1816
|
-
initialConfig,
|
|
1817
1832
|
})
|
|
1818
1833
|
|
|
1819
1834
|
const owners = ownerKeys.map((owner) => ({
|
|
@@ -1829,7 +1844,8 @@ describe('sortMultisigApprovals', () => {
|
|
|
1829
1844
|
|
|
1830
1845
|
test('behavior: orders approvals ascending by recovered owner address', () => {
|
|
1831
1846
|
const ordered = SignatureEnvelope.sortMultisigApprovals({
|
|
1832
|
-
|
|
1847
|
+
account,
|
|
1848
|
+
config,
|
|
1833
1849
|
payload,
|
|
1834
1850
|
// Provide approvals in reverse of the canonical order.
|
|
1835
1851
|
signatures: [...ascending].reverse().map((owner) => owner.signature),
|
|
@@ -1841,7 +1857,8 @@ describe('sortMultisigApprovals', () => {
|
|
|
1841
1857
|
const signatures = ascending.map((owner) => owner.signature)
|
|
1842
1858
|
expect(
|
|
1843
1859
|
SignatureEnvelope.sortMultisigApprovals({
|
|
1844
|
-
|
|
1860
|
+
account,
|
|
1861
|
+
config,
|
|
1845
1862
|
payload,
|
|
1846
1863
|
signatures,
|
|
1847
1864
|
}),
|
|
@@ -1850,7 +1867,8 @@ describe('sortMultisigApprovals', () => {
|
|
|
1850
1867
|
|
|
1851
1868
|
test('behavior: recovered order matches the config owner order', () => {
|
|
1852
1869
|
const ordered = SignatureEnvelope.sortMultisigApprovals({
|
|
1853
|
-
|
|
1870
|
+
account,
|
|
1871
|
+
config,
|
|
1854
1872
|
payload,
|
|
1855
1873
|
signatures: owners.map((owner) => owner.signature),
|
|
1856
1874
|
})
|
|
@@ -1859,23 +1877,6 @@ describe('sortMultisigApprovals', () => {
|
|
|
1859
1877
|
)
|
|
1860
1878
|
expect(recovered).toEqual(ascending.map((owner) => owner.address))
|
|
1861
1879
|
})
|
|
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
1880
|
})
|
|
1880
1881
|
|
|
1881
1882
|
describe('validate', () => {
|
|
@@ -2880,146 +2881,371 @@ describe('CoercionError', () => {
|
|
|
2880
2881
|
})
|
|
2881
2882
|
|
|
2882
2883
|
describe('multisig', () => {
|
|
2883
|
-
const
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
const
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2884
|
+
const initial =
|
|
2885
|
+
'0x05f897949dba7f426b711d4893c11611eacf7cc334e7146bf83ba000000000000000000000000000000000000000000000000000000000000000008001d7d6947e5f4552091a69125d5dfcb7b8c2659029395bdf01f843b841869437e01f64bebeb78a8a6b30bfd3a993819c8cad82c807515d9b9e9b36f98535dfaa5eebc597715d05f6ce4927747f14fa4cd2acc717fdcd3877146437f8f41b' as const
|
|
2886
|
+
const current =
|
|
2887
|
+
'0x05f897949dba7f426b711d4893c11611eacf7cc334e7146bf83ba000000000000000000000000000000000000000000000000000000000000000000101d7d6942b5ad5c4795c026514f8317c7a215e218dccd6cf01f843b8414a0e5b5b4a90f08e6e8d676a73a22dc2cf022ffdcc9299512b5d9a6daf66e0504a395a2ef6f6c0f173910b875c45d01aa66d928e56ba6f49bbc8186f80268bf51b' as const
|
|
2888
|
+
const nested =
|
|
2889
|
+
'0x05f8f0949969e2243075b27a8eab61009c59b77ab13b83f6f83ba033333333333333333333333333333333333333333333333333333333333333338001d7d6944f9f5b162a7464bcc260ce44d7ae0d935f9c583701f89cb89a05f897944f9f5b162a7464bcc260ce44d7ae0d935f9c5837f83ba022222222222222222222222222222222222222222222222222222222222222228001d7d6946813eb9362372eef6200f3b1dbc3f819671cba6901f843b841032aa6f3ea7b0b7069720d0f3891983c493d149326c5c957d864ed7371b8475e3e95325079b24491f4b6d68920e4b3bacd7c6094df6ed88b8674864ab559c8fb1b' as const
|
|
2890
|
+
|
|
2891
|
+
test('example: decodes and re-encodes the frozen initial witness', () => {
|
|
2892
|
+
const envelope = SignatureEnvelope.deserialize(initial)
|
|
2893
|
+
|
|
2894
|
+
expect(envelope).toMatchInlineSnapshot(`
|
|
2895
|
+
{
|
|
2896
|
+
"account": "0x9dba7f426b711d4893c11611eacf7cc334e7146b",
|
|
2897
|
+
"config": {
|
|
2898
|
+
"owners": [
|
|
2899
|
+
{
|
|
2900
|
+
"owner": "0x7e5f4552091a69125d5dfcb7b8c2659029395bdf",
|
|
2901
|
+
"weight": 1,
|
|
2902
|
+
},
|
|
2903
|
+
],
|
|
2904
|
+
"salt": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
2905
|
+
"threshold": 1,
|
|
2906
|
+
"version": 0n,
|
|
2907
|
+
},
|
|
2908
|
+
"signatures": [
|
|
2909
|
+
{
|
|
2910
|
+
"signature": {
|
|
2911
|
+
"r": "0x869437e01f64bebeb78a8a6b30bfd3a993819c8cad82c807515d9b9e9b36f985",
|
|
2912
|
+
"s": "0x35dfaa5eebc597715d05f6ce4927747f14fa4cd2acc717fdcd3877146437f8f4",
|
|
2913
|
+
"yParity": 0,
|
|
2914
|
+
},
|
|
2915
|
+
"type": "secp256k1",
|
|
2916
|
+
},
|
|
2917
|
+
],
|
|
2918
|
+
"type": "multisig",
|
|
2919
|
+
}
|
|
2920
|
+
`)
|
|
2921
|
+
expect(SignatureEnvelope.serialize(envelope)).toBe(initial)
|
|
2891
2922
|
})
|
|
2892
2923
|
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2924
|
+
test('example: decodes and re-encodes the frozen current witness', () => {
|
|
2925
|
+
const envelope = SignatureEnvelope.deserialize(current)
|
|
2926
|
+
|
|
2927
|
+
expect(envelope).toMatchInlineSnapshot(`
|
|
2928
|
+
{
|
|
2929
|
+
"account": "0x9dba7f426b711d4893c11611eacf7cc334e7146b",
|
|
2930
|
+
"config": {
|
|
2931
|
+
"owners": [
|
|
2932
|
+
{
|
|
2933
|
+
"owner": "0x2b5ad5c4795c026514f8317c7a215e218dccd6cf",
|
|
2934
|
+
"weight": 1,
|
|
2935
|
+
},
|
|
2936
|
+
],
|
|
2937
|
+
"salt": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
2938
|
+
"threshold": 1,
|
|
2939
|
+
"version": 1n,
|
|
2940
|
+
},
|
|
2941
|
+
"signatures": [
|
|
2942
|
+
{
|
|
2943
|
+
"signature": {
|
|
2944
|
+
"r": "0x4a0e5b5b4a90f08e6e8d676a73a22dc2cf022ffdcc9299512b5d9a6daf66e050",
|
|
2945
|
+
"s": "0x4a395a2ef6f6c0f173910b875c45d01aa66d928e56ba6f49bbc8186f80268bf5",
|
|
2946
|
+
"yParity": 0,
|
|
2947
|
+
},
|
|
2948
|
+
"type": "secp256k1",
|
|
2949
|
+
},
|
|
2950
|
+
],
|
|
2951
|
+
"type": "multisig",
|
|
2952
|
+
}
|
|
2953
|
+
`)
|
|
2954
|
+
expect(SignatureEnvelope.serialize(envelope)).toBe(current)
|
|
2897
2955
|
})
|
|
2898
2956
|
|
|
2899
|
-
test('
|
|
2900
|
-
const
|
|
2901
|
-
|
|
2957
|
+
test('example: decodes and re-encodes the frozen nested witness', () => {
|
|
2958
|
+
const envelope = SignatureEnvelope.deserialize(nested)
|
|
2959
|
+
|
|
2960
|
+
expect(envelope).toMatchInlineSnapshot(`
|
|
2961
|
+
{
|
|
2962
|
+
"account": "0x9969e2243075b27a8eab61009c59b77ab13b83f6",
|
|
2963
|
+
"config": {
|
|
2964
|
+
"owners": [
|
|
2965
|
+
{
|
|
2966
|
+
"owner": "0x4f9f5b162a7464bcc260ce44d7ae0d935f9c5837",
|
|
2967
|
+
"weight": 1,
|
|
2968
|
+
},
|
|
2969
|
+
],
|
|
2970
|
+
"salt": "0x3333333333333333333333333333333333333333333333333333333333333333",
|
|
2971
|
+
"threshold": 1,
|
|
2972
|
+
"version": 0n,
|
|
2973
|
+
},
|
|
2974
|
+
"signatures": [
|
|
2975
|
+
{
|
|
2976
|
+
"account": "0x4f9f5b162a7464bcc260ce44d7ae0d935f9c5837",
|
|
2977
|
+
"config": {
|
|
2978
|
+
"owners": [
|
|
2979
|
+
{
|
|
2980
|
+
"owner": "0x6813eb9362372eef6200f3b1dbc3f819671cba69",
|
|
2981
|
+
"weight": 1,
|
|
2982
|
+
},
|
|
2983
|
+
],
|
|
2984
|
+
"salt": "0x2222222222222222222222222222222222222222222222222222222222222222",
|
|
2985
|
+
"threshold": 1,
|
|
2986
|
+
"version": 0n,
|
|
2987
|
+
},
|
|
2988
|
+
"signatures": [
|
|
2989
|
+
{
|
|
2990
|
+
"signature": {
|
|
2991
|
+
"r": "0x032aa6f3ea7b0b7069720d0f3891983c493d149326c5c957d864ed7371b8475e",
|
|
2992
|
+
"s": "0x3e95325079b24491f4b6d68920e4b3bacd7c6094df6ed88b8674864ab559c8fb",
|
|
2993
|
+
"yParity": 0,
|
|
2994
|
+
},
|
|
2995
|
+
"type": "secp256k1",
|
|
2996
|
+
},
|
|
2997
|
+
],
|
|
2998
|
+
"type": "multisig",
|
|
2999
|
+
},
|
|
3000
|
+
],
|
|
3001
|
+
"type": "multisig",
|
|
3002
|
+
}
|
|
3003
|
+
`)
|
|
3004
|
+
expect(SignatureEnvelope.serialize(envelope)).toBe(nested)
|
|
2902
3005
|
})
|
|
2903
3006
|
|
|
2904
|
-
test('
|
|
2905
|
-
const
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
)
|
|
3007
|
+
test('behavior: preserves exact 65-byte secp256k1 precedence', () => {
|
|
3008
|
+
const serialized = ('0x05' +
|
|
3009
|
+
'00'.repeat(31) +
|
|
3010
|
+
'00'.repeat(31) +
|
|
3011
|
+
'01' +
|
|
3012
|
+
'00') as Hex.Hex
|
|
3013
|
+
|
|
3014
|
+
expect(Hex.size(serialized)).toBe(65)
|
|
3015
|
+
expect(SignatureEnvelope.deserialize(serialized).type).toBe('secp256k1')
|
|
2913
3016
|
})
|
|
2914
3017
|
|
|
2915
|
-
test('
|
|
3018
|
+
test('behavior: serializes account, config, and approvals in wire order', () => {
|
|
3019
|
+
const envelope = SignatureEnvelope.deserialize(initial)
|
|
3020
|
+
if (envelope.type !== 'multisig') throw new Error('unreachable')
|
|
2916
3021
|
const serialized = SignatureEnvelope.serialize(envelope)
|
|
2917
|
-
|
|
3022
|
+
const decoded = Rlp.toHex(Hex.slice(serialized, 1))
|
|
3023
|
+
|
|
3024
|
+
expect(decoded).toMatchInlineSnapshot(`
|
|
3025
|
+
[
|
|
3026
|
+
"0x9dba7f426b711d4893c11611eacf7cc334e7146b",
|
|
3027
|
+
[
|
|
3028
|
+
"0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
3029
|
+
"0x",
|
|
3030
|
+
"0x01",
|
|
3031
|
+
[
|
|
3032
|
+
[
|
|
3033
|
+
"0x7e5f4552091a69125d5dfcb7b8c2659029395bdf",
|
|
3034
|
+
"0x01",
|
|
3035
|
+
],
|
|
3036
|
+
],
|
|
3037
|
+
],
|
|
3038
|
+
[
|
|
3039
|
+
"0x869437e01f64bebeb78a8a6b30bfd3a993819c8cad82c807515d9b9e9b36f98535dfaa5eebc597715d05f6ce4927747f14fa4cd2acc717fdcd3877146437f8f41b",
|
|
3040
|
+
],
|
|
3041
|
+
]
|
|
3042
|
+
`)
|
|
2918
3043
|
})
|
|
2919
3044
|
|
|
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
|
-
})
|
|
3045
|
+
test('behavior: round trips initial and current RPC witnesses', () => {
|
|
3046
|
+
const initialEnvelope = SignatureEnvelope.deserialize(initial)
|
|
3047
|
+
const currentEnvelope = SignatureEnvelope.deserialize(current)
|
|
3048
|
+
const rpc = {
|
|
3049
|
+
current: SignatureEnvelope.toRpc(currentEnvelope),
|
|
3050
|
+
initial: SignatureEnvelope.toRpc(initialEnvelope),
|
|
3051
|
+
}
|
|
2935
3052
|
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
3053
|
+
expect(rpc).toMatchInlineSnapshot(`
|
|
3054
|
+
{
|
|
3055
|
+
"current": {
|
|
3056
|
+
"account": "0x9dba7f426b711d4893c11611eacf7cc334e7146b",
|
|
3057
|
+
"config": {
|
|
3058
|
+
"owners": [
|
|
3059
|
+
{
|
|
3060
|
+
"owner": "0x2b5ad5c4795c026514f8317c7a215e218dccd6cf",
|
|
3061
|
+
"weight": 1,
|
|
3062
|
+
},
|
|
3063
|
+
],
|
|
3064
|
+
"salt": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
3065
|
+
"threshold": 1,
|
|
3066
|
+
"version": "0x1",
|
|
3067
|
+
},
|
|
3068
|
+
"signatures": [
|
|
3069
|
+
{
|
|
3070
|
+
"r": "0x4a0e5b5b4a90f08e6e8d676a73a22dc2cf022ffdcc9299512b5d9a6daf66e050",
|
|
3071
|
+
"s": "0x4a395a2ef6f6c0f173910b875c45d01aa66d928e56ba6f49bbc8186f80268bf5",
|
|
3072
|
+
"type": "secp256k1",
|
|
3073
|
+
"yParity": "0x0",
|
|
3074
|
+
},
|
|
3075
|
+
],
|
|
3076
|
+
},
|
|
3077
|
+
"initial": {
|
|
3078
|
+
"account": "0x9dba7f426b711d4893c11611eacf7cc334e7146b",
|
|
3079
|
+
"config": {
|
|
3080
|
+
"owners": [
|
|
3081
|
+
{
|
|
3082
|
+
"owner": "0x7e5f4552091a69125d5dfcb7b8c2659029395bdf",
|
|
3083
|
+
"weight": 1,
|
|
3084
|
+
},
|
|
3085
|
+
],
|
|
3086
|
+
"salt": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
3087
|
+
"threshold": 1,
|
|
3088
|
+
"version": "0x0",
|
|
3089
|
+
},
|
|
3090
|
+
"signatures": [
|
|
3091
|
+
{
|
|
3092
|
+
"r": "0x869437e01f64bebeb78a8a6b30bfd3a993819c8cad82c807515d9b9e9b36f985",
|
|
3093
|
+
"s": "0x35dfaa5eebc597715d05f6ce4927747f14fa4cd2acc717fdcd3877146437f8f4",
|
|
3094
|
+
"type": "secp256k1",
|
|
3095
|
+
"yParity": "0x0",
|
|
3096
|
+
},
|
|
3097
|
+
],
|
|
3098
|
+
},
|
|
3099
|
+
}
|
|
3100
|
+
`)
|
|
3101
|
+
expect(SignatureEnvelope.fromRpc(rpc.initial)).toStrictEqual(
|
|
3102
|
+
initialEnvelope,
|
|
2940
3103
|
)
|
|
2941
|
-
expect(
|
|
2942
|
-
|
|
3104
|
+
expect(SignatureEnvelope.fromRpc(rpc.current)).toStrictEqual(
|
|
3105
|
+
currentEnvelope,
|
|
2943
3106
|
)
|
|
2944
3107
|
})
|
|
2945
3108
|
|
|
2946
|
-
test('
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
3109
|
+
test('behavior: derives the account only for an initial witness', () => {
|
|
3110
|
+
const initialEnvelope = SignatureEnvelope.deserialize(initial)
|
|
3111
|
+
if (initialEnvelope.type !== 'multisig') throw new Error('unreachable')
|
|
3112
|
+
if (initialEnvelope.config.version !== 0n) throw new Error('unreachable')
|
|
3113
|
+
const derived = SignatureEnvelope.from({
|
|
3114
|
+
config: initialEnvelope.config as MultisigConfig.Config<0n>,
|
|
3115
|
+
signatures: initialEnvelope.signatures,
|
|
3116
|
+
})
|
|
3117
|
+
const currentEnvelope = SignatureEnvelope.deserialize(current)
|
|
3118
|
+
if (currentEnvelope.type !== 'multisig') throw new Error('unreachable')
|
|
3119
|
+
|
|
3120
|
+
expect(derived.account).toBe(initialEnvelope.account)
|
|
3121
|
+
expect(() =>
|
|
3122
|
+
SignatureEnvelope.from({
|
|
3123
|
+
config: currentEnvelope.config,
|
|
3124
|
+
signatures: currentEnvelope.signatures,
|
|
2957
3125
|
} as never),
|
|
2958
|
-
).
|
|
3126
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
3127
|
+
`[MultisigConfig.InvalidConfigError: Invalid native multisig config: account address requires version zero.]`,
|
|
3128
|
+
)
|
|
2959
3129
|
})
|
|
2960
3130
|
|
|
2961
|
-
test('
|
|
3131
|
+
test('behavior: extracts the multisig account', () => {
|
|
3132
|
+
const envelope = SignatureEnvelope.deserialize(initial)
|
|
3133
|
+
if (envelope.type !== 'multisig') throw new Error('unreachable')
|
|
3134
|
+
|
|
2962
3135
|
expect(
|
|
2963
3136
|
SignatureEnvelope.extractAddress({
|
|
2964
3137
|
payload: '0xdeadbeef',
|
|
2965
3138
|
signature: envelope,
|
|
2966
3139
|
}),
|
|
2967
|
-
).toBe(account)
|
|
2968
|
-
})
|
|
2969
|
-
|
|
2970
|
-
test('
|
|
2971
|
-
const
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
3140
|
+
).toBe(envelope.account)
|
|
3141
|
+
})
|
|
3142
|
+
|
|
3143
|
+
test('example: matches the frozen eight-approval boundary vector', () => {
|
|
3144
|
+
const config = MultisigConfig.from({
|
|
3145
|
+
owners: Array.from({ length: 8 }, (_, index) => ({
|
|
3146
|
+
owner:
|
|
3147
|
+
`0x${(index + 1).toString(16).padStart(40, '0')}` as `0x${string}`,
|
|
3148
|
+
weight: 1,
|
|
3149
|
+
})),
|
|
3150
|
+
salt: `0x${'66'.repeat(32)}`,
|
|
3151
|
+
threshold: 8,
|
|
3152
|
+
})
|
|
3153
|
+
const approval = SignatureEnvelope.from({
|
|
3154
|
+
r: '0x840cfc572845f5786e702984c2a582528cad4b49b2a10b9db1be7fca90058565',
|
|
3155
|
+
s: '0x25e7109ceb98168d95b09b18bbf6b685130e0562f233877d492b94eee0c5b6d1',
|
|
3156
|
+
yParity: 0,
|
|
3157
|
+
})
|
|
3158
|
+
const envelope = SignatureEnvelope.from({
|
|
3159
|
+
config,
|
|
3160
|
+
signatures: Array.from(
|
|
3161
|
+
{ length: MultisigConfig.maxSignatures },
|
|
3162
|
+
() => approval,
|
|
3163
|
+
),
|
|
2977
3164
|
})
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
expect(
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
3165
|
+
const serialized = SignatureEnvelope.serialize(envelope)
|
|
3166
|
+
|
|
3167
|
+
expect({
|
|
3168
|
+
hash: Hash.keccak256(serialized),
|
|
3169
|
+
length: Hex.size(serialized),
|
|
3170
|
+
signatureCount: envelope.signatures.length,
|
|
3171
|
+
}).toMatchInlineSnapshot(`
|
|
3172
|
+
{
|
|
3173
|
+
"hash": "0x1cb47fcf31fb1ebb3177ec1744eeda195e4d789920d1214af32baaa6150ad5e2",
|
|
3174
|
+
"length": 787,
|
|
3175
|
+
"signatureCount": 8,
|
|
3176
|
+
}
|
|
3177
|
+
`)
|
|
2984
3178
|
})
|
|
2985
3179
|
|
|
2986
|
-
test('
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
3180
|
+
test('behavior: accepts initial and current nested witnesses', () => {
|
|
3181
|
+
const initialEnvelope = SignatureEnvelope.deserialize(initial)
|
|
3182
|
+
const currentEnvelope = SignatureEnvelope.deserialize(current)
|
|
3183
|
+
if (
|
|
3184
|
+
initialEnvelope.type !== 'multisig' ||
|
|
3185
|
+
currentEnvelope.type !== 'multisig'
|
|
3186
|
+
)
|
|
3187
|
+
throw new Error('unreachable')
|
|
3188
|
+
|
|
3189
|
+
for (const signature of [initialEnvelope, currentEnvelope]) {
|
|
3190
|
+
const config = MultisigConfig.from({
|
|
3191
|
+
owners: [{ owner: signature.account, weight: 1 }],
|
|
3192
|
+
salt: ('0x' + 'ff'.repeat(32)) as Hex.Hex,
|
|
3193
|
+
threshold: 1,
|
|
3194
|
+
version: 1n,
|
|
3195
|
+
})
|
|
3196
|
+
const envelope = SignatureEnvelope.from({
|
|
3197
|
+
account: '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
|
|
3198
|
+
config,
|
|
3199
|
+
signatures: [signature],
|
|
3200
|
+
})
|
|
3201
|
+
|
|
3202
|
+
expect(
|
|
3203
|
+
SignatureEnvelope.deserialize(SignatureEnvelope.serialize(envelope)),
|
|
3204
|
+
).toStrictEqual(envelope)
|
|
3205
|
+
}
|
|
2990
3206
|
})
|
|
2991
3207
|
|
|
2992
|
-
test('
|
|
3208
|
+
test('error: rejects an initial account mismatch', () => {
|
|
3209
|
+
const envelope = SignatureEnvelope.deserialize(initial)
|
|
3210
|
+
if (envelope.type !== 'multisig') throw new Error('unreachable')
|
|
3211
|
+
|
|
2993
3212
|
expect(() =>
|
|
2994
3213
|
SignatureEnvelope.assert({
|
|
2995
|
-
|
|
2996
|
-
account,
|
|
2997
|
-
signatures: [],
|
|
3214
|
+
...envelope,
|
|
3215
|
+
account: '0x1111111111111111111111111111111111111111',
|
|
2998
3216
|
}),
|
|
2999
3217
|
).toThrowErrorMatchingInlineSnapshot(
|
|
3000
|
-
`[SignatureEnvelope.InvalidMultisigApprovalError: Invalid native multisig owner approval: multisig
|
|
3218
|
+
`[SignatureEnvelope.InvalidMultisigApprovalError: Invalid native multisig owner approval: initial multisig config does not derive account.]`,
|
|
3001
3219
|
)
|
|
3002
3220
|
})
|
|
3003
3221
|
|
|
3004
|
-
test('
|
|
3222
|
+
test('error: rejects a self-owned current witness', () => {
|
|
3223
|
+
const envelope = SignatureEnvelope.deserialize(current)
|
|
3224
|
+
if (envelope.type !== 'multisig') throw new Error('unreachable')
|
|
3225
|
+
const account = envelope.config.owners[0]!.owner
|
|
3226
|
+
|
|
3005
3227
|
expect(() =>
|
|
3006
|
-
SignatureEnvelope.assert({
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
() => innerP256,
|
|
3012
|
-
),
|
|
3013
|
-
}),
|
|
3014
|
-
).not.toThrow()
|
|
3228
|
+
SignatureEnvelope.assert({ ...envelope, account }),
|
|
3229
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
3230
|
+
`[SignatureEnvelope.InvalidMultisigApprovalError: Invalid native multisig owner approval: multisig account cannot be an owner.]`,
|
|
3231
|
+
)
|
|
3232
|
+
})
|
|
3015
3233
|
|
|
3234
|
+
test('error: rejects empty and excess approvals', () => {
|
|
3235
|
+
const envelope = SignatureEnvelope.deserialize(current)
|
|
3236
|
+
if (envelope.type !== 'multisig') throw new Error('unreachable')
|
|
3237
|
+
|
|
3238
|
+
expect(() =>
|
|
3239
|
+
SignatureEnvelope.assert({ ...envelope, signatures: [] }),
|
|
3240
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
3241
|
+
`[SignatureEnvelope.InvalidMultisigApprovalError: Invalid native multisig owner approval: multisig signatures cannot be empty.]`,
|
|
3242
|
+
)
|
|
3016
3243
|
expect(() =>
|
|
3017
3244
|
SignatureEnvelope.assert({
|
|
3018
|
-
|
|
3019
|
-
account,
|
|
3245
|
+
...envelope,
|
|
3020
3246
|
signatures: Array.from(
|
|
3021
3247
|
{ length: MultisigConfig.maxSignatures + 1 },
|
|
3022
|
-
() =>
|
|
3248
|
+
() => envelope.signatures[0]!,
|
|
3023
3249
|
),
|
|
3024
3250
|
}),
|
|
3025
3251
|
).toThrowErrorMatchingInlineSnapshot(
|
|
@@ -3027,7 +3253,23 @@ describe('multisig', () => {
|
|
|
3027
3253
|
)
|
|
3028
3254
|
})
|
|
3029
3255
|
|
|
3030
|
-
test('
|
|
3256
|
+
test('error: rejects keychain owner approvals', () => {
|
|
3257
|
+
const envelope = SignatureEnvelope.deserialize(current)
|
|
3258
|
+
if (envelope.type !== 'multisig') throw new Error('unreachable')
|
|
3259
|
+
|
|
3260
|
+
expect(() =>
|
|
3261
|
+
SignatureEnvelope.assert({
|
|
3262
|
+
...envelope,
|
|
3263
|
+
signatures: [signature_keychain_secp256k1],
|
|
3264
|
+
} as never),
|
|
3265
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
3266
|
+
`[SignatureEnvelope.InvalidMultisigApprovalError: Invalid native multisig owner approval: keychain owner approvals are not allowed.]`,
|
|
3267
|
+
)
|
|
3268
|
+
})
|
|
3269
|
+
|
|
3270
|
+
test('error: rejects oversized primitive approvals', () => {
|
|
3271
|
+
const envelope = SignatureEnvelope.deserialize(current)
|
|
3272
|
+
if (envelope.type !== 'multisig') throw new Error('unreachable')
|
|
3031
3273
|
const oversized = {
|
|
3032
3274
|
...signature_webauthn,
|
|
3033
3275
|
metadata: {
|
|
@@ -3035,354 +3277,135 @@ describe('multisig', () => {
|
|
|
3035
3277
|
clientDataJSON: JSON.stringify({ value: 'x'.repeat(2048) }),
|
|
3036
3278
|
},
|
|
3037
3279
|
}
|
|
3280
|
+
|
|
3038
3281
|
expect(() =>
|
|
3039
3282
|
SignatureEnvelope.assert({
|
|
3040
|
-
|
|
3041
|
-
account,
|
|
3283
|
+
...envelope,
|
|
3042
3284
|
signatures: [oversized],
|
|
3043
3285
|
}),
|
|
3044
3286
|
).toThrowErrorMatchingInlineSnapshot(
|
|
3045
3287
|
`[SignatureEnvelope.InvalidMultisigApprovalError: Invalid native multisig owner approval: multisig owner signature exceeds 2049 bytes.]`,
|
|
3046
3288
|
)
|
|
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
3289
|
})
|
|
3056
3290
|
|
|
3057
|
-
test('
|
|
3291
|
+
test('error: rejects nesting deeper than two nodes', () => {
|
|
3292
|
+
const envelope = SignatureEnvelope.deserialize(initial)
|
|
3293
|
+
if (envelope.type !== 'multisig') throw new Error('unreachable')
|
|
3294
|
+
const depth2 = SignatureEnvelope.from({
|
|
3295
|
+
...envelope,
|
|
3296
|
+
signatures: [envelope],
|
|
3297
|
+
})
|
|
3298
|
+
|
|
3058
3299
|
expect(() =>
|
|
3059
3300
|
SignatureEnvelope.assert({
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
} as never),
|
|
3301
|
+
...envelope,
|
|
3302
|
+
signatures: [depth2],
|
|
3303
|
+
}),
|
|
3064
3304
|
).toThrowErrorMatchingInlineSnapshot(
|
|
3065
|
-
`[SignatureEnvelope.InvalidMultisigApprovalError: Invalid native multisig owner approval:
|
|
3305
|
+
`[SignatureEnvelope.InvalidMultisigApprovalError: Invalid native multisig owner approval: multisig nesting depth exceeds 2.]`,
|
|
3066
3306
|
)
|
|
3067
3307
|
})
|
|
3068
3308
|
|
|
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 }],
|
|
3309
|
+
test.each([
|
|
3310
|
+
{
|
|
3311
|
+
name: 'two fields',
|
|
3312
|
+
value: Rlp.fromHex([
|
|
3313
|
+
'0x9dba7f426b711d4893c11611eacf7cc334e7146b',
|
|
3314
|
+
[Hex.slice(initial, -65)],
|
|
3315
|
+
]),
|
|
3316
|
+
},
|
|
3317
|
+
{
|
|
3318
|
+
name: 'trailing field',
|
|
3319
|
+
value: Rlp.fromHex([
|
|
3320
|
+
'0x9dba7f426b711d4893c11611eacf7cc334e7146b',
|
|
3321
|
+
MultisigConfig.toTuple(
|
|
3322
|
+
MultisigConfig.from({
|
|
3323
|
+
owners: [
|
|
3324
|
+
{
|
|
3325
|
+
owner: '0x7e5f4552091a69125d5dfcb7b8c2659029395bdf',
|
|
3326
|
+
weight: 1,
|
|
3174
3327
|
},
|
|
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),
|
|
3328
|
+
],
|
|
3329
|
+
threshold: 1,
|
|
3330
|
+
}),
|
|
3332
3331
|
),
|
|
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
|
-
|
|
3332
|
+
[Hex.slice(initial, -65)],
|
|
3333
|
+
'0x',
|
|
3334
|
+
]),
|
|
3335
|
+
},
|
|
3336
|
+
])('error: rejects malformed $name wire shape', ({ value }) => {
|
|
3337
|
+
expect(() =>
|
|
3338
|
+
SignatureEnvelope.deserialize(Hex.concat('0x05', value)),
|
|
3339
|
+
).toThrowError(SignatureEnvelope.InvalidSerializedError)
|
|
3340
|
+
})
|
|
3341
|
+
|
|
3342
|
+
test.each([
|
|
3343
|
+
{
|
|
3344
|
+
field: 'salt',
|
|
3345
|
+
value: [
|
|
3346
|
+
('0x' + '00'.repeat(31)) as Hex.Hex,
|
|
3347
|
+
'0x',
|
|
3348
|
+
'0x01',
|
|
3349
|
+
[['0x7e5f4552091a69125d5dfcb7b8c2659029395bdf', '0x01']],
|
|
3350
|
+
],
|
|
3351
|
+
},
|
|
3352
|
+
{
|
|
3353
|
+
field: 'oversized version',
|
|
3354
|
+
value: [
|
|
3355
|
+
('0x' + '00'.repeat(32)) as Hex.Hex,
|
|
3356
|
+
('0x' + '01'.repeat(9)) as Hex.Hex,
|
|
3357
|
+
'0x01',
|
|
3358
|
+
[['0x7e5f4552091a69125d5dfcb7b8c2659029395bdf', '0x01']],
|
|
3359
|
+
],
|
|
3360
|
+
},
|
|
3361
|
+
{
|
|
3362
|
+
field: 'zero version',
|
|
3363
|
+
value: [
|
|
3364
|
+
('0x' + '00'.repeat(32)) as Hex.Hex,
|
|
3365
|
+
'0x00',
|
|
3366
|
+
'0x01',
|
|
3367
|
+
[['0x7e5f4552091a69125d5dfcb7b8c2659029395bdf', '0x01']],
|
|
3368
|
+
],
|
|
3369
|
+
},
|
|
3370
|
+
{
|
|
3371
|
+
field: 'zero-prefixed version',
|
|
3372
|
+
value: [
|
|
3373
|
+
('0x' + '00'.repeat(32)) as Hex.Hex,
|
|
3374
|
+
'0x0001',
|
|
3375
|
+
'0x01',
|
|
3376
|
+
[['0x7e5f4552091a69125d5dfcb7b8c2659029395bdf', '0x01']],
|
|
3377
|
+
],
|
|
3378
|
+
},
|
|
3379
|
+
{
|
|
3380
|
+
field: 'threshold',
|
|
3381
|
+
value: [
|
|
3382
|
+
('0x' + '00'.repeat(32)) as Hex.Hex,
|
|
3383
|
+
'0x',
|
|
3384
|
+
'0x0001',
|
|
3385
|
+
[['0x7e5f4552091a69125d5dfcb7b8c2659029395bdf', '0x01']],
|
|
3386
|
+
],
|
|
3387
|
+
},
|
|
3388
|
+
{
|
|
3389
|
+
field: 'weight',
|
|
3390
|
+
value: [
|
|
3391
|
+
('0x' + '00'.repeat(32)) as Hex.Hex,
|
|
3392
|
+
'0x',
|
|
3393
|
+
'0x01',
|
|
3394
|
+
[['0x7e5f4552091a69125d5dfcb7b8c2659029395bdf', '0x0001']],
|
|
3395
|
+
],
|
|
3396
|
+
},
|
|
3397
|
+
])('error: rejects a noncanonical $field', ({ value }) => {
|
|
3398
|
+
const malformed = Hex.concat(
|
|
3399
|
+
'0x05',
|
|
3400
|
+
Rlp.fromHex([
|
|
3401
|
+
'0x9dba7f426b711d4893c11611eacf7cc334e7146b',
|
|
3402
|
+
value as never,
|
|
3403
|
+
[Hex.slice(initial, -65)],
|
|
3404
|
+
]),
|
|
3405
|
+
)
|
|
3374
3406
|
|
|
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
|
-
})
|
|
3407
|
+
expect(() => SignatureEnvelope.deserialize(malformed)).toThrowError(
|
|
3408
|
+
SignatureEnvelope.InvalidSerializedError,
|
|
3409
|
+
)
|
|
3387
3410
|
})
|
|
3388
3411
|
})
|