@@ -346,7 +346,8 @@ impl<D: Decoder> Decodable<D> for TxOut {
346346}
347347
348348impl TxOut {
349- /// Whether this data represents nulldata (OP_RETURN followed by pushes)
349+ /// Whether this data represents nulldata (OP_RETURN followed by pushes,
350+ /// not necessarily minimal)
350351 pub fn is_null_data ( & self ) -> bool {
351352 let mut iter = self . script_pubkey . iter ( false ) ;
352353 if iter. next ( ) == Some ( Instruction :: Op ( opcodes:: all:: OP_RETURN ) ) {
@@ -363,12 +364,15 @@ impl TxOut {
363364 }
364365 }
365366
366- /// Whether this output is a pegout
367+ /// Whether this output is a pegout, which is a subset of nulldata with the
368+ /// following extra rules: (a) there must be at least 2 pushes, the first of
369+ /// which must be 32 bytes; (b) all pushes must use a push opcode rather than
370+ /// a numeric or reserved opcode.
367371 pub fn is_pegout ( & self ) -> bool {
368372 self . pegout_data ( ) . is_some ( )
369373 }
370374
371- /// Whether this output is a pegout; if so , returns the destination genesis block,
375+ /// If this output is a pegout, returns the destination genesis block,
372376 /// the destination script pubkey, and any additional data
373377 pub fn pegout_data ( & self ) -> Option < PegoutData > {
374378 // Must be NULLDATA
@@ -383,7 +387,7 @@ impl TxOut {
383387 return None ;
384388 } ;
385389
386- let mut iter = self . script_pubkey . iter ( true ) ;
390+ let mut iter = self . script_pubkey . iter ( false ) ;
387391
388392 iter. next ( ) ; // Skip OP_RETURN
389393
@@ -406,17 +410,27 @@ impl TxOut {
406410 } ;
407411
408412 // Return everything
413+ let mut found_non_data_push = false ;
409414 let remainder = iter
410- . map ( |x| if let Instruction :: PushBytes ( data) = x { data } else { unreachable ! ( ) } )
415+ . filter_map ( |x| if let Instruction :: PushBytes ( data) = x {
416+ Some ( data)
417+ } else {
418+ found_non_data_push = true ;
419+ None
420+ } )
411421 . collect ( ) ;
412422
413- Some ( PegoutData {
414- asset : self . asset ,
415- value : value,
416- genesis_hash : genesis_hash,
417- script_pubkey : script_pubkey,
418- extra_data : remainder,
419- } )
423+ if found_non_data_push {
424+ None
425+ } else {
426+ Some ( PegoutData {
427+ asset : self . asset ,
428+ value : value,
429+ genesis_hash : genesis_hash,
430+ script_pubkey : script_pubkey,
431+ extra_data : remainder,
432+ } )
433+ }
420434 }
421435
422436 /// Whether or not this output is a fee output
@@ -1481,9 +1495,105 @@ mod tests {
14811495 fdfdfdfdfd3ca059fdf2226a20000000000000000000000000000000000000000\
14821496 0000000000000000000000000\
14831497 ") ;
1484-
1498+
14851499 assert ! ( output. is_null_data( ) ) ;
14861500 assert ! ( !output. is_pegout( ) ) ;
14871501 }
1502+
1503+ #[ test]
1504+ fn pegout_tx_vector_1 ( ) {
1505+ let tx: Transaction = hex_deserialize ! ( "\
1506+ 0200000000021c39a226160dd8962eb273772950f0b603c319a8e4aa9912c9e8e\
1507+ 36b5bdf71a2000000006a473044022071212fcde89d1055d5b74f17a162b3dbe5\
1508+ 348ac8527a131dab5dcf8a97d67d2f02202edf12f3c69fed1fa0c23da608e6ade\
1509+ d86dd5c7b09da42f61b453c3a838e8cab012103557f25ff40f976670ddf59c719\
1510+ 38bade91684b76ad69dfed27049de2afec59e5feffffff853db31f986dd89c81f\
1511+ e87a84f385d7099c5ea841d762b26b03166e5e798dfbe000000006a4730440220\
1512+ 42c70729fb50930179a9d76f5febbda5b0ee50e62febf92de4dd10b9393554d80\
1513+ 2203140b107519243e4110c065017c8ae1ac04843f94b3f20a1e5faf7343dd761\
1514+ 59012102797ffcf7ccc8e2012a90e71962901a1ad740f2a28f2f563c76f9eb42a\
1515+ 8100f5efeffffff03016d521c38ec1ea15734ae22b7c46064412829c0d0579f0a\
1516+ 713d1c04ede979026f0100000000000f7869001976a914216d878ebff0c623909\
1517+ 889265d8dc1ab26e2ff4388ac016d521c38ec1ea15734ae22b7c46064412829c0\
1518+ d0579f0a713d1c04ede979026f0100000000000186a000fd02026a206fe28c0ab\
1519+ 6f1b372c1a6a246ae63f74f931e8365e15a089c68d61900000000001976a914df\
1520+ 662e2dd70fd82acba2d252cc897cb6e618093288ac21025f756509f5dbac47d54\
1521+ c9ef5ccf49895a4dbac4759005a74375f66c480e6c0864da1010ce552be292c37\
1522+ e7242d7e58e678a19349021d22f2712ea68de397b66167d141b09f98e3294e05b\
1523+ 51c1469bab3ddb7096f5aa2817e218d137879fb54dbe1659353e6e64add9cb2d6\
1524+ f9e8647bd1ca94d9a6a80d193d76f115596f7bcc8a07eaf85c738f31f4fb192b7\
1525+ 85aa2934bcb5e4f6a7b444da2bc64da3527a33cc7f0792630f57b92ba07dd0e47\
1526+ 2d5e2e08b2bca8f1c06e18a07f226dac8acbcc1dfafe8be893d9c5092808b1dec\
1527+ fbb955c5f82968bed609b0b2e2c55abe4b0c12bc0c7ea3976e0af2c6aadab3c90\
1528+ ed862a9846fc1a1c20ef220a050538d3c9ff12669653f9b055606dd45fe66f18a\
1529+ a819c8cda5c1b224dc19c0fbf028133d1256588834ea14cb44a84da3af8344365\
1530+ 7f9ff3eaa14216dc4ed06a92c0ce19be4fe066c9d830ee3acdd3062b9336ace12\
1531+ cc5935953284946bf6bc5c89f9a13d37dddd63e85173174a164f4b68cbc94d347\
1532+ b3d4a7e4ec79044b049375cc7b43b7657123b80f5834afca696b6bc7bf47fa677\
1533+ 42e1caa609424cba3ec9d9d156b5909debd0475d91d31134acce50420c2ea694e\
1534+ 2c2ea477a0bd14e670bccb42a0fb7009b41ee86a016d521c38ec1ea15734ae22b\
1535+ 7c46064412829c0d0579f0a713d1c04ede979026f0100000000000006fc000054\
1536+ 840300\
1537+ ") ;
1538+
1539+ assert_eq ! ( tx. input. len( ) , 2 ) ;
1540+ assert_eq ! ( tx. output. len( ) , 3 ) ;
1541+ assert ! ( !tx. output[ 0 ] . is_null_data( ) ) ;
1542+ assert ! ( !tx. output[ 0 ] . is_pegout( ) ) ;
1543+ assert ! ( !tx. output[ 0 ] . is_fee( ) ) ;
1544+
1545+ assert ! ( tx. output[ 1 ] . is_null_data( ) ) ;
1546+ assert ! ( tx. output[ 1 ] . is_pegout( ) ) ;
1547+ assert ! ( tx. output[ 1 ] . pegout_data( ) . is_some( ) ) ;
1548+ assert ! ( !tx. output[ 1 ] . is_fee( ) ) ;
1549+
1550+ assert ! ( !tx. output[ 2 ] . is_null_data( ) ) ;
1551+ assert ! ( !tx. output[ 2 ] . is_pegout( ) ) ;
1552+ assert ! ( tx. output[ 2 ] . is_fee( ) ) ;
1553+
1554+ assert_eq ! ( tx. output[ 0 ] . asset, tx. output[ 1 ] . asset) ;
1555+ assert_eq ! ( tx. output[ 2 ] . asset, tx. output[ 1 ] . asset) ;
1556+ }
1557+
1558+ #[ test]
1559+ fn pegout_with_numeric_pak ( ) {
1560+ let tx: Transaction = hex_deserialize ! ( "\
1561+ 0200000000021c39a226160dd8962eb273772950f0b603c319a8e4aa9912c9e8\
1562+ e36b5bdf71a2000000006a473044022071212fcde89d1055d5b74f17a162b3db\
1563+ e5348ac8527a131dab5dcf8a97d67d2f02202edf12f3c69fed1fa0c23da608e6\
1564+ aded86dd5c7b09da42f61b453c3a838e8cab012103557f25ff40f976670ddf59\
1565+ c71938bade91684b76ad69dfed27049de2afec59e5feffffff853db31f986dd8\
1566+ 9c81fe87a84f385d7099c5ea841d762b26b03166e5e798dfbe000000006a4730\
1567+ 44022042c70729fb50930179a9d76f5febbda5b0ee50e62febf92de4dd10b939\
1568+ 3554d802203140b107519243e4110c065017c8ae1ac04843f94b3f20a1e5faf7\
1569+ 343dd76159012102797ffcf7ccc8e2012a90e71962901a1ad740f2a28f2f563c\
1570+ 76f9eb42a8100f5efeffffff03016d521c38ec1ea15734ae22b7c46064412829\
1571+ c0d0579f0a713d1c04ede979026f0100000000000f7869001976a914216d878e\
1572+ bff0c623909889265d8dc1ab26e2ff4388ac016d521c38ec1ea15734ae22b7c4\
1573+ 6064412829c0d0579f0a713d1c04ede979026f0100000000000186a0005f6a20\
1574+ 6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000\
1575+ 1976a914df662e2dd70fd82acba2d252cc897cb6e618093288ac21025f756509\
1576+ f5dbac47d54c9ef5ccf49895a4dbac4759005a74375f66c480e6c08651016d52\
1577+ 1c38ec1ea15734ae22b7c46064412829c0d0579f0a713d1c04ede979026f0100\
1578+ 000000000006fc000054840300\
1579+ ") ;
1580+
1581+ assert_eq ! ( tx. input. len( ) , 2 ) ;
1582+ assert_eq ! ( tx. output. len( ) , 3 ) ;
1583+ assert ! ( !tx. output[ 0 ] . is_null_data( ) ) ;
1584+ assert ! ( !tx. output[ 0 ] . is_pegout( ) ) ;
1585+ assert ! ( !tx. output[ 0 ] . is_fee( ) ) ;
1586+
1587+ assert ! ( tx. output[ 1 ] . is_null_data( ) ) ;
1588+ assert ! ( !tx. output[ 1 ] . is_pegout( ) ) ;
1589+ assert ! ( !tx. output[ 1 ] . is_fee( ) ) ;
1590+
1591+ assert ! ( !tx. output[ 2 ] . is_null_data( ) ) ;
1592+ assert ! ( !tx. output[ 2 ] . is_pegout( ) ) ;
1593+ assert ! ( tx. output[ 2 ] . is_fee( ) ) ;
1594+
1595+ assert_eq ! ( tx. output[ 0 ] . asset, tx. output[ 1 ] . asset) ;
1596+ assert_eq ! ( tx. output[ 2 ] . asset, tx. output[ 1 ] . asset) ;
1597+ }
14881598}
14891599
0 commit comments