11import { Buffer } from 'buffer' ;
2- import { formatBytes , TinyPromiseQueue } from 'tiny-essentials' ;
2+ import { TinyPromiseQueue } from 'tiny-essentials' ;
33import { EventEmitter } from 'events' ;
44import TinyCryptoParser from '../lib/TinyCryptoParser.mjs' ;
55
@@ -66,6 +66,7 @@ class TinyChainInstance {
6666 #blockSizeLimit;
6767 #payloadSizeLimit;
6868 #blockContentSizeLimit;
69+ #textEncoder = new TextEncoder ( ) ;
6970
7071 /**
7172 * Important instance used to make event emitter.
@@ -271,7 +272,7 @@ class TinyChainInstance {
271272 * @param {Balances } [options.initialBalances={}] - Optional mapping of initial addresses to balances.
272273 * @param {string[] } [options.admins=[]] - List of admin public keys granted elevated permissions.
273274 *
274- * @param {number } [options.blockContentSizeLimit=-1] - Defines the maximum number of items allowed inside a single block's content.
275+ * @param {number } [options.blockContentSizeLimit=-1] - Defines the maximum size (in bytes) allowed inside a single block's content.
275276 * A value of -1 disables the limit entirely.
276277 *
277278 * @param {number } [options.blockSizeLimit=-1] - Defines the total maximum size (in bytes) for an entire block.
@@ -635,14 +636,33 @@ class TinyChainInstance {
635636 *
636637 */
637638 #createBlockInstance( options ) {
638- return new TinyChainBlock ( {
639+ const blockConfig = {
639640 baseFeePerGas : this . isCurrencyMode ( ) ? this . getBaseFeePerGas ( ) : 0n ,
640641 payloadString : this . #payloadString,
641642 parser : this . #parser,
642643 signer : this . #signer,
643644 chainId : this . getChainId ( ) ,
644645 ...options ,
645- } ) ;
646+ } ;
647+
648+ if ( this . #blockSizeLimit >= 0 ) {
649+ const blockSize = this . #textEncoder. encode (
650+ this . #parser. serializeDeep (
651+ Object . fromEntries (
652+ Object . entries ( blockConfig ) . filter (
653+ ( [ key , value ] ) => typeof value !== 'function' && ! [ 'parser' , 'signer' ] . includes ( key ) ,
654+ ) ,
655+ ) ,
656+ ) ,
657+ ) . length ;
658+
659+ if ( blockSize > this . #blockSizeLimit)
660+ throw new Error (
661+ `Block size exceeded: block is ${ blockSize } bytes, limit is ${ this . #blockSizeLimit} bytes` ,
662+ ) ;
663+ }
664+
665+ return new TinyChainBlock ( blockConfig ) ;
646666 }
647667
648668 /**
@@ -800,12 +820,14 @@ class TinyChainInstance {
800820 * @param {string } [options.address] - Sender address of the transaction.
801821 * @param {string } [options.addressType] - Type of address (e.g., 'user', 'contract'). Must be a non-empty string.
802822 *
803- * @throws {Error } If payload is not a string.
823+ * @throws {Error } If payload is not a string when required .
804824 * @throws {Error } If transfers is not an array.
805825 * @throws {Error } If any gas parameter is not a BigInt.
806826 * @throws {Error } If address is not a string.
807827 * @throws {Error } If addressType is invalid.
808828 * @throws {Error } If gas used exceeds the provided gas limit.
829+ * @throws {Error } If block content size exceeds the defined block content size limit.
830+ * @throws {Error } If payload size exceeds the defined payload size limit.
809831 *
810832 * @returns {bigint } Returns the estimated gas used for the transaction.
811833 */
@@ -818,7 +840,8 @@ class TinyChainInstance {
818840 address,
819841 addressType,
820842 } = { } ) {
821- if ( typeof payload !== 'string' ) throw new Error ( 'Payload must be a string' ) ;
843+ if ( this . #payloadString && typeof payload !== 'string' )
844+ throw new Error ( 'Payload must be a string' ) ;
822845 if ( ! Array . isArray ( transfers ) ) throw new Error ( 'Transfers must be an array' ) ;
823846 const gasUsed = this . isCurrencyMode ( ) ? this . estimateGasUsed ( transfers , payload ) : 0n ;
824847
@@ -833,9 +856,41 @@ class TinyChainInstance {
833856 if ( typeof addressType !== 'string' || addressType . length === 0 )
834857 throw new Error ( 'Invalid address type' ) ;
835858
836- if ( gasUsed > gasLimit )
837- throw new Error ( `Gas limit exceeded: used ${ gasUsed } > limit ${ gasLimit } ` ) ;
859+ const totalGas = gasUsed + this . getBaseFeePerGas ( ) ;
860+ if ( totalGas > gasLimit )
861+ throw new Error ( `Gas limit exceeded: used ${ totalGas } > limit ${ gasLimit } ` ) ;
838862 if ( Array . isArray ( transfers ) ) this . validateTransfers ( address , addressType , transfers ) ;
863+
864+ if ( this . #payloadSizeLimit >= 0 ) {
865+ const payloadSize = this . #textEncoder. encode (
866+ this . #payloadString ? payload : this . #parser. serializeDeep ( payload ) ,
867+ ) . length ;
868+
869+ if ( payloadSize > this . #payloadSizeLimit)
870+ throw new Error (
871+ `Payload size exceeded: payload is ${ payloadSize } bytes, limit is ${ this . #payloadSizeLimit} bytes` ,
872+ ) ;
873+ }
874+
875+ if ( this . #blockContentSizeLimit >= 0 ) {
876+ const blockContentSize = this . #textEncoder. encode (
877+ this . #parser. serializeDeep ( {
878+ payload,
879+ transfers,
880+ gasLimit,
881+ maxFeePerGas,
882+ maxPriorityFeePerGas,
883+ address,
884+ addressType,
885+ } ) ,
886+ ) . length ;
887+
888+ if ( blockContentSize > this . #blockContentSizeLimit)
889+ throw new Error (
890+ `Block content size exceeded: content is ${ blockContentSize } bytes, limit is ${ this . #blockContentSizeLimit} bytes` ,
891+ ) ;
892+ }
893+
839894 return gasUsed ;
840895 }
841896
0 commit comments