@@ -873,7 +873,7 @@ describe('TransactionSender Test', () => {
873873 } )
874874 } )
875875
876- describe ( '#signMultisig ' , ( ) => {
876+ describe ( '#signLegacyMultisig ' , ( ) => {
877877 const transactionObject = {
878878 version : '0x0' ,
879879 cellDeps : [
@@ -1158,5 +1158,291 @@ describe('TransactionSender Test', () => {
11581158 )
11591159 } )
11601160 } )
1161+
1162+ describe ( '#signMultisig' , ( ) => {
1163+ const transactionObject = {
1164+ version : '0x0' ,
1165+ cellDeps : [
1166+ CellDep . fromObject ( {
1167+ outPoint : OutPoint . fromObject ( {
1168+ txHash : '0x0d9c4af3dd158d6359c9d25d0a600f1dd20b86072b85a095e7bc70c34509b73d' ,
1169+ index : '0x0' ,
1170+ } ) ,
1171+ depType : 'depGroup' as DepType ,
1172+ } ) ,
1173+ ] ,
1174+ headerDeps : [ ] ,
1175+ inputs : [
1176+ Input . fromObject ( {
1177+ previousOutput : OutPoint . fromObject ( {
1178+ txHash : '0x1879851943fa686af29bed5c95acd566d0244e7b3ca89cf7c435622a5a5b4cb3' ,
1179+ index : '0x0' ,
1180+ } ) ,
1181+ since : '0x0' ,
1182+ lock : Script . fromObject ( {
1183+ args : '' ,
1184+ codeHash : SystemScriptInfo . MULTI_SIGN_CODE_HASH ,
1185+ hashType : SystemScriptInfo . MULTI_SIGN_HASH_TYPE ,
1186+ } ) ,
1187+ } ) ,
1188+ ] ,
1189+ outputs : [
1190+ Output . fromObject ( {
1191+ capacity : '0x174876e800' ,
1192+ lock : Script . fromObject ( {
1193+ codeHash : '0x36c971b8d41fbd94aabca77dc75e826729ac98447b46f91e00796155dddb0d29' ,
1194+ args : '0xe2193df51d78411601796b35b17b4f8f2cd85bd0' ,
1195+ hashType : 'type' as ScriptHashType ,
1196+ } ) ,
1197+ type : null ,
1198+ } ) ,
1199+ Output . fromObject ( {
1200+ capacity : '0x12319d9962f4' ,
1201+ lock : Script . fromObject ( {
1202+ codeHash : '0x36c971b8d41fbd94aabca77dc75e826729ac98447b46f91e00796155dddb0d29' ,
1203+ args : '0x36c329ed630d6ce750712a477543672adab57f4c' ,
1204+ hashType : 'type' as ScriptHashType ,
1205+ } ) ,
1206+ type : null ,
1207+ } ) ,
1208+ ] ,
1209+ outputsData : [ '0x' , '0x' ] ,
1210+ witnesses : [ ] ,
1211+ hash : '0x230ab250ee0ae681e88e462102e5c01a9994ac82bf0effbfb58d6c11a86579f1' ,
1212+ }
1213+
1214+ const createMultisigConfig = ( r : number , m : number , addresses : string [ ] ) : [ string , MultisigConfigModel ] => {
1215+ const blake160s = addresses . map ( v => addressToScript ( v ) . args )
1216+ const multiArgs = Multisig . hash ( blake160s , r , m , addresses . length )
1217+ return [
1218+ multiArgs ,
1219+ MultisigConfigModel . fromObject ( {
1220+ walletId : fakeWallet . id ,
1221+ r,
1222+ m,
1223+ n : addresses . length ,
1224+ blake160s : addresses . map ( v => addressToScript ( v ) . args ) ,
1225+ lockCodeHash : SystemScriptInfo . MULTI_SIGN_CODE_HASH ,
1226+ } ) ,
1227+ ]
1228+ }
1229+
1230+ it ( 'm is 1' , async ( ) => {
1231+ const addresses = [
1232+ 'ckt1qyq89x5ggpt0a5epm2k2gyxeffwkgfdxeg0s543mh4' ,
1233+ 'ckt1qyqql0vgjyxjxjxknkj6nq8jxa485xsyl66sy7c5f6' ,
1234+ ]
1235+ const [ multiArgs , multisigConfig ] = createMultisigConfig ( 1 , 1 , addresses )
1236+ const addr = {
1237+ walletId : fakeWallet . id ,
1238+ path : `m/44'/309'/0'/0/0` ,
1239+ blake160 : addressToScript ( addresses [ 0 ] ) . args ,
1240+ version : 'testnet' ,
1241+ }
1242+
1243+ const mockGAI = jest . fn ( )
1244+ mockGAI . mockReturnValueOnce ( [ addr ] )
1245+ transactionSender . getAddressInfos = mockGAI . bind ( transactionSender )
1246+ const tx = Transaction . fromObject ( transactionObject )
1247+ tx . inputs [ 0 ] ! . setLock (
1248+ SystemScriptInfo . generateMultiSignScript ( multiArgs , SystemScriptInfo . MULTI_SIGN_CODE_HASH )
1249+ )
1250+ const res = await transactionSender . signMultisig ( fakeWallet . id , tx , '1234' , [ multisigConfig ] )
1251+ expect ( res . witnesses [ 0 ] ) . toBe (
1252+ '0x810000001000000081000000810000006d00000000010102729a884056fed321daaca410d94a5d6425a6ca1f0fbd88910d2348d69da5a980f2376a7a1a04feb56b75ef075f1cb4a3e681846c7d8c6ad1575dc5889a2685df215dc566c346757d6d3d46b4201b38f55f87d381fc65e074c1292145fedda1e6d179e6b1052194e200'
1253+ )
1254+ } )
1255+
1256+ describe ( 'm is 2' , ( ) => {
1257+ const addresses = [
1258+ 'ckt1qyq89x5ggpt0a5epm2k2gyxeffwkgfdxeg0s543mh4' ,
1259+ 'ckt1qyqql0vgjyxjxjxknkj6nq8jxa485xsyl66sy7c5f6' ,
1260+ 'ckt1qyqt9wqszk2lurw7h86wrt826cg8zx2f0lnq6e4vpl' ,
1261+ ]
1262+ const [ multiArgs , multisigConfig ] = createMultisigConfig ( 1 , 2 , addresses )
1263+ const addr = {
1264+ walletId : fakeWallet . id ,
1265+ path : `m/44'/309'/0'/0/0` ,
1266+ blake160 : '' ,
1267+ version : 'testnet' ,
1268+ }
1269+
1270+ const mockGAI = jest . fn ( )
1271+ mockGAI . mockReturnValue (
1272+ [ addr , addr , addr ] . map ( ( v , idx ) => ( { ...v , blake160 : addressToScript ( addresses [ idx ] ) . args } ) )
1273+ )
1274+ let tx = Transaction . fromObject ( transactionObject )
1275+ it ( 'first sign' , async ( ) => {
1276+ const getAddressInfos = transactionSender . getAddressInfos
1277+ transactionSender . getAddressInfos = mockGAI . bind ( transactionSender )
1278+ tx . inputs [ 0 ] ! . setLock (
1279+ SystemScriptInfo . generateMultiSignScript ( multiArgs , SystemScriptInfo . MULTI_SIGN_CODE_HASH )
1280+ )
1281+ tx = await transactionSender . signMultisig ( fakeWallet . id , tx , '1234' , [ multisigConfig ] )
1282+ const lock = ( tx . witnesses [ 0 ] as WitnessArgs ) . lock !
1283+ const serializedMultiSign : string = Multisig . serialize (
1284+ addresses . map ( v => addressToScript ( v ) . args ) ,
1285+ 1 ,
1286+ 2 ,
1287+ 3
1288+ )
1289+ expect ( lock . startsWith ( serializedMultiSign ) ) . toBeTruthy ( )
1290+ transactionSender . getAddressInfos = getAddressInfos
1291+ } )
1292+ it ( 'second sign' , async ( ) => {
1293+ const getAddressInfos = transactionSender . getAddressInfos
1294+ transactionSender . getAddressInfos = mockGAI . bind ( transactionSender )
1295+ const res = await transactionSender . signMultisig ( fakeWallet . id , tx , '1234' , [ multisigConfig ] )
1296+ expect ( res . witnesses [ 0 ] ) . toBe (
1297+ '0xd600000010000000d6000000d6000000c200000000010203729a884056fed321daaca410d94a5d6425a6ca1f0fbd88910d2348d69da5a980f2376a7a1a04feb5b2b8101595fe0ddeb9f4e1acead6107119497fe622bca1e7a693dec7beaf28fdde42c60adc1ba0c4e4279bd1b252d6dbf297eb222775697d59f6f14be56411ebe8b1d38e2646410e7d6a77a417f3b54e67fb86e90122bca1e7a693dec7beaf28fdde42c60adc1ba0c4e4279bd1b252d6dbf297eb222775697d59f6f14be56411ebe8b1d38e2646410e7d6a77a417f3b54e67fb86e901'
1298+ )
1299+ transactionSender . getAddressInfos = getAddressInfos
1300+ } )
1301+ } )
1302+
1303+ it ( 'no matched multisig config, ignore and continue' , async ( ) => {
1304+ const showMessageBoxMock = jest
1305+ . spyOn ( dialog , 'showMessageBox' )
1306+ . mockImplementation ( ( ) => Promise . resolve ( { response : 1 , checkboxChecked : true } ) )
1307+ mockGAI . mockReturnValueOnce ( [ { path : '' } ] )
1308+ transactionSender . getAddressInfos = mockGAI . bind ( transactionSender )
1309+ const tx = Transaction . fromObject ( transactionObject )
1310+ await expect ( transactionSender . signMultisig ( fakeWallet . id , tx , '1234' , [ ] ) ) . resolves . not . toThrow ( )
1311+ expect ( showMessageBoxMock ) . toHaveBeenCalled ( )
1312+ } )
1313+
1314+ it ( 'no matched multisig config, throw exception' , async ( ) => {
1315+ const showMessageBoxMock = jest
1316+ . spyOn ( dialog , 'showMessageBox' )
1317+ . mockImplementation ( ( ) => Promise . resolve ( { response : 0 , checkboxChecked : false } ) )
1318+ mockGAI . mockReturnValueOnce ( [ { path : '' } ] )
1319+ transactionSender . getAddressInfos = mockGAI . bind ( transactionSender )
1320+ const tx = Transaction . fromObject ( transactionObject )
1321+ await expect ( transactionSender . signMultisig ( fakeWallet . id , tx , '1234' , [ ] ) ) . rejects . toThrowError (
1322+ new MultisigConfigNeedError ( )
1323+ )
1324+ expect ( showMessageBoxMock ) . toHaveBeenCalled ( )
1325+ } )
1326+
1327+ it ( 'throw exception no matched multisig config addresses' , async ( ) => {
1328+ const addresses = [
1329+ 'ckt1qyq89x5ggpt0a5epm2k2gyxeffwkgfdxeg0s543mh4' ,
1330+ 'ckt1qyqql0vgjyxjxjxknkj6nq8jxa485xsyl66sy7c5f6' ,
1331+ ]
1332+ const noMatchAddress = 'ckt1qyqf5v66n4vrxu75kks2ku06g7trnkdwt52s8000ee'
1333+ const [ multiArgs , multisigConfig ] = createMultisigConfig ( 1 , 1 , addresses )
1334+ const addr = {
1335+ walletId : fakeWallet . id ,
1336+ address : noMatchAddress ,
1337+ blake160 : addressToScript ( noMatchAddress ) . args ,
1338+ version : 'testnet' ,
1339+ }
1340+
1341+ const mockGAI = jest . fn ( )
1342+ mockGAI . mockReturnValueOnce ( [ addr ] )
1343+ transactionSender . getAddressInfos = mockGAI . bind ( transactionSender )
1344+
1345+ const tx = Transaction . fromObject ( transactionObject )
1346+ tx . inputs [ 0 ] ! . setLock (
1347+ SystemScriptInfo . generateMultiSignScript ( multiArgs , SystemScriptInfo . MULTI_SIGN_CODE_HASH )
1348+ )
1349+ await expect ( transactionSender . signMultisig ( fakeWallet . id , tx , '1234' , [ multisigConfig ] ) ) . rejects . toThrow (
1350+ new NoMatchAddressForSign ( )
1351+ )
1352+ } )
1353+
1354+ describe ( 'sign with hard wallet' , ( ) => {
1355+ beforeEach ( ( ) => {
1356+ stubbedGetWallet . mockReturnValue ( {
1357+ ...fakeWallet ,
1358+ isHardware ( ) {
1359+ return true
1360+ } ,
1361+ } )
1362+ } )
1363+
1364+ it ( 'm is 1' , async ( ) => {
1365+ const witnessLock = '0' . repeat ( 130 )
1366+ stubbedHardWalletGetCurrent . mockReturnValueOnce ( {
1367+ signTransaction : jest . fn ( ) . mockResolvedValueOnce ( witnessLock ) ,
1368+ } )
1369+ const addresses = [
1370+ 'ckt1qyq89x5ggpt0a5epm2k2gyxeffwkgfdxeg0s543mh4' ,
1371+ 'ckt1qyqql0vgjyxjxjxknkj6nq8jxa485xsyl66sy7c5f6' ,
1372+ ]
1373+ const [ multiArgs , multisigConfig ] = createMultisigConfig ( 1 , 1 , addresses )
1374+ const addr = {
1375+ walletId : fakeWallet . id ,
1376+ path : `m/44'/309'/0'/0/0` ,
1377+ blake160 : addressToScript ( addresses [ 0 ] ) . args ,
1378+ version : 'testnet' ,
1379+ }
1380+
1381+ const mockGAI = jest . fn ( )
1382+ mockGAI . mockReturnValueOnce ( [ addr ] )
1383+ transactionSender . getAddressInfos = mockGAI . bind ( transactionSender )
1384+ const tx = Transaction . fromObject ( transactionObject )
1385+ tx . inputs [ 0 ] ! . setLock (
1386+ SystemScriptInfo . generateMultiSignScript ( multiArgs , SystemScriptInfo . MULTI_SIGN_CODE_HASH )
1387+ )
1388+ const res = await transactionSender . signMultisig ( fakeWallet . id , tx , '1234' , [ multisigConfig ] )
1389+ const expectedValue = serializeWitnessArgs ( {
1390+ inputType : undefined ,
1391+ outputType : undefined ,
1392+ lock :
1393+ Multisig . serialize (
1394+ addresses . map ( v => addressToScript ( v ) . args ) ,
1395+ 1 ,
1396+ 1 ,
1397+ 2
1398+ ) + witnessLock ,
1399+ } )
1400+ expect ( res . witnesses [ 0 ] ) . toBe ( expectedValue )
1401+ } )
1402+ } )
1403+
1404+ it ( `input cell's length is 2` , async ( ) => {
1405+ const addresses = [
1406+ 'ckt1qyq89x5ggpt0a5epm2k2gyxeffwkgfdxeg0s543mh4' ,
1407+ 'ckt1qyqql0vgjyxjxjxknkj6nq8jxa485xsyl66sy7c5f6' ,
1408+ ]
1409+ const [ multiArgs , multisigConfig ] = createMultisigConfig ( 1 , 1 , addresses )
1410+ const addr = {
1411+ walletId : fakeWallet . id ,
1412+ path : `m/44'/309'/0'/0/0` ,
1413+ blake160 : addressToScript ( addresses [ 0 ] ) . args ,
1414+ version : 'testnet' ,
1415+ }
1416+
1417+ const mockGAI = jest . fn ( )
1418+ mockGAI . mockReturnValueOnce ( [ addr ] )
1419+ transactionSender . getAddressInfos = mockGAI . bind ( transactionSender )
1420+ const tx = Transaction . fromObject ( transactionObject )
1421+ tx . inputs [ 0 ] ! . setLock (
1422+ SystemScriptInfo . generateMultiSignScript ( multiArgs , SystemScriptInfo . MULTI_SIGN_CODE_HASH )
1423+ )
1424+ tx . inputs . push (
1425+ Input . fromObject ( {
1426+ previousOutput : OutPoint . fromObject ( {
1427+ txHash : '0x1879851943fa686af29bed5c95acd566d0244e7b3ca89cf7c435622a5a5b4cb3' ,
1428+ index : '0x0' ,
1429+ } ) ,
1430+ since : '0x0' ,
1431+ lock : Script . fromObject ( {
1432+ args : multiArgs ,
1433+ codeHash : SystemScriptInfo . MULTI_SIGN_CODE_HASH ,
1434+ hashType : SystemScriptInfo . MULTI_SIGN_HASH_TYPE ,
1435+ } ) ,
1436+ } )
1437+ )
1438+ tx . witnesses = [ '0x' , '0x' ]
1439+ const res = await transactionSender . signMultisig ( fakeWallet . id , tx , '1234' , [ multisigConfig ] )
1440+ expect ( res . witnesses ) . toHaveLength ( 2 )
1441+ expect ( res . witnesses [ 1 ] ) . toBe ( '0x' )
1442+ expect ( res . witnesses [ 0 ] ) . toBe (
1443+ '0x810000001000000081000000810000006d00000000010102729a884056fed321daaca410d94a5d6425a6ca1f0fbd88910d2348d69da5a980f2376a7a1a04feb5ffd5e0f69af2c2f1e2b1f687f9be8e758ae93ba8a8f5178cfaccb947865192f03899004fe92c9f27446bbf4dc86ba5088c55a2df969e1b1c9f9ab43c00c6443c01'
1444+ )
1445+ } )
1446+ } )
11611447 } )
11621448} )
0 commit comments