11import * as fs from 'fs' ;
2+ import * as crypto from 'crypto' ;
23import * as consts from './consts'
34import { ethers } from "ethers" ;
45import { namedAddress } from './accounts'
6+ import { S3Client , PutObjectCommand , CreateBucketCommand , HeadBucketCommand } from "@aws-sdk/client-s3" ;
57
68const path = require ( "path" ) ;
79
10+ const S3_CONFIG = {
11+ endpoint : "http://minio:9000" ,
12+ region : "us-east-1" ,
13+ credentials : {
14+ accessKeyId : "minioadmin" ,
15+ secretAccessKey : "minioadmin" ,
16+ } ,
17+ forcePathStyle : true ,
18+ } ;
19+
20+ const S3_BUCKET = "tx-filtering" ;
21+ const S3_OBJECT_KEY = "address-hashes.json" ;
22+
823function writePrysmConfig ( argv : any ) {
924 const prysm = `
1025CONFIG_NAME: interop
@@ -315,6 +330,26 @@ function writeConfigs(argv: any) {
315330 if ( argv . anytrust ) {
316331 simpleConfig . node [ "data-availability" ] [ "rpc-aggregator" ] . enable = true
317332 }
333+ if ( argv . txfiltering ) {
334+ simpleConfig . execution [ "address-filter" ] = {
335+ "enable" : true ,
336+ "s3" : {
337+ "access-key" : "minioadmin" ,
338+ "secret-key" : "minioadmin" ,
339+ "region" : "us-east-1" ,
340+ "endpoint" : "http://minio:9000" ,
341+ "bucket" : "tx-filtering" ,
342+ "object-key" : "address-hashes.json"
343+ } ,
344+ "poll-interval" : "30s"
345+ } ;
346+ simpleConfig . execution [ "transaction-filterer-rpc-client" ] = {
347+ "url" : "http://transaction-filterer:8547"
348+ } ;
349+ simpleConfig [ "init" ] = {
350+ "transaction-filtering-enabled" : true
351+ } ;
352+ }
318353 fs . writeFileSync ( path . join ( consts . configpath , "sequencer_config.json" ) , JSON . stringify ( simpleConfig ) )
319354 } else {
320355 let validatorConfig = JSON . parse ( baseConfJSON )
@@ -338,6 +373,26 @@ function writeConfigs(argv: any) {
338373 "redis-url" : argv . redisUrl
339374 } ;
340375 }
376+ if ( argv . txfiltering ) {
377+ sequencerConfig . execution [ "address-filter" ] = {
378+ "enable" : true ,
379+ "s3" : {
380+ "access-key" : "minioadmin" ,
381+ "secret-key" : "minioadmin" ,
382+ "region" : "us-east-1" ,
383+ "endpoint" : "http://minio:9000" ,
384+ "bucket" : "tx-filtering" ,
385+ "object-key" : "address-hashes.json"
386+ } ,
387+ "poll-interval" : "30s"
388+ } ;
389+ sequencerConfig . execution [ "transaction-filterer-rpc-client" ] = {
390+ "url" : "http://transaction-filterer:8547"
391+ } ;
392+ sequencerConfig [ "init" ] = {
393+ "transaction-filtering-enabled" : true
394+ } ;
395+ }
341396 fs . writeFileSync ( path . join ( consts . configpath , "sequencer_config.json" ) , JSON . stringify ( sequencerConfig ) )
342397
343398 let posterConfig = JSON . parse ( baseConfJSON )
@@ -418,7 +473,7 @@ function writeL2ChainConfig(argv: any) {
418473 "EnableArbOS" : true ,
419474 "AllowDebugPrecompiles" : true ,
420475 "DataAvailabilityCommittee" : argv . anytrust ,
421- "InitialArbOSVersion" : 40 ,
476+ "InitialArbOSVersion" : 60 ,
422477 "InitialChainOwner" : argv . l2owner ,
423478 "GenesisBlockNum" : 0
424479 }
@@ -451,7 +506,7 @@ function writeL3ChainConfig(argv: any) {
451506 "EnableArbOS" : true ,
452507 "AllowDebugPrecompiles" : true ,
453508 "DataAvailabilityCommittee" : false ,
454- "InitialArbOSVersion" : 40 ,
509+ "InitialArbOSVersion" : 60 ,
455510 "InitialChainOwner" : argv . l2owner ,
456511 "GenesisBlockNum" : 0
457512 }
@@ -658,6 +713,11 @@ export const writeConfigCommand = {
658713 describe : "run sequencer in timeboost mode" ,
659714 default : false
660715 } ,
716+ txfiltering : {
717+ boolean : true ,
718+ describe : "enable transaction filtering mode" ,
719+ default : false
720+ } ,
661721 } ,
662722 handler : ( argv : any ) => {
663723 writeConfigs ( argv )
@@ -754,3 +814,176 @@ export const writeL2ReferenceDAConfigCommand = {
754814 writeL2ReferenceDAConfig ( argv )
755815 }
756816}
817+
818+ function writeTransactionFiltererConfig ( ) {
819+ const config = {
820+ "chain-id" : 412346 ,
821+ "sequencer" : {
822+ "url" : "http://sequencer:8547"
823+ } ,
824+ "wallet" : {
825+ "account" : namedAddress ( "filterer" ) ,
826+ "password" : consts . l1passphrase ,
827+ "pathname" : consts . l1keystore
828+ } ,
829+ "http" : {
830+ "addr" : "0.0.0.0" ,
831+ "port" : 8547
832+ }
833+ } ;
834+ fs . writeFileSync ( path . join ( consts . configpath , "transaction_filterer_config.json" ) , JSON . stringify ( config ) ) ;
835+ }
836+
837+ export const writeTransactionFiltererConfigCommand = {
838+ command : "write-tx-filterer-config" ,
839+ describe : "writes transaction-filterer service config file" ,
840+ handler : ( ) => {
841+ writeTransactionFiltererConfig ( )
842+ }
843+ }
844+
845+ export const initTxFilteringMinioCommand = {
846+ command : "init-tx-filtering-minio" ,
847+ describe : "initializes MinIO bucket and empty address hash list" ,
848+ handler : async ( ) => {
849+ const salt = crypto . randomBytes ( 32 ) . toString ( 'hex' ) ;
850+ const initialAddressList = {
851+ "salt" : salt ,
852+ "hashing_scheme" : "Sha256" ,
853+ "address_hashes" : [ ]
854+ } ;
855+ fs . writeFileSync ( path . join ( consts . configpath , "initial_address_hashes.json" ) , JSON . stringify ( initialAddressList , null , 2 ) ) ;
856+ fs . writeFileSync ( path . join ( consts . configpath , "tx_filtering_salt.hex" ) , salt ) ;
857+
858+ const s3Client = new S3Client ( S3_CONFIG ) ;
859+
860+ try {
861+ await s3Client . send ( new HeadBucketCommand ( { Bucket : S3_BUCKET } ) ) ;
862+ console . log ( "Bucket already exists:" , S3_BUCKET ) ;
863+ } catch ( err : any ) {
864+ if ( err . name === "NotFound" || err . $metadata ?. httpStatusCode === 404 ) {
865+ await s3Client . send ( new CreateBucketCommand ( { Bucket : S3_BUCKET } ) ) ;
866+ console . log ( "Created bucket:" , S3_BUCKET ) ;
867+ } else {
868+ throw err ;
869+ }
870+ }
871+
872+ await uploadFilteredAddressesToMinio ( ) ;
873+ console . log ( "Initialized tx-filtering bucket with empty address list." ) ;
874+ }
875+ }
876+
877+ function computeAddressHash ( address : string , salt : string ) : string {
878+ const normalizedAddress = address . toLowerCase ( ) ;
879+ const data = salt + normalizedAddress . replace ( '0x' , '' ) ;
880+ const hash = crypto . createHash ( 'sha256' ) . update ( Buffer . from ( data , 'hex' ) ) . digest ( 'hex' ) ;
881+ return hash ;
882+ }
883+
884+ async function uploadFilteredAddressesToMinio ( ) {
885+ console . log ( "Uploading address list to MinIO..." ) ;
886+ const s3Client = new S3Client ( S3_CONFIG ) ;
887+
888+ const addressListPath = path . join ( consts . configpath , "initial_address_hashes.json" ) ;
889+ const content = fs . readFileSync ( addressListPath ) . toString ( ) ;
890+
891+ await s3Client . send ( new PutObjectCommand ( {
892+ Bucket : S3_BUCKET ,
893+ Key : S3_OBJECT_KEY ,
894+ Body : content ,
895+ ContentType : "application/json" ,
896+ } ) ) ;
897+
898+ console . log ( "Upload complete." ) ;
899+ }
900+
901+ export const hashAddressCommand = {
902+ command : "hash-address" ,
903+ describe : "computes SHA256 hash for an address with salt" ,
904+ builder : {
905+ address : {
906+ string : true ,
907+ describe : "address to hash" ,
908+ demandOption : true
909+ } ,
910+ } ,
911+ handler : ( argv : any ) => {
912+ const saltPath = path . join ( consts . configpath , "tx_filtering_salt.hex" ) ;
913+ if ( ! fs . existsSync ( saltPath ) ) {
914+ console . error ( "Salt file not found. Run init-tx-filtering-minio first." ) ;
915+ process . exit ( 1 ) ;
916+ }
917+ const salt = fs . readFileSync ( saltPath ) . toString ( ) . trim ( ) ;
918+ const hash = computeAddressHash ( argv . address , salt ) ;
919+ console . log ( hash ) ;
920+ }
921+ }
922+
923+ export const addFilteredAddressCommand = {
924+ command : "add-filtered-address" ,
925+ describe : "adds an address hash to the S3 filter list" ,
926+ builder : {
927+ address : {
928+ string : true ,
929+ describe : "address to add to filter list" ,
930+ demandOption : true
931+ } ,
932+ } ,
933+ handler : async ( argv : any ) => {
934+ const saltPath = path . join ( consts . configpath , "tx_filtering_salt.hex" ) ;
935+ if ( ! fs . existsSync ( saltPath ) ) {
936+ console . error ( "Salt file not found. Run init-tx-filtering-minio first." ) ;
937+ process . exit ( 1 ) ;
938+ }
939+ const salt = fs . readFileSync ( saltPath ) . toString ( ) . trim ( ) ;
940+ const hash = computeAddressHash ( argv . address , salt ) ;
941+
942+ const addressListPath = path . join ( consts . configpath , "initial_address_hashes.json" ) ;
943+ const addressList = JSON . parse ( fs . readFileSync ( addressListPath ) . toString ( ) ) ;
944+
945+ const exists = addressList . address_hashes . some ( ( entry : any ) => entry . hash === hash ) ;
946+ if ( ! exists ) {
947+ addressList . address_hashes . push ( { hash : hash } ) ;
948+ fs . writeFileSync ( addressListPath , JSON . stringify ( addressList , null , 2 ) ) ;
949+ console . log ( "Added address hash:" , hash ) ;
950+ await uploadFilteredAddressesToMinio ( ) ;
951+ } else {
952+ console . log ( "Address hash already in list:" , hash ) ;
953+ }
954+ }
955+ }
956+
957+ export const removeFilteredAddressCommand = {
958+ command : "remove-filtered-address" ,
959+ describe : "removes an address hash from the S3 filter list" ,
960+ builder : {
961+ address : {
962+ string : true ,
963+ describe : "address to remove from filter list" ,
964+ demandOption : true
965+ } ,
966+ } ,
967+ handler : async ( argv : any ) => {
968+ const saltPath = path . join ( consts . configpath , "tx_filtering_salt.hex" ) ;
969+ if ( ! fs . existsSync ( saltPath ) ) {
970+ console . error ( "Salt file not found. Run init-tx-filtering-minio first." ) ;
971+ process . exit ( 1 ) ;
972+ }
973+ const salt = fs . readFileSync ( saltPath ) . toString ( ) . trim ( ) ;
974+ const hash = computeAddressHash ( argv . address , salt ) ;
975+
976+ const addressListPath = path . join ( consts . configpath , "initial_address_hashes.json" ) ;
977+ const addressList = JSON . parse ( fs . readFileSync ( addressListPath ) . toString ( ) ) ;
978+
979+ const index = addressList . address_hashes . findIndex ( ( entry : any ) => entry . hash === hash ) ;
980+ if ( index > - 1 ) {
981+ addressList . address_hashes . splice ( index , 1 ) ;
982+ fs . writeFileSync ( addressListPath , JSON . stringify ( addressList , null , 2 ) ) ;
983+ console . log ( "Removed address hash:" , hash ) ;
984+ await uploadFilteredAddressesToMinio ( ) ;
985+ } else {
986+ console . log ( "Address hash not in list:" , hash ) ;
987+ }
988+ }
989+ }
0 commit comments