@@ -1380,7 +1380,7 @@ func TestEIP1559DynamicFeeTransaction(t *testing.T) {
13801380
13811381 // Common setup for both test approaches
13821382 maxFeePerGas := new (big.Int ).Mul (env .MustGetGasPrice (), big .NewInt (2 )) // 2x base fee
1383- maxPriorityFeePerGas := big .NewInt (1000000000 ) // 1 Gwei tip
1383+ maxPriorityFeePerGas := big .NewInt (10000000000 ) // 1 Gwei tip
13841384
13851385 t .Run ("native geth type" , func (t * testing.T ) {
13861386 // Test using eth_sendRawTransaction with manually constructed DynamicFeeTx
@@ -1458,7 +1458,7 @@ func TestEIP1559DynamicFeeTransactionWithAccessList(t *testing.T) {
14581458 require .NoError (t , err )
14591459
14601460 maxFeePerGas := new (big.Int ).Mul (env .MustGetGasPrice (), big .NewInt (2 ))
1461- maxPriorityFeePerGas := big .NewInt (1000000000 )
1461+ maxPriorityFeePerGas := big .NewInt (10000000000 )
14621462 accessList := types.AccessList {{Address : contractAddr , StorageKeys : []common.Hash {common .HexToHash ("0x0" )}}}
14631463
14641464 t .Run ("native geth type" , func (t * testing.T ) {
@@ -1550,7 +1550,7 @@ func TestEIP4844BlobTransaction(t *testing.T) {
15501550 blobHashes := sidecar .BlobHashes ()
15511551
15521552 maxFeePerGas := new (big.Int ).Mul (env .MustGetGasPrice (), big .NewInt (2 ))
1553- maxPriorityFeePerGas := big .NewInt (1000000000 )
1553+ maxPriorityFeePerGas := big .NewInt (10000000000 )
15541554 blobFeeCap := maxFeePerGas // Same as maxFeePerGas for simplicity
15551555
15561556 t .Run ("native geth type" , func (t * testing.T ) {
@@ -1565,7 +1565,7 @@ func TestEIP4844BlobTransaction(t *testing.T) {
15651565 Value : uint256 .NewInt (2000 ),
15661566 BlobFeeCap : uint256 .MustFromBig (maxFeePerGas ),
15671567 BlobHashes : blobHashes ,
1568- Sidecar : sidecar ,
1568+ // Sidecar: sidecar, // includes sidecar will make the blob tx exceeds tx size limit
15691569 }
15701570
15711571 signedBlobTx , err := types .SignTx (types .NewTx (blobTx ), env .Signer (), from )
@@ -1669,10 +1669,280 @@ func TestInvalidGasPriceConfiguration(t *testing.T) {
16691669 GasPrice : (* hexutil .Big )(env .MustGetGasPrice ()),
16701670 Value : (* hexutil .Big )(big .NewInt (1000 )),
16711671 MaxFeePerGas : (* hexutil .Big )(env .MustGetGasPrice ()),
1672- MaxPriorityFeePerGas : (* hexutil .Big )(big .NewInt (1000000000 )),
1672+ MaxPriorityFeePerGas : (* hexutil .Big )(big .NewInt (10000000000 )),
16731673 }
16741674
16751675 _ , err := env .SendTransaction (args )
16761676 require .Error (t , err )
16771677 require .Contains (t , err .Error (), "both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified" )
16781678}
1679+
1680+ // TestSendRawTransactionValidation tests the security validations in SendRawTransaction
1681+ func TestSendRawTransactionValidation (t * testing.T ) {
1682+ env := newSoloTestEnv (t )
1683+
1684+ t .Run ("empty transaction data" , func (t * testing.T ) {
1685+ var txHash common.Hash
1686+ err := env .RawClient .Call (& txHash , "eth_sendRawTransaction" , hexutil.Bytes {})
1687+ require .Error (t , err )
1688+ require .Contains (t , err .Error (), "empty transaction data" )
1689+ })
1690+
1691+ t .Run ("oversized transaction data" , func (t * testing.T ) {
1692+ // Create a transaction larger than 128KB
1693+ oversizedData := make ([]byte , 130 * 1024 )
1694+ var txHash common.Hash
1695+ err := env .RawClient .Call (& txHash , "eth_sendRawTransaction" , hexutil .Bytes (oversizedData ))
1696+ require .Error (t , err )
1697+ require .Contains (t , err .Error (), "transaction size" )
1698+ require .Contains (t , err .Error (), "exceeds maximum" )
1699+ })
1700+
1701+ t .Run ("invalid transaction encoding" , func (t * testing.T ) {
1702+ // Invalid RLP/binary data
1703+ invalidData := []byte {0xff , 0xff , 0xff }
1704+ var txHash common.Hash
1705+ err := env .RawClient .Call (& txHash , "eth_sendRawTransaction" , hexutil .Bytes (invalidData ))
1706+ require .Error (t , err )
1707+ require .Contains (t , err .Error (), "invalid transaction encoding" )
1708+ })
1709+
1710+ t .Run ("dynamic fee transaction validation" , func (t * testing.T ) {
1711+ from , fromAddr := env .NewAccountWithL2Funds ()
1712+ env .accountManager .Add (from )
1713+ _ , toAddr := env .NewAccountWithL2Funds ()
1714+
1715+ // Create a dynamic fee transaction with zero gas limit (invalid)
1716+ dynamicTx := & types.DynamicFeeTx {
1717+ ChainID : big .NewInt (int64 (env .ChainID )),
1718+ Nonce : env .NonceAt (fromAddr ),
1719+ GasFeeCap : big .NewInt (10000000000 ),
1720+ GasTipCap : big .NewInt (500000000 ),
1721+ Gas : 0 , // Invalid: zero gas limit
1722+ To : & toAddr ,
1723+ Value : big .NewInt (1000 ),
1724+ }
1725+
1726+ signedTx , err := types .SignTx (types .NewTx (dynamicTx ), env .Signer (), from )
1727+ require .NoError (t , err )
1728+
1729+ rawBytes , err := signedTx .MarshalBinary ()
1730+ require .NoError (t , err )
1731+
1732+ var txHash common.Hash
1733+ err = env .RawClient .Call (& txHash , "eth_sendRawTransaction" , hexutil .Bytes (rawBytes ))
1734+ require .Error (t , err )
1735+ require .Contains (t , err .Error (), "transaction gas limit cannot be zero" )
1736+ })
1737+
1738+ t .Run ("blob transaction validation" , func (t * testing.T ) {
1739+ from , fromAddr := env .NewAccountWithL2Funds ()
1740+ env .accountManager .Add (from )
1741+
1742+ // Create a blob transaction with no blobs (invalid)
1743+ _ , toAddr := env .NewAccountWithL2Funds ()
1744+ blobTx := & types.BlobTx {
1745+ ChainID : uint256 .NewInt (uint64 (env .ChainID )),
1746+ Nonce : env .NonceAt (fromAddr ),
1747+ GasFeeCap : uint256 .NewInt (2000000000 ),
1748+ GasTipCap : uint256 .NewInt (10000000000 ),
1749+ Gas : 21000 ,
1750+ To : toAddr ,
1751+ Value : uint256 .NewInt (1000 ),
1752+ BlobFeeCap : uint256 .NewInt (10000000000 ),
1753+ BlobHashes : []common.Hash {}, // Invalid: blob tx must contain at least one blob
1754+ }
1755+
1756+ signedTx , err := types .SignTx (types .NewTx (blobTx ), env .Signer (), from )
1757+ require .NoError (t , err )
1758+
1759+ rawBytes , err := signedTx .MarshalBinary ()
1760+ require .NoError (t , err )
1761+
1762+ var txHash common.Hash
1763+ err = env .RawClient .Call (& txHash , "eth_sendRawTransaction" , hexutil .Bytes (rawBytes ))
1764+ require .Error (t , err )
1765+ require .Contains (t , err .Error (), "blob transaction must contain at least one blob" )
1766+ })
1767+ }
1768+
1769+ // TestSendRawTransactionSecurityHardening tests advanced security validations
1770+ func TestSendRawTransactionSecurityHardening (t * testing.T ) {
1771+ env := newSoloTestEnv (t )
1772+
1773+ t .Run ("malformed RLP structure" , func (t * testing.T ) {
1774+ // Create deeply nested RLP structure to test complexity validation
1775+ malformedRLP := []byte {0xc0 } // Empty list
1776+ for i := 0 ; i < 20 ; i ++ { // Create deep nesting
1777+ malformedRLP = append ([]byte {0xc1 }, malformedRLP ... )
1778+ }
1779+
1780+ var txHash common.Hash
1781+ err := env .RawClient .Call (& txHash , "eth_sendRawTransaction" , hexutil .Bytes (malformedRLP ))
1782+ require .Error (t , err )
1783+ require .Contains (t , err .Error (), "RLP structure too deep" )
1784+ })
1785+
1786+ t .Run ("invalid transaction type" , func (t * testing.T ) {
1787+ // Test unsupported transaction type
1788+ invalidTypeData := []byte {0x7f } // Unsupported type 0x7f
1789+ invalidTypeData = append (invalidTypeData , make ([]byte , 100 )... ) // Add some payload
1790+
1791+ var txHash common.Hash
1792+ err := env .RawClient .Call (& txHash , "eth_sendRawTransaction" , hexutil .Bytes (invalidTypeData ))
1793+ require .Error (t , err )
1794+ require .Contains (t , err .Error (), "unsupported transaction type: 0x7f" )
1795+ })
1796+
1797+ t .Run ("malicious legacy type as typed" , func (t * testing.T ) {
1798+ // Test invalid typed transaction with legacy type 0x00
1799+ maliciousData := []byte {0x00 , 0xc0 } // Type 0x00 with empty payload
1800+
1801+ var txHash common.Hash
1802+ err := env .RawClient .Call (& txHash , "eth_sendRawTransaction" , hexutil .Bytes (maliciousData ))
1803+ require .Error (t , err )
1804+ require .Contains (t , err .Error (), "invalid typed transaction: legacy type 0x00" )
1805+ })
1806+
1807+ t .Run ("extremely large gas limit" , func (t * testing.T ) {
1808+ from , fromAddr := env .NewAccountWithL2Funds ()
1809+ env .accountManager .Add (from )
1810+ _ , toAddr := env .NewAccountWithL2Funds ()
1811+
1812+ // Create transaction with extremely large gas limit
1813+ maliciousTx := & types.LegacyTx {
1814+ Nonce : env .NonceAt (fromAddr ),
1815+ GasPrice : env .MustGetGasPrice (),
1816+ Gas : 40000000 , // 40M gas - exceeds our 30M limit
1817+ To : & toAddr ,
1818+ Value : big .NewInt (1000 ),
1819+ }
1820+
1821+ signedTx , err := types .SignTx (types .NewTx (maliciousTx ), env .Signer (), from )
1822+ require .NoError (t , err )
1823+
1824+ rawBytes , err := signedTx .MarshalBinary ()
1825+ require .NoError (t , err )
1826+
1827+ var txHash common.Hash
1828+ err = env .RawClient .Call (& txHash , "eth_sendRawTransaction" , hexutil .Bytes (rawBytes ))
1829+ require .Error (t , err )
1830+ require .Contains (t , err .Error (), "transaction gas limit" )
1831+ require .Contains (t , err .Error (), "exceeds maximum" )
1832+ })
1833+
1834+ t .Run ("extremely large data payload" , func (t * testing.T ) {
1835+ from , fromAddr := env .NewAccountWithL2Funds ()
1836+ env .accountManager .Add (from )
1837+ _ , toAddr := env .NewAccountWithL2Funds ()
1838+
1839+ // Create transaction with extremely large data payload
1840+ largeData := make ([]byte , 70 * 1024 ) // 70KB data - exceeds limit
1841+ maliciousTx := & types.LegacyTx {
1842+ Nonce : env .NonceAt (fromAddr ),
1843+ GasPrice : env .MustGetGasPrice (),
1844+ Gas : 21000 ,
1845+ To : & toAddr ,
1846+ Value : big .NewInt (1000 ),
1847+ Data : largeData ,
1848+ }
1849+
1850+ signedTx , err := types .SignTx (types .NewTx (maliciousTx ), env .Signer (), from )
1851+ require .NoError (t , err )
1852+
1853+ rawBytes , err := signedTx .MarshalBinary ()
1854+ require .NoError (t , err )
1855+
1856+ var txHash common.Hash
1857+ err = env .RawClient .Call (& txHash , "eth_sendRawTransaction" , hexutil .Bytes (rawBytes ))
1858+ require .Error (t , err )
1859+ require .Contains (t , err .Error (), "transaction data size" )
1860+ require .Contains (t , err .Error (), "exceeds maximum" )
1861+ })
1862+
1863+ t .Run ("wrong chain ID attack" , func (t * testing.T ) {
1864+ from , fromAddr := env .NewAccountWithL2Funds ()
1865+ env .accountManager .Add (from )
1866+ _ , toAddr := env .NewAccountWithL2Funds ()
1867+
1868+ // Create transaction with correct chain ID first, then manually modify
1869+ maliciousTx := & types.DynamicFeeTx {
1870+ ChainID : big .NewInt (int64 (env .ChainID )),
1871+ Nonce : env .NonceAt (fromAddr ),
1872+ GasFeeCap : env .MustGetGasPrice (),
1873+ GasTipCap : big .NewInt (10000000000 ),
1874+ Gas : 21000 ,
1875+ To : & toAddr ,
1876+ Value : big .NewInt (1000 ),
1877+ }
1878+
1879+ signedTx , err := types .SignTx (types .NewTx (maliciousTx ), env .Signer (), from )
1880+ require .NoError (t , err )
1881+
1882+ // Modify the raw bytes to have wrong chain ID after signing
1883+ // This simulates a malicious modification attempt
1884+ _ , err = signedTx .MarshalBinary ()
1885+ require .NoError (t , err )
1886+
1887+ // Create a new transaction with wrong chain ID and use our validation
1888+ wrongChainTx := & types.DynamicFeeTx {
1889+ ChainID : big .NewInt (999999 ), // Wrong chain ID
1890+ Nonce : env .NonceAt (fromAddr ),
1891+ GasFeeCap : env .MustGetGasPrice (),
1892+ GasTipCap : big .NewInt (10000000000 ),
1893+ Gas : 21000 ,
1894+ To : & toAddr ,
1895+ Value : big .NewInt (1000 ),
1896+ }
1897+
1898+ // Don't sign this one, create raw bytes manually to bypass signing validation
1899+ // This tests our post-unmarshal validation
1900+ wrongTx := types .NewTx (wrongChainTx )
1901+ wrongRawBytes , _ := wrongTx .MarshalBinary ()
1902+
1903+ var txHash common.Hash
1904+ err = env .RawClient .Call (& txHash , "eth_sendRawTransaction" , hexutil .Bytes (wrongRawBytes ))
1905+ require .Error (t , err )
1906+ // The error could be from signing validation or our chain ID validation
1907+ require .True (t ,
1908+ strings .Contains (err .Error (), "transaction chain ID" ) ||
1909+ strings .Contains (err .Error (), "invalid chain id for signer" ),
1910+ "Expected chain ID validation error, got: %s" , err .Error ())
1911+ })
1912+
1913+ t .Run ("complex RLP structure attack" , func (t * testing.T ) {
1914+ // Create RLP with too many elements
1915+ complexRLP := make ([]byte , 0 , 10000 )
1916+ complexRLP = append (complexRLP , 0xc0 ) // Start list
1917+
1918+ // Add many small elements to trigger complexity check
1919+ for i := 0 ; i < 1200 ; i ++ {
1920+ complexRLP = append (complexRLP , 0x80 ) // Empty string
1921+ }
1922+
1923+ var txHash common.Hash
1924+ err := env .RawClient .Call (& txHash , "eth_sendRawTransaction" , hexutil .Bytes (complexRLP ))
1925+ require .Error (t , err )
1926+ require .Contains (t , err .Error (), "RLP structure too complex" )
1927+ })
1928+
1929+ t .Run ("insufficient payload for typed transaction" , func (t * testing.T ) {
1930+ // Test typed transaction with insufficient payload
1931+ insufficientData := []byte {0x02 } // Type 0x02 but no payload
1932+
1933+ var txHash common.Hash
1934+ err := env .RawClient .Call (& txHash , "eth_sendRawTransaction" , hexutil .Bytes (insufficientData ))
1935+ require .Error (t , err )
1936+ require .Contains (t , err .Error (), "typed transaction missing payload" )
1937+ })
1938+
1939+ t .Run ("non-list legacy transaction" , func (t * testing.T ) {
1940+ // Test legacy transaction that's not an RLP list
1941+ nonListData := []byte {0x80 } // RLP string, not list
1942+
1943+ var txHash common.Hash
1944+ err := env .RawClient .Call (& txHash , "eth_sendRawTransaction" , hexutil .Bytes (nonListData ))
1945+ require .Error (t , err )
1946+ require .Contains (t , err .Error (), "legacy transaction must be RLP list" )
1947+ })
1948+ }
0 commit comments