ox 0.14.29 → 0.14.31
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 +30 -0
- package/_cjs/core/AbiParameters.js +2 -1
- package/_cjs/core/AbiParameters.js.map +1 -1
- package/_cjs/core/internal/abiParameters.js +10 -2
- package/_cjs/core/internal/abiParameters.js.map +1 -1
- package/_cjs/tempo/KeyAuthorization.js +39 -10
- package/_cjs/tempo/KeyAuthorization.js.map +1 -1
- package/_cjs/tempo/MultisigConfig.js +26 -26
- package/_cjs/tempo/MultisigConfig.js.map +1 -1
- package/_cjs/tempo/SignatureEnvelope.js +189 -55
- package/_cjs/tempo/SignatureEnvelope.js.map +1 -1
- package/_cjs/version.js +1 -1
- package/_esm/core/AbiParameters.js +4 -1
- package/_esm/core/AbiParameters.js.map +1 -1
- package/_esm/core/internal/abiParameters.js +17 -2
- package/_esm/core/internal/abiParameters.js.map +1 -1
- package/_esm/tempo/KeyAuthorization.js +41 -9
- package/_esm/tempo/KeyAuthorization.js.map +1 -1
- package/_esm/tempo/MultisigConfig.js +53 -81
- package/_esm/tempo/MultisigConfig.js.map +1 -1
- package/_esm/tempo/SignatureEnvelope.js +202 -75
- package/_esm/tempo/SignatureEnvelope.js.map +1 -1
- package/_esm/tempo/index.js +2 -2
- package/_esm/version.js +1 -1
- package/_types/core/AbiParameters.d.ts.map +1 -1
- package/_types/core/internal/abiParameters.d.ts.map +1 -1
- package/_types/core/internal/rpcSchemas/eth.d.ts +16 -0
- package/_types/core/internal/rpcSchemas/eth.d.ts.map +1 -1
- package/_types/tempo/KeyAuthorization.d.ts +22 -16
- package/_types/tempo/KeyAuthorization.d.ts.map +1 -1
- package/_types/tempo/MultisigConfig.d.ts +43 -83
- package/_types/tempo/MultisigConfig.d.ts.map +1 -1
- package/_types/tempo/SignatureEnvelope.d.ts +62 -40
- package/_types/tempo/SignatureEnvelope.d.ts.map +1 -1
- package/_types/tempo/index.d.ts +2 -2
- package/_types/version.d.ts +1 -1
- package/core/AbiParameters.ts +3 -1
- package/core/internal/abiParameters.ts +17 -2
- package/core/internal/rpcSchemas/eth.ts +16 -0
- package/package.json +11 -1
- package/tempo/KeyAuthorization.test-d/package.json +6 -0
- package/tempo/KeyAuthorization.test-d.ts +62 -0
- package/tempo/KeyAuthorization.test.ts +132 -0
- package/tempo/KeyAuthorization.ts +76 -29
- package/tempo/MultisigConfig.test.ts +86 -37
- package/tempo/MultisigConfig.ts +75 -132
- package/tempo/SignatureEnvelope.test-d/package.json +6 -0
- package/tempo/SignatureEnvelope.test-d.ts +97 -0
- package/tempo/SignatureEnvelope.test.ts +322 -30
- package/tempo/SignatureEnvelope.ts +314 -118
- package/tempo/e2e.test.ts +22 -207
- package/tempo/index.ts +2 -2
- package/tempo/multisig.e2e.test.ts +582 -0
- package/version.ts +1 -1
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
Hex,
|
|
4
4
|
P256,
|
|
5
5
|
PublicKey,
|
|
6
|
+
Rlp,
|
|
6
7
|
Secp256k1,
|
|
7
8
|
Signature,
|
|
8
9
|
WebAuthnP256,
|
|
@@ -1109,7 +1110,7 @@ describe('from', () => {
|
|
|
1109
1110
|
],
|
|
1110
1111
|
})
|
|
1111
1112
|
|
|
1112
|
-
test('behavior: derives `account
|
|
1113
|
+
test('behavior: derives `account` from `genesisConfig`', () => {
|
|
1113
1114
|
const envelope = SignatureEnvelope.from({
|
|
1114
1115
|
genesisConfig,
|
|
1115
1116
|
signatures: [SignatureEnvelope.from(signature_secp256k1)],
|
|
@@ -1118,7 +1119,6 @@ describe('from', () => {
|
|
|
1118
1119
|
expect(envelope).toMatchObject({
|
|
1119
1120
|
type: 'multisig',
|
|
1120
1121
|
account: MultisigConfig.getAddress(genesisConfig),
|
|
1121
|
-
genesisConfigId: MultisigConfig.toId(genesisConfig),
|
|
1122
1122
|
})
|
|
1123
1123
|
expect('genesisConfig' in envelope).toBe(false)
|
|
1124
1124
|
expect((envelope as SignatureEnvelope.Multisig).init).toBeUndefined()
|
|
@@ -1155,19 +1155,16 @@ describe('from', () => {
|
|
|
1155
1155
|
expect((envelope as SignatureEnvelope.Multisig).init).toEqual(otherConfig)
|
|
1156
1156
|
})
|
|
1157
1157
|
|
|
1158
|
-
test('behavior: `{account
|
|
1158
|
+
test('behavior: `{ account }` form still works', () => {
|
|
1159
1159
|
const account = MultisigConfig.getAddress(genesisConfig)
|
|
1160
|
-
const genesisConfigId = MultisigConfig.toId(genesisConfig)
|
|
1161
1160
|
const envelope = SignatureEnvelope.from({
|
|
1162
1161
|
account,
|
|
1163
|
-
genesisConfigId,
|
|
1164
1162
|
signatures: [SignatureEnvelope.from(signature_secp256k1)],
|
|
1165
1163
|
})
|
|
1166
1164
|
|
|
1167
1165
|
expect(envelope).toMatchObject({
|
|
1168
1166
|
type: 'multisig',
|
|
1169
1167
|
account,
|
|
1170
|
-
genesisConfigId,
|
|
1171
1168
|
})
|
|
1172
1169
|
})
|
|
1173
1170
|
})
|
|
@@ -1836,9 +1833,8 @@ describe('sortMultisigApprovals', () => {
|
|
|
1836
1833
|
expect(recovered).toEqual(ascending.map((owner) => owner.address))
|
|
1837
1834
|
})
|
|
1838
1835
|
|
|
1839
|
-
test('behavior: `genesisConfig` and `{account
|
|
1836
|
+
test('behavior: `genesisConfig` and `{ account }` produce identical ordering', () => {
|
|
1840
1837
|
const account = MultisigConfig.getAddress(genesisConfig)
|
|
1841
|
-
const genesisConfigId = MultisigConfig.toId(genesisConfig)
|
|
1842
1838
|
const signatures = owners.map((owner) => owner.signature)
|
|
1843
1839
|
|
|
1844
1840
|
const fromConfig = SignatureEnvelope.sortMultisigApprovals({
|
|
@@ -1846,13 +1842,12 @@ describe('sortMultisigApprovals', () => {
|
|
|
1846
1842
|
payload,
|
|
1847
1843
|
signatures,
|
|
1848
1844
|
})
|
|
1849
|
-
const
|
|
1845
|
+
const fromAccount = SignatureEnvelope.sortMultisigApprovals({
|
|
1850
1846
|
account,
|
|
1851
|
-
genesisConfigId,
|
|
1852
1847
|
payload,
|
|
1853
1848
|
signatures,
|
|
1854
1849
|
})
|
|
1855
|
-
expect(fromConfig).toEqual(
|
|
1850
|
+
expect(fromConfig).toEqual(fromAccount)
|
|
1856
1851
|
})
|
|
1857
1852
|
})
|
|
1858
1853
|
|
|
@@ -2855,8 +2850,6 @@ describe('CoercionError', () => {
|
|
|
2855
2850
|
|
|
2856
2851
|
describe('multisig', () => {
|
|
2857
2852
|
const account = '0x8ba6d26ff5c4e82ba0c8caf8c8ca794e1489a7ae'
|
|
2858
|
-
const genesisConfigId =
|
|
2859
|
-
'0x01781fe551182476f2422c759e82d81c92e3263737afbbad57def6e8b69d21f5'
|
|
2860
2853
|
|
|
2861
2854
|
// P256 signatures do not carry `yParity` in the wire format, so use a clean
|
|
2862
2855
|
// inner signature for round-trip equality checks.
|
|
@@ -2869,7 +2862,6 @@ describe('multisig', () => {
|
|
|
2869
2862
|
const envelope = SignatureEnvelope.from({
|
|
2870
2863
|
type: 'multisig',
|
|
2871
2864
|
account,
|
|
2872
|
-
genesisConfigId,
|
|
2873
2865
|
signatures: [SignatureEnvelope.from(signature_secp256k1), innerP256],
|
|
2874
2866
|
})
|
|
2875
2867
|
|
|
@@ -2878,13 +2870,61 @@ describe('multisig', () => {
|
|
|
2878
2870
|
expect(serialized.startsWith('0x05')).toBe(true)
|
|
2879
2871
|
})
|
|
2880
2872
|
|
|
2873
|
+
test('serialize: wire shape is `0x05 || rlp([account, signatures])`', () => {
|
|
2874
|
+
const deterministic = SignatureEnvelope.from({
|
|
2875
|
+
type: 'multisig',
|
|
2876
|
+
account,
|
|
2877
|
+
signatures: [innerP256],
|
|
2878
|
+
})
|
|
2879
|
+
expect(SignatureEnvelope.serialize(deterministic)).toMatchInlineSnapshot(
|
|
2880
|
+
`"0x05f89b948ba6d26ff5c4e82ba0c8caf8c8ca794e1489a7aef884b88201ccbb3485d4726235f13cb15ef394fb7158179fb7b1925eccec0147671090c52e77c3c53373cc1e3b05e7c23f609deb17cea8fe097300c45411237e9fe4166b35ad8ac16e167d6992c3e120d7f17d2376bc1cbcf30c46ba6dd00ce07303e742f511edf6ce1c32de66846f56afa7be1cbd729bc35750b6d0cdcf3ec9d75461aba001"`,
|
|
2881
|
+
)
|
|
2882
|
+
})
|
|
2883
|
+
|
|
2881
2884
|
test('serialize/deserialize round-trip', () => {
|
|
2882
2885
|
const serialized = SignatureEnvelope.serialize(envelope)
|
|
2883
2886
|
expect(SignatureEnvelope.deserialize(serialized)).toEqual(envelope)
|
|
2884
2887
|
})
|
|
2885
2888
|
|
|
2889
|
+
test('deserialize: rejects the legacy three-field wire shape', () => {
|
|
2890
|
+
const init = MultisigConfig.toTuple({
|
|
2891
|
+
threshold: 1,
|
|
2892
|
+
owners: [
|
|
2893
|
+
{ owner: '0x1111111111111111111111111111111111111111', weight: 1 },
|
|
2894
|
+
],
|
|
2895
|
+
})
|
|
2896
|
+
const legacy = Hex.concat(
|
|
2897
|
+
'0x05',
|
|
2898
|
+
Rlp.fromHex([account, [SignatureEnvelope.serialize(innerP256)], init]),
|
|
2899
|
+
)
|
|
2900
|
+
expect(() => SignatureEnvelope.deserialize(legacy)).toThrowError(
|
|
2901
|
+
SignatureEnvelope.InvalidSerializedError,
|
|
2902
|
+
)
|
|
2903
|
+
})
|
|
2904
|
+
|
|
2905
|
+
test('deserialize: rejects an invalid initialized account', () => {
|
|
2906
|
+
const malformed = Hex.concat(
|
|
2907
|
+
'0x05',
|
|
2908
|
+
Rlp.fromHex(['0x11', [SignatureEnvelope.serialize(innerP256)]]),
|
|
2909
|
+
)
|
|
2910
|
+
expect(() => SignatureEnvelope.deserialize(malformed)).toThrowError(
|
|
2911
|
+
SignatureEnvelope.InvalidSerializedError,
|
|
2912
|
+
)
|
|
2913
|
+
})
|
|
2914
|
+
|
|
2886
2915
|
test('getType', () => {
|
|
2887
2916
|
expect(SignatureEnvelope.getType(envelope)).toBe('multisig')
|
|
2917
|
+
expect(
|
|
2918
|
+
SignatureEnvelope.getType({
|
|
2919
|
+
init: {
|
|
2920
|
+
threshold: 1,
|
|
2921
|
+
owners: [
|
|
2922
|
+
{ owner: '0x1111111111111111111111111111111111111111', weight: 1 },
|
|
2923
|
+
],
|
|
2924
|
+
},
|
|
2925
|
+
signatures: [innerP256],
|
|
2926
|
+
} as never),
|
|
2927
|
+
).toBe('multisig')
|
|
2888
2928
|
})
|
|
2889
2929
|
|
|
2890
2930
|
test('extractAddress returns the multisig account', () => {
|
|
@@ -2897,9 +2937,19 @@ describe('multisig', () => {
|
|
|
2897
2937
|
})
|
|
2898
2938
|
|
|
2899
2939
|
test('toRpc/fromRpc round-trip', () => {
|
|
2900
|
-
const rpc = SignatureEnvelope.toRpc(
|
|
2901
|
-
|
|
2940
|
+
const rpc = SignatureEnvelope.toRpc(
|
|
2941
|
+
envelope,
|
|
2942
|
+
) as SignatureEnvelope.MultisigRpc
|
|
2943
|
+
expect(rpc).toMatchObject({
|
|
2944
|
+
account,
|
|
2945
|
+
signatures: [{ type: 'secp256k1' }, { type: 'p256' }],
|
|
2946
|
+
})
|
|
2947
|
+
expect(rpc.init).toBeUndefined()
|
|
2948
|
+
expect('type' in rpc).toBe(false)
|
|
2902
2949
|
expect(SignatureEnvelope.fromRpc(rpc)).toEqual(envelope)
|
|
2950
|
+
expect(
|
|
2951
|
+
SignatureEnvelope.fromRpc({ ...rpc, type: 'multisig' } as never),
|
|
2952
|
+
).toEqual(envelope)
|
|
2903
2953
|
})
|
|
2904
2954
|
|
|
2905
2955
|
test('assert: missing properties', () => {
|
|
@@ -2908,6 +2958,156 @@ describe('multisig', () => {
|
|
|
2908
2958
|
).toThrowError()
|
|
2909
2959
|
})
|
|
2910
2960
|
|
|
2961
|
+
test('assert: rejects empty owner approvals', () => {
|
|
2962
|
+
expect(() =>
|
|
2963
|
+
SignatureEnvelope.assert({
|
|
2964
|
+
type: 'multisig',
|
|
2965
|
+
account,
|
|
2966
|
+
signatures: [],
|
|
2967
|
+
}),
|
|
2968
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
2969
|
+
`[SignatureEnvelope.InvalidMultisigApprovalError: Invalid native multisig owner approval: multisig signatures cannot be empty.]`,
|
|
2970
|
+
)
|
|
2971
|
+
})
|
|
2972
|
+
|
|
2973
|
+
test('assert: accepts at most eight owner approvals', () => {
|
|
2974
|
+
expect(() =>
|
|
2975
|
+
SignatureEnvelope.assert({
|
|
2976
|
+
type: 'multisig',
|
|
2977
|
+
account,
|
|
2978
|
+
signatures: Array.from(
|
|
2979
|
+
{ length: MultisigConfig.maxSignatures },
|
|
2980
|
+
() => innerP256,
|
|
2981
|
+
),
|
|
2982
|
+
}),
|
|
2983
|
+
).not.toThrow()
|
|
2984
|
+
|
|
2985
|
+
expect(() =>
|
|
2986
|
+
SignatureEnvelope.assert({
|
|
2987
|
+
type: 'multisig',
|
|
2988
|
+
account,
|
|
2989
|
+
signatures: Array.from(
|
|
2990
|
+
{ length: MultisigConfig.maxSignatures + 1 },
|
|
2991
|
+
() => innerP256,
|
|
2992
|
+
),
|
|
2993
|
+
}),
|
|
2994
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
2995
|
+
`[SignatureEnvelope.InvalidMultisigApprovalError: Invalid native multisig owner approval: multisig signatures exceed 8.]`,
|
|
2996
|
+
)
|
|
2997
|
+
})
|
|
2998
|
+
|
|
2999
|
+
test('assert: rejects oversized owner approvals', () => {
|
|
3000
|
+
const oversized = {
|
|
3001
|
+
...signature_webauthn,
|
|
3002
|
+
metadata: {
|
|
3003
|
+
...signature_webauthn.metadata,
|
|
3004
|
+
clientDataJSON: JSON.stringify({ value: 'x'.repeat(2048) }),
|
|
3005
|
+
},
|
|
3006
|
+
}
|
|
3007
|
+
expect(() =>
|
|
3008
|
+
SignatureEnvelope.assert({
|
|
3009
|
+
type: 'multisig',
|
|
3010
|
+
account,
|
|
3011
|
+
signatures: [oversized],
|
|
3012
|
+
}),
|
|
3013
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
3014
|
+
`[SignatureEnvelope.InvalidMultisigApprovalError: Invalid native multisig owner approval: multisig owner signature exceeds 2049 bytes.]`,
|
|
3015
|
+
)
|
|
3016
|
+
})
|
|
3017
|
+
|
|
3018
|
+
test('assert: rejects keychain owner approvals', () => {
|
|
3019
|
+
expect(() =>
|
|
3020
|
+
SignatureEnvelope.assert({
|
|
3021
|
+
type: 'multisig',
|
|
3022
|
+
account,
|
|
3023
|
+
signatures: [signature_keychain_secp256k1],
|
|
3024
|
+
} as never),
|
|
3025
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
3026
|
+
`[SignatureEnvelope.InvalidMultisigApprovalError: Invalid native multisig owner approval: keychain owner approvals are not allowed.]`,
|
|
3027
|
+
)
|
|
3028
|
+
})
|
|
3029
|
+
|
|
3030
|
+
describe('nested approvals', () => {
|
|
3031
|
+
const nestedAccount = '0x1111111111111111111111111111111111111111'
|
|
3032
|
+
const nested = SignatureEnvelope.from({
|
|
3033
|
+
type: 'multisig',
|
|
3034
|
+
account: nestedAccount,
|
|
3035
|
+
signatures: [innerP256],
|
|
3036
|
+
})
|
|
3037
|
+
|
|
3038
|
+
test('serialize/deserialize round-trip with a nested multisig approval', () => {
|
|
3039
|
+
const parent = SignatureEnvelope.from({
|
|
3040
|
+
type: 'multisig',
|
|
3041
|
+
account,
|
|
3042
|
+
signatures: [innerP256, nested],
|
|
3043
|
+
})
|
|
3044
|
+
const serialized = SignatureEnvelope.serialize(parent)
|
|
3045
|
+
expect(SignatureEnvelope.deserialize(serialized)).toEqual(parent)
|
|
3046
|
+
})
|
|
3047
|
+
|
|
3048
|
+
test('assert: accepts the maximum nesting depth', () => {
|
|
3049
|
+
const depth2 = SignatureEnvelope.from({
|
|
3050
|
+
type: 'multisig',
|
|
3051
|
+
account,
|
|
3052
|
+
signatures: [nested],
|
|
3053
|
+
})
|
|
3054
|
+
expect(() => SignatureEnvelope.assert(depth2)).not.toThrowError()
|
|
3055
|
+
})
|
|
3056
|
+
|
|
3057
|
+
test('assert: rejects nesting deeper than the maximum', () => {
|
|
3058
|
+
const depth3 = SignatureEnvelope.from({
|
|
3059
|
+
type: 'multisig',
|
|
3060
|
+
account,
|
|
3061
|
+
signatures: [
|
|
3062
|
+
SignatureEnvelope.from({
|
|
3063
|
+
type: 'multisig',
|
|
3064
|
+
account: nestedAccount,
|
|
3065
|
+
signatures: [nested],
|
|
3066
|
+
}),
|
|
3067
|
+
],
|
|
3068
|
+
})
|
|
3069
|
+
expect(() =>
|
|
3070
|
+
SignatureEnvelope.assert(depth3),
|
|
3071
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
3072
|
+
`[SignatureEnvelope.InvalidMultisigApprovalError: Invalid native multisig owner approval: multisig nesting depth exceeds 2.]`,
|
|
3073
|
+
)
|
|
3074
|
+
})
|
|
3075
|
+
|
|
3076
|
+
test('deserialize: rejects nesting deeper than the maximum during decode', () => {
|
|
3077
|
+
const primitive = SignatureEnvelope.serialize(innerP256)
|
|
3078
|
+
const depth1 = Hex.concat(
|
|
3079
|
+
'0x05',
|
|
3080
|
+
Rlp.fromHex([nestedAccount, [primitive]]),
|
|
3081
|
+
)
|
|
3082
|
+
const depth2 = Hex.concat('0x05', Rlp.fromHex([nestedAccount, [depth1]]))
|
|
3083
|
+
const depth3 = Hex.concat('0x05', Rlp.fromHex([account, [depth2]]))
|
|
3084
|
+
|
|
3085
|
+
expect(() => SignatureEnvelope.deserialize(depth3)).toThrowError(
|
|
3086
|
+
SignatureEnvelope.InvalidSerializedError,
|
|
3087
|
+
)
|
|
3088
|
+
})
|
|
3089
|
+
|
|
3090
|
+
test('assert: rejects nested approvals carrying `init`', () => {
|
|
3091
|
+
expect(() =>
|
|
3092
|
+
SignatureEnvelope.assert({
|
|
3093
|
+
type: 'multisig',
|
|
3094
|
+
account,
|
|
3095
|
+
signatures: [
|
|
3096
|
+
{
|
|
3097
|
+
...nested,
|
|
3098
|
+
init: {
|
|
3099
|
+
threshold: 1,
|
|
3100
|
+
owners: [{ owner: nestedAccount, weight: 1 }],
|
|
3101
|
+
},
|
|
3102
|
+
},
|
|
3103
|
+
],
|
|
3104
|
+
} as never),
|
|
3105
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
3106
|
+
`[SignatureEnvelope.InvalidMultisigApprovalError: Invalid native multisig owner approval: nested multisig owner approvals cannot carry \`init\`.]`,
|
|
3107
|
+
)
|
|
3108
|
+
})
|
|
3109
|
+
})
|
|
3110
|
+
|
|
2911
3111
|
describe('init (bootstrap)', () => {
|
|
2912
3112
|
const init = {
|
|
2913
3113
|
salt: `0x${'00'.repeat(32)}` as const,
|
|
@@ -2919,11 +3119,11 @@ describe('multisig', () => {
|
|
|
2919
3119
|
},
|
|
2920
3120
|
],
|
|
2921
3121
|
}
|
|
3122
|
+
const bootstrapAccount = MultisigConfig.getAddress(init)
|
|
2922
3123
|
|
|
2923
3124
|
const bootstrapEnvelope = SignatureEnvelope.from({
|
|
2924
3125
|
type: 'multisig',
|
|
2925
|
-
account,
|
|
2926
|
-
genesisConfigId,
|
|
3126
|
+
account: bootstrapAccount,
|
|
2927
3127
|
signatures: [SignatureEnvelope.from(signature_secp256k1), innerP256],
|
|
2928
3128
|
init,
|
|
2929
3129
|
})
|
|
@@ -2935,13 +3135,68 @@ describe('multisig', () => {
|
|
|
2935
3135
|
)
|
|
2936
3136
|
})
|
|
2937
3137
|
|
|
3138
|
+
test('deserialize: validates bootstrap init semantics', () => {
|
|
3139
|
+
const invalid = Hex.concat(
|
|
3140
|
+
'0x05',
|
|
3141
|
+
Rlp.fromHex([
|
|
3142
|
+
[MultisigConfig.zeroSalt, '0x01', []],
|
|
3143
|
+
[SignatureEnvelope.serialize(innerP256)],
|
|
3144
|
+
]),
|
|
3145
|
+
)
|
|
3146
|
+
expect(() => SignatureEnvelope.deserialize(invalid)).toThrowError(
|
|
3147
|
+
MultisigConfig.InvalidConfigError,
|
|
3148
|
+
)
|
|
3149
|
+
})
|
|
3150
|
+
|
|
3151
|
+
test('deserialize: rejects noncanonical bootstrap field widths', () => {
|
|
3152
|
+
const signatures = [SignatureEnvelope.serialize(innerP256)]
|
|
3153
|
+
const shortSalt = Hex.concat(
|
|
3154
|
+
'0x05',
|
|
3155
|
+
Rlp.fromHex([
|
|
3156
|
+
[`0x${'00'.repeat(31)}`, '0x01', [[init.owners[0]!.owner, '0x01']]],
|
|
3157
|
+
signatures,
|
|
3158
|
+
]),
|
|
3159
|
+
)
|
|
3160
|
+
const wideThreshold = Hex.concat(
|
|
3161
|
+
'0x05',
|
|
3162
|
+
Rlp.fromHex([
|
|
3163
|
+
[init.salt, '0x0001', [[init.owners[0]!.owner, '0x01']]],
|
|
3164
|
+
signatures,
|
|
3165
|
+
]),
|
|
3166
|
+
)
|
|
3167
|
+
const wideWeight = Hex.concat(
|
|
3168
|
+
'0x05',
|
|
3169
|
+
Rlp.fromHex([
|
|
3170
|
+
[init.salt, '0x01', [[init.owners[0]!.owner, '0x0001']]],
|
|
3171
|
+
signatures,
|
|
3172
|
+
]),
|
|
3173
|
+
)
|
|
3174
|
+
|
|
3175
|
+
for (const serialized of [shortSalt, wideThreshold, wideWeight])
|
|
3176
|
+
expect(() => SignatureEnvelope.deserialize(serialized)).toThrowError(
|
|
3177
|
+
SignatureEnvelope.InvalidSerializedError,
|
|
3178
|
+
)
|
|
3179
|
+
})
|
|
3180
|
+
|
|
3181
|
+
test('serialize: wire shape is `0x05 || rlp([init, signatures])`', () => {
|
|
3182
|
+
const serialized = SignatureEnvelope.serialize(bootstrapEnvelope)
|
|
3183
|
+
const [address] = Rlp.toHex(Hex.slice(serialized, 1)) as readonly [
|
|
3184
|
+
MultisigConfig.Tuple,
|
|
3185
|
+
readonly Hex.Hex[],
|
|
3186
|
+
]
|
|
3187
|
+
expect(MultisigConfig.fromTuple(address)).toEqual(init)
|
|
3188
|
+
})
|
|
3189
|
+
|
|
2938
3190
|
test('serialize/deserialize round-trip preserves non-zero salt', () => {
|
|
3191
|
+
const saltedInit = {
|
|
3192
|
+
...init,
|
|
3193
|
+
salt: `0x${'42'.repeat(32)}` as const,
|
|
3194
|
+
}
|
|
2939
3195
|
const salted = SignatureEnvelope.from({
|
|
2940
3196
|
type: 'multisig',
|
|
2941
|
-
account,
|
|
2942
|
-
genesisConfigId,
|
|
3197
|
+
account: MultisigConfig.getAddress(saltedInit),
|
|
2943
3198
|
signatures: [SignatureEnvelope.from(signature_secp256k1), innerP256],
|
|
2944
|
-
init:
|
|
3199
|
+
init: saltedInit,
|
|
2945
3200
|
})
|
|
2946
3201
|
const serialized = SignatureEnvelope.serialize(salted)
|
|
2947
3202
|
const deserialized = SignatureEnvelope.deserialize(
|
|
@@ -2964,27 +3219,52 @@ describe('multisig', () => {
|
|
|
2964
3219
|
})
|
|
2965
3220
|
|
|
2966
3221
|
test('toRpc/fromRpc round-trip with init', () => {
|
|
2967
|
-
const rpc = SignatureEnvelope.toRpc(
|
|
3222
|
+
const rpc = SignatureEnvelope.toRpc(
|
|
3223
|
+
bootstrapEnvelope,
|
|
3224
|
+
) as SignatureEnvelope.MultisigRpc
|
|
2968
3225
|
expect(rpc.init).toEqual(init)
|
|
3226
|
+
expect(rpc.account).toBeUndefined()
|
|
3227
|
+
expect('type' in rpc).toBe(false)
|
|
2969
3228
|
expect(SignatureEnvelope.fromRpc(rpc)).toEqual(bootstrapEnvelope)
|
|
2970
3229
|
})
|
|
2971
3230
|
|
|
2972
|
-
test('toRpc encodes owner approvals as
|
|
3231
|
+
test('toRpc encodes owner approvals as structured RPC envelopes', () => {
|
|
2973
3232
|
const multisig = bootstrapEnvelope as SignatureEnvelope.Multisig
|
|
2974
3233
|
const rpc = SignatureEnvelope.toRpc(
|
|
2975
3234
|
multisig,
|
|
2976
3235
|
) as SignatureEnvelope.MultisigRpc
|
|
2977
3236
|
expect(rpc.signatures).toEqual(
|
|
2978
|
-
multisig.signatures.map((
|
|
3237
|
+
multisig.signatures.map((signature) =>
|
|
3238
|
+
SignatureEnvelope.toRpc(signature),
|
|
3239
|
+
),
|
|
2979
3240
|
)
|
|
2980
3241
|
})
|
|
2981
3242
|
|
|
2982
3243
|
test('fromRpc detects multisig by shape (no `type` field)', () => {
|
|
2983
3244
|
const rpc = SignatureEnvelope.toRpc(bootstrapEnvelope)
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
3245
|
+
expect(SignatureEnvelope.fromRpc(rpc)).toEqual(bootstrapEnvelope)
|
|
3246
|
+
})
|
|
3247
|
+
|
|
3248
|
+
test('fromRpc accepts the opposite static property as undefined', () => {
|
|
3249
|
+
const rpc = SignatureEnvelope.toRpc(
|
|
2987
3250
|
bootstrapEnvelope,
|
|
3251
|
+
) as SignatureEnvelope.MultisigRpc
|
|
3252
|
+
expect(
|
|
3253
|
+
SignatureEnvelope.fromRpc({ ...rpc, account: undefined } as never),
|
|
3254
|
+
).toEqual(bootstrapEnvelope)
|
|
3255
|
+
})
|
|
3256
|
+
|
|
3257
|
+
test('fromRpc rejects the legacy combined account and init shape', () => {
|
|
3258
|
+
const rpc = SignatureEnvelope.toRpc(
|
|
3259
|
+
bootstrapEnvelope,
|
|
3260
|
+
) as SignatureEnvelope.MultisigRpc
|
|
3261
|
+
expect(() =>
|
|
3262
|
+
SignatureEnvelope.fromRpc({
|
|
3263
|
+
...rpc,
|
|
3264
|
+
account: bootstrapAccount,
|
|
3265
|
+
} as never),
|
|
3266
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
3267
|
+
`[SignatureEnvelope.InvalidMultisigApprovalError: Invalid native multisig owner approval: RPC multisig must contain exactly one of \`account\` or \`init\`.]`,
|
|
2988
3268
|
)
|
|
2989
3269
|
})
|
|
2990
3270
|
|
|
@@ -2992,12 +3272,24 @@ describe('multisig', () => {
|
|
|
2992
3272
|
expect(() =>
|
|
2993
3273
|
SignatureEnvelope.assert({
|
|
2994
3274
|
type: 'multisig',
|
|
2995
|
-
account,
|
|
2996
|
-
|
|
2997
|
-
signatures: [],
|
|
3275
|
+
account: bootstrapAccount,
|
|
3276
|
+
signatures: [innerP256],
|
|
2998
3277
|
init: { threshold: 1, owners: [] },
|
|
2999
3278
|
} as never),
|
|
3000
3279
|
).toThrowError()
|
|
3001
3280
|
})
|
|
3281
|
+
|
|
3282
|
+
test('assert: init must derive the supplied account', () => {
|
|
3283
|
+
expect(() =>
|
|
3284
|
+
SignatureEnvelope.assert({
|
|
3285
|
+
type: 'multisig',
|
|
3286
|
+
account,
|
|
3287
|
+
signatures: [innerP256],
|
|
3288
|
+
init,
|
|
3289
|
+
}),
|
|
3290
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
3291
|
+
`[SignatureEnvelope.InvalidMultisigApprovalError: Invalid native multisig owner approval: multisig init does not derive account.]`,
|
|
3292
|
+
)
|
|
3293
|
+
})
|
|
3002
3294
|
})
|
|
3003
3295
|
})
|