@@ -820,7 +820,35 @@ byte base64 5rZMNsevs5sULO+54aN+OvU6lQ503z2X+SSYUABIx7E=
820820 }
821821 _ , err = TxnGroup (txnGroups [0 ], & blkHdr , nil , & dummyLedger )
822822 require .Error (t , err )
823- require .Contains (t , err .Error (), "only have one of Sig, Msig, or LMsig" )
823+ require .Contains (t , err .Error (), "should only have one of Sig, Msig, or LMsig" )
824+ txnGroups [0 ][0 ].Lsig .Msig .Subsigs = nil
825+
826+ ///// logic with sig and LMsig
827+ txnGroups [0 ][0 ].Lsig .LMsig .Subsigs = make ([]crypto.MultisigSubsig , 1 )
828+ txnGroups [0 ][0 ].Lsig .LMsig .Subsigs [0 ] = crypto.MultisigSubsig {
829+ Key : crypto.PublicKey {0x1 },
830+ Sig : crypto.Signature {0x2 },
831+ }
832+ _ , err = TxnGroup (txnGroups [0 ], & blkHdr , nil , & dummyLedger )
833+ require .Error (t , err )
834+ require .Contains (t , err .Error (), "should only have one of Sig, Msig, or LMsig" )
835+ txnGroups [0 ][0 ].Lsig .Sig = crypto.Signature {}
836+ txnGroups [0 ][0 ].Lsig .LMsig .Subsigs = nil
837+
838+ ///// logic with Msig and LMsig
839+ txnGroups [0 ][0 ].Lsig .Msig .Subsigs = make ([]crypto.MultisigSubsig , 1 )
840+ txnGroups [0 ][0 ].Lsig .Msig .Subsigs [0 ] = crypto.MultisigSubsig {
841+ Key : crypto.PublicKey {0x1 },
842+ Sig : crypto.Signature {0x2 },
843+ }
844+ txnGroups [0 ][0 ].Lsig .LMsig .Subsigs = make ([]crypto.MultisigSubsig , 1 )
845+ txnGroups [0 ][0 ].Lsig .LMsig .Subsigs [0 ] = crypto.MultisigSubsig {
846+ Key : crypto.PublicKey {0x3 },
847+ Sig : crypto.Signature {0x4 },
848+ }
849+ _ , err = TxnGroup (txnGroups [0 ], & blkHdr , nil , & dummyLedger )
850+ require .Error (t , err )
851+ require .Contains (t , err .Error (), "should only have one of Sig, Msig, or LMsig" )
824852
825853}
826854
@@ -1029,8 +1057,18 @@ byte base64 5rZMNsevs5sULO+54aN+OvU6lQ503z2X+SSYUABIx7E=
10291057func TestTxnGroupCacheUpdateLogicWithMultiSig (t * testing.T ) {
10301058 partitiontest .PartitionTest (t )
10311059
1060+ testVersions := []protocol.ConsensusVersion {protocol .ConsensusV40 , protocol .ConsensusFuture }
1061+ for _ , consensusVer := range testVersions {
1062+ t .Run (string (consensusVer ), func (t * testing.T ) {
1063+ useLMsig := config .Consensus [consensusVer ].LogicSigLMsig
1064+ testTxnGroupCacheUpdateLogicWithMultiSig (t , consensusVer , useLMsig )
1065+ })
1066+ }
1067+ }
1068+
1069+ func testTxnGroupCacheUpdateLogicWithMultiSig (t * testing.T , consensusVer protocol.ConsensusVersion , useLMsig bool ) {
10321070 secrets , _ , pks , multiAddress := generateMultiSigAccounts (t , 30 )
1033- blkHdr := createDummyBlockHeader ()
1071+ blkHdr := createDummyBlockHeader (consensusVer )
10341072
10351073 const numOfTxn = 20
10361074 signedTxn := make ([]transactions.SignedTxn , numOfTxn )
@@ -1055,8 +1093,12 @@ byte base64 5rZMNsevs5sULO+54aN+OvU6lQ503z2X+SSYUABIx7E=
10551093 signedTxn [i ].Txn .Sender = multiAddress [s ]
10561094 signedTxn [i ].Lsig .Args = [][]byte {[]byte ("=0\x97 S\x85 H\xe9 \x91 B\xfd \xdb ;1\xf5 Z\xae c?\xae \xf2 I\x93 \x08 \x12 \x94 \xaa ~\x06 \x08 \x84 9b" )}
10571095 signedTxn [i ].Lsig .Logic = op .Program
1058- program := logic.MultisigProgram {Addr : crypto .Digest (multiAddress [s ]), Program : op .Program }
1059-
1096+ var program crypto.Hashable
1097+ if useLMsig {
1098+ program = logic.MultisigProgram {Addr : crypto .Digest (multiAddress [s ]), Program : op .Program }
1099+ } else {
1100+ program = logic .Program (op .Program )
1101+ }
10601102 // create multi sig that 2 out of 3 has signed the txn
10611103 var sigs [2 ]crypto.MultisigSig
10621104 for j := 0 ; j < 2 ; j ++ {
@@ -1066,7 +1108,11 @@ byte base64 5rZMNsevs5sULO+54aN+OvU6lQ503z2X+SSYUABIx7E=
10661108 }
10671109 msig , err := crypto .MultisigAssemble (sigs [:])
10681110 require .NoError (t , err )
1069- signedTxn [i ].Lsig .LMsig = msig
1111+ if useLMsig {
1112+ signedTxn [i ].Lsig .LMsig = msig
1113+ } else {
1114+ signedTxn [i ].Lsig .Msig = msig
1115+ }
10701116 }
10711117
10721118 txnGroups := make ([][]transactions.SignedTxn , len (signedTxn ))
@@ -1076,10 +1122,18 @@ byte base64 5rZMNsevs5sULO+54aN+OvU6lQ503z2X+SSYUABIx7E=
10761122 }
10771123
10781124 breakSignatureFunc := func (txn * transactions.SignedTxn ) {
1079- txn .Lsig .LMsig .Subsigs [0 ].Sig [0 ]++
1125+ if useLMsig {
1126+ txn .Lsig .LMsig .Subsigs [0 ].Sig [0 ]++
1127+ } else {
1128+ txn .Lsig .Msig .Subsigs [0 ].Sig [0 ]++
1129+ }
10801130 }
10811131 restoreSignatureFunc := func (txn * transactions.SignedTxn ) {
1082- txn .Lsig .LMsig .Subsigs [0 ].Sig [0 ]--
1132+ if useLMsig {
1133+ txn .Lsig .LMsig .Subsigs [0 ].Sig [0 ]--
1134+ } else {
1135+ txn .Lsig .Msig .Subsigs [0 ].Sig [0 ]--
1136+ }
10831137 }
10841138
10851139 verifyGroup (t , txnGroups , & blkHdr , breakSignatureFunc , restoreSignatureFunc , crypto .ErrBatchHasFailedSigs .Error ())
@@ -1212,3 +1266,268 @@ func BenchmarkTxn(b *testing.B) {
12121266 }
12131267 b .StopTimer ()
12141268}
1269+
1270+ // TestLogicSigMultisigValidation verifies that signatures are properly validated
1271+ // in different contexts (single-sig vs multisig, different multisig addresses).
1272+ func TestLogicSigMultisigValidation (t * testing.T ) {
1273+ partitiontest .PartitionTest (t )
1274+
1275+ t .Run ("v40" , func (t * testing.T ) { testLogicSigMultisigValidation (t , protocol .ConsensusV40 , false ) })
1276+ t .Run ("v41" , func (t * testing.T ) { testLogicSigMultisigValidation (t , protocol .ConsensusV41 , true ) })
1277+ t .Run ("future" , func (t * testing.T ) { testLogicSigMultisigValidation (t , protocol .ConsensusFuture , true ) })
1278+ }
1279+
1280+ func testLogicSigMultisigValidation (t * testing.T , consensusVer protocol.ConsensusVersion , useLMsig bool ) {
1281+ ops , err := logic .AssembleString ("int 1" )
1282+ require .NoError (t , err )
1283+ program := ops .Program
1284+
1285+ // Generate test keys
1286+ secrets := make ([]* crypto.SignatureSecrets , 3 )
1287+ for i := range secrets {
1288+ var seed crypto.Seed
1289+ crypto .RandBytes (seed [:])
1290+ secrets [i ] = crypto .GenerateSignatureSecrets (seed )
1291+ }
1292+
1293+ // Helper to create a test transaction
1294+ makeTestTxn := func (sender basics.Address ) transactions.SignedTxn {
1295+ return transactions.SignedTxn {
1296+ Txn : transactions.Transaction {
1297+ Type : protocol .PaymentTx ,
1298+ Header : transactions.Header {
1299+ Sender : sender ,
1300+ Fee : basics.MicroAlgos {Raw : 1000 },
1301+ FirstValid : 1 ,
1302+ LastValid : 100 ,
1303+ GenesisHash : crypto .Hash ([]byte {1 , 2 , 3 , 4 , 5 }),
1304+ },
1305+ PaymentTxnFields : transactions.PaymentTxnFields {
1306+ Receiver : basics.Address {},
1307+ Amount : basics.MicroAlgos {Raw : 1000 },
1308+ },
1309+ },
1310+ }
1311+ }
1312+
1313+ // Helper to verify a logic sig
1314+ verifyLogicSig := func (t * testing.T , stxn transactions.SignedTxn ) error {
1315+ blkHdr := createDummyBlockHeader (consensusVer )
1316+ dummyLedger := DummyLedgerForSignature {}
1317+ groupCtx , err := PrepareGroupContext ([]transactions.SignedTxn {stxn }, & blkHdr , & dummyLedger , nil )
1318+ require .NoError (t , err )
1319+ return logicSigVerify (0 , groupCtx )
1320+ }
1321+
1322+ t .Run ("MultisigToSingleSig" , func (t * testing.T ) {
1323+ pks := []crypto.PublicKey {secrets [0 ].SignatureVerifier }
1324+ msigAddr , err := crypto .MultisigAddrGen (1 , 1 , pks )
1325+ require .NoError (t , err )
1326+
1327+ // Sign in multisig context
1328+ var msig crypto.MultisigSig
1329+ if useLMsig { // >=v41: use MultisigProgram with address binding
1330+ msig , err = crypto .MultisigSign (logic.MultisigProgram {Addr : msigAddr , Program : program }, msigAddr , 1 , 1 , pks , * secrets [0 ])
1331+ } else { // v40: use Program directly
1332+ msig , err = crypto .MultisigSign (logic .Program (program ), msigAddr , 1 , 1 , pks , * secrets [0 ])
1333+ }
1334+ require .NoError (t , err )
1335+
1336+ // Try to use multisig signature as single sig
1337+ stxn := makeTestTxn (basics .Address (secrets [0 ].SignatureVerifier ))
1338+ stxn .Lsig = transactions.LogicSig {
1339+ Logic : program ,
1340+ Sig : msig .Subsigs [0 ].Sig ,
1341+ }
1342+
1343+ err = verifyLogicSig (t , stxn )
1344+ if useLMsig {
1345+ require .ErrorContains (t , err , "At least one signature didn't pass verification" )
1346+ } else {
1347+ require .NoError (t , err )
1348+ }
1349+ })
1350+
1351+ t .Run ("SingleSigToMultisig" , func (t * testing.T ) {
1352+ // Sign as single sig
1353+ singleSig := secrets [0 ].Sign (logic .Program (program ))
1354+
1355+ // Create multisig with same key
1356+ pks := []crypto.PublicKey {secrets [0 ].SignatureVerifier }
1357+ msigAddr , err := crypto .MultisigAddrGen (1 , 1 , pks )
1358+ require .NoError (t , err )
1359+
1360+ // Try to use single sig in multisig
1361+ stxn := makeTestTxn (basics .Address (msigAddr ))
1362+ msigWithSingleSig := crypto.MultisigSig {Version : 1 , Threshold : 1 ,
1363+ Subsigs : []crypto.MultisigSubsig {{Key : secrets [0 ].SignatureVerifier , Sig : singleSig }},
1364+ }
1365+
1366+ if useLMsig { // >=v41: use LMsig field
1367+ stxn .Lsig = transactions.LogicSig {Logic : program , LMsig : msigWithSingleSig }
1368+ err = verifyLogicSig (t , stxn )
1369+ require .ErrorContains (t , err , "At least one signature didn't pass verification" )
1370+ } else { // v40: use Msig field
1371+ stxn .Lsig = transactions.LogicSig {Logic : program , Msig : msigWithSingleSig }
1372+ err = verifyLogicSig (t , stxn )
1373+ require .NoError (t , err )
1374+ }
1375+ })
1376+
1377+ t .Run ("CrossMultisigValidation" , func (t * testing.T ) {
1378+ // Create two different 1-of-2 multisigs
1379+ pks1 := []crypto.PublicKey {secrets [0 ].SignatureVerifier , secrets [1 ].SignatureVerifier , secrets [2 ].SignatureVerifier }
1380+ pks2 := []crypto.PublicKey {secrets [0 ].SignatureVerifier , secrets [1 ].SignatureVerifier }
1381+
1382+ msigAddr1 , err := crypto .MultisigAddrGen (1 , 2 , pks1 )
1383+ require .NoError (t , err )
1384+ msigAddr2 , err := crypto .MultisigAddrGen (1 , 2 , pks2 )
1385+ require .NoError (t , err )
1386+
1387+ // Sign for each multisig
1388+ var sig1 , sig2 crypto.MultisigSig
1389+ if useLMsig { // >=v41: use MultisigProgram with address binding
1390+ sig1 , err = crypto .MultisigSign (logic.MultisigProgram {Addr : msigAddr1 , Program : program }, msigAddr1 , 1 , 2 , pks1 , * secrets [0 ])
1391+ require .NoError (t , err )
1392+ sig2 , err = crypto .MultisigSign (logic.MultisigProgram {Addr : msigAddr2 , Program : program }, msigAddr2 , 1 , 2 , pks2 , * secrets [1 ])
1393+ require .NoError (t , err )
1394+ } else { // v40: use Program directly
1395+ sig1 , err = crypto .MultisigSign (logic .Program (program ), msigAddr1 , 1 , 2 , pks1 , * secrets [0 ])
1396+ require .NoError (t , err )
1397+ sig2 , err = crypto .MultisigSign (logic .Program (program ), msigAddr2 , 1 , 2 , pks2 , * secrets [1 ])
1398+ require .NoError (t , err )
1399+ }
1400+
1401+ // Try to mix signatures from different multisigs
1402+ stxn := makeTestTxn (basics .Address (msigAddr2 ))
1403+ mixedMsig := crypto.MultisigSig {Version : 1 , Threshold : 2 ,
1404+ Subsigs : []crypto.MultisigSubsig {
1405+ {Key : secrets [0 ].SignatureVerifier , Sig : sig1 .Subsigs [0 ].Sig }, // from msigAddr1
1406+ {Key : secrets [1 ].SignatureVerifier , Sig : sig2 .Subsigs [1 ].Sig }, // from msigAddr2
1407+ },
1408+ }
1409+
1410+ if useLMsig { // >=v41: use LMsig field
1411+ stxn .Lsig = transactions.LogicSig {Logic : program , LMsig : mixedMsig }
1412+ err = verifyLogicSig (t , stxn )
1413+ require .ErrorContains (t , err , "At least one signature didn't pass verification" )
1414+ } else { // v40: use Msig field
1415+ stxn .Lsig = transactions.LogicSig {Logic : program , Msig : mixedMsig }
1416+ err = verifyLogicSig (t , stxn )
1417+ require .NoError (t , err )
1418+ }
1419+ })
1420+
1421+ t .Run ("DisableMsig" , func (t * testing.T ) {
1422+ // Run on consensus when Msig is disabled, only LMsig allowed
1423+ if config .Consensus [consensusVer ].LogicSigMsig || ! config .Consensus [consensusVer ].LogicSigLMsig {
1424+ t .Skip ("requires LogicSigMsig=false and LogicSigLMsig=true" )
1425+ }
1426+
1427+ pks := []crypto.PublicKey {secrets [0 ].SignatureVerifier , secrets [1 ].SignatureVerifier }
1428+ msigAddr , err := crypto .MultisigAddrGen (1 , 2 , pks )
1429+ require .NoError (t , err )
1430+
1431+ // Sign with address binding
1432+ sig1 , err := crypto .MultisigSign (logic.MultisigProgram {Addr : msigAddr , Program : program }, msigAddr , 1 , 2 , pks , * secrets [0 ])
1433+ require .NoError (t , err )
1434+ sig2 , err := crypto .MultisigSign (logic.MultisigProgram {Addr : msigAddr , Program : program }, msigAddr , 1 , 2 , pks , * secrets [1 ])
1435+ require .NoError (t , err )
1436+
1437+ msig , err := crypto .MultisigAssemble ([]crypto.MultisigSig {sig1 , sig2 })
1438+ require .NoError (t , err )
1439+
1440+ // Create a transaction
1441+ stxn := makeTestTxn (basics .Address (msigAddr ))
1442+
1443+ // Test with Msig field - should be rejected
1444+ stxn .Lsig = transactions.LogicSig {Logic : program , Msig : msig }
1445+ err = verifyLogicSig (t , stxn )
1446+ require .ErrorContains (t , err , "LogicSig Msig field not supported in this consensus version" )
1447+
1448+ // Test with LMsig field - should work
1449+ stxn .Lsig = transactions.LogicSig {Logic : program , LMsig : msig }
1450+ err = verifyLogicSig (t , stxn )
1451+ require .NoError (t , err )
1452+
1453+ // Test with both fields - should fail
1454+ stxn .Lsig = transactions.LogicSig {Logic : program , Msig : msig , LMsig : msig }
1455+ err = verifyLogicSig (t , stxn )
1456+ require .ErrorContains (t , err , "LogicSig should only have one of Sig, Msig, or LMsig but has more than one" )
1457+ })
1458+ }
1459+
1460+ func TestLogicSigMsigBothFlags (t * testing.T ) {
1461+ partitiontest .PartitionTest (t )
1462+
1463+ // Create a test consensus version with both flags enabled
1464+ consensusVer := protocol .ConsensusCurrentVersion
1465+ testConsensus := config .Consensus [consensusVer ]
1466+ testConsensus .LogicSigMsig = true
1467+ testConsensus .LogicSigLMsig = true
1468+ config .Consensus ["test-lmsig-flags" ] = testConsensus
1469+ defer delete (config .Consensus , "test-lmsig-flags" )
1470+
1471+ // Simple test program that always approves
1472+ ops , err := logic .AssembleString ("int 1" )
1473+ require .NoError (t , err )
1474+ program := ops .Program
1475+
1476+ // Create test keys
1477+ var seed crypto.Seed
1478+ crypto .RandBytes (seed [:])
1479+ secret := crypto .GenerateSignatureSecrets (seed )
1480+ pks := []crypto.PublicKey {secret .SignatureVerifier }
1481+
1482+ msigAddr , err := crypto .MultisigAddrGen (1 , 1 , pks )
1483+ require .NoError (t , err )
1484+
1485+ // Sign with both methods
1486+ msig , err := crypto .MultisigSign (logic .Program (program ), msigAddr , 1 , 1 , pks , * secret )
1487+ require .NoError (t , err )
1488+
1489+ lmsig , err := crypto .MultisigSign (logic.MultisigProgram {Addr : msigAddr , Program : program }, msigAddr , 1 , 1 , pks , * secret )
1490+ require .NoError (t , err )
1491+
1492+ // Create test transaction
1493+ stxn := transactions.SignedTxn {
1494+ Txn : transactions.Transaction {
1495+ Type : protocol .PaymentTx ,
1496+ Header : transactions.Header {
1497+ Sender : basics .Address (msigAddr ),
1498+ Fee : basics.MicroAlgos {Raw : 1000 },
1499+ FirstValid : 1 ,
1500+ LastValid : 100 ,
1501+ GenesisHash : crypto .Hash ([]byte {1 , 2 , 3 , 4 , 5 }),
1502+ },
1503+ PaymentTxnFields : transactions.PaymentTxnFields {
1504+ Receiver : basics.Address {},
1505+ Amount : basics.MicroAlgos {Raw : 1000 },
1506+ },
1507+ },
1508+ }
1509+
1510+ // Helper to verify a logic sig
1511+ verifyLogicSig := func () error {
1512+ blkHdr := createDummyBlockHeader ("test-lmsig-flags" )
1513+ dummyLedger := DummyLedgerForSignature {}
1514+ groupCtx , err := PrepareGroupContext ([]transactions.SignedTxn {stxn }, & blkHdr , & dummyLedger , nil )
1515+ require .NoError (t , err )
1516+ return logicSigVerify (0 , groupCtx )
1517+ }
1518+
1519+ // Test with Msig field only - should work
1520+ stxn .Lsig = transactions.LogicSig {Logic : program , Msig : msig }
1521+ err = verifyLogicSig ()
1522+ require .NoError (t , err )
1523+
1524+ // Test with LMsig field only - should work
1525+ stxn .Lsig = transactions.LogicSig {Logic : program , LMsig : lmsig }
1526+ err = verifyLogicSig ()
1527+ require .NoError (t , err )
1528+
1529+ // Test with both fields - should fail
1530+ stxn .Lsig = transactions.LogicSig {Logic : program , Msig : msig , LMsig : lmsig }
1531+ err = verifyLogicSig ()
1532+ require .ErrorContains (t , err , "LogicSig should only have one of Sig, Msig, or LMsig but has more than one" )
1533+ }
0 commit comments