diff --git a/.env.example b/.env.example index e4dd70aa..5a902bbb 100644 --- a/.env.example +++ b/.env.example @@ -1,8 +1,8 @@ ##### ORCHESTRATOR ##### -HOST= -PORT= -MAX_BLOCK_TO_PROCESS= +MADARA_ORCHESTRATOR_HOST= +MADARA_ORCHESTRATOR_PORT= +MADARA_ORCHESTRATOR_MAX_BLOCK_NO_TO_PROCESS= ##### AWS CONFIG ##### @@ -17,7 +17,7 @@ AWS_DEFAULT_REGION="localhost" ##### STORAGE ##### DATA_STORAGE= -AWS_S3_BUCKET_NAME= +MADARA_ORCHESTRATOR_AWS_S3_BUCKET_NAME= ##### QUEUE ##### @@ -30,39 +30,39 @@ SQS_WORKER_TRIGGER_QUEUE_URL= ##### SNS ##### ALERTS="sns" -AWS_SNS_ARN="arn:aws:sns:us-east-1:000000000000:madara-orchestrator-arn" +MADARA_ORCHESTRATOR_AWS_SNS_ARN="arn:aws:sns:us-east-1:000000000000:madara-orchestrator-arn" ##### DATABASE ##### DATABASE= -MONGODB_CONNECTION_STRING= -DATABASE_NAME= +MADARA_ORCHESTRATOR_MONGODB_CONNECTION_URL= +MADARA_ORCHESTRATOR_DATABASE_NAME= ##### PROVER ##### PROVER_SERVICE= -SHARP_CUSTOMER_ID= -SHARP_URL= -SHARP_USER_CRT= -SHARP_USER_KEY= -SHARP_SERVER_CRT= -SHARP_PROOF_LAYOUT= +MADARA_ORCHESTRATOR_SHARP_CUSTOMER_ID= +MADARA_ORCHESTRATOR_SHARP_URL= +MADARA_ORCHESTRATOR_SHARP_USER_CRT= +MADARA_ORCHESTRATOR_SHARP_USER_KEY= +MADARA_ORCHESTRATOR_SHARP_SERVER_CRT= +MADARA_ORCHESTRATOR_SHARP_PROOF_LAYOUT= ##### ON CHAIN CONFIG ##### DA_LAYER= SETTLEMENT_LAYER= -SETTLEMENT_RPC_URL= -MADARA_RPC_URL= -GPS_VERIFIER_CONTRACT_ADDRESS= +MADARA_ORCHESTRATOR_ETHEREUM_SETTLEMENT_RPC_URL= +MADARA_ORCHESTRATOR_MADARA_RPC_URL= +MADARA_ORCHESTRATOR_GPS_VERIFIER_CONTRACT_ADDRESS= PRIVATE_KEY= -ETHEREUM_PRIVATE_KEY= -L1_CORE_CONTRACT_ADDRESS= +MADARA_ORCHESTRATOR_ETHEREUM_PRIVATE_KEY= +MADARA_ORCHESTRATOR_L1_CORE_CONTRACT_ADDRESS= ##### STARKNET SETTLEMENT (L3s) ##### -STARKNET_PRIVATE_KEY= -STARKNET_ACCOUNT_ADDRESS= +MADARA_ORCHESTRATOR_STARKNET_PRIVATE_KEY= +MADARA_ORCHESTRATOR_STARKNET_ACCOUNT_ADDRESS= ##### Instrumentation ##### -OTEL_SERVICE_NAME= -OTEL_COLLECTOR_ENDPOINT= +MADARA_ORCHESTRATOR_OTEL_SERVICE_NAME= +MADARA_ORCHESTRATOR_OTEL_COLLECTOR_ENDPOINT= diff --git a/.env.test b/.env.test index c45e6245..c1bc7ea8 100644 --- a/.env.test +++ b/.env.test @@ -1,92 +1,103 @@ -##### ORCHESTRATOR ##### -HOST=127.0.0.1 -PORT=3000 -MAX_BLOCK_TO_PROCESS= -MIN_BLOCK_TO_PROCESS= +#### AWS CONFIG #### -##### AWS CONFIG ##### +AWS_ACCESS_KEY_ID=AWS_ACCESS_KEY_ID +AWS_SECRET_ACCESS_KEY=AWS_SECRET_ACCESS_KEY +AWS_REGION=us-east-1 -AWS_ACCESS_KEY_ID="AWS_ACCESS_KEY_ID" -AWS_SECRET_ACCESS_KEY="AWS_SECRET_ACCESS_KEY" -AWS_REGION="us-east-1" +# For Aws sdk +AWS_ENDPOINT_URL=http://localhost.localstack.cloud:4566 +# For Omniqueue +AWS_DEFAULT_REGION=localhost -##### Omniqueue ##### -AWS_DEFAULT_REGION="localhost" +#### ALERTS #### -##### STORAGE ##### +MADARA_ORCHESTRATOR_AWS_SNS_ARN=arn:aws:sns:us-east-1:000000000000:madara-orchestrator-arn -DATA_STORAGE="s3" -AWS_S3_BUCKET_NAME="madara-orchestrator-test-bucket" -##### QUEUE ##### +#### DATA AVAILABILITY #### -QUEUE_PROVIDER="sqs" +## ETHEREUM ## -SQS_SNOS_JOB_PROCESSING_QUEUE_URL="http://sqs.us-east-1.localhost.localstack.cloud:4566/000000000000/madara_orchestrator_snos_job_processing_queue" -SQS_SNOS_JOB_VERIFICATION_QUEUE_URL="http://sqs.us-east-1.localhost.localstack.cloud:4566/000000000000/madara_orchestrator_snos_job_verification_queue" +MADARA_ORCHESTRATOR_ETHEREUM_DA_RPC_URL=https://eth-sepolia.public.blastapi.io -SQS_PROVING_JOB_PROCESSING_QUEUE_URL="http://sqs.us-east-1.localhost.localstack.cloud:4566/000000000000/madara_orchestrator_proving_job_processing_queue" -SQS_PROVING_JOB_VERIFICATION_QUEUE_URL="http://sqs.us-east-1.localhost.localstack.cloud:4566/000000000000/madara_orchestrator_proving_job_verification_queue" -SQS_DATA_SUBMISSION_JOB_PROCESSING_QUEUE_URL="http://sqs.us-east-1.localhost.localstack.cloud:4566/000000000000/madara_orchestrator_data_submission_job_processing_queue" -SQS_DATA_SUBMISSION_JOB_VERIFICATION_QUEUE_URL="http://sqs.us-east-1.localhost.localstack.cloud:4566/000000000000/madara_orchestrator_data_submission_job_verification_queue" +#### DATABASE #### -SQS_UPDATE_STATE_JOB_PROCESSING_QUEUE_URL="http://sqs.us-east-1.localhost.localstack.cloud:4566/000000000000/madara_orchestrator_update_state_job_processing_queue" -SQS_UPDATE_STATE_JOB_VERIFICATION_QUEUE_URL="http://sqs.us-east-1.localhost.localstack.cloud:4566/000000000000/madara_orchestrator_update_state_job_verification_queue" +## MONGODB ## -SQS_JOB_HANDLE_FAILURE_QUEUE_URL="http://sqs.us-east-1.localhost.localstack.cloud:4566/000000000000/madara_orchestrator_job_handle_failure_queue" -SQS_WORKER_TRIGGER_QUEUE_URL="http://sqs.us-east-1.localhost.localstack.cloud:4566/000000000000/madara_orchestrator_worker_trigger_queue" +MADARA_ORCHESTRATOR_MONGODB_CONNECTION_URL=mongodb://localhost:27017 +MADARA_ORCHESTRATOR_DATABASE_NAME=orchestrator -##### SNS ##### +#### PROVER #### -ALERTS="sns" -AWS_SNS_ARN="arn:aws:sns:us-east-1:000000000000:madara-orchestrator-arn" +## SHARP ## -##### DATABASE ##### +MADARA_ORCHESTRATOR_SHARP_CUSTOMER_ID=sharp_consumer_id +MADARA_ORCHESTRATOR_SHARP_URL=http://127.0.0.1:6000 +MADARA_ORCHESTRATOR_SHARP_USER_CRT=LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUR4ekNDQXErZ0F3SUJBZ0lVTjBSK0xpb1MzL2ZadUZsK291RjZNNFk2RnRZd0RRWUpLb1pJaHZjTkFRRUwKQlFBd2N6RUxNQWtHQTFVRUJoTUNTVTR4RXpBUkJnTlZCQWdNQ2xOdmJXVXRVM1JoZEdVeElUQWZCZ05WQkFvTQpHRWx1ZEdWeWJtVjBJRmRwWkdkcGRITWdVSFI1SUV4MFpERU5NQXNHQTFVRUF3d0VVMVJTU3pFZE1Cc0dDU3FHClNJYjNEUUVKQVJZT1lXSmpRR3RoY201dmRDNTRlWG93SGhjTk1qUXdPREV6TVRNd05UTTBXaGNOTWpVd09ERXoKTVRNd05UTTBXakJ6TVFzd0NRWURWUVFHRXdKSlRqRVRNQkVHQTFVRUNBd0tVMjl0WlMxVGRHRjBaVEVoTUI4RwpBMVVFQ2d3WVNXNTBaWEp1WlhRZ1YybGtaMmwwY3lCUWRIa2dUSFJrTVEwd0N3WURWUVFEREFSVFZGSkxNUjB3Ckd3WUpLb1pJaHZjTkFRa0JGZzVoWW1OQWEyRnlibTkwTG5oNWVqQ0NBU0l3RFFZSktvWklodmNOQVFFQkJRQUQKZ2dFUEFEQ0NBUW9DZ2dFQkFOSEtaUGRqWSs4QWo4ZFV2V0xReEl5NTNrK1BHY001T2FlYnpTV3FER0xGSlBOdgpkVzJvWjFCSnNEb2hobWZFSCt5ZEFoQXEvbzc4NDljblg2VDJTOVhta25wdnNud2dRckU5Z3lqSmV3MUxBRzNHCm10U0lOMWJJSm9peWJ3QUR5NGxPd0xrVzUzdFdueHBSazVVVmZUU1hLYVRRTnlHd2o3Q2xMSGthcnlZYVk3OVkKOXlHMFJ2RkFkb1IzczBveWthNkFLV0d1WjhOdWd4NTY2bysyWllRenJteWVNU1NGYkhNdW1aUkxYb0hpazhBSgpLZXJ0bnNBRC9LMVJRYm80Y21ubHFoTVRhQktiTEFVVjVteFVvMlpveFBJVU9tREE5N3IyMmRTYkRkRlVjeC9kCjhQcDB6VXNycXdQckJlcW5SMXdLOE80MUlHajUzRnUzVmxDeS94MENBd0VBQWFOVE1GRXdIUVlEVlIwT0JCWUUKRkc0T0lvKzcvckJyZlR4S2FFMGx2L1dwRDJ3UE1COEdBMVVkSXdRWU1CYUFGRzRPSW8rNy9yQnJmVHhLYUUwbAp2L1dwRDJ3UE1BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFEMURDZkR3CnpoSXRGMWd5YVdhWURZRHErZjJSUHBFRWVaWk1BSDdJV0ZTajRrTzhmVHN1RnN6bFoyNXNlR3ZHYW4xQ3F4alQKYnJ3MXliVlJQeGZMUWgxRlZMMGhFeDZWYXhGditxMmtqUmlCQmZURFBxWGxYcmpaaUYrZTNPS3lKSVhnNkpIUAppbVpBV0dyRFBHNkorQi90bHRaQ3VLZVhLK1FUcnRSOVVCL29hOWVaQWc5RXNkOVJsZDRNeVo5b0NtdUNPU1hmCnk1THFkVlgrNENpTnJXQ3BwM1B2M2MyL28rZ0RMQjUzZ252R056RjR6Q1FIZ0RtN0RNZnpmZlY1TUMwV1MvWXkKVnpyUG11Sys0Y0tSK3dMOFZITVNEeC9ybTFhYnh0dEN2VW92MUw5dVZ1QUNGc29yNmdsR0N1RDNNQ0dIa0pNNgpxaS8rM1haeHhxeGw1Rzg9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K +MADARA_ORCHESTRATOR_SHARP_USER_KEY=LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRRFJ5bVQzWTJQdkFJL0gKVkwxaTBNU011ZDVQanhuRE9UbW5tODBscWd4aXhTVHpiM1Z0cUdkUVNiQTZJWVpueEIvc25RSVFLdjZPL09QWApKMStrOWt2VjVwSjZiN0o4SUVLeFBZTW95WHNOU3dCdHhwclVpRGRXeUNhSXNtOEFBOHVKVHNDNUZ1ZDdWcDhhClVaT1ZGWDAwbHltazBEY2hzSSt3cFN4NUdxOG1HbU8vV1BjaHRFYnhRSGFFZDdOS01wR3VnQ2xocm1mRGJvTWUKZXVxUHRtV0VNNjVzbmpFa2hXeHpMcG1VUzE2QjRwUEFDU25xN1o3QUEveXRVVUc2T0hKcDVhb1RFMmdTbXl3RgpGZVpzVktObWFNVHlGRHBnd1BlNjl0blVtdzNSVkhNZjNmRDZkTTFMSzZzRDZ3WHFwMGRjQ3ZEdU5TQm8rZHhiCnQxWlFzdjhkQWdNQkFBRUNnZ0VBQU9mcDFiT2xLOVFKeXVlUHhjeDIvTkNVcUMxTEJDL01FdkEyUzVKWGFWbkcKbGhLR0pFb1U0Q0RoVk83dUlLYVZLTFZvMjk4RHFHUnBLM1d0RVE1TE40bytXYTcveTA5c1drMlVzbWxrVWFOZwpSaGtVZEJSK2dLNXVsQ3FKRml2dUJoTEQvRWlnQ1VWUGZKS2JtNG96TnpYcjVSMU5ENlV1aWFtODdtenlFcTBLCmZsVXlhR0RZNGdIdFNBOVBENVBFYlUveFpKeitKaHk5T2l3aVRXV0MrSHoyb2c3UWRDRDE2RlhGcit2VHpQN0MKb2tFb0VDZFNPRWlMalVENjBhS2ZxRmFCVm5MTkVudC9QSytmY1RBM05mNGtSMnFDNk9ZWjVFb09zYm1ka29ZTgpyU3NJZW9XblMxOEhvekZud2w3Z05wTUtjNmRzQzRBTldOVDFsTkhCb1FLQmdRRHlaUDFJSlppZUh6NlExaUVTCm5zd2tnblZCQUQ0SlVLR1ZDMHA3dk4yclNDZXh4c05ZZXFPTEEyZGZCUGpOVjd3blFKcUgxT05XellOMUJVSUUKeThLTCtFZVl6Q3RZa21LL21wSGJIMzNjd2tJODBuMHJROU1BalZMTlJ2YVVEOWp1NFBsRzFqaEFZUVVyTkViZQpKRlVpSk83aDVQa1llZG50SitqSHFpQnRoUUtCZ1FEZGtPbndmL0szYk4xenR0bXZQd0VicjhkVWJjRVh5NDFOCkl5VWwrZW1WSlgzYktKM0duNDZnQ2RsTTdkYmpwS3JVZ3oxL2JsZTgvMkVFckJvSEFRNkMrU2pEaGhvL01CbnIKekZheTBoK3YxbjBnZnNNVzRoOEF4cEFwc25OYnh6K2g1Wm5uSnRTd0srUjB3U0VJVVEzRjAxL2hMWWhLQ2l5OApwbW5HQi9hU3VRS0JnRzdxd1cvVExGd214ZlYyMXBsenFzeUdHZXVObGRXalhOMGIxcEI2b3lDdW11TmhwYUFHCk5uSDFNOGNxT2tPVWd4ZWZHMWRPbGx6eEc5ZGZlWTlDUWhyVW1NYVZucndmK0NuZkxDRU43d1VtcXpLenl1MFMKVXlwc2dOaElRYXNNK1dLTjllTnhRVHBNYXhZVERONjMxM0VSWDNKazJZdFdydDh6cFBSQXFDZ1ZBb0dCQU54egpUa0NMbmJ6aFphbTNlZm9DenlCMEVma3dSdHBkSGxkc3E0NlFqTmRuK1VSd3NpTXBLR2lWeEE3bDZsU1B4NlV3CmU2VHA3Z1JQZUlHRWwxVDJ1VENacGZSODNtcVdlb1FCeVJXZE9nZmplcFkxYWZpL3ZhY3c2Y21ERTRKeXloNVUKYTMveFE5ZVJwSHFDbWxKREMxZ1V5eVlwL3B2a2FjUytNeW5sVEhHSkFvR0FQekdTSzdXOHBUYldSVEFoaTVrSQpwZk5kWk1tcnRodUxNT3F6TGhyRjZublpldk9OdTBoYXVhZktlVElFd2w0clhYZHFKQlJBaWZKMFFsLzZKWFFkCmd1VzFrZWk1Ui8rUFZ5eUhab042c3NXSTNWYklwUUloUmt6UENnTDZhbHEwSzFpT1dlV1lIOHdORGRRdlB1T2UKRkZPOEovSzNxV0NtWjU0ODBBbTNhT0U9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K +MADARA_ORCHESTRATOR_SHARP_SERVER_CRT=LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURhekNDQWxPZ0F3SUJBZ0lVRUR0Rjd0YVNTUnVQQTJ6Uk1aNWNzY2JCRm5jd0RRWUpLb1pJaHZjTkFRRUwKQlFBd1JURUxNQWtHQTFVRUJoTUNTVTR4RXpBUkJnTlZCQWdNQ2xOdmJXVXRVM1JoZEdVeElUQWZCZ05WQkFvTQpHRWx1ZEdWeWJtVjBJRmRwWkdkcGRITWdVSFI1SUV4MFpEQWVGdzB5TkRBNE1UTXhNekEzTVROYUZ3MHlOVEE0Ck1UTXhNekEzTVROYU1FVXhDekFKQmdOVkJBWVRBa2xPTVJNd0VRWURWUVFJREFwVGIyMWxMVk4wWVhSbE1TRXcKSHdZRFZRUUtEQmhKYm5SbGNtNWxkQ0JYYVdSbmFYUnpJRkIwZVNCTWRHUXdnZ0VpTUEwR0NTcUdTSWIzRFFFQgpBUVVBQTRJQkR3QXdnZ0VLQW9JQkFRRFRHcEEwNEZ1QlNFaE5PNVYvMGxTaDkvSEgxeVRZT2dRVFdoOG43eDlRCnZGMHpvZFZueVFIdjE5elU5eVdia2xvOEkvOXFBVm9lRzdXTnpUVFg2Q295ZlNjb1YvazN0Q2UwVnVWMlFJTVQKdW82SzJSU05CVHB1TlNqNTlzUiszVTQ2OFRBQnY0YVpsYjU4TU5CRXM3MVRieVpLRHBGRVRkMkg3T0ZKajg4QQpNRi9MaXJkeDZPOFdZL0tDeisxd1ZXL1JRdytYYjRJSWx4bXJFOC9UZ3FNSEo4dFUxYkZiOWJNcTEvOTN5YWtJClU1V2J2NVhXKzFwZFVyTUFNcTFFaC9vZThMN2pFaFdvZXZrNzgyU0kwUk0xeG5MaEtrUUVBYXd6Zkg2ODZiR2YKUHQ3RkFIQ1pGaWJ4KzZzSkg0R1M3S25iK0x5bk9ud3phMWZPUXZEZmcvRm5BZ01CQUFHalV6QlJNQjBHQTFVZApEZ1FXQkJUYlFUdmlUTW1xNXlNK2ZJRVI4VjdTZk1pK3B6QWZCZ05WSFNNRUdEQVdnQlRiUVR2aVRNbXE1eU0rCmZJRVI4VjdTZk1pK3B6QVBCZ05WSFJNQkFmOEVCVEFEQVFIL01BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRREYKTllyRnpBa2RIVkhjUkd5SUNsTi9IVGswaldOcTVSdTB1RUpDQ21Dbm9ZY1pRSTlDQlcwTkl3dGpZUkpTKzR1UwordWh4VWpSYTA5YXdOWDhvYmU0dDZjK25HRnhZMGZqamk0cGZnbU1kMWNJeGdsM3E3Nlp0ZkllRGR6alRLRXN1CjRFUTVadnEwMnJvTEZ0ZjEvL3dRVG0xNkNKdFpGWnhNZ1phYnNxc2JRc3M2dWdMUGtTTmdBWjI1L2VhcWhnQ20KTjFUV2FxL0xJMVBLSkxPK085NFlMa2FsNVpyOTJCOXk4Q0VKVUVuSTA1R1N1MmJUOFM2a0ZBMEpadEszTW9SbwpqRWZWV1lQVHR5TFR4amNvRndCcDlHaXZYSDdSdHBxMDlmSmFhU1pNekxmNGlyNHpBdXprbExBNWZvampPNXlKCllnYlVaQUU2aS81N1NFWjR3VmxTCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K +# MADARA_ORCHESTRATOR_SHARP_RPC_NODE_URL should be same as MADARA_ORCHESTRATOR_ETHEREUM_SETTLEMENT_RPC_URL +MADARA_ORCHESTRATOR_SHARP_RPC_NODE_URL=http://127.0.0.1:8545 +MADARA_ORCHESTRATOR_SHARP_PROOF_LAYOUT=small +MADARA_ORCHESTRATOR_GPS_VERIFIER_CONTRACT_ADDRESS=0x07ec0D28e50322Eb0C159B9090ecF3aeA8346DFe -DATABASE="mongodb" -MONGODB_CONNECTION_STRING="mongodb://localhost:27017" -DATABASE_NAME="orchestrator" -##### PROVER ##### +#### QUEUE #### -PROVER_SERVICE="sharp" -SHARP_CUSTOMER_ID="sharp_consumer_id" -SHARP_URL="http://127.0.0.1:6000" -# [IMP!!!] These are test certificates (they don't work) -SHARP_USER_CRT="LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUR4ekNDQXErZ0F3SUJBZ0lVTjBSK0xpb1MzL2ZadUZsK291RjZNNFk2RnRZd0RRWUpLb1pJaHZjTkFRRUwKQlFBd2N6RUxNQWtHQTFVRUJoTUNTVTR4RXpBUkJnTlZCQWdNQ2xOdmJXVXRVM1JoZEdVeElUQWZCZ05WQkFvTQpHRWx1ZEdWeWJtVjBJRmRwWkdkcGRITWdVSFI1SUV4MFpERU5NQXNHQTFVRUF3d0VVMVJTU3pFZE1Cc0dDU3FHClNJYjNEUUVKQVJZT1lXSmpRR3RoY201dmRDNTRlWG93SGhjTk1qUXdPREV6TVRNd05UTTBXaGNOTWpVd09ERXoKTVRNd05UTTBXakJ6TVFzd0NRWURWUVFHRXdKSlRqRVRNQkVHQTFVRUNBd0tVMjl0WlMxVGRHRjBaVEVoTUI4RwpBMVVFQ2d3WVNXNTBaWEp1WlhRZ1YybGtaMmwwY3lCUWRIa2dUSFJrTVEwd0N3WURWUVFEREFSVFZGSkxNUjB3Ckd3WUpLb1pJaHZjTkFRa0JGZzVoWW1OQWEyRnlibTkwTG5oNWVqQ0NBU0l3RFFZSktvWklodmNOQVFFQkJRQUQKZ2dFUEFEQ0NBUW9DZ2dFQkFOSEtaUGRqWSs4QWo4ZFV2V0xReEl5NTNrK1BHY001T2FlYnpTV3FER0xGSlBOdgpkVzJvWjFCSnNEb2hobWZFSCt5ZEFoQXEvbzc4NDljblg2VDJTOVhta25wdnNud2dRckU5Z3lqSmV3MUxBRzNHCm10U0lOMWJJSm9peWJ3QUR5NGxPd0xrVzUzdFdueHBSazVVVmZUU1hLYVRRTnlHd2o3Q2xMSGthcnlZYVk3OVkKOXlHMFJ2RkFkb1IzczBveWthNkFLV0d1WjhOdWd4NTY2bysyWllRenJteWVNU1NGYkhNdW1aUkxYb0hpazhBSgpLZXJ0bnNBRC9LMVJRYm80Y21ubHFoTVRhQktiTEFVVjVteFVvMlpveFBJVU9tREE5N3IyMmRTYkRkRlVjeC9kCjhQcDB6VXNycXdQckJlcW5SMXdLOE80MUlHajUzRnUzVmxDeS94MENBd0VBQWFOVE1GRXdIUVlEVlIwT0JCWUUKRkc0T0lvKzcvckJyZlR4S2FFMGx2L1dwRDJ3UE1COEdBMVVkSXdRWU1CYUFGRzRPSW8rNy9yQnJmVHhLYUUwbAp2L1dwRDJ3UE1BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFEMURDZkR3CnpoSXRGMWd5YVdhWURZRHErZjJSUHBFRWVaWk1BSDdJV0ZTajRrTzhmVHN1RnN6bFoyNXNlR3ZHYW4xQ3F4alQKYnJ3MXliVlJQeGZMUWgxRlZMMGhFeDZWYXhGditxMmtqUmlCQmZURFBxWGxYcmpaaUYrZTNPS3lKSVhnNkpIUAppbVpBV0dyRFBHNkorQi90bHRaQ3VLZVhLK1FUcnRSOVVCL29hOWVaQWc5RXNkOVJsZDRNeVo5b0NtdUNPU1hmCnk1THFkVlgrNENpTnJXQ3BwM1B2M2MyL28rZ0RMQjUzZ252R056RjR6Q1FIZ0RtN0RNZnpmZlY1TUMwV1MvWXkKVnpyUG11Sys0Y0tSK3dMOFZITVNEeC9ybTFhYnh0dEN2VW92MUw5dVZ1QUNGc29yNmdsR0N1RDNNQ0dIa0pNNgpxaS8rM1haeHhxeGw1Rzg9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K" -SHARP_USER_KEY="LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRRFJ5bVQzWTJQdkFJL0gKVkwxaTBNU011ZDVQanhuRE9UbW5tODBscWd4aXhTVHpiM1Z0cUdkUVNiQTZJWVpueEIvc25RSVFLdjZPL09QWApKMStrOWt2VjVwSjZiN0o4SUVLeFBZTW95WHNOU3dCdHhwclVpRGRXeUNhSXNtOEFBOHVKVHNDNUZ1ZDdWcDhhClVaT1ZGWDAwbHltazBEY2hzSSt3cFN4NUdxOG1HbU8vV1BjaHRFYnhRSGFFZDdOS01wR3VnQ2xocm1mRGJvTWUKZXVxUHRtV0VNNjVzbmpFa2hXeHpMcG1VUzE2QjRwUEFDU25xN1o3QUEveXRVVUc2T0hKcDVhb1RFMmdTbXl3RgpGZVpzVktObWFNVHlGRHBnd1BlNjl0blVtdzNSVkhNZjNmRDZkTTFMSzZzRDZ3WHFwMGRjQ3ZEdU5TQm8rZHhiCnQxWlFzdjhkQWdNQkFBRUNnZ0VBQU9mcDFiT2xLOVFKeXVlUHhjeDIvTkNVcUMxTEJDL01FdkEyUzVKWGFWbkcKbGhLR0pFb1U0Q0RoVk83dUlLYVZLTFZvMjk4RHFHUnBLM1d0RVE1TE40bytXYTcveTA5c1drMlVzbWxrVWFOZwpSaGtVZEJSK2dLNXVsQ3FKRml2dUJoTEQvRWlnQ1VWUGZKS2JtNG96TnpYcjVSMU5ENlV1aWFtODdtenlFcTBLCmZsVXlhR0RZNGdIdFNBOVBENVBFYlUveFpKeitKaHk5T2l3aVRXV0MrSHoyb2c3UWRDRDE2RlhGcit2VHpQN0MKb2tFb0VDZFNPRWlMalVENjBhS2ZxRmFCVm5MTkVudC9QSytmY1RBM05mNGtSMnFDNk9ZWjVFb09zYm1ka29ZTgpyU3NJZW9XblMxOEhvekZud2w3Z05wTUtjNmRzQzRBTldOVDFsTkhCb1FLQmdRRHlaUDFJSlppZUh6NlExaUVTCm5zd2tnblZCQUQ0SlVLR1ZDMHA3dk4yclNDZXh4c05ZZXFPTEEyZGZCUGpOVjd3blFKcUgxT05XellOMUJVSUUKeThLTCtFZVl6Q3RZa21LL21wSGJIMzNjd2tJODBuMHJROU1BalZMTlJ2YVVEOWp1NFBsRzFqaEFZUVVyTkViZQpKRlVpSk83aDVQa1llZG50SitqSHFpQnRoUUtCZ1FEZGtPbndmL0szYk4xenR0bXZQd0VicjhkVWJjRVh5NDFOCkl5VWwrZW1WSlgzYktKM0duNDZnQ2RsTTdkYmpwS3JVZ3oxL2JsZTgvMkVFckJvSEFRNkMrU2pEaGhvL01CbnIKekZheTBoK3YxbjBnZnNNVzRoOEF4cEFwc25OYnh6K2g1Wm5uSnRTd0srUjB3U0VJVVEzRjAxL2hMWWhLQ2l5OApwbW5HQi9hU3VRS0JnRzdxd1cvVExGd214ZlYyMXBsenFzeUdHZXVObGRXalhOMGIxcEI2b3lDdW11TmhwYUFHCk5uSDFNOGNxT2tPVWd4ZWZHMWRPbGx6eEc5ZGZlWTlDUWhyVW1NYVZucndmK0NuZkxDRU43d1VtcXpLenl1MFMKVXlwc2dOaElRYXNNK1dLTjllTnhRVHBNYXhZVERONjMxM0VSWDNKazJZdFdydDh6cFBSQXFDZ1ZBb0dCQU54egpUa0NMbmJ6aFphbTNlZm9DenlCMEVma3dSdHBkSGxkc3E0NlFqTmRuK1VSd3NpTXBLR2lWeEE3bDZsU1B4NlV3CmU2VHA3Z1JQZUlHRWwxVDJ1VENacGZSODNtcVdlb1FCeVJXZE9nZmplcFkxYWZpL3ZhY3c2Y21ERTRKeXloNVUKYTMveFE5ZVJwSHFDbWxKREMxZ1V5eVlwL3B2a2FjUytNeW5sVEhHSkFvR0FQekdTSzdXOHBUYldSVEFoaTVrSQpwZk5kWk1tcnRodUxNT3F6TGhyRjZublpldk9OdTBoYXVhZktlVElFd2w0clhYZHFKQlJBaWZKMFFsLzZKWFFkCmd1VzFrZWk1Ui8rUFZ5eUhab042c3NXSTNWYklwUUloUmt6UENnTDZhbHEwSzFpT1dlV1lIOHdORGRRdlB1T2UKRkZPOEovSzNxV0NtWjU0ODBBbTNhT0U9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K" -SHARP_SERVER_CRT="LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURhekNDQWxPZ0F3SUJBZ0lVRUR0Rjd0YVNTUnVQQTJ6Uk1aNWNzY2JCRm5jd0RRWUpLb1pJaHZjTkFRRUwKQlFBd1JURUxNQWtHQTFVRUJoTUNTVTR4RXpBUkJnTlZCQWdNQ2xOdmJXVXRVM1JoZEdVeElUQWZCZ05WQkFvTQpHRWx1ZEdWeWJtVjBJRmRwWkdkcGRITWdVSFI1SUV4MFpEQWVGdzB5TkRBNE1UTXhNekEzTVROYUZ3MHlOVEE0Ck1UTXhNekEzTVROYU1FVXhDekFKQmdOVkJBWVRBa2xPTVJNd0VRWURWUVFJREFwVGIyMWxMVk4wWVhSbE1TRXcKSHdZRFZRUUtEQmhKYm5SbGNtNWxkQ0JYYVdSbmFYUnpJRkIwZVNCTWRHUXdnZ0VpTUEwR0NTcUdTSWIzRFFFQgpBUVVBQTRJQkR3QXdnZ0VLQW9JQkFRRFRHcEEwNEZ1QlNFaE5PNVYvMGxTaDkvSEgxeVRZT2dRVFdoOG43eDlRCnZGMHpvZFZueVFIdjE5elU5eVdia2xvOEkvOXFBVm9lRzdXTnpUVFg2Q295ZlNjb1YvazN0Q2UwVnVWMlFJTVQKdW82SzJSU05CVHB1TlNqNTlzUiszVTQ2OFRBQnY0YVpsYjU4TU5CRXM3MVRieVpLRHBGRVRkMkg3T0ZKajg4QQpNRi9MaXJkeDZPOFdZL0tDeisxd1ZXL1JRdytYYjRJSWx4bXJFOC9UZ3FNSEo4dFUxYkZiOWJNcTEvOTN5YWtJClU1V2J2NVhXKzFwZFVyTUFNcTFFaC9vZThMN2pFaFdvZXZrNzgyU0kwUk0xeG5MaEtrUUVBYXd6Zkg2ODZiR2YKUHQ3RkFIQ1pGaWJ4KzZzSkg0R1M3S25iK0x5bk9ud3phMWZPUXZEZmcvRm5BZ01CQUFHalV6QlJNQjBHQTFVZApEZ1FXQkJUYlFUdmlUTW1xNXlNK2ZJRVI4VjdTZk1pK3B6QWZCZ05WSFNNRUdEQVdnQlRiUVR2aVRNbXE1eU0rCmZJRVI4VjdTZk1pK3B6QVBCZ05WSFJNQkFmOEVCVEFEQVFIL01BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRREYKTllyRnpBa2RIVkhjUkd5SUNsTi9IVGswaldOcTVSdTB1RUpDQ21Dbm9ZY1pRSTlDQlcwTkl3dGpZUkpTKzR1UwordWh4VWpSYTA5YXdOWDhvYmU0dDZjK25HRnhZMGZqamk0cGZnbU1kMWNJeGdsM3E3Nlp0ZkllRGR6alRLRXN1CjRFUTVadnEwMnJvTEZ0ZjEvL3dRVG0xNkNKdFpGWnhNZ1phYnNxc2JRc3M2dWdMUGtTTmdBWjI1L2VhcWhnQ20KTjFUV2FxL0xJMVBLSkxPK085NFlMa2FsNVpyOTJCOXk4Q0VKVUVuSTA1R1N1MmJUOFM2a0ZBMEpadEszTW9SbwpqRWZWV1lQVHR5TFR4amNvRndCcDlHaXZYSDdSdHBxMDlmSmFhU1pNekxmNGlyNHpBdXprbExBNWZvampPNXlKCllnYlVaQUU2aS81N1NFWjR3VmxTCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K" -SHARP_PROOF_LAYOUT="small" +## AWS SQS ## -##### ON CHAIN CONFIG ##### +MADARA_ORCHESTRATOR_SQS_PREFIX=madara_orchestrator +MADARA_ORCHESTRATOR_SQS_SUFFIX=queue +MADARA_ORCHESTRATOR_SQS_BASE_QUEUE_URL=http://sqs.us-east-1.localhost.localstack.cloud:4566/000000000000 -DA_LAYER="ethereum" -SETTLEMENT_LAYER="ethereum" -SETTLEMENT_RPC_URL="https://eth-sepolia.public.blastapi.io" -MADARA_RPC_URL=http://localhost:9944 -GPS_VERIFIER_CONTRACT_ADDRESS="0x07ec0D28e50322Eb0C159B9090ecF3aeA8346DFe" -ETHEREUM_PRIVATE_KEY="0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" -L1_CORE_CONTRACT_ADDRESS="0xE2Bb56ee936fd6433DC0F6e7e3b8365C906AA057" +#### SETTLEMENT #### -##### SNOS ##### -## This is needed right now because Madara doesn't support getProof -RPC_FOR_SNOS="http://localhost:9545" +## ETHEREUM ## -##### STARKNET SETTLEMENT ##### -STARKNET_PRIVATE_KEY=0x76f2ccdb23f29bc7b69278e947c01c6160a31cf02c19d06d0f6e5ab1d768b86 -STARKNET_ACCOUNT_ADDRESS=0x3bb306a004034dba19e6cf7b161e7a4fef64bc1078419e8ad1876192f0b8cd1 +MADARA_ORCHESTRATOR_ETHEREUM_SETTLEMENT_RPC_URL=https://eth-sepolia.public.blastapi.io +MADARA_ORCHESTRATOR_ETHEREUM_PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 +MADARA_ORCHESTRATOR_L1_CORE_CONTRACT_ADDRESS=0xE2Bb56ee936fd6433DC0F6e7e3b8365C906AA057 +MADARA_ORCHESTRATOR_STARKNET_OPERATOR_ADDRESS=0x5b98B836969A60FEC50Fa925905Dd1D382a7db43 -## Instrumentation -OTEL_SERVICE_NAME="madara_orchestrator" -OTEL_COLLECTOR_ENDPOINT="" +## STARKNET ## -##### Tests ##### +MADARA_ORCHESTRATOR_STARKNET_SETTLEMENT_RPC_URL=https://starknet-sepolia.public.blastapi.io +MADARA_ORCHESTRATOR_STARKNET_PRIVATE_KEY=0x76f2ccdb23f29bc7b69278e947c01c6160a31cf02c19d06d0f6e5ab1d768b86 +MADARA_ORCHESTRATOR_STARKNET_ACCOUNT_ADDRESS=0x3bb306a004034dba19e6cf7b161e7a4fef64bc1078419e8ad1876192f0b8cd1 +MADARA_ORCHESTRATOR_STARKNET_CAIRO_CORE_CONTRACT_ADDRESS= +MADARA_ORCHESTRATOR_STARKNET_FINALITY_RETRY_WAIT_IN_SECS=10 +# MADARA_ORCHESTRATOR_MADARA_BINARY_PATH= -STARKNET_OPERATOR_ADDRESS="0x5b98B836969A60FEC50Fa925905Dd1D382a7db43" -AWS_SNS_ARN_NAME="madara-orchestrator-arn" -MADARA_BINARY_PATH="/path/to/madara" -# pick up by AWS sdk -AWS_ENDPOINT_URL="http://localhost.localstack.cloud:4566" +#### STORAGE #### + +## AWS S3 ## + +MADARA_ORCHESTRATOR_AWS_S3_BUCKET_NAME=madara-orchestrator-test-bucket + + +#### INSTRUMENTATION #### + +## OTEL ## + +MADARA_ORCHESTRATOR_OTEL_SERVICE_NAME=orchestrator + + +#### SERVER #### + +MADARA_ORCHESTRATOR_HOST=127.0.0.1 +MADARA_ORCHESTRATOR_PORT=3000 + + +#### SERVICE #### + +MADARA_ORCHESTRATOR_MAX_BLOCK_NO_TO_PROCESS= +MADARA_ORCHESTRATOR_MIN_BLOCK_NO_TO_PROCESS= +MADARA_ORCHESTRATOR_MADARA_RPC_URL=http://81.16.176.130:9545 + + +#### SNOS #### + +MADARA_ORCHESTRATOR_RPC_FOR_SNOS=http://81.16.176.130:9545 diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 3efd0897..8db529c8 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -110,7 +110,7 @@ jobs: mv target/debug/madara ../madara-binary cd .. echo -e " - MADARA_BINARY_PATH=\"$(pwd)/madara-binary\"" >> .env.test + MADARA_ORCHESTRATOR_MADARA_BINARY_PATH=\"$(pwd)/madara-binary\"" >> .env.test cat .env.test - name: Getting neccesary files for testing @@ -128,8 +128,8 @@ jobs: - name: Run llvm-cov tests env: - SETTLEMENT_RPC_URL: ${{ secrets.ETHEREUM_SEPOLIA_BLAST_RPC }} - RPC_FOR_SNOS: ${{ secrets.RPC_FOR_SNOS }} + MADARA_ORCHESTRATOR_ETHEREUM_SETTLEMENT_RPC_URL: ${{ secrets.ETHEREUM_SEPOLIA_BLAST_RPC }} + MADARA_ORCHESTRATOR_RPC_FOR_SNOS: ${{ secrets.RPC_FOR_SNOS }} # the self hosted runner has a different region so we override it here AWS_REGION: us-east-1 run: RUST_LOG=debug RUST_BACKTRACE=1 cargo llvm-cov nextest --release --features testing --lcov --output-path lcov.info --test-threads=1 --workspace --exclude=e2e-tests --no-fail-fast diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test.yml index 6391f2fb..ecf3466a 100644 --- a/.github/workflows/e2e-test.yml +++ b/.github/workflows/e2e-test.yml @@ -83,8 +83,8 @@ jobs: - name: Run e2e test env: - SETTLEMENT_RPC_URL: ${{ secrets.ETHEREUM_SEPOLIA_BLAST_RPC }} - RPC_FOR_SNOS: ${{ secrets.RPC_FOR_SNOS }} + MADARA_ORCHESTRATOR_ETHEREUM_SETTLEMENT_RPC_URL: ${{ secrets.ETHEREUM_SEPOLIA_BLAST_RPC }} + MADARA_ORCHESTRATOR_RPC_FOR_SNOS: ${{ secrets.RPC_FOR_SNOS }} # the self hosted runner has a different region so we override it here AWS_REGION: us-east-1 run: | diff --git a/CHANGELOG.md b/CHANGELOG.md index b7282e47..4d61f910 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ## Added -- setup functions added for cloud and db +- Added cli args support for all the services +- Setup functions added for cloud and db - panic handling in process job - upgrade ETH L1 bridge for withdrawals to work - added makefile and submodules diff --git a/Cargo.lock b/Cargo.lock index 4909a7c6..e895e6d3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4264,6 +4264,7 @@ dependencies = [ "aws-sdk-sqs", "bytes", "chrono", + "clap", "color-eyre", "dotenvy", "ethereum-settlement-client", @@ -4276,6 +4277,8 @@ dependencies = [ "serde", "serde_json", "starknet", + "strum 0.26.2", + "strum_macros 0.26.4", "testcontainers", "tokio", "tokio-stream", @@ -6947,6 +6950,7 @@ dependencies = [ "c-kzg", "cairo-vm 1.0.1 (git+https://github.com/Moonsong-Labs/cairo-vm?branch=notlesh%2Fsegment-arena-relocation-fix)", "chrono", + "clap", "color-eyre", "da-client-interface", "dotenvy", @@ -6986,6 +6990,7 @@ dependencies = [ "starknet-core 0.9.0", "starknet-os", "starknet-settlement-client", + "strum 0.26.2", "strum_macros 0.26.4", "tempfile", "thiserror", @@ -9674,6 +9679,8 @@ dependencies = [ name = "starknet-settlement-client" version = "0.1.0" dependencies = [ + "alloy 0.2.1", + "alloy-primitives 0.7.7", "appchain-core-contract-client", "async-std", "async-trait", @@ -10878,11 +10885,13 @@ dependencies = [ "opentelemetry-stdout", "opentelemetry_sdk", "serde", + "serde_json", "thiserror", "tracing", "tracing-core", "tracing-opentelemetry", "tracing-subscriber", + "url", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index d09b920a..ed7c1a7d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,7 @@ alloy = { version = "0.2.1", features = [ "json-rpc", "rpc-client", ] } +alloy-primitives = { version = "0.7.7", default-features = false } aws-config = { version = "1.1.7", features = ["behavior-version-latest"] } aws-sdk-s3 = { version = "1.38.0", features = ["behavior-version-latest"] } aws-sdk-eventbridge = { version = "1.41.0", features = [ @@ -60,6 +61,8 @@ rstest = "0.22.0" serde = { version = "1.0.197", features = ["derive"] } serde_json = "1.0.114" starknet = "0.11.0" +strum = "0.26.0" +strum_macros = "0.26.0" tempfile = "3.12.0" thiserror = "1.0.57" tokio = { version = "1.37.0" } @@ -74,6 +77,7 @@ lazy_static = "1.4.0" stark_evm_adapter = "0.1.1" hex = "0.4" itertools = "0.13.0" +clap = { version = "4.4", features = ["derive", "env"] } mockall = "0.13.0" testcontainers = "0.18.0" once_cell = "1.8" diff --git a/crates/da-clients/ethereum/src/config.rs b/crates/da-clients/ethereum/src/config.rs deleted file mode 100644 index f5fa9d86..00000000 --- a/crates/da-clients/ethereum/src/config.rs +++ /dev/null @@ -1,29 +0,0 @@ -use std::str::FromStr; - -use alloy::network::Ethereum; -use alloy::providers::ProviderBuilder; -use alloy::rpc::client::RpcClient; -use serde::{Deserialize, Serialize}; -use url::Url; -use utils::settings::Settings; - -use crate::EthereumDaClient; - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct EthereumDaConfig { - pub rpc_url: String, -} - -impl EthereumDaConfig { - pub fn new_with_settings(settings: &impl Settings) -> color_eyre::Result { - Ok(Self { rpc_url: settings.get_settings_or_panic("SETTLEMENT_RPC_URL") }) - } - - pub async fn build_client(&self) -> EthereumDaClient { - let client = - RpcClient::new_http(Url::from_str(self.rpc_url.as_str()).expect("Failed to parse SETTLEMENT_RPC_URL")); - let provider = ProviderBuilder::<_, Ethereum>::new().on_client(client); - - EthereumDaClient { provider } - } -} diff --git a/crates/da-clients/ethereum/src/lib.rs b/crates/da-clients/ethereum/src/lib.rs index 5bf7ab63..ee156c91 100644 --- a/crates/da-clients/ethereum/src/lib.rs +++ b/crates/da-clients/ethereum/src/lib.rs @@ -13,19 +13,30 @@ use da_client_interface::{DaClient, DaVerificationStatus}; use mockall::automock; use mockall::predicate::*; use reqwest::Client; +use serde::{Deserialize, Serialize}; use url::Url; -use utils::settings::Settings; -use crate::config::EthereumDaConfig; - -pub const DA_SETTINGS_NAME: &str = "ethereum"; +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct EthereumDaValidatedArgs { + pub ethereum_da_rpc_url: Url, +} -pub mod config; pub struct EthereumDaClient { #[allow(dead_code)] provider: RootProvider>, } +impl EthereumDaClient { + pub async fn new_with_args(ethereum_da_params: &EthereumDaValidatedArgs) -> Self { + let client = RpcClient::new_http( + Url::from_str(ethereum_da_params.ethereum_da_rpc_url.as_str()) + .expect("Failed to parse ethereum_da_rpc_url"), + ); + let provider = ProviderBuilder::<_, Ethereum>::new().on_client(client); + Self { provider } + } +} + #[automock] #[async_trait] impl DaClient for EthereumDaClient { @@ -47,14 +58,3 @@ impl DaClient for EthereumDaClient { 131072 } } - -impl EthereumDaClient { - pub fn new_with_settings(settings: &impl Settings) -> Self { - let config = EthereumDaConfig::new_with_settings(settings) - .expect("Not able to create EthereumDaClient from given settings."); - let client = - RpcClient::new_http(Url::from_str(config.rpc_url.as_str()).expect("Failed to parse SETTLEMENT_RPC_URL")); - let provider = ProviderBuilder::<_, Ethereum>::new().on_client(client); - EthereumDaClient { provider } - } -} diff --git a/crates/orchestrator/Cargo.toml b/crates/orchestrator/Cargo.toml index a62bc239..fb15e1a1 100644 --- a/crates/orchestrator/Cargo.toml +++ b/crates/orchestrator/Cargo.toml @@ -31,6 +31,7 @@ bytes.workspace = true c-kzg = { workspace = true } cairo-vm = { workspace = true } chrono = { workspace = true } +clap.workspace = true color-eyre = { workspace = true } da-client-interface = { workspace = true } dotenvy = { workspace = true } @@ -62,7 +63,8 @@ starknet = { workspace = true } starknet-core = "0.9.0" starknet-os = { workspace = true } starknet-settlement-client = { workspace = true } -strum_macros = "0.26.4" +strum = { workspace = true } +strum_macros = { workspace = true } tempfile = { workspace = true } thiserror = { workspace = true } tokio = { workspace = true, features = ["sync", "macros", "rt-multi-thread"] } diff --git a/crates/orchestrator/src/alerts/aws_sns/config.rs b/crates/orchestrator/src/alerts/aws_sns/config.rs deleted file mode 100644 index 6387dd72..00000000 --- a/crates/orchestrator/src/alerts/aws_sns/config.rs +++ /dev/null @@ -1,19 +0,0 @@ -use serde::{Deserialize, Serialize}; -use utils::settings::Settings; - -#[derive(Clone, Serialize, Deserialize)] -pub struct AWSSNSConfig { - /// AWS SNS ARN - pub sns_arn: String, - /// AWS SNS region - pub sns_arn_region: String, -} - -impl AWSSNSConfig { - pub fn new_with_settings(settings: &impl Settings) -> color_eyre::Result { - Ok(Self { - sns_arn: settings.get_settings_or_panic("AWS_SNS_ARN"), - sns_arn_region: settings.get_settings_or_panic("AWS_REGION"), - }) - } -} diff --git a/crates/orchestrator/src/alerts/aws_sns/mod.rs b/crates/orchestrator/src/alerts/aws_sns/mod.rs index 3416be91..cc0a608c 100644 --- a/crates/orchestrator/src/alerts/aws_sns/mod.rs +++ b/crates/orchestrator/src/alerts/aws_sns/mod.rs @@ -1,16 +1,15 @@ -mod config; - -use std::sync::Arc; - use async_trait::async_trait; +use aws_config::SdkConfig; use aws_sdk_sns::Client; -use utils::settings::Settings; -use crate::alerts::aws_sns::config::AWSSNSConfig; use crate::alerts::Alerts; -use crate::config::ProviderConfig; -pub const AWS_SNS_SETTINGS_NAME: &str = "sns"; +#[derive(Debug, Clone)] +pub struct AWSSNSValidatedArgs { + // TODO: convert to ARN type, and validate it + // NOTE: aws is using str to represent ARN : https://docs.aws.amazon.com/sdk-for-rust/latest/dg/rust_sns_code_examples.html + pub topic_arn: String, +} pub struct AWSSNS { client: Client, @@ -18,11 +17,8 @@ pub struct AWSSNS { } impl AWSSNS { - pub async fn new_with_settings(settings: &impl Settings, provider_config: Arc) -> Self { - let sns_config = - AWSSNSConfig::new_with_settings(settings).expect("Not able to get Aws sns config from provided settings"); - let config = provider_config.get_aws_client_or_panic(); - Self { client: Client::new(config), topic_arn: sns_config.sns_arn } + pub async fn new_with_args(aws_sns_params: &AWSSNSValidatedArgs, aws_config: &SdkConfig) -> Self { + Self { client: Client::new(aws_config), topic_arn: aws_sns_params.topic_arn.clone() } } } @@ -39,4 +35,8 @@ impl Alerts for AWSSNS { log::info!("SNS topic created. Topic ARN: {}", topic_arn); Ok(()) } + + async fn get_topic_name(&self) -> String { + self.topic_arn.split(":").last().expect("Failed to get last part of topic ARN").to_string() + } } diff --git a/crates/orchestrator/src/alerts/mod.rs b/crates/orchestrator/src/alerts/mod.rs index 30220cf8..bef94150 100644 --- a/crates/orchestrator/src/alerts/mod.rs +++ b/crates/orchestrator/src/alerts/mod.rs @@ -1,6 +1,5 @@ use async_trait::async_trait; use mockall::automock; -use utils::settings::Settings; pub mod aws_sns; @@ -9,10 +8,11 @@ pub mod aws_sns; pub trait Alerts: Send + Sync { /// To send an alert message to our alert service async fn send_alert_message(&self, message_body: String) -> color_eyre::Result<()>; + async fn get_topic_name(&self) -> String; async fn create_alert(&self, topic_name: &str) -> color_eyre::Result<()>; - async fn setup(&self, settings_provider: Box) -> color_eyre::Result<()> { - let sns_topic_name = settings_provider.get_settings_or_panic("ALERT_TOPIC_NAME"); - self.create_alert(&sns_topic_name).await?; + async fn setup(&self) -> color_eyre::Result<()> { + let topic_name = self.get_topic_name().await; + self.create_alert(&topic_name).await?; Ok(()) } } diff --git a/crates/orchestrator/src/cli/alert/aws_sns.rs b/crates/orchestrator/src/cli/alert/aws_sns.rs new file mode 100644 index 00000000..2e9e9860 --- /dev/null +++ b/crates/orchestrator/src/cli/alert/aws_sns.rs @@ -0,0 +1,14 @@ +use clap::Args; + +/// Parameters used to config AWS SNS. +#[derive(Debug, Clone, Args)] +#[group()] +pub struct AWSSNSCliArgs { + /// Use the AWS SNS client + #[arg(long)] + pub aws_sns: bool, + + /// The ARN of the SNS topic. + #[arg(env = "MADARA_ORCHESTRATOR_AWS_SNS_ARN", long, default_value = Some("arn:aws:sns:us-east-1:000000000000:madara-orchestrator-arn"))] + pub sns_arn: Option, +} diff --git a/crates/orchestrator/src/cli/alert/mod.rs b/crates/orchestrator/src/cli/alert/mod.rs new file mode 100644 index 00000000..f22dd0ac --- /dev/null +++ b/crates/orchestrator/src/cli/alert/mod.rs @@ -0,0 +1,8 @@ +use crate::alerts::aws_sns::AWSSNSValidatedArgs; + +pub mod aws_sns; + +#[derive(Clone, Debug)] +pub enum AlertValidatedArgs { + AWSSNS(AWSSNSValidatedArgs), +} diff --git a/crates/orchestrator/src/cli/cron/event_bridge.rs b/crates/orchestrator/src/cli/cron/event_bridge.rs new file mode 100644 index 00000000..923d3736 --- /dev/null +++ b/crates/orchestrator/src/cli/cron/event_bridge.rs @@ -0,0 +1,21 @@ +use clap::Args; + +/// CLI arguments for the aws event bridge. +#[derive(Debug, Clone, Args)] +#[group()] +pub struct AWSEventBridgeCliArgs { + /// Use the AWS Event Bridge client + #[arg(long)] + pub aws_event_bridge: bool, + + /// The name of the queue for the event bridge + #[arg(env = "MADARA_ORCHESTRATOR_EVENT_BRIDGE_TARGET_QUEUE_NAME", long, default_value = Some("madara_orchestrator_worker_trigger_queue"), help = "The name of the SNS queue to send messages to from the event bridge.")] + pub target_queue_name: Option, + /// The cron time for the event bridge trigger rule. + #[arg(env = "MADARA_ORCHESTRATOR_EVENT_BRIDGE_CRON_TIME", long, default_value = Some("60"), help = "The cron time for the event bridge trigger rule. Defaults to 10 seconds.")] + pub cron_time: Option, + + /// The name of the event bridge trigger rule. + #[arg(env = "MADARA_ORCHESTRATOR_EVENT_BRIDGE_TRIGGER_RULE_NAME", long, default_value = Some("madara-orchestrator-event-bridge-trigger-rule-name"), help = "The name of the event bridge trigger rule.")] + pub trigger_rule_name: Option, +} diff --git a/crates/orchestrator/src/cli/cron/mod.rs b/crates/orchestrator/src/cli/cron/mod.rs new file mode 100644 index 00000000..19e8a5cf --- /dev/null +++ b/crates/orchestrator/src/cli/cron/mod.rs @@ -0,0 +1,8 @@ +use crate::cron::event_bridge::AWSEventBridgeValidatedArgs; + +pub mod event_bridge; + +#[derive(Clone, Debug)] +pub enum CronValidatedArgs { + AWSEventBridge(AWSEventBridgeValidatedArgs), +} diff --git a/crates/orchestrator/src/cli/da/ethereum.rs b/crates/orchestrator/src/cli/da/ethereum.rs new file mode 100644 index 00000000..7c9c5cec --- /dev/null +++ b/crates/orchestrator/src/cli/da/ethereum.rs @@ -0,0 +1,15 @@ +use clap::Args; +use url::Url; + +/// Parameters used to config Ethereum. +#[derive(Debug, Clone, Args)] +#[group(requires_all = ["ethereum_da_rpc_url"])] +pub struct EthereumDaCliArgs { + /// Use the Ethereum DA layer. + #[arg(long)] + pub da_on_ethereum: bool, + + /// The RPC URL of the Ethereum node. + #[arg(env = "MADARA_ORCHESTRATOR_ETHEREUM_DA_RPC_URL", long)] + pub ethereum_da_rpc_url: Option, +} diff --git a/crates/orchestrator/src/cli/da/mod.rs b/crates/orchestrator/src/cli/da/mod.rs new file mode 100644 index 00000000..54c98496 --- /dev/null +++ b/crates/orchestrator/src/cli/da/mod.rs @@ -0,0 +1,8 @@ +use ethereum_da_client::EthereumDaValidatedArgs; + +pub mod ethereum; + +#[derive(Debug, Clone)] +pub enum DaValidatedArgs { + Ethereum(EthereumDaValidatedArgs), +} diff --git a/crates/orchestrator/src/cli/database/mod.rs b/crates/orchestrator/src/cli/database/mod.rs new file mode 100644 index 00000000..b2bd6cb1 --- /dev/null +++ b/crates/orchestrator/src/cli/database/mod.rs @@ -0,0 +1,8 @@ +use crate::database::mongodb::MongoDBValidatedArgs; + +pub mod mongodb; + +#[derive(Debug, Clone)] +pub enum DatabaseValidatedArgs { + MongoDB(MongoDBValidatedArgs), +} diff --git a/crates/orchestrator/src/cli/database/mongodb.rs b/crates/orchestrator/src/cli/database/mongodb.rs new file mode 100644 index 00000000..f357a69a --- /dev/null +++ b/crates/orchestrator/src/cli/database/mongodb.rs @@ -0,0 +1,18 @@ +use clap::Args; + +/// Parameters used to config MongoDB. +#[derive(Debug, Clone, Args)] +#[group(requires_all = ["mongodb_connection_url"])] +pub struct MongoDBCliArgs { + /// Use the MongoDB client + #[arg(long)] + pub mongodb: bool, + + /// The connection string to the MongoDB server. + #[arg(env = "MADARA_ORCHESTRATOR_MONGODB_CONNECTION_URL", long, default_value = Some("mongodb://localhost:27017"))] + pub mongodb_connection_url: Option, + + /// The name of the database. + #[arg(env = "MADARA_ORCHESTRATOR_DATABASE_NAME", long, default_value = Some("orchestrator"))] + pub mongodb_database_name: Option, +} diff --git a/crates/orchestrator/src/cli/instrumentation.rs b/crates/orchestrator/src/cli/instrumentation.rs new file mode 100644 index 00000000..648806f3 --- /dev/null +++ b/crates/orchestrator/src/cli/instrumentation.rs @@ -0,0 +1,20 @@ +use clap::Args; +use tracing::Level; +use url::Url; + +/// Parameters used to config instrumentation. +#[derive(Debug, Clone, Args)] +#[group()] +pub struct InstrumentationCliArgs { + /// The name of the instrumentation service. + #[arg(env = "MADARA_ORCHESTRATOR_OTEL_SERVICE_NAME", long, default_value = "orchestrator")] + pub otel_service_name: Option, + + /// The endpoint of the collector. + #[arg(env = "MADARA_ORCHESTRATOR_OTEL_COLLECTOR_ENDPOINT", long)] + pub otel_collector_endpoint: Option, + + /// The log level. + #[arg(env = "RUST_LOG", long, default_value = "INFO")] + pub log_level: Level, +} diff --git a/crates/orchestrator/src/cli/mod.rs b/crates/orchestrator/src/cli/mod.rs new file mode 100644 index 00000000..43359276 --- /dev/null +++ b/crates/orchestrator/src/cli/mod.rs @@ -0,0 +1,807 @@ +use alert::AlertValidatedArgs; +use clap::{ArgGroup, Parser, Subcommand}; +use cron::event_bridge::AWSEventBridgeCliArgs; +use cron::CronValidatedArgs; +use da::DaValidatedArgs; +use database::DatabaseValidatedArgs; +use prover::ProverValidatedArgs; +use provider::aws::AWSConfigCliArgs; +use provider::ProviderValidatedArgs; +use queue::QueueValidatedArgs; +use snos::SNOSParams; +use storage::StorageValidatedArgs; +use url::Url; + +use crate::config::ServiceParams; +use crate::routes::ServerParams; +use crate::telemetry::InstrumentationParams; + +pub mod alert; +pub mod cron; +pub mod da; +pub mod database; +pub mod instrumentation; +pub mod prover; +pub mod provider; +pub mod queue; +pub mod server; +pub mod service; +pub mod settlement; +pub mod snos; +pub mod storage; + +#[derive(Parser, Debug)] +pub struct Cli { + #[command(subcommand)] + pub command: Commands, +} + +#[derive(Subcommand, Debug)] +pub enum Commands { + /// Run the orchestrator + Run { + #[command(flatten)] + run_command: Box, + }, + /// Setup the orchestrator + Setup { + #[command(flatten)] + setup_command: Box, + }, +} + +#[derive(Parser, Debug)] +#[command(author, version, about, long_about = None)] +#[clap( + group( + ArgGroup::new("provider") + .args(&["aws"]) + .required(true) + .multiple(false) + ), + group( + ArgGroup::new("settlement_layer") + .args(&["settle_on_ethereum", "settle_on_starknet"]) + .required(true) + .multiple(false) + ), + group( + ArgGroup::new("storage") + .args(&["aws_s3"]) + .required(true) + .multiple(false) + .requires("provider") + ), + group( + ArgGroup::new("queue") + .args(&["aws_sqs"]) + .required(true) + .multiple(false) + .requires("provider") + ), + group( + ArgGroup::new("alert") + .args(&["aws_sns"]) + .required(true) + .multiple(false) + .requires("provider") + ), + group( + ArgGroup::new("prover") + .args(&["sharp"]) + .required(true) + .multiple(false) + ), + group( + ArgGroup::new("da_layer") + .args(&["da_on_ethereum"]) + .required(true) + .multiple(false) + ), +)] +pub struct RunCmd { + // Provider Config + #[clap(flatten)] + pub aws_config_args: AWSConfigCliArgs, + + // Settlement Layer + #[clap(flatten)] + ethereum_args: settlement::ethereum::EthereumSettlementCliArgs, + + #[clap(flatten)] + starknet_args: settlement::starknet::StarknetSettlementCliArgs, + + // Storage + #[clap(flatten)] + pub aws_s3_args: storage::aws_s3::AWSS3CliArgs, + + // Queue + #[clap(flatten)] + pub aws_sqs_args: queue::aws_sqs::AWSSQSCliArgs, + + // Server + #[clap(flatten)] + pub server_args: server::ServerCliArgs, + + // Alert + #[clap(flatten)] + pub aws_sns_args: alert::aws_sns::AWSSNSCliArgs, + + // Database + #[clap(flatten)] + pub mongodb_args: database::mongodb::MongoDBCliArgs, + + // Data Availability Layer + #[clap(flatten)] + pub ethereum_da_args: da::ethereum::EthereumDaCliArgs, + + // Prover + #[clap(flatten)] + pub sharp_args: prover::sharp::SharpCliArgs, + + // SNOS + #[clap(flatten)] + pub snos_args: snos::SNOSCliArgs, + + #[arg(env = "MADARA_ORCHESTRATOR_MADARA_RPC_URL", long, required = true)] + pub madara_rpc_url: Url, + + // Service + #[clap(flatten)] + pub service_args: service::ServiceCliArgs, + #[clap(flatten)] + pub instrumentation_args: instrumentation::InstrumentationCliArgs, +} + +impl RunCmd { + pub fn validate_provider_params(&self) -> Result { + validate_params::validate_provider_params(&self.aws_config_args) + } + + pub fn validate_alert_params(&self) -> Result { + validate_params::validate_alert_params(&self.aws_sns_args, &self.aws_config_args) + } + + pub fn validate_queue_params(&self) -> Result { + validate_params::validate_queue_params(&self.aws_sqs_args, &self.aws_config_args) + } + + pub fn validate_storage_params(&self) -> Result { + validate_params::validate_storage_params(&self.aws_s3_args, &self.aws_config_args) + } + + pub fn validate_database_params(&self) -> Result { + validate_params::validate_database_params(&self.mongodb_args) + } + + pub fn validate_da_params(&self) -> Result { + validate_params::validate_da_params(&self.ethereum_da_args) + } + + pub fn validate_settlement_params(&self) -> Result { + validate_params::validate_settlement_params(&self.ethereum_args, &self.starknet_args) + } + + pub fn validate_prover_params(&self) -> Result { + validate_params::validate_prover_params(&self.sharp_args) + } + + pub fn validate_instrumentation_params(&self) -> Result { + validate_params::validate_instrumentation_params(&self.instrumentation_args) + } + + pub fn validate_server_params(&self) -> Result { + validate_params::validate_server_params(&self.server_args) + } + + pub fn validate_service_params(&self) -> Result { + validate_params::validate_service_params(&self.service_args) + } + + pub fn validate_snos_params(&self) -> Result { + validate_params::validate_snos_params(&self.snos_args) + } +} + +#[derive(Parser, Debug)] +#[command(author, version, about, long_about = None)] +#[clap( + group( + ArgGroup::new("provider") + .args(&["aws"]) + .required(true) + .multiple(false) + ), + group( + ArgGroup::new("storage") + .args(&["aws_s3"]) + .required(true) + .multiple(false) + .requires("provider") + ), + group( + ArgGroup::new("queue") + .args(&["aws_sqs"]) + .required(true) + .multiple(false) + .requires("provider") + ), + group( + ArgGroup::new("alert") + .args(&["aws_sns"]) + .required(true) + .multiple(false) + .requires("provider") + ), + group( + ArgGroup::new("cron") + .args(&["aws_event_bridge"]) + .required(true) + .multiple(false) + .requires("provider") + ), +)] +pub struct SetupCmd { + // AWS Config + #[clap(flatten)] + pub aws_config_args: AWSConfigCliArgs, + + // Storage + #[clap(flatten)] + pub aws_s3_args: storage::aws_s3::AWSS3CliArgs, + + // Queue + #[clap(flatten)] + pub aws_sqs_args: queue::aws_sqs::AWSSQSCliArgs, + + // Alert + #[clap(flatten)] + pub aws_sns_args: alert::aws_sns::AWSSNSCliArgs, + + // Cron + #[clap(flatten)] + pub aws_event_bridge_args: AWSEventBridgeCliArgs, +} + +impl SetupCmd { + pub fn validate_provider_params(&self) -> Result { + validate_params::validate_provider_params(&self.aws_config_args) + } + + pub fn validate_storage_params(&self) -> Result { + validate_params::validate_storage_params(&self.aws_s3_args, &self.aws_config_args) + } + + pub fn validate_queue_params(&self) -> Result { + validate_params::validate_queue_params(&self.aws_sqs_args, &self.aws_config_args) + } + + pub fn validate_alert_params(&self) -> Result { + validate_params::validate_alert_params(&self.aws_sns_args, &self.aws_config_args) + } + + pub fn validate_cron_params(&self) -> Result { + validate_params::validate_cron_params(&self.aws_event_bridge_args, &self.aws_config_args) + } +} + +pub mod validate_params { + use std::str::FromStr as _; + use std::time::Duration; + + use alloy::primitives::Address; + use ethereum_da_client::EthereumDaValidatedArgs; + use ethereum_settlement_client::EthereumSettlementValidatedArgs; + use sharp_service::SharpValidatedArgs; + use starknet_settlement_client::StarknetSettlementValidatedArgs; + use url::Url; + + use super::alert::aws_sns::AWSSNSCliArgs; + use super::alert::AlertValidatedArgs; + use super::cron::event_bridge::AWSEventBridgeCliArgs; + use super::cron::CronValidatedArgs; + use super::da::ethereum::EthereumDaCliArgs; + use super::da::DaValidatedArgs; + use super::database::mongodb::MongoDBCliArgs; + use super::database::DatabaseValidatedArgs; + use super::instrumentation::InstrumentationCliArgs; + use super::prover::sharp::SharpCliArgs; + use super::prover::ProverValidatedArgs; + use super::provider::aws::AWSConfigCliArgs; + use super::provider::{AWSConfigValidatedArgs, ProviderValidatedArgs}; + use super::queue::aws_sqs::AWSSQSCliArgs; + use super::queue::QueueValidatedArgs; + use super::server::ServerCliArgs; + use super::service::ServiceCliArgs; + use super::settlement::ethereum::EthereumSettlementCliArgs; + use super::settlement::starknet::StarknetSettlementCliArgs; + use super::settlement::SettlementValidatedArgs; + use super::snos::{SNOSCliArgs, SNOSParams}; + use super::storage::aws_s3::AWSS3CliArgs; + use super::storage::StorageValidatedArgs; + use crate::alerts::aws_sns::AWSSNSValidatedArgs; + use crate::config::ServiceParams; + use crate::cron::event_bridge::AWSEventBridgeValidatedArgs; + use crate::data_storage::aws_s3::AWSS3ValidatedArgs; + use crate::database::mongodb::MongoDBValidatedArgs; + use crate::queue::sqs::AWSSQSValidatedArgs; + use crate::routes::ServerParams; + use crate::telemetry::InstrumentationParams; + + pub(crate) fn validate_provider_params( + aws_config_args: &AWSConfigCliArgs, + ) -> Result { + if aws_config_args.aws { + Ok(ProviderValidatedArgs::AWS(AWSConfigValidatedArgs { + aws_access_key_id: aws_config_args.aws_access_key_id.clone(), + aws_secret_access_key: aws_config_args.aws_secret_access_key.clone(), + aws_region: aws_config_args.aws_region.clone(), + })) + } else { + Err("Only AWS is supported as of now".to_string()) + } + } + + pub(crate) fn validate_alert_params( + aws_sns_args: &AWSSNSCliArgs, + aws_config_args: &AWSConfigCliArgs, + ) -> Result { + if aws_sns_args.aws_sns && aws_config_args.aws { + Ok(AlertValidatedArgs::AWSSNS(AWSSNSValidatedArgs { + topic_arn: aws_sns_args.sns_arn.clone().expect("SNS ARN is required"), + })) + } else { + Err("Only AWS SNS is supported as of now".to_string()) + } + } + + pub(crate) fn validate_queue_params( + aws_sqs_args: &AWSSQSCliArgs, + aws_config_args: &AWSConfigCliArgs, + ) -> Result { + if aws_sqs_args.aws_sqs && aws_config_args.aws { + Ok(QueueValidatedArgs::AWSSQS(AWSSQSValidatedArgs { + queue_base_url: Url::parse(&aws_sqs_args.queue_base_url.clone().expect("Queue base URL is required")) + .expect("Invalid queue base URL"), + sqs_prefix: aws_sqs_args.sqs_prefix.clone().expect("SQS prefix is required"), + sqs_suffix: aws_sqs_args.sqs_suffix.clone().expect("SQS suffix is required"), + })) + } else { + Err("Only AWS SQS is supported as of now".to_string()) + } + } + + pub(crate) fn validate_storage_params( + aws_s3_args: &AWSS3CliArgs, + aws_config_args: &AWSConfigCliArgs, + ) -> Result { + if aws_s3_args.aws_s3 && aws_config_args.aws { + Ok(StorageValidatedArgs::AWSS3(AWSS3ValidatedArgs { + bucket_name: aws_s3_args.bucket_name.clone().expect("Bucket name is required"), + })) + } else { + Err("Only AWS S3 is supported as of now".to_string()) + } + } + + pub(crate) fn validate_cron_params( + aws_event_bridge_args: &AWSEventBridgeCliArgs, + aws_config_args: &AWSConfigCliArgs, + ) -> Result { + if aws_event_bridge_args.aws_event_bridge && aws_config_args.aws { + Ok(CronValidatedArgs::AWSEventBridge(AWSEventBridgeValidatedArgs { + target_queue_name: aws_event_bridge_args + .target_queue_name + .clone() + .expect("Target queue name is required"), + cron_time: Duration::from_secs( + aws_event_bridge_args + .cron_time + .clone() + .expect("Cron time is required") + .parse::() + .expect("Failed to parse cron time"), + ), + trigger_rule_name: aws_event_bridge_args + .trigger_rule_name + .clone() + .expect("Trigger rule name is required"), + })) + } else { + Err("Only AWS Event Bridge is supported as of now".to_string()) + } + } + + pub(crate) fn validate_database_params(mongodb_args: &MongoDBCliArgs) -> Result { + if mongodb_args.mongodb { + Ok(DatabaseValidatedArgs::MongoDB(MongoDBValidatedArgs { + connection_url: Url::parse( + &mongodb_args.mongodb_connection_url.clone().expect("MongoDB connection URL is required"), + ) + .expect("Invalid MongoDB connection URL"), + database_name: mongodb_args.mongodb_database_name.clone().expect("MongoDB database name is required"), + })) + } else { + Err("Only MongoDB is supported as of now".to_string()) + } + } + + pub(crate) fn validate_da_params(ethereum_da_args: &EthereumDaCliArgs) -> Result { + if ethereum_da_args.da_on_ethereum { + Ok(DaValidatedArgs::Ethereum(EthereumDaValidatedArgs { + ethereum_da_rpc_url: ethereum_da_args + .ethereum_da_rpc_url + .clone() + .expect("Ethereum DA RPC URL is required"), + })) + } else { + Err("Only Ethereum is supported as of now".to_string()) + } + } + + pub(crate) fn validate_settlement_params( + ethereum_args: &EthereumSettlementCliArgs, + starknet_args: &StarknetSettlementCliArgs, + ) -> Result { + match (ethereum_args.settle_on_ethereum, starknet_args.settle_on_starknet) { + (true, true) => Err("Cannot settle on both Ethereum and Starknet".to_string()), + (true, false) => { + let l1_core_contract_address = Address::from_str( + ðereum_args.l1_core_contract_address.clone().expect("L1 core contract address is required"), + ) + .expect("Invalid L1 core contract address"); + let starknet_operator_address = Address::from_str( + ðereum_args.starknet_operator_address.clone().expect("Starknet operator address is required"), + ) + .expect("Invalid Starknet operator address"); + + let ethereum_params = EthereumSettlementValidatedArgs { + ethereum_rpc_url: ethereum_args.ethereum_rpc_url.clone().expect("Ethereum RPC URL is required"), + ethereum_private_key: ethereum_args + .ethereum_private_key + .clone() + .expect("Ethereum private key is required"), + l1_core_contract_address, + starknet_operator_address, + }; + Ok(SettlementValidatedArgs::Ethereum(ethereum_params)) + } + (false, true) => { + let starknet_params = StarknetSettlementValidatedArgs { + starknet_rpc_url: starknet_args.starknet_rpc_url.clone().expect("Starknet RPC URL is required"), + starknet_private_key: starknet_args + .starknet_private_key + .clone() + .expect("Starknet private key is required"), + starknet_account_address: starknet_args + .starknet_account_address + .clone() + .expect("Starknet account address is required"), + starknet_cairo_core_contract_address: starknet_args + .starknet_cairo_core_contract_address + .clone() + .expect("Starknet Cairo core contract address is required"), + starknet_finality_retry_wait_in_secs: starknet_args + .starknet_finality_retry_wait_in_secs + .expect("Starknet finality retry wait in seconds is required"), + }; + Ok(SettlementValidatedArgs::Starknet(starknet_params)) + } + (false, false) => Err("Settlement layer is required".to_string()), + } + } + + pub(crate) fn validate_prover_params(sharp_args: &SharpCliArgs) -> Result { + if sharp_args.sharp { + Ok(ProverValidatedArgs::Sharp(SharpValidatedArgs { + sharp_customer_id: sharp_args.sharp_customer_id.clone().expect("Sharp customer ID is required"), + sharp_url: sharp_args.sharp_url.clone().expect("Sharp URL is required"), + sharp_user_crt: sharp_args.sharp_user_crt.clone().expect("Sharp user certificate is required"), + sharp_user_key: sharp_args.sharp_user_key.clone().expect("Sharp user key is required"), + sharp_rpc_node_url: sharp_args.sharp_rpc_node_url.clone().expect("Sharp RPC node URL is required"), + sharp_proof_layout: sharp_args.sharp_proof_layout.clone().expect("Sharp proof layout is required"), + gps_verifier_contract_address: sharp_args + .gps_verifier_contract_address + .clone() + .expect("GPS verifier contract address is required"), + sharp_server_crt: sharp_args.sharp_server_crt.clone().expect("Sharp server certificate is required"), + })) + } else { + Err("Only Sharp is supported as of now".to_string()) + } + } + + pub(crate) fn validate_instrumentation_params( + instrumentation_args: &InstrumentationCliArgs, + ) -> Result { + Ok(InstrumentationParams { + otel_service_name: instrumentation_args.otel_service_name.clone().expect("Otel service name is required"), + otel_collector_endpoint: instrumentation_args.otel_collector_endpoint.clone(), + log_level: instrumentation_args.log_level, + }) + } + + pub(crate) fn validate_server_params(server_args: &ServerCliArgs) -> Result { + Ok(ServerParams { host: server_args.host.clone(), port: server_args.port }) + } + + pub(crate) fn validate_service_params(service_args: &ServiceCliArgs) -> Result { + Ok(ServiceParams { + // return None if the value is empty string + max_block_to_process: service_args.max_block_to_process.clone().and_then(|s| { + if s.is_empty() { None } else { Some(s.parse::().expect("Failed to parse max block to process")) } + }), + min_block_to_process: service_args.min_block_to_process.clone().and_then(|s| { + if s.is_empty() { None } else { Some(s.parse::().expect("Failed to parse min block to process")) } + }), + }) + } + + pub(crate) fn validate_snos_params(snos_args: &SNOSCliArgs) -> Result { + Ok(SNOSParams { rpc_for_snos: snos_args.rpc_for_snos.clone() }) + } + + #[cfg(test)] + pub mod test { + + use rstest::rstest; + use tracing::Level; + use url::Url; + + use crate::cli::alert::aws_sns::AWSSNSCliArgs; + use crate::cli::cron::event_bridge::AWSEventBridgeCliArgs; + use crate::cli::da::ethereum::EthereumDaCliArgs; + use crate::cli::database::mongodb::MongoDBCliArgs; + use crate::cli::instrumentation::InstrumentationCliArgs; + use crate::cli::prover::sharp::SharpCliArgs; + use crate::cli::provider::aws::AWSConfigCliArgs; + use crate::cli::queue::aws_sqs::AWSSQSCliArgs; + use crate::cli::server::ServerCliArgs; + use crate::cli::service::ServiceCliArgs; + use crate::cli::settlement::ethereum::EthereumSettlementCliArgs; + use crate::cli::settlement::starknet::StarknetSettlementCliArgs; + use crate::cli::snos::SNOSCliArgs; + use crate::cli::storage::aws_s3::AWSS3CliArgs; + use crate::cli::validate_params::{ + validate_alert_params, validate_cron_params, validate_da_params, validate_database_params, + validate_instrumentation_params, validate_prover_params, validate_provider_params, validate_queue_params, + validate_server_params, validate_service_params, validate_settlement_params, validate_snos_params, + validate_storage_params, + }; + + #[rstest] + #[case(true)] + #[case(false)] + fn test_validate_provider_params(#[case] is_aws: bool) { + let aws_config_args: AWSConfigCliArgs = AWSConfigCliArgs { + aws: is_aws, + aws_access_key_id: "".to_string(), + aws_secret_access_key: "".to_string(), + aws_region: "".to_string(), + }; + + let provider_params = validate_provider_params(&aws_config_args); + if is_aws { + assert!(provider_params.is_ok()); + } else { + assert!(provider_params.is_err()); + } + } + + #[rstest] + #[case(true, true)] + #[case(true, false)] + #[case(false, true)] + #[case(false, false)] + fn test_validate_alert_params(#[case] is_aws: bool, #[case] is_sns: bool) { + let aws_config_args: AWSConfigCliArgs = AWSConfigCliArgs { + aws: is_aws, + aws_access_key_id: "".to_string(), + aws_secret_access_key: "".to_string(), + aws_region: "".to_string(), + }; + let aws_sns_args: AWSSNSCliArgs = AWSSNSCliArgs { aws_sns: is_sns, sns_arn: Some("".to_string()) }; + + let alert_params = validate_alert_params(&aws_sns_args, &aws_config_args); + if is_aws && is_sns { + assert!(alert_params.is_ok()); + } else { + assert!(alert_params.is_err()); + } + } + + #[rstest] + #[case(true, true)] + #[case(true, false)] + #[case(false, true)] + #[case(false, false)] + fn test_validate_queue_params(#[case] is_aws: bool, #[case] is_sqs: bool) { + let aws_config_args: AWSConfigCliArgs = AWSConfigCliArgs { + aws: is_aws, + aws_access_key_id: "".to_string(), + aws_secret_access_key: "".to_string(), + aws_region: "".to_string(), + }; + let aws_sqs_args: AWSSQSCliArgs = AWSSQSCliArgs { + aws_sqs: is_sqs, + queue_base_url: Some("http://sqs.us-east-1.localhost.localstack.cloud:4566/000000000000".to_string()), + sqs_prefix: Some("".to_string()), + sqs_suffix: Some("".to_string()), + }; + let queue_params = validate_queue_params(&aws_sqs_args, &aws_config_args); + if is_aws && is_sqs { + assert!(queue_params.is_ok()); + } else { + assert!(queue_params.is_err()); + } + } + + #[rstest] + #[case(true, true)] + #[case(true, false)] + #[case(false, true)] + #[case(false, false)] + fn test_validate_storage_params(#[case] is_aws: bool, #[case] is_s3: bool) { + let aws_s3_args: AWSS3CliArgs = AWSS3CliArgs { aws_s3: is_s3, bucket_name: Some("".to_string()) }; + let aws_config_args: AWSConfigCliArgs = AWSConfigCliArgs { + aws: is_aws, + aws_access_key_id: "".to_string(), + aws_secret_access_key: "".to_string(), + aws_region: "".to_string(), + }; + let storage_params = validate_storage_params(&aws_s3_args, &aws_config_args); + if is_aws && is_s3 { + assert!(storage_params.is_ok()); + } else { + assert!(storage_params.is_err()); + } + } + + #[rstest] + #[case(true)] + #[case(false)] + fn test_validate_database_params(#[case] is_mongodb: bool) { + let mongodb_args: MongoDBCliArgs = MongoDBCliArgs { + mongodb: is_mongodb, + mongodb_connection_url: Some("mongodb://localhost:27017".to_string()), + mongodb_database_name: Some("orchestrator".to_string()), + }; + let database_params = validate_database_params(&mongodb_args); + if is_mongodb { + assert!(database_params.is_ok()); + } else { + assert!(database_params.is_err()); + } + } + + #[rstest] + #[case(true)] + #[case(false)] + fn test_validate_da_params(#[case] is_ethereum: bool) { + let ethereum_da_args: EthereumDaCliArgs = EthereumDaCliArgs { + da_on_ethereum: is_ethereum, + ethereum_da_rpc_url: Some(Url::parse("http://localhost:8545").unwrap()), + }; + let da_params = validate_da_params(ðereum_da_args); + if is_ethereum { + assert!(da_params.is_ok()); + } else { + assert!(da_params.is_err()); + } + } + + #[rstest] + #[case(true, false)] + #[case(false, true)] + #[case(false, false)] + #[case(true, true)] + fn test_validate_settlement_params(#[case] is_ethereum: bool, #[case] is_starknet: bool) { + let ethereum_args: EthereumSettlementCliArgs = EthereumSettlementCliArgs { + ethereum_rpc_url: Some(Url::parse("http://localhost:8545").unwrap()), + ethereum_private_key: Some("".to_string()), + l1_core_contract_address: Some("0xE2Bb56ee936fd6433DC0F6e7e3b8365C906AA057".to_string()), + starknet_operator_address: Some("0x5b98B836969A60FEC50Fa925905Dd1D382a7db43".to_string()), + settle_on_ethereum: is_ethereum, + }; + let starknet_args: StarknetSettlementCliArgs = StarknetSettlementCliArgs { + starknet_rpc_url: Some(Url::parse("http://localhost:8545").unwrap()), + starknet_private_key: Some("".to_string()), + starknet_account_address: Some("".to_string()), + starknet_cairo_core_contract_address: Some("".to_string()), + starknet_finality_retry_wait_in_secs: Some(0), + settle_on_starknet: is_starknet, + }; + let settlement_params = validate_settlement_params(ðereum_args, &starknet_args); + if is_ethereum ^ is_starknet { + assert!(settlement_params.is_ok()); + } else { + assert!(settlement_params.is_err()); + } + } + + #[rstest] + #[case(true)] + #[case(false)] + fn test_validate_prover_params(#[case] is_sharp: bool) { + let sharp_args: SharpCliArgs = SharpCliArgs { + sharp: is_sharp, + sharp_customer_id: Some("".to_string()), + sharp_url: Some(Url::parse("http://localhost:8545").unwrap()), + sharp_user_crt: Some("".to_string()), + sharp_user_key: Some("".to_string()), + sharp_rpc_node_url: Some(Url::parse("http://localhost:8545").unwrap()), + sharp_proof_layout: Some("".to_string()), + gps_verifier_contract_address: Some("".to_string()), + sharp_server_crt: Some("".to_string()), + }; + let prover_params = validate_prover_params(&sharp_args); + if is_sharp { + assert!(prover_params.is_ok()); + } else { + assert!(prover_params.is_err()); + } + } + + #[rstest] + #[case(true)] + #[case(false)] + fn test_validate_cron_params(#[case] is_aws: bool) { + let aws_event_bridge_args: AWSEventBridgeCliArgs = AWSEventBridgeCliArgs { + aws_event_bridge: is_aws, + target_queue_name: Some(String::from("test")), + cron_time: Some(String::from("12")), + trigger_rule_name: Some(String::from("test")), + }; + let aws_config_args: AWSConfigCliArgs = AWSConfigCliArgs { + aws: is_aws, + aws_access_key_id: "".to_string(), + aws_secret_access_key: "".to_string(), + aws_region: "".to_string(), + }; + let cron_params = validate_cron_params(&aws_event_bridge_args, &aws_config_args); + if is_aws { + assert!(cron_params.is_ok()); + } else { + assert!(cron_params.is_err()); + } + } + + #[rstest] + fn test_validate_instrumentation_params() { + let instrumentation_args: InstrumentationCliArgs = InstrumentationCliArgs { + otel_service_name: Some("".to_string()), + otel_collector_endpoint: None, + log_level: Level::INFO, + }; + let instrumentation_params = validate_instrumentation_params(&instrumentation_args); + assert!(instrumentation_params.is_ok()); + } + + #[rstest] + fn test_validate_server_params() { + let server_args: ServerCliArgs = ServerCliArgs { host: "".to_string(), port: 0 }; + let server_params = validate_server_params(&server_args); + assert!(server_params.is_ok()); + } + + #[rstest] + fn test_validate_snos_params() { + let snos_args: SNOSCliArgs = SNOSCliArgs { rpc_for_snos: Url::parse("http://localhost:8545").unwrap() }; + let snos_params = validate_snos_params(&snos_args); + assert!(snos_params.is_ok()); + } + + #[rstest] + fn test_validate_service_params() { + let service_args: ServiceCliArgs = ServiceCliArgs { + max_block_to_process: Some("66645".to_string()), + min_block_to_process: Some("100".to_string()), + }; + let service_params = validate_service_params(&service_args); + assert!(service_params.is_ok()); + let service_params = service_params.unwrap(); + assert_eq!(service_params.max_block_to_process, Some(66645)); + assert_eq!(service_params.min_block_to_process, Some(100)); + } + } +} diff --git a/crates/orchestrator/src/cli/prover/mod.rs b/crates/orchestrator/src/cli/prover/mod.rs new file mode 100644 index 00000000..01a4cf02 --- /dev/null +++ b/crates/orchestrator/src/cli/prover/mod.rs @@ -0,0 +1,8 @@ +use sharp_service::SharpValidatedArgs; + +pub mod sharp; + +#[derive(Debug, Clone)] +pub enum ProverValidatedArgs { + Sharp(SharpValidatedArgs), +} diff --git a/crates/orchestrator/src/cli/prover/sharp.rs b/crates/orchestrator/src/cli/prover/sharp.rs new file mode 100644 index 00000000..46e3ffee --- /dev/null +++ b/crates/orchestrator/src/cli/prover/sharp.rs @@ -0,0 +1,44 @@ +use clap::Args; +use url::Url; + +/// Parameters used to config Sharp. +#[derive(Debug, Clone, Args)] +#[group(requires_all = ["sharp_customer_id", "sharp_url", "sharp_user_crt", "sharp_user_key", "sharp_server_crt", "gps_verifier_contract_address", "sharp_rpc_node_url"])] +pub struct SharpCliArgs { + /// Use the Sharp prover. + #[arg(long)] + pub sharp: bool, + + /// The customer id for Sharp. + #[arg(env = "MADARA_ORCHESTRATOR_SHARP_CUSTOMER_ID", long)] + pub sharp_customer_id: Option, + + /// The URL of the Sharp server. + #[arg(env = "MADARA_ORCHESTRATOR_SHARP_URL", long)] + pub sharp_url: Option, + + /// The user certificate for Sharp. + #[arg(env = "MADARA_ORCHESTRATOR_SHARP_USER_CRT", long)] + pub sharp_user_crt: Option, + + /// The user key for Sharp. + #[arg(env = "MADARA_ORCHESTRATOR_SHARP_USER_KEY", long)] + pub sharp_user_key: Option, + + /// The RPC node URL for Sharp. + #[arg(env = "MADARA_ORCHESTRATOR_SHARP_RPC_NODE_URL", long)] + pub sharp_rpc_node_url: Option, + + /// The server certificate for Sharp. + #[arg(env = "MADARA_ORCHESTRATOR_SHARP_SERVER_CRT", long)] + pub sharp_server_crt: Option, + + /// The proof layout for Sharp. + #[arg(env = "MADARA_ORCHESTRATOR_SHARP_PROOF_LAYOUT", long, default_value = "small")] + pub sharp_proof_layout: Option, + + // TODO: GPS is a direct dependency of Sharp, hence GPS can be kept in SharpValidatedArgs + /// The GPS verifier contract address. + #[arg(env = "MADARA_ORCHESTRATOR_GPS_VERIFIER_CONTRACT_ADDRESS", long)] + pub gps_verifier_contract_address: Option, +} diff --git a/crates/orchestrator/src/cli/provider/aws.rs b/crates/orchestrator/src/cli/provider/aws.rs new file mode 100644 index 00000000..a3bb6ac6 --- /dev/null +++ b/crates/orchestrator/src/cli/provider/aws.rs @@ -0,0 +1,23 @@ +use clap::Args; +use serde::Serialize; + +/// Parameters used to config AWS. +#[derive(Debug, Clone, Args, Serialize)] +#[group(requires_all = ["aws_access_key_id", "aws_secret_access_key", "aws_region"])] +pub struct AWSConfigCliArgs { + /// Use this flag to enable AWS provider. + #[arg(long)] + pub aws: bool, + + /// The access key ID. + #[arg(env = "AWS_ACCESS_KEY_ID", long)] + pub aws_access_key_id: String, + + /// The secret access key. + #[arg(env = "AWS_SECRET_ACCESS_KEY", long)] + pub aws_secret_access_key: String, + + /// The region. + #[arg(env = "AWS_REGION", long)] + pub aws_region: String, +} diff --git a/crates/orchestrator/src/cli/provider/mod.rs b/crates/orchestrator/src/cli/provider/mod.rs new file mode 100644 index 00000000..7f51fe33 --- /dev/null +++ b/crates/orchestrator/src/cli/provider/mod.rs @@ -0,0 +1,13 @@ +pub mod aws; + +#[derive(Debug, Clone)] +pub enum ProviderValidatedArgs { + AWS(AWSConfigValidatedArgs), +} + +#[derive(Debug, Clone)] +pub struct AWSConfigValidatedArgs { + pub aws_access_key_id: String, + pub aws_secret_access_key: String, + pub aws_region: String, +} diff --git a/crates/orchestrator/src/cli/queue/aws_sqs.rs b/crates/orchestrator/src/cli/queue/aws_sqs.rs new file mode 100644 index 00000000..39ee0874 --- /dev/null +++ b/crates/orchestrator/src/cli/queue/aws_sqs.rs @@ -0,0 +1,22 @@ +use clap::Args; + +/// Parameters used to config AWS SQS. +#[derive(Debug, Clone, Args)] +#[group(requires_all = ["queue_base_url"])] +pub struct AWSSQSCliArgs { + /// Use the AWS sqs client + #[arg(long)] + pub aws_sqs: bool, + + /// The prefix of the queue. + #[arg(env = "MADARA_ORCHESTRATOR_SQS_PREFIX", long, default_value = Some("madara_orchestrator"))] + pub sqs_prefix: Option, + + /// The suffix of the queue. + #[arg(env = "MADARA_ORCHESTRATOR_SQS_SUFFIX", long, default_value = Some("queue"))] + pub sqs_suffix: Option, + + /// The QUEUE url + #[arg(env = "MADARA_ORCHESTRATOR_SQS_BASE_QUEUE_URL", long)] + pub queue_base_url: Option, +} diff --git a/crates/orchestrator/src/cli/queue/mod.rs b/crates/orchestrator/src/cli/queue/mod.rs new file mode 100644 index 00000000..c3efd271 --- /dev/null +++ b/crates/orchestrator/src/cli/queue/mod.rs @@ -0,0 +1,8 @@ +use crate::queue::sqs::AWSSQSValidatedArgs; + +pub mod aws_sqs; + +#[derive(Clone, Debug)] +pub enum QueueValidatedArgs { + AWSSQS(AWSSQSValidatedArgs), +} diff --git a/crates/orchestrator/src/cli/server.rs b/crates/orchestrator/src/cli/server.rs new file mode 100644 index 00000000..6f365ef9 --- /dev/null +++ b/crates/orchestrator/src/cli/server.rs @@ -0,0 +1,14 @@ +use clap::Args; + +/// Parameters used to config the server. +#[derive(Debug, Clone, Args)] +#[group()] +pub struct ServerCliArgs { + /// The host to listen on. + #[arg(env = "MADARA_ORCHESTRATOR_HOST", long, default_value = "127.0.0.1")] + pub host: String, + + /// The port to listen on. + #[arg(env = "MADARA_ORCHESTRATOR_PORT", long, default_value = "3000")] + pub port: u16, +} diff --git a/crates/orchestrator/src/cli/service.rs b/crates/orchestrator/src/cli/service.rs new file mode 100644 index 00000000..1512eb97 --- /dev/null +++ b/crates/orchestrator/src/cli/service.rs @@ -0,0 +1,12 @@ +use clap::Args; + +#[derive(Debug, Clone, Args)] +pub struct ServiceCliArgs { + /// The maximum block to process. + #[arg(env = "MADARA_ORCHESTRATOR_MAX_BLOCK_NO_TO_PROCESS", long)] + pub max_block_to_process: Option, + + /// The minimum block to process. + #[arg(env = "MADARA_ORCHESTRATOR_MIN_BLOCK_NO_TO_PROCESS", long)] + pub min_block_to_process: Option, +} diff --git a/crates/orchestrator/src/cli/settlement/ethereum.rs b/crates/orchestrator/src/cli/settlement/ethereum.rs new file mode 100644 index 00000000..ae47d197 --- /dev/null +++ b/crates/orchestrator/src/cli/settlement/ethereum.rs @@ -0,0 +1,26 @@ +use clap::Args; +use url::Url; + +#[derive(Debug, Clone, Args)] +#[group(requires_all = ["ethereum_rpc_url", "ethereum_private_key", "l1_core_contract_address", "starknet_operator_address"])] +pub struct EthereumSettlementCliArgs { + /// Use the Ethereum settlement layer. + #[arg(long)] + pub settle_on_ethereum: bool, + + /// The URL of the Ethereum RPC node. + #[arg(env = "MADARA_ORCHESTRATOR_ETHEREUM_SETTLEMENT_RPC_URL", long)] + pub ethereum_rpc_url: Option, + + /// The private key of the Ethereum account. + #[arg(env = "MADARA_ORCHESTRATOR_ETHEREUM_PRIVATE_KEY", long)] + pub ethereum_private_key: Option, + + /// The address of the L1 core contract. + #[arg(env = "MADARA_ORCHESTRATOR_L1_CORE_CONTRACT_ADDRESS", long)] + pub l1_core_contract_address: Option, + + /// The address of the Starknet operator. + #[arg(env = "MADARA_ORCHESTRATOR_STARKNET_OPERATOR_ADDRESS", long)] + pub starknet_operator_address: Option, +} diff --git a/crates/orchestrator/src/cli/settlement/mod.rs b/crates/orchestrator/src/cli/settlement/mod.rs new file mode 100644 index 00000000..1cf7c848 --- /dev/null +++ b/crates/orchestrator/src/cli/settlement/mod.rs @@ -0,0 +1,11 @@ +use ethereum_settlement_client::EthereumSettlementValidatedArgs; +use starknet_settlement_client::StarknetSettlementValidatedArgs; + +pub mod ethereum; +pub mod starknet; + +#[derive(Clone, Debug)] +pub enum SettlementValidatedArgs { + Ethereum(EthereumSettlementValidatedArgs), + Starknet(StarknetSettlementValidatedArgs), +} diff --git a/crates/orchestrator/src/cli/settlement/starknet.rs b/crates/orchestrator/src/cli/settlement/starknet.rs new file mode 100644 index 00000000..9a171b22 --- /dev/null +++ b/crates/orchestrator/src/cli/settlement/starknet.rs @@ -0,0 +1,30 @@ +use clap::Args; +use url::Url; + +#[derive(Debug, Clone, Args)] +#[group(requires_all = ["starknet_rpc_url", "starknet_private_key", "starknet_account_address", "starknet_cairo_core_contract_address", "starknet_finality_retry_wait_in_secs"])] +pub struct StarknetSettlementCliArgs { + /// Use the Starknet settlement layer. + #[arg(long)] + pub settle_on_starknet: bool, + + /// The URL of the Ethereum RPC node. + #[arg(env = "MADARA_ORCHESTRATOR_STARKNET_SETTLEMENT_RPC_URL", long)] + pub starknet_rpc_url: Option, + + /// The private key of the Ethereum account. + #[arg(env = "MADARA_ORCHESTRATOR_STARKNET_PRIVATE_KEY", long)] + pub starknet_private_key: Option, + + /// The address of the Starknet account. + #[arg(env = "MADARA_ORCHESTRATOR_STARKNET_ACCOUNT_ADDRESS", long)] + pub starknet_account_address: Option, + + /// The address of the Cairo core contract. + #[arg(env = "MADARA_ORCHESTRATOR_STARKNET_CAIRO_CORE_CONTRACT_ADDRESS", long)] + pub starknet_cairo_core_contract_address: Option, + + /// The number of seconds to wait for finality. + #[arg(env = "MADARA_ORCHESTRATOR_STARKNET_FINALITY_RETRY_WAIT_IN_SECS", long)] + pub starknet_finality_retry_wait_in_secs: Option, +} diff --git a/crates/orchestrator/src/cli/snos.rs b/crates/orchestrator/src/cli/snos.rs new file mode 100644 index 00000000..12851ac8 --- /dev/null +++ b/crates/orchestrator/src/cli/snos.rs @@ -0,0 +1,15 @@ +use clap::Args; +use url::Url; + +#[derive(Debug, Clone, Args)] +#[group(requires_all = ["rpc_for_snos"])] +pub struct SNOSCliArgs { + /// The RPC URL for SNOS. + #[arg(env = "MADARA_ORCHESTRATOR_RPC_FOR_SNOS", long)] + pub rpc_for_snos: Url, +} + +#[derive(Debug, Clone)] +pub struct SNOSParams { + pub rpc_for_snos: Url, +} diff --git a/crates/orchestrator/src/cli/storage/aws_s3.rs b/crates/orchestrator/src/cli/storage/aws_s3.rs new file mode 100644 index 00000000..d8da0692 --- /dev/null +++ b/crates/orchestrator/src/cli/storage/aws_s3.rs @@ -0,0 +1,14 @@ +use clap::Args; + +/// Parameters used to config AWS S3. +#[derive(Debug, Clone, Args)] +#[group()] // Note: we are not using bucket_name in requires_all because it has a default value. +pub struct AWSS3CliArgs { + /// Use the AWS s3 client + #[arg(long)] + pub aws_s3: bool, + + /// The name of the S3 bucket. + #[arg(env = "MADARA_ORCHESTRATOR_AWS_S3_BUCKET_NAME", long, default_value = Some("madara-orchestrator-bucket"))] + pub bucket_name: Option, +} diff --git a/crates/orchestrator/src/cli/storage/mod.rs b/crates/orchestrator/src/cli/storage/mod.rs new file mode 100644 index 00000000..f905f899 --- /dev/null +++ b/crates/orchestrator/src/cli/storage/mod.rs @@ -0,0 +1,8 @@ +use crate::data_storage::aws_s3::AWSS3ValidatedArgs; + +pub mod aws_s3; + +#[derive(Clone, Debug)] +pub enum StorageValidatedArgs { + AWSS3(AWSS3ValidatedArgs), +} diff --git a/crates/orchestrator/src/config.rs b/crates/orchestrator/src/config.rs index 927bf243..dc0fd408 100644 --- a/crates/orchestrator/src/config.rs +++ b/crates/orchestrator/src/config.rs @@ -1,17 +1,14 @@ -#[cfg(feature = "testing")] -use std::str::FromStr; use std::sync::Arc; -#[cfg(feature = "testing")] -use alloy::primitives::Address; #[cfg(feature = "testing")] use alloy::providers::RootProvider; use aws_config::meta::region::RegionProviderChain; use aws_config::{Region, SdkConfig}; use aws_credential_types::Credentials; +use color_eyre::eyre::eyre; use da_client_interface::DaClient; use dotenvy::dotenv; -use ethereum_da_client::config::EthereumDaConfig; +use ethereum_da_client::EthereumDaClient; use ethereum_settlement_client::EthereumSettlementClient; use prover_client_interface::ProverClient; use settlement_client_interface::SettlementClient; @@ -19,28 +16,32 @@ use sharp_service::SharpProverService; use starknet::providers::jsonrpc::HttpTransport; use starknet::providers::{JsonRpcClient, Url}; use starknet_settlement_client::StarknetSettlementClient; -use utils::env_utils::get_env_var_or_panic; -use utils::settings::env::EnvSettingsProvider; -use utils::settings::Settings; use crate::alerts::aws_sns::AWSSNS; use crate::alerts::Alerts; +use crate::cli::alert::AlertValidatedArgs; +use crate::cli::da::DaValidatedArgs; +use crate::cli::database::DatabaseValidatedArgs; +use crate::cli::prover::ProverValidatedArgs; +use crate::cli::provider::{AWSConfigValidatedArgs, ProviderValidatedArgs}; +use crate::cli::queue::QueueValidatedArgs; +use crate::cli::settlement::SettlementValidatedArgs; +use crate::cli::snos::SNOSParams; +use crate::cli::storage::StorageValidatedArgs; +use crate::cli::RunCmd; use crate::data_storage::aws_s3::AWSS3; use crate::data_storage::DataStorage; use crate::database::mongodb::MongoDb; use crate::database::Database; use crate::queue::sqs::SqsQueue; use crate::queue::QueueProvider; +use crate::routes::ServerParams; /// The app config. It can be accessed from anywhere inside the service /// by calling `config` function. pub struct Config { - /// The RPC url used by the [starknet_client] - starknet_rpc_url: Url, - /// The RPC url to be used when running SNOS - /// When Madara supports getProof, we can re use - /// starknet_rpc_url for SNOS as well - snos_url: Url, + /// The orchestrator config + orchestrator_params: OrchestratorParams, /// The starknet client to get data from the node starknet_client: Arc>, /// The DA client to interact with the DA layer @@ -59,6 +60,19 @@ pub struct Config { alerts: Box, } +#[derive(Debug, Clone)] +pub struct ServiceParams { + pub max_block_to_process: Option, + pub min_block_to_process: Option, +} + +pub struct OrchestratorParams { + pub madara_rpc_url: Url, + pub snos_config: SNOSParams, + pub service_config: ServiceParams, + pub server_config: ServerParams, +} + /// `ProviderConfig` is an enum used to represent the global config built /// using the settings provider. More providers can be added eg : GCP, AZURE etc. /// @@ -78,47 +92,68 @@ impl ProviderConfig { } /// To build a `SdkConfig` for AWS provider. -pub async fn get_aws_config(settings_provider: &impl Settings) -> SdkConfig { - let region = settings_provider.get_settings_or_panic("AWS_REGION"); +pub async fn get_aws_config(aws_config: &AWSConfigValidatedArgs) -> SdkConfig { + let region = aws_config.aws_region.clone(); let region_provider = RegionProviderChain::first_try(Region::new(region)).or_default_provider(); - let credentials = Credentials::from_keys( - settings_provider.get_settings_or_panic("AWS_ACCESS_KEY_ID"), - settings_provider.get_settings_or_panic("AWS_SECRET_ACCESS_KEY"), - None, - ); + let credentials = + Credentials::from_keys(aws_config.aws_access_key_id.clone(), aws_config.aws_secret_access_key.clone(), None); aws_config::from_env().credentials_provider(credentials).region(region_provider).load().await } /// Initializes the app config -pub async fn init_config() -> color_eyre::Result> { +pub async fn init_config(run_cmd: &RunCmd) -> color_eyre::Result> { dotenv().ok(); - let settings_provider = EnvSettingsProvider {}; - let provider_config = Arc::new(ProviderConfig::AWS(Box::new(get_aws_config(&settings_provider).await))); + let provider_params = run_cmd.validate_provider_params().expect("Failed to validate provider params"); + let provider_config = build_provider_config(&provider_params).await; + + let orchestrator_params = OrchestratorParams { + madara_rpc_url: run_cmd.madara_rpc_url.clone(), + snos_config: run_cmd.validate_snos_params().expect("Failed to validate SNOS params"), + service_config: run_cmd.validate_service_params().expect("Failed to validate service params"), + server_config: run_cmd.validate_server_params().expect("Failed to validate server params"), + }; - // init starknet client - let rpc_url = Url::parse(&settings_provider.get_settings_or_panic("MADARA_RPC_URL")).expect("Failed to parse URL"); - let snos_url = Url::parse(&settings_provider.get_settings_or_panic("RPC_FOR_SNOS")).expect("Failed to parse URL"); - let provider = JsonRpcClient::new(HttpTransport::new(rpc_url.clone())); + let rpc_client = JsonRpcClient::new(HttpTransport::new(orchestrator_params.madara_rpc_url.clone())); // init database - let database = build_database_client(&settings_provider).await; - let da_client = build_da_client(&settings_provider).await; - let settlement_client = build_settlement_client(&settings_provider).await?; - let prover_client = build_prover_service(&settings_provider); - let storage_client = build_storage_client(&settings_provider, provider_config.clone()).await; - let alerts_client = build_alert_client(&settings_provider, provider_config.clone()).await; + let database_params = + run_cmd.validate_database_params().map_err(|e| eyre!("Failed to validate database params: {e}"))?; + let database = build_database_client(&database_params).await; + + // init DA client + let da_params = run_cmd.validate_da_params().map_err(|e| eyre!("Failed to validate DA params: {e}"))?; + let da_client = build_da_client(&da_params).await; + + // init settlement + let settlement_params = + run_cmd.validate_settlement_params().map_err(|e| eyre!("Failed to validate settlement params: {e}"))?; + let settlement_client = build_settlement_client(&settlement_params).await?; + + // init prover + let prover_params = run_cmd.validate_prover_params().map_err(|e| eyre!("Failed to validate prover params: {e}"))?; + let prover_client = build_prover_service(&prover_params); + + // init storage + let data_storage_params = + run_cmd.validate_storage_params().map_err(|e| eyre!("Failed to validate storage params: {e}"))?; + let storage_client = build_storage_client(&data_storage_params, provider_config.clone()).await; + + // init alerts + let alert_params = run_cmd.validate_alert_params().map_err(|e| eyre!("Failed to validate alert params: {e}"))?; + let alerts_client = build_alert_client(&alert_params, provider_config.clone()).await; // init the queue // TODO: we use omniqueue for now which doesn't support loading AWS config // from `SdkConfig`. We can later move to using `aws_sdk_sqs`. This would require // us stop using the generic omniqueue abstractions for message ack/nack - let queue = build_queue_client(); + // init queue + let queue_params = run_cmd.validate_queue_params().map_err(|e| eyre!("Failed to validate queue params: {e}"))?; + let queue = build_queue_client(&queue_params, provider_config.clone()).await; Ok(Arc::new(Config::new( - rpc_url, - snos_url, - Arc::new(provider), + orchestrator_params, + Arc::new(rpc_client), da_client, prover_client, settlement_client, @@ -133,8 +168,7 @@ impl Config { /// Create a new config #[allow(clippy::too_many_arguments)] pub fn new( - starknet_rpc_url: Url, - snos_url: Url, + orchestrator_params: OrchestratorParams, starknet_client: Arc>, da_client: Box, prover_client: Box, @@ -145,8 +179,7 @@ impl Config { alerts: Box, ) -> Self { Self { - starknet_rpc_url, - snos_url, + orchestrator_params, starknet_client, da_client, prover_client, @@ -160,12 +193,22 @@ impl Config { /// Returns the starknet rpc url pub fn starknet_rpc_url(&self) -> &Url { - &self.starknet_rpc_url + &self.orchestrator_params.madara_rpc_url + } + + /// Returns the server config + pub fn server_config(&self) -> &ServerParams { + &self.orchestrator_params.server_config } /// Returns the snos rpc url - pub fn snos_url(&self) -> &Url { - &self.snos_url + pub fn snos_config(&self) -> &SNOSParams { + &self.orchestrator_params.snos_config + } + + /// Returns the service config + pub fn service_config(&self) -> &ServiceParams { + &self.orchestrator_params.service_config } /// Returns the starknet client @@ -209,81 +252,95 @@ impl Config { } } +/// Builds the provider config +pub async fn build_provider_config(provider_params: &ProviderValidatedArgs) -> Arc { + match provider_params { + ProviderValidatedArgs::AWS(aws_params) => { + Arc::new(ProviderConfig::AWS(Box::new(get_aws_config(aws_params).await))) + } + } +} + /// Builds the DA client based on the environment variable DA_LAYER -pub async fn build_da_client(settings_provider: &impl Settings) -> Box { - match get_env_var_or_panic("DA_LAYER").as_str() { - "ethereum" => { - let config = EthereumDaConfig::new_with_settings(settings_provider) - .expect("Not able to build config from the given settings provider."); - Box::new(config.build_client().await) +pub async fn build_da_client(da_params: &DaValidatedArgs) -> Box { + match da_params { + DaValidatedArgs::Ethereum(ethereum_da_params) => { + Box::new(EthereumDaClient::new_with_args(ethereum_da_params).await) } - _ => panic!("Unsupported DA layer"), } } /// Builds the prover service based on the environment variable PROVER_SERVICE -pub fn build_prover_service(settings_provider: &impl Settings) -> Box { - match get_env_var_or_panic("PROVER_SERVICE").as_str() { - "sharp" => Box::new(SharpProverService::new_with_settings(settings_provider)), - _ => panic!("Unsupported prover service"), +pub fn build_prover_service(prover_params: &ProverValidatedArgs) -> Box { + match prover_params { + ProverValidatedArgs::Sharp(sharp_params) => Box::new(SharpProverService::new_with_args(sharp_params)), } } /// Builds the settlement client depending on the env variable SETTLEMENT_LAYER pub async fn build_settlement_client( - settings_provider: &impl Settings, + settlement_params: &SettlementValidatedArgs, ) -> color_eyre::Result> { - match get_env_var_or_panic("SETTLEMENT_LAYER").as_str() { - "ethereum" => { + match settlement_params { + SettlementValidatedArgs::Ethereum(ethereum_settlement_params) => { #[cfg(not(feature = "testing"))] { - Ok(Box::new(EthereumSettlementClient::new_with_settings(settings_provider))) + Ok(Box::new(EthereumSettlementClient::new_with_args(ethereum_settlement_params))) } #[cfg(feature = "testing")] { - Ok(Box::new(EthereumSettlementClient::with_test_settings( - RootProvider::new_http(get_env_var_or_panic("SETTLEMENT_RPC_URL").as_str().parse()?), - Address::from_str(&get_env_var_or_panic("L1_CORE_CONTRACT_ADDRESS"))?, - Url::from_str(get_env_var_or_panic("SETTLEMENT_RPC_URL").as_str())?, - Some(Address::from_str(get_env_var_or_panic("STARKNET_OPERATOR_ADDRESS").as_str())?), + Ok(Box::new(EthereumSettlementClient::with_test_params( + RootProvider::new_http(ethereum_settlement_params.ethereum_rpc_url.clone()), + ethereum_settlement_params.l1_core_contract_address, + ethereum_settlement_params.ethereum_rpc_url.clone(), + Some(ethereum_settlement_params.starknet_operator_address), ))) } } - "starknet" => Ok(Box::new(StarknetSettlementClient::new_with_settings(settings_provider).await)), - _ => panic!("Unsupported Settlement layer"), + SettlementValidatedArgs::Starknet(starknet_settlement_params) => { + Ok(Box::new(StarknetSettlementClient::new_with_args(starknet_settlement_params).await)) + } } } pub async fn build_storage_client( - settings_provider: &impl Settings, + data_storage_params: &StorageValidatedArgs, provider_config: Arc, ) -> Box { - match get_env_var_or_panic("DATA_STORAGE").as_str() { - "s3" => Box::new(AWSS3::new_with_settings(settings_provider, provider_config).await), - _ => panic!("Unsupported Storage Client"), + match data_storage_params { + StorageValidatedArgs::AWSS3(aws_s3_params) => { + let aws_config = provider_config.get_aws_client_or_panic(); + Box::new(AWSS3::new_with_args(aws_s3_params, aws_config).await) + } } } pub async fn build_alert_client( - settings_provider: &impl Settings, + alert_params: &AlertValidatedArgs, provider_config: Arc, ) -> Box { - match get_env_var_or_panic("ALERTS").as_str() { - "sns" => Box::new(AWSSNS::new_with_settings(settings_provider, provider_config).await), - _ => panic!("Unsupported Alert Client"), + match alert_params { + AlertValidatedArgs::AWSSNS(aws_sns_params) => { + let aws_config = provider_config.get_aws_client_or_panic(); + Box::new(AWSSNS::new_with_args(aws_sns_params, aws_config).await) + } } } -pub fn build_queue_client() -> Box { - match get_env_var_or_panic("QUEUE_PROVIDER").as_str() { - "sqs" => Box::new(SqsQueue {}), - _ => panic!("Unsupported Queue Client"), +pub async fn build_queue_client( + queue_params: &QueueValidatedArgs, + provider_config: Arc, +) -> Box { + match queue_params { + QueueValidatedArgs::AWSSQS(aws_sqs_params) => { + let aws_config = provider_config.get_aws_client_or_panic(); + Box::new(SqsQueue::new_with_args(aws_sqs_params.clone(), aws_config)) + } } } -pub async fn build_database_client(settings_provider: &impl Settings) -> Box { - match get_env_var_or_panic("DATABASE").as_str() { - "mongodb" => Box::new(MongoDb::new_with_settings(settings_provider).await), - _ => panic!("Unsupported Database Client"), +pub async fn build_database_client(database_params: &DatabaseValidatedArgs) -> Box { + match database_params { + DatabaseValidatedArgs::MongoDB(mongodb_params) => Box::new(MongoDb::new_with_args(mongodb_params).await), } } diff --git a/crates/orchestrator/src/cron/event_bridge.rs b/crates/orchestrator/src/cron/event_bridge.rs index 9feeb70a..b8b5c6e3 100644 --- a/crates/orchestrator/src/cron/event_bridge.rs +++ b/crates/orchestrator/src/cron/event_bridge.rs @@ -1,54 +1,60 @@ use std::time::Duration; use async_trait::async_trait; +use aws_config::SdkConfig; use aws_sdk_eventbridge::types::{InputTransformer, RuleState, Target}; +use aws_sdk_eventbridge::Client as EventBridgeClient; use aws_sdk_sqs::types::QueueAttributeName; +use aws_sdk_sqs::Client as SqsClient; use crate::cron::Cron; -use crate::setup::SetupConfig; -pub struct AWSEventBridge {} +#[derive(Clone, Debug)] +pub struct AWSEventBridgeValidatedArgs { + pub target_queue_name: String, + pub cron_time: Duration, + pub trigger_rule_name: String, +} + +pub struct AWSEventBridge { + target_queue_name: String, + cron_time: Duration, + trigger_rule_name: String, + client: EventBridgeClient, + queue_client: SqsClient, +} + +impl AWSEventBridge { + pub fn new_with_args(params: &AWSEventBridgeValidatedArgs, aws_config: &SdkConfig) -> Self { + Self { + target_queue_name: params.target_queue_name.clone(), + cron_time: params.cron_time, + trigger_rule_name: params.trigger_rule_name.clone(), + client: aws_sdk_eventbridge::Client::new(aws_config), + queue_client: aws_sdk_sqs::Client::new(aws_config), + } + } +} #[async_trait] #[allow(unreachable_patterns)] impl Cron for AWSEventBridge { - async fn create_cron( - &self, - config: &SetupConfig, - cron_time: Duration, - trigger_rule_name: String, - ) -> color_eyre::Result<()> { - let config = match config { - SetupConfig::AWS(config) => config, - _ => panic!("Unsupported Event Bridge configuration"), - }; - let event_bridge_client = aws_sdk_eventbridge::Client::new(config); - event_bridge_client + async fn create_cron(&self) -> color_eyre::Result<()> { + self.client .put_rule() - .name(&trigger_rule_name) - .schedule_expression(duration_to_rate_string(cron_time)) + .name(&self.trigger_rule_name) + .schedule_expression(duration_to_rate_string(self.cron_time)) .state(RuleState::Enabled) .send() .await?; Ok(()) } - async fn add_cron_target_queue( - &self, - config: &SetupConfig, - target_queue_name: String, - message: String, - trigger_rule_name: String, - ) -> color_eyre::Result<()> { - let config = match config { - SetupConfig::AWS(config) => config, - _ => panic!("Unsupported Event Bridge configuration"), - }; - let event_bridge_client = aws_sdk_eventbridge::Client::new(config); - let sqs_client = aws_sdk_sqs::Client::new(config); - let queue_url = sqs_client.get_queue_url().queue_name(target_queue_name).send().await?; - - let queue_attributes = sqs_client + async fn add_cron_target_queue(&self, message: String) -> color_eyre::Result<()> { + let queue_url = self.queue_client.get_queue_url().queue_name(&self.target_queue_name).send().await?; + + let queue_attributes = self + .queue_client .get_queue_attributes() .queue_url(queue_url.queue_url.unwrap()) .attribute_names(QueueAttributeName::QueueArn) @@ -60,9 +66,9 @@ impl Cron for AWSEventBridge { let input_transformer = InputTransformer::builder().input_paths_map("$.time", "time").input_template(message).build()?; - event_bridge_client + self.client .put_targets() - .rule(trigger_rule_name) + .rule(&self.trigger_rule_name) .targets( Target::builder() .id(uuid::Uuid::new_v4().to_string()) diff --git a/crates/orchestrator/src/cron/mod.rs b/crates/orchestrator/src/cron/mod.rs index c2c9085d..5f1ed51a 100644 --- a/crates/orchestrator/src/cron/mod.rs +++ b/crates/orchestrator/src/cron/mod.rs @@ -1,51 +1,27 @@ -use std::time::Duration; - use async_trait::async_trait; use lazy_static::lazy_static; use crate::queue::job_queue::{WorkerTriggerMessage, WorkerTriggerType}; -use crate::setup::SetupConfig; pub mod event_bridge; lazy_static! { - pub static ref CRON_DURATION: Duration = Duration::from_mins(1); - // TODO : we can take this from clap. - pub static ref TARGET_QUEUE_NAME: String = String::from("madara_orchestrator_worker_trigger_queue"); pub static ref WORKER_TRIGGERS: Vec = vec![ WorkerTriggerType::Snos, WorkerTriggerType::Proving, WorkerTriggerType::DataSubmission, WorkerTriggerType::UpdateState ]; - pub static ref WORKER_TRIGGER_RULE_NAME: String = String::from("worker_trigger_scheduled"); } #[async_trait] pub trait Cron { - async fn create_cron( - &self, - config: &SetupConfig, - cron_time: Duration, - trigger_rule_name: String, - ) -> color_eyre::Result<()>; - async fn add_cron_target_queue( - &self, - config: &SetupConfig, - target_queue_name: String, - message: String, - trigger_rule_name: String, - ) -> color_eyre::Result<()>; - async fn setup(&self, config: SetupConfig) -> color_eyre::Result<()> { - self.create_cron(&config, *CRON_DURATION, WORKER_TRIGGER_RULE_NAME.clone()).await?; + async fn create_cron(&self) -> color_eyre::Result<()>; + async fn add_cron_target_queue(&self, message: String) -> color_eyre::Result<()>; + async fn setup(&self) -> color_eyre::Result<()> { + self.create_cron().await?; for triggers in WORKER_TRIGGERS.iter() { - self.add_cron_target_queue( - &config, - TARGET_QUEUE_NAME.clone(), - get_worker_trigger_message(triggers.clone())?, - WORKER_TRIGGER_RULE_NAME.clone(), - ) - .await?; + self.add_cron_target_queue(get_worker_trigger_message(triggers.clone())?).await?; } Ok(()) } diff --git a/crates/orchestrator/src/data_storage/aws_s3/config.rs b/crates/orchestrator/src/data_storage/aws_s3/config.rs deleted file mode 100644 index 7217099a..00000000 --- a/crates/orchestrator/src/data_storage/aws_s3/config.rs +++ /dev/null @@ -1,19 +0,0 @@ -use serde::{Deserialize, Serialize}; -use utils::settings::Settings; - -use crate::data_storage::DataStorageConfig; - -/// Represents AWS S3 config struct with all the necessary variables. -#[derive(Clone, Serialize, Deserialize)] -pub struct AWSS3Config { - /// S3 Bucket Name - pub bucket_name: String, -} - -/// Implementation of `DataStorageConfig` for `AWSS3Config` -impl DataStorageConfig for AWSS3Config { - /// To return the config struct by creating it from the environment variables. - fn new_with_settings(settings: &impl Settings) -> Self { - Self { bucket_name: settings.get_settings_or_panic("AWS_S3_BUCKET_NAME") } - } -} diff --git a/crates/orchestrator/src/data_storage/aws_s3/mod.rs b/crates/orchestrator/src/data_storage/aws_s3/mod.rs index e1f11a1c..0ca6a3f9 100644 --- a/crates/orchestrator/src/data_storage/aws_s3/mod.rs +++ b/crates/orchestrator/src/data_storage/aws_s3/mod.rs @@ -1,20 +1,18 @@ -use std::sync::Arc; - use async_trait::async_trait; +use aws_config::SdkConfig; use aws_sdk_s3::primitives::ByteStream; use aws_sdk_s3::Client; use bytes::Bytes; use color_eyre::Result; -use utils::settings::Settings; -use crate::config::ProviderConfig; -use crate::data_storage::aws_s3::config::AWSS3Config; -use crate::data_storage::{DataStorage, DataStorageConfig}; +use crate::data_storage::DataStorage; pub const S3_SETTINGS_NAME: &str = "s3"; -/// Module for AWS S3 config structs and implementations -pub mod config; +#[derive(Debug, Clone)] +pub struct AWSS3ValidatedArgs { + pub bucket_name: String, +} /// AWSS3 represents AWS S3 client object containing the client and the config itself. pub struct AWSS3 { @@ -27,15 +25,13 @@ pub struct AWSS3 { /// - initializing a new AWS S3 client impl AWSS3 { /// To init the struct with main settings - pub async fn new_with_settings(settings: &impl Settings, provider_config: Arc) -> Self { - let s3_config = AWSS3Config::new_with_settings(settings); - let aws_config = provider_config.get_aws_client_or_panic(); + pub async fn new_with_args(s3_config: &AWSS3ValidatedArgs, aws_config: &SdkConfig) -> Self { // Building AWS S3 config let mut s3_config_builder = aws_sdk_s3::config::Builder::from(aws_config); // this is necessary for it to work with localstack in test cases s3_config_builder.set_force_path_style(Some(true)); let client = Client::from_conf(s3_config_builder.build()); - Self { client, bucket: s3_config.bucket_name } + Self { client, bucket: s3_config.bucket_name.clone() } } } diff --git a/crates/orchestrator/src/data_storage/mod.rs b/crates/orchestrator/src/data_storage/mod.rs index f85b74f2..2e0e8626 100644 --- a/crates/orchestrator/src/data_storage/mod.rs +++ b/crates/orchestrator/src/data_storage/mod.rs @@ -5,7 +5,8 @@ use async_trait::async_trait; use bytes::Bytes; use color_eyre::Result; use mockall::automock; -use utils::settings::Settings; + +use crate::cli::storage::StorageValidatedArgs; /// Data Storage Trait /// @@ -22,15 +23,9 @@ pub trait DataStorage: Send + Sync { async fn get_data(&self, key: &str) -> Result; async fn put_data(&self, data: Bytes, key: &str) -> Result<()>; async fn create_bucket(&self, bucket_name: &str) -> Result<()>; - async fn setup(&self, settings_provider: Box) -> Result<()> { - let bucket_name = settings_provider.get_settings_or_panic("STORAGE_BUCKET_NAME"); - self.create_bucket(&bucket_name).await + async fn setup(&self, storage_params: &StorageValidatedArgs) -> Result<()> { + match storage_params { + StorageValidatedArgs::AWSS3(aws_s3_params) => self.create_bucket(&aws_s3_params.bucket_name).await, + } } } - -/// **DataStorageConfig** : Trait method to represent the config struct needed for -/// initialisation of data storage client -pub trait DataStorageConfig { - /// Get a config file from environment vars in system or - fn new_with_settings(settings: &impl Settings) -> Self; -} diff --git a/crates/orchestrator/src/database/mod.rs b/crates/orchestrator/src/database/mod.rs index 09c8ec3b..b2ff7554 100644 --- a/crates/orchestrator/src/database/mod.rs +++ b/crates/orchestrator/src/database/mod.rs @@ -2,7 +2,6 @@ use ::mongodb::bson::doc; use async_trait::async_trait; use color_eyre::Result; use mockall::automock; -use utils::settings::Settings; use uuid::Uuid; use crate::jobs::types::{JobItem, JobStatus, JobType}; @@ -51,7 +50,3 @@ pub trait Database: Send + Sync { // TODO: can be extendible to support multiple status. async fn get_jobs_by_statuses(&self, status: Vec, limit: Option) -> Result>; } - -pub trait DatabaseConfig { - fn new_with_settings(settings: &impl Settings) -> Self; -} diff --git a/crates/orchestrator/src/database/mongodb/config.rs b/crates/orchestrator/src/database/mongodb/config.rs deleted file mode 100644 index a41407b6..00000000 --- a/crates/orchestrator/src/database/mongodb/config.rs +++ /dev/null @@ -1,19 +0,0 @@ -use serde::{Deserialize, Serialize}; -use utils::settings::Settings; - -use crate::database::DatabaseConfig; - -#[derive(Debug, Serialize, Deserialize)] -pub struct MongoDbConfig { - pub url: String, - pub database_name: String, -} - -impl DatabaseConfig for MongoDbConfig { - fn new_with_settings(settings: &impl Settings) -> Self { - Self { - url: settings.get_settings_or_panic("MONGODB_CONNECTION_STRING"), - database_name: settings.get_settings_or_panic("DATABASE_NAME"), - } - } -} diff --git a/crates/orchestrator/src/database/mongodb/mod.rs b/crates/orchestrator/src/database/mongodb/mod.rs index f2989db3..4f18eae8 100644 --- a/crates/orchestrator/src/database/mongodb/mod.rs +++ b/crates/orchestrator/src/database/mongodb/mod.rs @@ -1,4 +1,3 @@ -use ::utils::settings::Settings; use async_std::stream::StreamExt; use async_trait::async_trait; use chrono::{SubsecRound, Utc}; @@ -11,27 +10,31 @@ use mongodb::options::{ UpdateOptions, }; use mongodb::{bson, Client, Collection}; +use url::Url; use utils::ToDocument; use uuid::Uuid; -use crate::database::mongodb::config::MongoDbConfig; -use crate::database::{Database, DatabaseConfig}; +use crate::database::Database; use crate::jobs::types::{JobItem, JobItemUpdates, JobStatus, JobType}; use crate::jobs::JobError; -pub mod config; mod utils; +#[derive(Debug, Clone)] +pub struct MongoDBValidatedArgs { + pub connection_url: Url, + pub database_name: String, +} + pub struct MongoDb { client: Client, database_name: String, } impl MongoDb { - pub async fn new_with_settings(settings: &impl Settings) -> Self { - let mongo_db_settings = MongoDbConfig::new_with_settings(settings); + pub async fn new_with_args(mongodb_params: &MongoDBValidatedArgs) -> Self { let mut client_options = - ClientOptions::parse(mongo_db_settings.url).await.expect("Failed to parse MongoDB Url"); + ClientOptions::parse(mongodb_params.connection_url.clone()).await.expect("Failed to parse MongoDB Url"); // Set the server_api field of the client_options object to set the version of the Stable API on the // client let server_api = ServerApi::builder().version(ServerApiVersion::V1).build(); @@ -46,7 +49,7 @@ impl MongoDb { .expect("Failed to ping MongoDB deployment"); tracing::debug!("Pinged your deployment. You successfully connected to MongoDB!"); - Self { client, database_name: mongo_db_settings.database_name } + Self { client, database_name: mongodb_params.database_name.clone() } } /// Mongodb client uses Arc internally, reducing the cost of clone. diff --git a/crates/orchestrator/src/jobs/snos_job/mod.rs b/crates/orchestrator/src/jobs/snos_job/mod.rs index f257a211..c82d48c3 100644 --- a/crates/orchestrator/src/jobs/snos_job/mod.rs +++ b/crates/orchestrator/src/jobs/snos_job/mod.rs @@ -103,7 +103,7 @@ impl Job for SnosJob { let block_number = self.get_block_number_from_metadata(job)?; tracing::debug!(job_id = %job.internal_id, block_number = %block_number, "Retrieved block number from metadata"); - let snos_url = config.snos_url().to_string(); + let snos_url = config.snos_config().rpc_for_snos.to_string(); let snos_url = snos_url.trim_end_matches('/'); tracing::debug!(job_id = %job.internal_id, "Calling prove_block function"); let (cairo_pie, snos_output) = diff --git a/crates/orchestrator/src/lib.rs b/crates/orchestrator/src/lib.rs index 86d432b5..5e0bcf6b 100644 --- a/crates/orchestrator/src/lib.rs +++ b/crates/orchestrator/src/lib.rs @@ -31,3 +31,6 @@ pub mod telemetry; pub mod tests; /// Contains workers which act like cron jobs pub mod workers; + +/// Contains the CLI arguments for the service +pub mod cli; diff --git a/crates/orchestrator/src/main.rs b/crates/orchestrator/src/main.rs index dc05bc4e..90461de6 100644 --- a/crates/orchestrator/src/main.rs +++ b/crates/orchestrator/src/main.rs @@ -1,7 +1,10 @@ +use clap::Parser as _; use dotenvy::dotenv; +use orchestrator::cli::{Cli, Commands, RunCmd, SetupCmd}; use orchestrator::config::init_config; use orchestrator::queue::init_consumers; use orchestrator::routes::setup_server; +use orchestrator::setup::setup_cloud; use orchestrator::telemetry::{setup_analytics, shutdown_analytics}; /// Start the server @@ -11,14 +14,29 @@ use orchestrator::telemetry::{setup_analytics, shutdown_analytics}; #[allow(clippy::needless_return)] async fn main() { dotenv().ok(); + + let cli = Cli::parse(); + + match &cli.command { + Commands::Run { run_command } => { + run_orchestrator(run_command).await.expect("Failed to run orchestrator"); + } + Commands::Setup { setup_command } => { + setup_orchestrator(setup_command).await.expect("Failed to setup orchestrator"); + } + } +} + +async fn run_orchestrator(run_cmd: &RunCmd) -> color_eyre::Result<()> { // Analytics Setup - let meter_provider = setup_analytics(); + let instrumentation_params = run_cmd.validate_instrumentation_params().expect("Invalid instrumentation params"); + let meter_provider = setup_analytics(&instrumentation_params); tracing::info!(service = "orchestrator", "Starting orchestrator service"); color_eyre::install().expect("Unable to install color_eyre"); // initial config setup - let config = init_config().await.expect("Config instantiation failed"); + let config = init_config(run_cmd).await.expect("Config instantiation failed"); tracing::debug!(service = "orchestrator", "Configuration initialized"); // initialize the server @@ -38,6 +56,13 @@ async fn main() { tokio::signal::ctrl_c().await.expect("Failed to listen for ctrl+c"); // Analytics Shutdown - shutdown_analytics(meter_provider); + shutdown_analytics(meter_provider, &instrumentation_params); tracing::info!(service = "orchestrator", "Orchestrator service shutting down"); + + Ok(()) +} + +async fn setup_orchestrator(setup_cmd: &SetupCmd) -> color_eyre::Result<()> { + setup_cloud(setup_cmd).await.expect("Failed to setup cloud"); + Ok(()) } diff --git a/crates/orchestrator/src/queue/job_queue.rs b/crates/orchestrator/src/queue/job_queue.rs index 616c3b62..16b5eedd 100644 --- a/crates/orchestrator/src/queue/job_queue.rs +++ b/crates/orchestrator/src/queue/job_queue.rs @@ -11,6 +11,7 @@ use thiserror::Error; use tokio::time::sleep; use uuid::Uuid; +use super::QueueType; use crate::config::Config; use crate::jobs::types::JobType; use crate::jobs::{handle_job_failure, process_job, verify_job, JobError, OtherError}; @@ -21,28 +22,6 @@ use crate::workers::snos::SnosWorker; use crate::workers::update_state::UpdateStateWorker; use crate::workers::Worker; -pub const SNOS_JOB_PROCESSING_QUEUE: &str = "madara_orchestrator_snos_job_processing_queue"; -pub const SNOS_JOB_VERIFICATION_QUEUE: &str = "madara_orchestrator_snos_job_verification_queue"; - -pub const PROVING_JOB_PROCESSING_QUEUE: &str = "madara_orchestrator_proving_job_processing_queue"; -pub const PROVING_JOB_VERIFICATION_QUEUE: &str = "madara_orchestrator_proving_job_verification_queue"; - -pub const PROOF_REGISTRATION_JOB_PROCESSING_QUEUE: &str = "madara_orchestrator_proof_registration_job_processing_queue"; -pub const PROOF_REGISTRATION_JOB_VERIFICATION_QUEUE: &str = - "madara_orchestrator_proof_registration_job_verification_queue"; - -pub const DATA_SUBMISSION_JOB_PROCESSING_QUEUE: &str = "madara_orchestrator_data_submission_job_processing_queue"; -pub const DATA_SUBMISSION_JOB_VERIFICATION_QUEUE: &str = "madara_orchestrator_data_submission_job_verification_queue"; - -pub const UPDATE_STATE_JOB_PROCESSING_QUEUE: &str = "madara_orchestrator_update_state_job_processing_queue"; -pub const UPDATE_STATE_JOB_VERIFICATION_QUEUE: &str = "madara_orchestrator_update_state_job_verification_queue"; - -// Below is the Dead Letter Queue for the above queues. -pub const JOB_HANDLE_FAILURE_QUEUE: &str = "madara_orchestrator_job_handle_failure_queue"; - -// Queues for SNOS worker trigger listening -pub const WORKER_TRIGGER_QUEUE: &str = "madara_orchestrator_worker_trigger_queue"; - #[derive(Error, Debug, PartialEq)] pub enum ConsumptionError { #[error("Failed to consume message from queue, error {error_msg:?}")] @@ -118,30 +97,28 @@ enum DeliveryReturnType { } pub trait QueueNameForJobType { - fn process_queue_name(&self) -> String; - fn verify_queue_name(&self) -> String; + fn process_queue_name(&self) -> QueueType; + fn verify_queue_name(&self) -> QueueType; } impl QueueNameForJobType for JobType { - fn process_queue_name(&self) -> String { + fn process_queue_name(&self) -> QueueType { match self { - JobType::SnosRun => SNOS_JOB_PROCESSING_QUEUE, - JobType::ProofCreation => PROVING_JOB_PROCESSING_QUEUE, - JobType::ProofRegistration => PROOF_REGISTRATION_JOB_PROCESSING_QUEUE, - JobType::DataSubmission => DATA_SUBMISSION_JOB_PROCESSING_QUEUE, - JobType::StateTransition => UPDATE_STATE_JOB_PROCESSING_QUEUE, + JobType::SnosRun => QueueType::SnosJobProcessing, + JobType::ProofCreation => QueueType::ProvingJobProcessing, + JobType::ProofRegistration => QueueType::ProofRegistrationJobProcessing, + JobType::DataSubmission => QueueType::DataSubmissionJobProcessing, + JobType::StateTransition => QueueType::UpdateStateJobProcessing, } - .to_string() } - fn verify_queue_name(&self) -> String { + fn verify_queue_name(&self) -> QueueType { match self { - JobType::SnosRun => SNOS_JOB_VERIFICATION_QUEUE, - JobType::ProofCreation => PROVING_JOB_VERIFICATION_QUEUE, - JobType::ProofRegistration => PROOF_REGISTRATION_JOB_VERIFICATION_QUEUE, - JobType::DataSubmission => DATA_SUBMISSION_JOB_VERIFICATION_QUEUE, - JobType::StateTransition => UPDATE_STATE_JOB_VERIFICATION_QUEUE, + JobType::SnosRun => QueueType::SnosJobVerification, + JobType::ProofCreation => QueueType::ProvingJobVerification, + JobType::ProofRegistration => QueueType::ProofRegistrationJobVerification, + JobType::DataSubmission => QueueType::DataSubmissionJobVerification, + JobType::StateTransition => QueueType::UpdateStateJobVerification, } - .to_string() } } @@ -161,7 +138,7 @@ pub async fn add_job_to_verification_queue( } pub async fn consume_job_from_queue( - queue: String, + queue: QueueType, handler: F, config: Arc, ) -> Result<(), ConsumptionError> @@ -172,7 +149,7 @@ where { tracing::trace!(queue = %queue, "Attempting to consume job from queue"); - let delivery = get_delivery_from_queue(&queue, config.clone()).await?; + let delivery = get_delivery_from_queue(queue.clone(), config.clone()).await?; let message = match delivery { DeliveryReturnType::Message(message) => { @@ -206,7 +183,7 @@ where /// Function to consume the message from the worker trigger queues and spawn the worker /// for respective message received. pub async fn consume_worker_trigger_messages_from_queue( - queue: String, + queue: QueueType, handler: F, config: Arc, ) -> Result<(), ConsumptionError> @@ -216,7 +193,7 @@ where Fut: Future> + Send, { tracing::debug!("Consuming from queue {:?}", queue); - let delivery = get_delivery_from_queue(&queue, config.clone()).await?; + let delivery = get_delivery_from_queue(queue, Arc::clone(&config)).await?; let message = match delivery { DeliveryReturnType::Message(message) => message, @@ -346,8 +323,11 @@ fn get_worker_handler_from_worker_trigger_type(worker_trigger_type: WorkerTrigge } /// To get the delivery from the message queue using the queue name -async fn get_delivery_from_queue(queue: &str, config: Arc) -> Result { - match config.queue().consume_message_from_queue(queue.to_string()).await { +async fn get_delivery_from_queue( + queue: QueueType, + config: Arc, +) -> Result { + match config.queue().consume_message_from_queue(queue).await { Ok(d) => Ok(DeliveryReturnType::Message(d)), Err(QueueError::NoData) => Ok(DeliveryReturnType::NoMessage), Err(e) => Err(ConsumptionError::FailedToConsumeFromQueue { error_msg: e.to_string() }), @@ -370,36 +350,21 @@ macro_rules! spawn_consumer { } pub async fn init_consumers(config: Arc) -> Result<(), JobError> { - spawn_consumer!(SNOS_JOB_PROCESSING_QUEUE.to_string(), process_job, consume_job_from_queue, config.clone()); - spawn_consumer!(SNOS_JOB_VERIFICATION_QUEUE.to_string(), verify_job, consume_job_from_queue, config.clone()); + spawn_consumer!(QueueType::SnosJobProcessing, process_job, consume_job_from_queue, config.clone()); + spawn_consumer!(QueueType::SnosJobVerification, verify_job, consume_job_from_queue, config.clone()); - spawn_consumer!(PROVING_JOB_PROCESSING_QUEUE.to_string(), process_job, consume_job_from_queue, config.clone()); - spawn_consumer!(PROVING_JOB_VERIFICATION_QUEUE.to_string(), verify_job, consume_job_from_queue, config.clone()); + spawn_consumer!(QueueType::ProvingJobProcessing, process_job, consume_job_from_queue, config.clone()); + spawn_consumer!(QueueType::ProvingJobVerification, verify_job, consume_job_from_queue, config.clone()); - spawn_consumer!( - DATA_SUBMISSION_JOB_PROCESSING_QUEUE.to_string(), - process_job, - consume_job_from_queue, - config.clone() - ); - spawn_consumer!( - DATA_SUBMISSION_JOB_VERIFICATION_QUEUE.to_string(), - verify_job, - consume_job_from_queue, - config.clone() - ); + spawn_consumer!(QueueType::DataSubmissionJobProcessing, process_job, consume_job_from_queue, config.clone()); + spawn_consumer!(QueueType::DataSubmissionJobVerification, verify_job, consume_job_from_queue, config.clone()); - spawn_consumer!(UPDATE_STATE_JOB_PROCESSING_QUEUE.to_string(), process_job, consume_job_from_queue, config.clone()); - spawn_consumer!( - UPDATE_STATE_JOB_VERIFICATION_QUEUE.to_string(), - verify_job, - consume_job_from_queue, - config.clone() - ); + spawn_consumer!(QueueType::UpdateStateJobProcessing, process_job, consume_job_from_queue, config.clone()); + spawn_consumer!(QueueType::UpdateStateJobVerification, verify_job, consume_job_from_queue, config.clone()); - spawn_consumer!(JOB_HANDLE_FAILURE_QUEUE.to_string(), handle_job_failure, consume_job_from_queue, config.clone()); + spawn_consumer!(QueueType::JobHandleFailure, handle_job_failure, consume_job_from_queue, config.clone()); - spawn_consumer!(WORKER_TRIGGER_QUEUE.to_string(), spawn_worker, consume_worker_trigger_messages_from_queue, config); + spawn_consumer!(QueueType::WorkerTrigger, spawn_worker, consume_worker_trigger_messages_from_queue, config); Ok(()) } @@ -411,7 +376,7 @@ async fn spawn_worker(worker: Box, config: Arc) -> color_eyr } Ok(()) } -async fn add_job_to_queue(id: Uuid, queue: String, delay: Option, config: Arc) -> EyreResult<()> { +async fn add_job_to_queue(id: Uuid, queue: QueueType, delay: Option, config: Arc) -> EyreResult<()> { let message = JobQueueMessage { id }; config.queue().send_message_to_queue(queue.clone(), serde_json::to_string(&message)?, delay).await?; tracing::info!( diff --git a/crates/orchestrator/src/queue/mod.rs b/crates/orchestrator/src/queue/mod.rs index 926e493d..cf9d9ece 100644 --- a/crates/orchestrator/src/queue/mod.rs +++ b/crates/orchestrator/src/queue/mod.rs @@ -9,77 +9,97 @@ use color_eyre::Result as EyreResult; use lazy_static::lazy_static; use mockall::automock; use omniqueue::{Delivery, QueueError}; +use strum_macros::{Display, EnumIter}; use crate::config::Config; use crate::jobs::JobError; -use crate::setup::SetupConfig; + +#[derive(Display, Debug, Clone, PartialEq, Eq, EnumIter)] +pub enum QueueType { + #[strum(serialize = "snos_job_processing")] + SnosJobProcessing, + #[strum(serialize = "snos_job_verification")] + SnosJobVerification, + #[strum(serialize = "proving_job_processing")] + ProvingJobProcessing, + #[strum(serialize = "proving_job_verification")] + ProvingJobVerification, + #[strum(serialize = "proof_registration_job_processing")] + ProofRegistrationJobProcessing, + #[strum(serialize = "proof_registration_job_verification")] + ProofRegistrationJobVerification, + #[strum(serialize = "data_submission_job_processing")] + DataSubmissionJobProcessing, + #[strum(serialize = "data_submission_job_verification")] + DataSubmissionJobVerification, + #[strum(serialize = "update_state_job_processing")] + UpdateStateJobProcessing, + #[strum(serialize = "update_state_job_verification")] + UpdateStateJobVerification, + #[strum(serialize = "job_handle_failure")] + JobHandleFailure, + #[strum(serialize = "worker_trigger")] + WorkerTrigger, +} #[derive(Clone)] -pub struct DlqConfig<'a> { +pub struct DlqConfig { pub max_receive_count: i32, - pub dlq_name: &'a str, + pub dlq_name: QueueType, } #[derive(Clone)] -pub struct QueueConfig<'a> { - pub name: String, +pub struct QueueConfig { + pub name: QueueType, pub visibility_timeout: i32, - pub dlq_config: Option>, + pub dlq_config: Option, } +// TODO: use QueueType::iter() or format! lazy_static! { - pub static ref JOB_HANDLE_FAILURE_QUEUE: String = String::from("madara_orchestrator_job_handle_failure_queue"); - pub static ref QUEUES: Vec> = vec![ - QueueConfig { - name: String::from("madara_orchestrator_snos_job_processing_queue"), - visibility_timeout: 300, - dlq_config: Some(DlqConfig { max_receive_count: 5, dlq_name: &JOB_HANDLE_FAILURE_QUEUE }) - }, - QueueConfig { - name: String::from("madara_orchestrator_snos_job_verification_queue"), - visibility_timeout: 300, - dlq_config: Some(DlqConfig { max_receive_count: 5, dlq_name: &JOB_HANDLE_FAILURE_QUEUE }) - }, + pub static ref QUEUES: Vec = vec![ + QueueConfig { name: QueueType::JobHandleFailure, visibility_timeout: 300, dlq_config: None }, QueueConfig { - name: String::from("madara_orchestrator_proving_job_processing_queue"), + name: QueueType::SnosJobProcessing, visibility_timeout: 300, - dlq_config: Some(DlqConfig { max_receive_count: 5, dlq_name: &JOB_HANDLE_FAILURE_QUEUE }) + dlq_config: Some(DlqConfig { max_receive_count: 5, dlq_name: QueueType::JobHandleFailure }) }, QueueConfig { - name: String::from("madara_orchestrator_proving_job_verification_queue"), + name: QueueType::SnosJobVerification, visibility_timeout: 300, - dlq_config: Some(DlqConfig { max_receive_count: 5, dlq_name: &JOB_HANDLE_FAILURE_QUEUE }) + dlq_config: Some(DlqConfig { max_receive_count: 5, dlq_name: QueueType::JobHandleFailure }) }, QueueConfig { - name: String::from("madara_orchestrator_data_submission_job_processing_queue"), + name: QueueType::ProvingJobProcessing, visibility_timeout: 300, - dlq_config: Some(DlqConfig { max_receive_count: 5, dlq_name: &JOB_HANDLE_FAILURE_QUEUE }) + dlq_config: Some(DlqConfig { max_receive_count: 5, dlq_name: QueueType::JobHandleFailure }) }, QueueConfig { - name: String::from("madara_orchestrator_data_submission_job_verification_queue"), + name: QueueType::ProvingJobVerification, visibility_timeout: 300, - dlq_config: Some(DlqConfig { max_receive_count: 5, dlq_name: &JOB_HANDLE_FAILURE_QUEUE }) + dlq_config: Some(DlqConfig { max_receive_count: 5, dlq_name: QueueType::JobHandleFailure }) }, QueueConfig { - name: String::from("madara_orchestrator_update_state_job_processing_queue"), + name: QueueType::DataSubmissionJobProcessing, visibility_timeout: 300, - dlq_config: Some(DlqConfig { max_receive_count: 5, dlq_name: &JOB_HANDLE_FAILURE_QUEUE }) + dlq_config: Some(DlqConfig { max_receive_count: 5, dlq_name: QueueType::JobHandleFailure }) }, QueueConfig { - name: String::from("madara_orchestrator_update_state_job_verification_queue"), + name: QueueType::DataSubmissionJobVerification, visibility_timeout: 300, - dlq_config: Some(DlqConfig { max_receive_count: 5, dlq_name: &JOB_HANDLE_FAILURE_QUEUE }) + dlq_config: Some(DlqConfig { max_receive_count: 5, dlq_name: QueueType::JobHandleFailure }) }, QueueConfig { - name: String::from("madara_orchestrator_job_handle_failure_queue"), + name: QueueType::UpdateStateJobProcessing, visibility_timeout: 300, - dlq_config: None + dlq_config: Some(DlqConfig { max_receive_count: 5, dlq_name: QueueType::JobHandleFailure }) }, QueueConfig { - name: String::from("madara_orchestrator_worker_trigger_queue"), + name: QueueType::UpdateStateJobVerification, visibility_timeout: 300, - dlq_config: None + dlq_config: Some(DlqConfig { max_receive_count: 5, dlq_name: QueueType::JobHandleFailure }) }, + QueueConfig { name: QueueType::WorkerTrigger, visibility_timeout: 300, dlq_config: None }, ]; } @@ -91,13 +111,14 @@ lazy_static! { #[automock] #[async_trait] pub trait QueueProvider: Send + Sync { - async fn send_message_to_queue(&self, queue: String, payload: String, delay: Option) -> EyreResult<()>; - async fn consume_message_from_queue(&self, queue: String) -> Result; - async fn create_queue<'a>(&self, queue_config: &QueueConfig<'a>, config: &SetupConfig) -> EyreResult<()>; - async fn setup(&self, config: SetupConfig) -> EyreResult<()> { + async fn send_message_to_queue(&self, queue: QueueType, payload: String, delay: Option) + -> EyreResult<()>; + async fn consume_message_from_queue(&self, queue: QueueType) -> std::result::Result; + async fn create_queue(&self, queue_config: &QueueConfig) -> EyreResult<()>; + async fn setup(&self) -> EyreResult<()> { // Creating the queues : for queue in QUEUES.iter() { - self.create_queue(queue, &config).await?; + self.create_queue(queue).await?; } Ok(()) } diff --git a/crates/orchestrator/src/queue/sqs/mod.rs b/crates/orchestrator/src/queue/sqs/mod.rs index dc392881..dbb167c5 100644 --- a/crates/orchestrator/src/queue/sqs/mod.rs +++ b/crates/orchestrator/src/queue/sqs/mod.rs @@ -2,49 +2,60 @@ use std::collections::HashMap; use std::time::Duration; use async_trait::async_trait; +use aws_config::SdkConfig; use aws_sdk_sqs::types::QueueAttributeName; use aws_sdk_sqs::Client; use color_eyre::eyre::eyre; use color_eyre::Result; -use lazy_static::lazy_static; use omniqueue::backends::{SqsBackend, SqsConfig, SqsConsumer, SqsProducer}; use omniqueue::{Delivery, QueueError}; -use utils::env_utils::get_env_var_or_panic; - -use crate::queue::job_queue::{ - DATA_SUBMISSION_JOB_PROCESSING_QUEUE, DATA_SUBMISSION_JOB_VERIFICATION_QUEUE, JOB_HANDLE_FAILURE_QUEUE, - PROOF_REGISTRATION_JOB_PROCESSING_QUEUE, PROOF_REGISTRATION_JOB_VERIFICATION_QUEUE, PROVING_JOB_PROCESSING_QUEUE, - PROVING_JOB_VERIFICATION_QUEUE, SNOS_JOB_PROCESSING_QUEUE, SNOS_JOB_VERIFICATION_QUEUE, - UPDATE_STATE_JOB_PROCESSING_QUEUE, UPDATE_STATE_JOB_VERIFICATION_QUEUE, WORKER_TRIGGER_QUEUE, -}; +use serde::Serialize; +use url::Url; + +use super::QueueType; use crate::queue::{QueueConfig, QueueProvider}; -use crate::setup::SetupConfig; - -pub struct SqsQueue; - -lazy_static! { - /// Maps Queue Name to Env var of queue URL. - pub static ref QUEUE_NAME_TO_ENV_VAR_MAPPING: HashMap<&'static str, &'static str> = HashMap::from([ - (DATA_SUBMISSION_JOB_PROCESSING_QUEUE, "SQS_DATA_SUBMISSION_JOB_PROCESSING_QUEUE_URL"), - (DATA_SUBMISSION_JOB_VERIFICATION_QUEUE, "SQS_DATA_SUBMISSION_JOB_VERIFICATION_QUEUE_URL"), - (PROOF_REGISTRATION_JOB_PROCESSING_QUEUE, "SQS_PROOF_REGISTRATION_JOB_PROCESSING_QUEUE_URL"), - (PROOF_REGISTRATION_JOB_VERIFICATION_QUEUE, "SQS_PROOF_REGISTRATION_JOB_VERIFICATION_QUEUE_URL"), - (PROVING_JOB_PROCESSING_QUEUE, "SQS_PROVING_JOB_PROCESSING_QUEUE_URL"), - (PROVING_JOB_VERIFICATION_QUEUE, "SQS_PROVING_JOB_VERIFICATION_QUEUE_URL"), - (SNOS_JOB_PROCESSING_QUEUE, "SQS_SNOS_JOB_PROCESSING_QUEUE_URL"), - (SNOS_JOB_VERIFICATION_QUEUE, "SQS_SNOS_JOB_VERIFICATION_QUEUE_URL"), - (UPDATE_STATE_JOB_PROCESSING_QUEUE, "SQS_UPDATE_STATE_JOB_PROCESSING_QUEUE_URL"), - (UPDATE_STATE_JOB_VERIFICATION_QUEUE, "SQS_UPDATE_STATE_JOB_VERIFICATION_QUEUE_URL"), - (JOB_HANDLE_FAILURE_QUEUE, "SQS_JOB_HANDLE_FAILURE_QUEUE_URL"), - (WORKER_TRIGGER_QUEUE, "SQS_WORKER_TRIGGER_QUEUE_URL"), - ]); + +#[derive(Debug, Clone, Serialize)] +pub struct AWSSQSValidatedArgs { + pub queue_base_url: Url, + pub sqs_prefix: String, + pub sqs_suffix: String, +} + +pub struct SqsQueue { + client: Client, + queue_base_url: Url, + sqs_prefix: String, + sqs_suffix: String, +} + +impl SqsQueue { + pub fn new_with_args(params: AWSSQSValidatedArgs, aws_config: &SdkConfig) -> Self { + let sqs_config_builder = aws_sdk_sqs::config::Builder::from(aws_config); + let client = Client::from_conf(sqs_config_builder.build()); + Self { + client, + queue_base_url: params.queue_base_url, + sqs_prefix: params.sqs_prefix, + sqs_suffix: params.sqs_suffix, + } + } + + pub fn get_queue_url(&self, queue_type: QueueType) -> String { + let name = format!("{}/{}", self.queue_base_url, self.get_queue_name(queue_type)); + name + } + + pub fn get_queue_name(&self, queue_type: QueueType) -> String { + format!("{}_{}_{}", self.sqs_prefix, queue_type, self.sqs_suffix) + } } #[allow(unreachable_patterns)] #[async_trait] impl QueueProvider for SqsQueue { - async fn send_message_to_queue(&self, queue: String, payload: String, delay: Option) -> Result<()> { - let queue_url = get_queue_url(queue); + async fn send_message_to_queue(&self, queue: QueueType, payload: String, delay: Option) -> Result<()> { + let queue_url = self.get_queue_url(queue); let producer = get_producer(queue_url).await?; match delay { @@ -55,27 +66,26 @@ impl QueueProvider for SqsQueue { Ok(()) } - async fn consume_message_from_queue(&self, queue: String) -> std::result::Result { - let queue_url = get_queue_url(queue); + async fn consume_message_from_queue(&self, queue: QueueType) -> std::result::Result { + let queue_url = self.get_queue_url(queue); let mut consumer = get_consumer(queue_url).await?; consumer.receive().await } - async fn create_queue<'a>(&self, queue_config: &QueueConfig<'a>, config: &SetupConfig) -> Result<()> { - let config = match config { - SetupConfig::AWS(config) => config, - _ => panic!("Unsupported SQS configuration"), - }; - let sqs_client = Client::new(config); - let res = sqs_client.create_queue().queue_name(&queue_config.name).send().await?; + async fn create_queue(&self, queue_config: &QueueConfig) -> Result<()> { + let res = self.client.create_queue().queue_name(self.get_queue_name(queue_config.name.clone())).send().await?; let queue_url = res.queue_url().ok_or_else(|| eyre!("Not able to get queue url from result"))?; let mut attributes = HashMap::new(); attributes.insert(QueueAttributeName::VisibilityTimeout, queue_config.visibility_timeout.to_string()); if let Some(dlq_config) = &queue_config.dlq_config { - let dlq_url = Self::get_queue_url_from_client(dlq_config.dlq_name, &sqs_client).await?; - let dlq_arn = Self::get_queue_arn(&sqs_client, &dlq_url).await?; + let dlq_url = Self::get_queue_url_from_client( + self.get_queue_name(dlq_config.dlq_name.clone()).as_str(), + &self.client, + ) + .await?; + let dlq_arn = Self::get_queue_arn(&self.client, &dlq_url).await?; let policy = format!( r#"{{"deadLetterTargetArn":"{}","maxReceiveCount":"{}"}}"#, dlq_arn, &dlq_config.max_receive_count @@ -83,7 +93,7 @@ impl QueueProvider for SqsQueue { attributes.insert(QueueAttributeName::RedrivePolicy, policy); } - sqs_client.set_queue_attributes().queue_url(queue_url).set_attributes(Some(attributes)).send().await?; + self.client.set_queue_attributes().queue_url(queue_url).set_attributes(Some(attributes)).send().await?; Ok(()) } @@ -114,13 +124,6 @@ impl SqsQueue { } } -/// To fetch the queue URL from the environment variables -fn get_queue_url(queue_name: String) -> String { - get_env_var_or_panic( - QUEUE_NAME_TO_ENV_VAR_MAPPING.get(queue_name.as_str()).expect("Not able to get the queue env var name."), - ) -} - // TODO: store the producer and consumer in memory to avoid creating a new one every time async fn get_producer(queue: String) -> Result { let (producer, _) = diff --git a/crates/orchestrator/src/routes/mod.rs b/crates/orchestrator/src/routes/mod.rs index 21c4d9bd..b43b6a7e 100644 --- a/crates/orchestrator/src/routes/mod.rs +++ b/crates/orchestrator/src/routes/mod.rs @@ -7,13 +7,18 @@ use axum::response::{IntoResponse, Response}; use axum::{Json, Router}; use job_routes::job_router; use serde::Serialize; -use utils::env_utils::get_env_var_or_default; use crate::config::Config; pub mod app_routes; pub mod job_routes; +#[derive(Debug, Clone)] +pub struct ServerParams { + pub host: String, + pub port: u16, +} + #[derive(Debug, Serialize)] struct ApiResponse where @@ -50,7 +55,7 @@ where } pub async fn setup_server(config: Arc) -> SocketAddr { - let (api_server_url, listener) = get_server_url().await; + let (api_server_url, listener) = get_server_url(config.server_config()).await; let job_routes = job_router(config.clone()); let app_routes = app_router(); @@ -63,10 +68,8 @@ pub async fn setup_server(config: Arc) -> SocketAddr { api_server_url } -pub async fn get_server_url() -> (SocketAddr, tokio::net::TcpListener) { - let host = get_env_var_or_default("HOST", "127.0.0.1"); - let port = get_env_var_or_default("PORT", "3000").parse::().expect("PORT must be a u16"); - let address = format!("{}:{}", host, port); +pub async fn get_server_url(server_params: &ServerParams) -> (SocketAddr, tokio::net::TcpListener) { + let address = format!("{}:{}", server_params.host, server_params.port); let listener = tokio::net::TcpListener::bind(address.clone()).await.expect("Failed to get listener"); let api_server_url = listener.local_addr().expect("Unable to bind address to listener."); diff --git a/crates/orchestrator/src/setup/mod.rs b/crates/orchestrator/src/setup/mod.rs index 317453a2..6e1a3f0a 100644 --- a/crates/orchestrator/src/setup/mod.rs +++ b/crates/orchestrator/src/setup/mod.rs @@ -1,97 +1,93 @@ use std::process::Command; -use std::sync::Arc; -use aws_config::environment::EnvironmentVariableCredentialsProvider; -use aws_config::{from_env, Region, SdkConfig}; -use aws_credential_types::provider::ProvideCredentials; -use utils::env_utils::get_env_var_or_panic; -use utils::settings::env::EnvSettingsProvider; +use aws_config::SdkConfig; use crate::alerts::aws_sns::AWSSNS; use crate::alerts::Alerts; -use crate::config::{get_aws_config, ProviderConfig}; +use crate::cli::alert::AlertValidatedArgs; +use crate::cli::cron::CronValidatedArgs; +use crate::cli::queue::QueueValidatedArgs; +use crate::cli::storage::StorageValidatedArgs; +use crate::cli::SetupCmd; +use crate::config::build_provider_config; +use crate::cron::event_bridge::AWSEventBridge; use crate::cron::Cron; use crate::data_storage::aws_s3::AWSS3; use crate::data_storage::DataStorage; -use crate::queue::QueueProvider; +use crate::queue::sqs::SqsQueue; +use crate::queue::QueueProvider as _; #[derive(Clone)] pub enum SetupConfig { AWS(SdkConfig), } -pub enum ConfigType { - AWS, -} - -async fn setup_config(client_type: ConfigType) -> SetupConfig { - match client_type { - ConfigType::AWS => { - let region_provider = Region::new(get_env_var_or_panic("AWS_REGION")); - let creds = EnvironmentVariableCredentialsProvider::new().provide_credentials().await.unwrap(); - SetupConfig::AWS(from_env().region(region_provider).credentials_provider(creds).load().await) - } - } -} +// Note: we are using println! instead of tracing::info! because telemetry is not yet initialized +// and it get initialized during the run_orchestrator function. +pub async fn setup_cloud(setup_cmd: &SetupCmd) -> color_eyre::Result<()> { + println!("Setting up cloud. โณ"); + // AWS + let provider_params = setup_cmd.validate_provider_params().expect("Failed to validate provider params"); + let provider_config = build_provider_config(&provider_params).await; -// TODO : move this to main.rs after moving to clap. -pub async fn setup_cloud() -> color_eyre::Result<()> { - log::info!("Setting up cloud."); - let settings_provider = EnvSettingsProvider {}; - let provider_config = Arc::new(ProviderConfig::AWS(Box::new(get_aws_config(&settings_provider).await))); + // Data Storage + println!("Setting up data storage. โณ"); + let data_storage_params = setup_cmd.validate_storage_params().expect("Failed to validate storage params"); + let aws_config = provider_config.get_aws_client_or_panic(); - log::info!("Setting up data storage."); - match get_env_var_or_panic("DATA_STORAGE").as_str() { - "s3" => { - let s3 = Box::new(AWSS3::new_with_settings(&settings_provider, provider_config.clone()).await); - s3.setup(Box::new(settings_provider.clone())).await? + match data_storage_params { + StorageValidatedArgs::AWSS3(aws_s3_params) => { + let s3 = Box::new(AWSS3::new_with_args(&aws_s3_params, aws_config).await); + s3.setup(&StorageValidatedArgs::AWSS3(aws_s3_params.clone())).await? } - _ => panic!("Unsupported Storage Client"), } - log::info!("Data storage setup completed โœ…"); + println!("Data storage setup completed โœ…"); - log::info!("Setting up queues"); - match get_env_var_or_panic("QUEUE_PROVIDER").as_str() { - "sqs" => { - let config = setup_config(ConfigType::AWS).await; - let sqs = Box::new(crate::queue::sqs::SqsQueue {}); - sqs.setup(config).await? + // Queues + println!("Setting up queues. โณ"); + let queue_params = setup_cmd.validate_queue_params().expect("Failed to validate queue params"); + match queue_params { + QueueValidatedArgs::AWSSQS(aws_sqs_params) => { + let sqs = Box::new(SqsQueue::new_with_args(aws_sqs_params, aws_config)); + sqs.setup().await? } - _ => panic!("Unsupported Queue Client"), } - log::info!("Queues setup completed โœ…"); + println!("Queues setup completed โœ…"); - log::info!("Setting up cron"); - match get_env_var_or_panic("CRON_PROVIDER").as_str() { - "event_bridge" => { - let config = setup_config(ConfigType::AWS).await; - let event_bridge = Box::new(crate::cron::event_bridge::AWSEventBridge {}); - event_bridge.setup(config).await? + // Cron + println!("Setting up cron. โณ"); + let cron_params = setup_cmd.validate_cron_params().expect("Failed to validate cron params"); + match cron_params { + CronValidatedArgs::AWSEventBridge(aws_event_bridge_params) => { + let aws_config = provider_config.get_aws_client_or_panic(); + let event_bridge = Box::new(AWSEventBridge::new_with_args(&aws_event_bridge_params, aws_config)); + event_bridge.setup().await? } - _ => panic!("Unsupported Event Bridge Client"), } - log::info!("Cron setup completed โœ…"); + println!("Cron setup completed โœ…"); - log::info!("Setting up alerts."); - match get_env_var_or_panic("ALERTS").as_str() { - "sns" => { - let sns = Box::new(AWSSNS::new_with_settings(&settings_provider, provider_config).await); - sns.setup(Box::new(settings_provider)).await? + // Alerts + println!("Setting up alerts. โณ"); + let alert_params = setup_cmd.validate_alert_params().expect("Failed to validate alert params"); + match alert_params { + AlertValidatedArgs::AWSSNS(aws_sns_params) => { + let aws_config = provider_config.get_aws_client_or_panic(); + let sns = Box::new(AWSSNS::new_with_args(&aws_sns_params, aws_config).await); + sns.setup().await? } - _ => panic!("Unsupported Alert Client"), } - log::info!("Alerts setup completed โœ…"); + println!("Alerts setup completed โœ…"); Ok(()) } pub async fn setup_db() -> color_eyre::Result<()> { // We run the js script in the folder root: - log::info!("Setting up database."); + println!("Setting up database."); Command::new("node").arg("migrate-mongo-config.js").output()?; - log::info!("Database setup completed โœ…"); + println!("Database setup completed โœ…"); Ok(()) } diff --git a/crates/orchestrator/src/telemetry.rs b/crates/orchestrator/src/telemetry.rs index f1be094b..80315360 100644 --- a/crates/orchestrator/src/telemetry.rs +++ b/crates/orchestrator/src/telemetry.rs @@ -1,4 +1,3 @@ -use std::str::FromStr; use std::time::Duration; use opentelemetry::trace::TracerProvider; @@ -15,18 +14,23 @@ use tracing_opentelemetry::OpenTelemetryLayer; use tracing_subscriber::layer::SubscriberExt as _; use tracing_subscriber::util::SubscriberInitExt as _; use tracing_subscriber::EnvFilter; -use utils::env_utils::{get_env_var_optional, get_env_var_or_default}; +use url::Url; pub struct OTELConfig { - endpoint: String, + endpoint: Url, service_name: String, } -pub fn setup_analytics() -> Option { - let otel_config = get_otel_config(); +#[derive(Debug, Clone)] +pub struct InstrumentationParams { + pub otel_service_name: String, + pub otel_collector_endpoint: Option, + pub log_level: Level, +} - let log_level = get_env_var_or_default("RUST_LOG", "INFO"); - let level = Level::from_str(&log_level).unwrap_or(Level::INFO); +pub fn setup_analytics(instrumentation: &InstrumentationParams) -> Option { + let otel_config = get_otel_config(instrumentation); + let level = instrumentation.log_level; let tracing_subscriber = tracing_subscriber::registry() .with(tracing_subscriber::filter::LevelFilter::from_level(level)) @@ -53,21 +57,21 @@ pub fn setup_analytics() -> Option { } } -fn get_otel_config() -> Option { - let otel_endpoint = get_env_var_optional("OTEL_COLLECTOR_ENDPOINT").expect("Failed to get OTEL_COLLECTOR_ENDPOINT"); - let otel_service_name = get_env_var_optional("OTEL_SERVICE_NAME").expect("Failed to get OTEL_SERVICE_NAME"); +fn get_otel_config(instrumentation: &InstrumentationParams) -> Option { + let otel_endpoint = instrumentation.otel_collector_endpoint.clone(); + let otel_service_name = instrumentation.otel_service_name.clone(); - match (otel_endpoint, otel_service_name) { - (Some(endpoint), Some(service_name)) => Some(OTELConfig { endpoint, service_name }), + match otel_endpoint { + Some(endpoint) => Some(OTELConfig { endpoint, service_name: otel_service_name }), _ => { - tracing::warn!("OTEL_COLLECTOR_ENDPOINT or OTEL_SERVICE_NAME is not set"); + tracing::warn!("MADARA_ORCHESTRATOR_OTEL_COLLECTOR_ENDPOINT is not set"); None } } } -pub fn shutdown_analytics(meter_provider: Option) { - let otel_config = get_otel_config(); +pub fn shutdown_analytics(meter_provider: Option, instrumentation: &InstrumentationParams) { + let otel_config = get_otel_config(instrumentation); // guard clause if otel is disabled if otel_config.is_none() { @@ -142,9 +146,8 @@ fn init_logs(otel_config: &OTELConfig) -> Result JobItem { @@ -51,9 +48,14 @@ pub fn custom_job_item(default_job_item: JobItem, #[default(String::from("0"))] job_item } -pub async fn create_sns_arn(provider_config: Arc) -> Result<(), SdkError> { +pub async fn create_sns_arn( + provider_config: Arc, + alert_params: &AlertValidatedArgs, +) -> Result<(), SdkError> { + let AlertValidatedArgs::AWSSNS(aws_sns_params) = alert_params; + let topic_name = aws_sns_params.topic_arn.split(":").last().unwrap(); let sns_client = get_sns_client(provider_config.get_aws_client_or_panic()).await; - sns_client.create_topic().name(get_env_var_or_panic("AWS_SNS_ARN_NAME")).send().await?; + sns_client.create_topic().name(topic_name).send().await?; Ok(()) } @@ -61,43 +63,46 @@ pub async fn get_sns_client(aws_config: &SdkConfig) -> aws_sdk_sns::client::Clie aws_sdk_sns::Client::new(aws_config) } -pub async fn drop_database() -> color_eyre::Result<()> { - let db_client: Client = MongoDb::new_with_settings(&EnvSettingsProvider {}).await.client(); - // dropping all the collection. - // use .collection::("") - // if only particular collection is to be dropped - db_client.database("orchestrator").drop(None).await?; +pub async fn drop_database(database_params: &DatabaseValidatedArgs) -> color_eyre::Result<()> { + match database_params { + DatabaseValidatedArgs::MongoDB(mongodb_params) => { + let db_client: Client = MongoDb::new_with_args(mongodb_params).await.client(); + // dropping all the collection. + // use .collection::("") + // if only particular collection is to be dropped + db_client.database(&mongodb_params.database_name).drop(None).await?; + } + } Ok(()) } // SQS structs & functions -pub async fn create_sqs_queues(provider_config: Arc) -> color_eyre::Result<()> { - let sqs_client = get_sqs_client(provider_config).await; - - // Dropping sqs queues - let list_queues_output = sqs_client.list_queues().send().await?; - let queue_urls = list_queues_output.queue_urls(); - tracing::debug!("Found {} queues", queue_urls.len()); - for queue_url in queue_urls { - match sqs_client.delete_queue().queue_url(queue_url).send().await { - Ok(_) => tracing::debug!("Successfully deleted queue: {}", queue_url), - Err(e) => tracing::error!("Error deleting queue {}: {:?}", queue_url, e), +pub async fn create_queues( + provider_config: Arc, + queue_params: &QueueValidatedArgs, +) -> color_eyre::Result<()> { + match queue_params { + QueueValidatedArgs::AWSSQS(aws_sqs_params) => { + let sqs_client = get_sqs_client(provider_config).await; + + // Dropping sqs queues + let list_queues_output = sqs_client.list_queues().send().await?; + let queue_urls = list_queues_output.queue_urls(); + tracing::debug!("Found {} queues", queue_urls.len()); + for queue_url in queue_urls { + match sqs_client.delete_queue().queue_url(queue_url).send().await { + Ok(_) => tracing::debug!("Successfully deleted queue: {}", queue_url), + Err(e) => tracing::error!("Error deleting queue {}: {:?}", queue_url, e), + } + } + + for queue_type in QueueType::iter() { + let queue_name = format!("{}_{}_{}", aws_sqs_params.sqs_prefix, queue_type, aws_sqs_params.sqs_suffix); + sqs_client.create_queue().queue_name(queue_name).send().await?; + } } } - - // Creating SQS queues - sqs_client.create_queue().queue_name(DATA_SUBMISSION_JOB_PROCESSING_QUEUE).send().await?; - sqs_client.create_queue().queue_name(DATA_SUBMISSION_JOB_VERIFICATION_QUEUE).send().await?; - sqs_client.create_queue().queue_name(SNOS_JOB_PROCESSING_QUEUE).send().await?; - sqs_client.create_queue().queue_name(SNOS_JOB_VERIFICATION_QUEUE).send().await?; - sqs_client.create_queue().queue_name(PROVING_JOB_PROCESSING_QUEUE).send().await?; - sqs_client.create_queue().queue_name(PROVING_JOB_VERIFICATION_QUEUE).send().await?; - sqs_client.create_queue().queue_name(PROOF_REGISTRATION_JOB_PROCESSING_QUEUE).send().await?; - sqs_client.create_queue().queue_name(PROOF_REGISTRATION_JOB_VERIFICATION_QUEUE).send().await?; - sqs_client.create_queue().queue_name(UPDATE_STATE_JOB_PROCESSING_QUEUE).send().await?; - sqs_client.create_queue().queue_name(UPDATE_STATE_JOB_VERIFICATION_QUEUE).send().await?; - Ok(()) } @@ -112,6 +117,10 @@ pub struct MessagePayloadType { pub(crate) id: Uuid, } -pub async fn get_storage_client(provider_config: Arc) -> Box { - Box::new(AWSS3::new_with_settings(&EnvSettingsProvider {}, provider_config).await) +pub async fn get_storage_client( + storage_cfg: &AWSS3ValidatedArgs, + provider_config: Arc, +) -> Box { + let aws_config = provider_config.get_aws_client_or_panic(); + Box::new(AWSS3::new_with_args(storage_cfg, aws_config).await) } diff --git a/crates/orchestrator/src/tests/config.rs b/crates/orchestrator/src/tests/config.rs index 637a64f9..811e5ec2 100644 --- a/crates/orchestrator/src/tests/config.rs +++ b/crates/orchestrator/src/tests/config.rs @@ -1,24 +1,43 @@ use std::net::SocketAddr; +use std::str::FromStr as _; use std::sync::Arc; +use alloy::primitives::Address; use axum::Router; use da_client_interface::{DaClient, MockDaClient}; +use ethereum_da_client::EthereumDaValidatedArgs; +use ethereum_settlement_client::EthereumSettlementValidatedArgs; use httpmock::MockServer; use prover_client_interface::{MockProverClient, ProverClient}; use settlement_client_interface::{MockSettlementClient, SettlementClient}; +use sharp_service::SharpValidatedArgs; use starknet::providers::jsonrpc::HttpTransport; use starknet::providers::JsonRpcClient; +use tracing::Level; use url::Url; -use utils::settings::env::EnvSettingsProvider; -use utils::settings::Settings; +use utils::env_utils::{get_env_var_optional, get_env_var_or_default, get_env_var_or_panic}; +use crate::alerts::aws_sns::AWSSNSValidatedArgs; use crate::alerts::Alerts; -use crate::config::{get_aws_config, Config, ProviderConfig}; +use crate::cli::alert::AlertValidatedArgs; +use crate::cli::da::DaValidatedArgs; +use crate::cli::database::DatabaseValidatedArgs; +use crate::cli::prover::ProverValidatedArgs; +use crate::cli::provider::AWSConfigValidatedArgs; +use crate::cli::queue::QueueValidatedArgs; +use crate::cli::settlement::SettlementValidatedArgs; +use crate::cli::snos::SNOSParams; +use crate::cli::storage::StorageValidatedArgs; +use crate::config::{get_aws_config, Config, OrchestratorParams, ProviderConfig, ServiceParams}; +use crate::data_storage::aws_s3::AWSS3ValidatedArgs; use crate::data_storage::{DataStorage, MockDataStorage}; +use crate::database::mongodb::MongoDBValidatedArgs; use crate::database::{Database, MockDatabase}; +use crate::queue::sqs::AWSSQSValidatedArgs; use crate::queue::{MockQueueProvider, QueueProvider}; -use crate::routes::{get_server_url, setup_server}; -use crate::tests::common::{create_sns_arn, create_sqs_queues, drop_database}; +use crate::routes::{get_server_url, setup_server, ServerParams}; +use crate::telemetry::InstrumentationParams; +use crate::tests::common::{create_queues, create_sns_arn, drop_database}; // Inspiration : https://rust-unofficial.github.io/patterns/patterns/creational/builder.html // TestConfigBuilder allows to heavily customise the global configs based on the test's requirement. @@ -181,10 +200,9 @@ impl TestConfigBuilder { pub async fn build(self) -> TestConfigBuilderReturns { dotenvy::from_filename("../.env.test").expect("Failed to load the .env.test file"); - let settings_provider = EnvSettingsProvider {}; - let provider_config = Arc::new(ProviderConfig::AWS(Box::new(get_aws_config(&settings_provider).await))); + let params = get_env_params(); - use std::sync::Arc; + let provider_config = Arc::new(ProviderConfig::AWS(Box::new(get_aws_config(¶ms.aws_params).await))); let TestConfigBuilder { starknet_rpc_url_type, @@ -199,33 +217,39 @@ impl TestConfigBuilder { api_server_type, } = self; - let (starknet_rpc_url, starknet_client, starknet_server) = + let (_starknet_rpc_url, starknet_client, starknet_server) = implement_client::init_starknet_client(starknet_rpc_url_type, starknet_client_type).await; - let alerts = implement_client::init_alerts(alerts_type, &settings_provider, provider_config.clone()).await; - let da_client = implement_client::init_da_client(da_client_type, &settings_provider).await; - let settlement_client = - implement_client::init_settlement_client(settlement_client_type, &settings_provider).await; + // init alerts + let alerts = implement_client::init_alerts(alerts_type, ¶ms.alert_params, provider_config.clone()).await; + + let da_client = implement_client::init_da_client(da_client_type, ¶ms.da_params).await; - let prover_client = implement_client::init_prover_client(prover_client_type, &settings_provider).await; + let settlement_client = + implement_client::init_settlement_client(settlement_client_type, ¶ms.settlement_params).await; - let snos_url = - Url::parse(&settings_provider.get_settings_or_panic("RPC_FOR_SNOS")).expect("Failed to parse URL"); + let prover_client = implement_client::init_prover_client(prover_client_type, ¶ms.prover_params).await; // External Dependencies - let storage = implement_client::init_storage_client(storage_type, provider_config.clone()).await; - let database = implement_client::init_database(database_type, settings_provider).await; - let queue = implement_client::init_queue_client(queue_type).await; + let storage = + implement_client::init_storage_client(storage_type, ¶ms.storage_params, provider_config.clone()).await; + + let database = implement_client::init_database(database_type, ¶ms.db_params).await; + + let queue = + implement_client::init_queue_client(queue_type, params.queue_params.clone(), provider_config.clone()).await; // Deleting and Creating the queues in sqs. - create_sqs_queues(provider_config.clone()).await.expect("Not able to delete and create the queues."); + + create_queues(provider_config.clone(), ¶ms.queue_params) + .await + .expect("Not able to delete and create the queues."); // Deleting the database - drop_database().await.expect("Unable to drop the database."); + drop_database(¶ms.db_params).await.expect("Unable to drop the database."); // Creating the SNS ARN - create_sns_arn(provider_config.clone()).await.expect("Unable to create the sns arn"); + create_sns_arn(provider_config.clone(), ¶ms.alert_params).await.expect("Unable to create the sns arn"); let config = Arc::new(Config::new( - starknet_rpc_url, - snos_url, + params.orchestrator_params, starknet_client, da_client, prover_client, @@ -251,7 +275,7 @@ async fn implement_api_server(api_server_type: ConfigType, config: Arc) match api_server_type { ConfigType::Mock(client) => { if let MockType::Server(router) = client { - let (api_server_url, listener) = get_server_url().await; + let (api_server_url, listener) = get_server_url(config.server_config()).await; let app = Router::new().merge(router); tokio::spawn(async move { @@ -277,12 +301,16 @@ pub mod implement_client { use settlement_client_interface::{MockSettlementClient, SettlementClient}; use starknet::providers::jsonrpc::HttpTransport; use starknet::providers::{JsonRpcClient, Url}; - use utils::env_utils::get_env_var_or_panic; - use utils::settings::env::EnvSettingsProvider; - use utils::settings::Settings; use super::{ConfigType, MockType}; use crate::alerts::{Alerts, MockAlerts}; + use crate::cli::alert::AlertValidatedArgs; + use crate::cli::da::DaValidatedArgs; + use crate::cli::database::DatabaseValidatedArgs; + use crate::cli::prover::ProverValidatedArgs; + use crate::cli::queue::QueueValidatedArgs; + use crate::cli::settlement::SettlementValidatedArgs; + use crate::cli::storage::StorageValidatedArgs; use crate::config::{ build_alert_client, build_da_client, build_database_client, build_prover_service, build_queue_client, build_settlement_client, ProviderConfig, @@ -314,22 +342,22 @@ pub mod implement_client { implement_mock_client_conversion!(SettlementClient, SettlementClient); implement_mock_client_conversion!(DaClient, DaClient); - pub(crate) async fn init_da_client(service: ConfigType, settings_provider: &impl Settings) -> Box { + pub(crate) async fn init_da_client(service: ConfigType, da_params: &DaValidatedArgs) -> Box { match service { ConfigType::Mock(client) => client.into(), - ConfigType::Actual => build_da_client(settings_provider).await, + ConfigType::Actual => build_da_client(da_params).await, ConfigType::Dummy => Box::new(MockDaClient::new()), } } pub(crate) async fn init_settlement_client( service: ConfigType, - settings_provider: &impl Settings, + settlement_cfg: &SettlementValidatedArgs, ) -> Box { match service { ConfigType::Mock(client) => client.into(), ConfigType::Actual => { - build_settlement_client(settings_provider).await.expect("Failed to initialise settlement_client") + build_settlement_client(settlement_cfg).await.expect("Failed to initialise settlement_client") } ConfigType::Dummy => Box::new(MockSettlementClient::new()), } @@ -337,60 +365,64 @@ pub mod implement_client { pub(crate) async fn init_prover_client( service: ConfigType, - settings_provider: &impl Settings, + prover_params: &ProverValidatedArgs, ) -> Box { match service { ConfigType::Mock(client) => client.into(), - ConfigType::Actual => build_prover_service(settings_provider), + ConfigType::Actual => build_prover_service(prover_params), ConfigType::Dummy => Box::new(MockProverClient::new()), } } pub(crate) async fn init_alerts( service: ConfigType, - settings_provider: &impl Settings, + alert_params: &AlertValidatedArgs, provider_config: Arc, ) -> Box { match service { ConfigType::Mock(client) => client.into(), - ConfigType::Actual => build_alert_client(settings_provider, provider_config).await, + ConfigType::Actual => build_alert_client(alert_params, provider_config).await, ConfigType::Dummy => Box::new(MockAlerts::new()), } } pub(crate) async fn init_storage_client( service: ConfigType, + storage_cfg: &StorageValidatedArgs, provider_config: Arc, ) -> Box { match service { ConfigType::Mock(client) => client.into(), - ConfigType::Actual => { - let storage = get_storage_client(provider_config).await; - match get_env_var_or_panic("DATA_STORAGE").as_str() { - "s3" => storage.as_ref().create_bucket(&get_env_var_or_panic("AWS_S3_BUCKET_NAME")).await.unwrap(), - _ => panic!("Unsupported Storage Client"), + ConfigType::Actual => match storage_cfg { + StorageValidatedArgs::AWSS3(aws_s3_params) => { + let storage = get_storage_client(aws_s3_params, provider_config).await; + storage.as_ref().create_bucket(&aws_s3_params.bucket_name).await.unwrap(); + storage } - storage - } + }, ConfigType::Dummy => Box::new(MockDataStorage::new()), } } - pub(crate) async fn init_queue_client(service: ConfigType) -> Box { + pub(crate) async fn init_queue_client( + service: ConfigType, + queue_params: QueueValidatedArgs, + provider_config: Arc, + ) -> Box { match service { ConfigType::Mock(client) => client.into(), - ConfigType::Actual => build_queue_client(), + ConfigType::Actual => build_queue_client(&queue_params, provider_config).await, ConfigType::Dummy => Box::new(MockQueueProvider::new()), } } pub(crate) async fn init_database( service: ConfigType, - settings_provider: EnvSettingsProvider, + database_params: &DatabaseValidatedArgs, ) -> Box { match service { ConfigType::Mock(client) => client.into(), - ConfigType::Actual => build_database_client(&settings_provider).await, + ConfigType::Actual => build_database_client(database_params).await, ConfigType::Dummy => Box::new(MockDatabase::new()), } } @@ -435,3 +467,127 @@ pub mod implement_client { (rpc_url, starknet_client, server) } } + +struct EnvParams { + aws_params: AWSConfigValidatedArgs, + alert_params: AlertValidatedArgs, + queue_params: QueueValidatedArgs, + storage_params: StorageValidatedArgs, + db_params: DatabaseValidatedArgs, + da_params: DaValidatedArgs, + settlement_params: SettlementValidatedArgs, + prover_params: ProverValidatedArgs, + orchestrator_params: OrchestratorParams, + #[allow(dead_code)] + instrumentation_params: InstrumentationParams, +} + +fn get_env_params() -> EnvParams { + let db_params = DatabaseValidatedArgs::MongoDB(MongoDBValidatedArgs { + connection_url: Url::parse(&get_env_var_or_panic("MADARA_ORCHESTRATOR_MONGODB_CONNECTION_URL")) + .expect("Invalid MongoDB connection URL"), + database_name: get_env_var_or_panic("MADARA_ORCHESTRATOR_DATABASE_NAME"), + }); + + let storage_params = StorageValidatedArgs::AWSS3(AWSS3ValidatedArgs { + bucket_name: get_env_var_or_panic("MADARA_ORCHESTRATOR_AWS_S3_BUCKET_NAME"), + }); + + let queue_params = QueueValidatedArgs::AWSSQS(AWSSQSValidatedArgs { + queue_base_url: Url::parse(&get_env_var_or_panic("MADARA_ORCHESTRATOR_SQS_BASE_QUEUE_URL")) + .expect("Invalid queue base URL"), + sqs_prefix: get_env_var_or_panic("MADARA_ORCHESTRATOR_SQS_PREFIX"), + sqs_suffix: get_env_var_or_panic("MADARA_ORCHESTRATOR_SQS_SUFFIX"), + }); + + let aws_params = AWSConfigValidatedArgs { + aws_access_key_id: get_env_var_or_panic("AWS_ACCESS_KEY_ID"), + aws_secret_access_key: get_env_var_or_panic("AWS_SECRET_ACCESS_KEY"), + aws_region: get_env_var_or_panic("AWS_REGION"), + }; + + let da_params = DaValidatedArgs::Ethereum(EthereumDaValidatedArgs { + ethereum_da_rpc_url: Url::parse(&get_env_var_or_panic("MADARA_ORCHESTRATOR_ETHEREUM_DA_RPC_URL")) + .expect("Failed to parse MADARA_ORCHESTRATOR_ETHEREUM_RPC_URL"), + }); + + let alert_params = AlertValidatedArgs::AWSSNS(AWSSNSValidatedArgs { + topic_arn: get_env_var_or_panic("MADARA_ORCHESTRATOR_AWS_SNS_ARN"), + }); + + let settlement_params = SettlementValidatedArgs::Ethereum(EthereumSettlementValidatedArgs { + ethereum_rpc_url: Url::parse(&get_env_var_or_panic("MADARA_ORCHESTRATOR_ETHEREUM_SETTLEMENT_RPC_URL")) + .expect("Failed to parse MADARA_ORCHESTRATOR_ETHEREUM_RPC_URL"), + ethereum_private_key: get_env_var_or_panic("MADARA_ORCHESTRATOR_ETHEREUM_PRIVATE_KEY"), + l1_core_contract_address: Address::from_str(&get_env_var_or_panic( + "MADARA_ORCHESTRATOR_L1_CORE_CONTRACT_ADDRESS", + )) + .expect("Invalid L1 core contract address"), + starknet_operator_address: Address::from_str(&get_env_var_or_panic( + "MADARA_ORCHESTRATOR_STARKNET_OPERATOR_ADDRESS", + )) + .expect("Invalid Starknet operator address"), + }); + + let snos_config = SNOSParams { + rpc_for_snos: Url::parse(&get_env_var_or_panic("MADARA_ORCHESTRATOR_RPC_FOR_SNOS")) + .expect("Failed to parse MADARA_ORCHESTRATOR_RPC_FOR_SNOS"), + }; + + let env = get_env_var_optional("MADARA_ORCHESTRATOR_MAX_BLOCK_NO_TO_PROCESS").expect("Couldn't get max block"); + let max_block: Option = env.and_then(|s| if s.is_empty() { None } else { Some(s.parse::().unwrap()) }); + + let env = get_env_var_optional("MADARA_ORCHESTRATOR_MIN_BLOCK_NO_TO_PROCESS").expect("Couldn't get min block"); + let min_block: Option = env.and_then(|s| if s.is_empty() { None } else { Some(s.parse::().unwrap()) }); + + let service_config = ServiceParams { max_block_to_process: max_block, min_block_to_process: min_block }; + + let server_config = ServerParams { + host: get_env_var_or_panic("MADARA_ORCHESTRATOR_HOST"), + port: get_env_var_or_panic("MADARA_ORCHESTRATOR_PORT") + .parse() + .expect("Failed to parse MADARA_ORCHESTRATOR_PORT"), + }; + + let orchestrator_params = OrchestratorParams { + madara_rpc_url: Url::parse(&get_env_var_or_panic("MADARA_ORCHESTRATOR_MADARA_RPC_URL")) + .expect("Failed to parse MADARA_ORCHESTRATOR_MADARA_RPC_URL"), + snos_config, + service_config, + server_config, + }; + + let instrumentation_params = InstrumentationParams { + otel_service_name: get_env_var_or_panic("MADARA_ORCHESTRATOR_OTEL_SERVICE_NAME"), + otel_collector_endpoint: get_env_var_optional("MADARA_ORCHESTRATOR_OTEL_COLLECTOR_ENDPOINT") + .expect("Couldn't get otel collector endpoint") + .map(|url| Url::parse(&url).expect("Failed to parse MADARA_ORCHESTRATOR_OTEL_COLLECTOR_ENDPOINT")), + log_level: Level::from_str(&get_env_var_or_default("RUST_LOG", "info")).expect("Failed to parse RUST_LOG"), + }; + + let prover_params = ProverValidatedArgs::Sharp(SharpValidatedArgs { + sharp_customer_id: get_env_var_or_panic("MADARA_ORCHESTRATOR_SHARP_CUSTOMER_ID"), + sharp_url: Url::parse(&get_env_var_or_panic("MADARA_ORCHESTRATOR_SHARP_URL")) + .expect("Failed to parse MADARA_ORCHESTRATOR_SHARP_URL"), + sharp_user_crt: get_env_var_or_panic("MADARA_ORCHESTRATOR_SHARP_USER_CRT"), + sharp_user_key: get_env_var_or_panic("MADARA_ORCHESTRATOR_SHARP_USER_KEY"), + sharp_rpc_node_url: Url::parse(&get_env_var_or_panic("MADARA_ORCHESTRATOR_SHARP_RPC_NODE_URL")) + .expect("Failed to parse MADARA_ORCHESTRATOR_SHARP_RPC_NODE_URL"), + sharp_server_crt: get_env_var_or_panic("MADARA_ORCHESTRATOR_SHARP_SERVER_CRT"), + sharp_proof_layout: get_env_var_or_panic("MADARA_ORCHESTRATOR_SHARP_PROOF_LAYOUT"), + gps_verifier_contract_address: get_env_var_or_panic("MADARA_ORCHESTRATOR_GPS_VERIFIER_CONTRACT_ADDRESS"), + }); + + EnvParams { + aws_params, + alert_params, + queue_params, + storage_params, + db_params, + da_params, + settlement_params, + prover_params, + instrumentation_params, + orchestrator_params, + } +} diff --git a/crates/orchestrator/src/tests/data_storage/mod.rs b/crates/orchestrator/src/tests/data_storage/mod.rs index 968e1c61..cd7d12fd 100644 --- a/crates/orchestrator/src/tests/data_storage/mod.rs +++ b/crates/orchestrator/src/tests/data_storage/mod.rs @@ -13,8 +13,6 @@ use crate::tests::config::{ConfigType, TestConfigBuilder}; async fn test_put_and_get_data_s3() -> color_eyre::Result<()> { let services = TestConfigBuilder::new().configure_storage_client(ConfigType::Actual).build().await; - dotenvy::from_filename("../.env.test")?; - let s3_client = services.config.storage(); let mock_data = json!( diff --git a/crates/orchestrator/src/tests/jobs/da_job/mod.rs b/crates/orchestrator/src/tests/jobs/da_job/mod.rs index 871318e7..57fe6bbf 100644 --- a/crates/orchestrator/src/tests/jobs/da_job/mod.rs +++ b/crates/orchestrator/src/tests/jobs/da_job/mod.rs @@ -14,7 +14,6 @@ use crate::jobs::da_job::test::{get_nonce_attached, read_state_update_from_file} use crate::jobs::da_job::{DaError, DaJob}; use crate::jobs::types::{ExternalId, JobItem, JobStatus, JobType}; use crate::jobs::{Job, JobError}; -use crate::tests::common::drop_database; use crate::tests::config::{ConfigType, TestConfigBuilder}; /// Tests the DA Job's handling of a blob length exceeding the supported size. @@ -234,5 +233,4 @@ async fn test_da_job_process_job_success( ); state_update_mock.assert(); - let _ = drop_database().await; } diff --git a/crates/orchestrator/src/tests/jobs/mod.rs b/crates/orchestrator/src/tests/jobs/mod.rs index b10c66e8..c34fe0ff 100644 --- a/crates/orchestrator/src/tests/jobs/mod.rs +++ b/crates/orchestrator/src/tests/jobs/mod.rs @@ -18,9 +18,8 @@ use crate::jobs::types::{ExternalId, JobItem, JobStatus, JobType, JobVerificatio use crate::jobs::{ create_job, handle_job_failure, increment_key_in_metadata, process_job, verify_job, Job, JobError, MockJob, }; -use crate::queue::job_queue::{ - QueueNameForJobType, DATA_SUBMISSION_JOB_PROCESSING_QUEUE, DATA_SUBMISSION_JOB_VERIFICATION_QUEUE, -}; +use crate::queue::job_queue::QueueNameForJobType; +use crate::queue::QueueType; use crate::tests::common::MessagePayloadType; use crate::tests::config::{ConfigType, TestConfigBuilder}; @@ -430,19 +429,11 @@ async fn verify_job_with_verified_status_works() { sleep(Duration::from_secs(5)).await; // Queue checks. - let consumed_messages_verification_queue = services - .config - .queue() - .consume_message_from_queue(DATA_SUBMISSION_JOB_VERIFICATION_QUEUE.to_string()) - .await - .unwrap_err(); + let consumed_messages_verification_queue = + services.config.queue().consume_message_from_queue(QueueType::DataSubmissionJobVerification).await.unwrap_err(); assert_matches!(consumed_messages_verification_queue, QueueError::NoData); - let consumed_messages_processing_queue = services - .config - .queue() - .consume_message_from_queue(DATA_SUBMISSION_JOB_PROCESSING_QUEUE.to_string()) - .await - .unwrap_err(); + let consumed_messages_processing_queue = + services.config.queue().consume_message_from_queue(QueueType::DataSubmissionJobProcessing).await.unwrap_err(); assert_matches!(consumed_messages_processing_queue, QueueError::NoData); } @@ -484,12 +475,8 @@ async fn verify_job_with_rejected_status_adds_to_queue_works() { sleep(Duration::from_secs(5)).await; // Queue checks. - let consumed_messages = services - .config - .queue() - .consume_message_from_queue(DATA_SUBMISSION_JOB_PROCESSING_QUEUE.to_string()) - .await - .unwrap(); + let consumed_messages = + services.config.queue().consume_message_from_queue(QueueType::DataSubmissionJobProcessing).await.unwrap(); let consumed_message_payload: MessagePayloadType = consumed_messages.payload_serde_json().unwrap().unwrap(); assert_eq!(consumed_message_payload.id, job_item.id); } diff --git a/crates/orchestrator/src/tests/jobs/snos_job/mod.rs b/crates/orchestrator/src/tests/jobs/snos_job/mod.rs index 306a5fce..8a48f86e 100644 --- a/crates/orchestrator/src/tests/jobs/snos_job/mod.rs +++ b/crates/orchestrator/src/tests/jobs/snos_job/mod.rs @@ -48,7 +48,7 @@ async fn test_verify_job(#[from(default_job_item)] mut job_item: JobItem) { /// We have a private pathfinder node used to run the Snos [prove_block] function. /// It must be set or the test below will be ignored, since the Snos cannot run /// without a Pathinder node for the moment. -const SNOS_PATHFINDER_RPC_URL_ENV: &str = "RPC_FOR_SNOS"; +const SNOS_PATHFINDER_RPC_URL_ENV: &str = "MADARA_ORCHESTRATOR_RPC_FOR_SNOS"; #[rstest] #[tokio::test(flavor = "multi_thread")] diff --git a/crates/orchestrator/src/tests/server/job_routes.rs b/crates/orchestrator/src/tests/server/job_routes.rs index 458b33ff..c2a3b0cc 100644 --- a/crates/orchestrator/src/tests/server/job_routes.rs +++ b/crates/orchestrator/src/tests/server/job_routes.rs @@ -23,7 +23,7 @@ use crate::tests::config::{ConfigType, TestConfigBuilder}; async fn setup_trigger() -> (SocketAddr, Arc) { dotenvy::from_filename("../.env.test").expect("Failed to load the .env.test file"); - let madara_url = get_env_var_or_panic("MADARA_RPC_URL"); + let madara_url = get_env_var_or_panic("MADARA_ORCHESTRATOR_MADARA_RPC_URL"); let provider = JsonRpcClient::new(HttpTransport::new( Url::parse(madara_url.as_str().to_string().as_str()).expect("Failed to parse URL"), )); diff --git a/crates/orchestrator/src/tests/workers/proving/mod.rs b/crates/orchestrator/src/tests/workers/proving/mod.rs index 24a602b9..753b957f 100644 --- a/crates/orchestrator/src/tests/workers/proving/mod.rs +++ b/crates/orchestrator/src/tests/workers/proving/mod.rs @@ -15,7 +15,6 @@ use crate::database::MockDatabase; use crate::jobs::job_handler_factory::mock_factory; use crate::jobs::types::{JobItem, JobStatus, JobType}; use crate::jobs::{Job, MockJob}; -use crate::queue::job_queue::PROVING_JOB_PROCESSING_QUEUE; use crate::queue::MockQueueProvider; use crate::tests::config::TestConfigBuilder; use crate::tests::workers::utils::{db_checks_proving_worker, get_job_by_mock_id_vector}; @@ -26,6 +25,8 @@ use crate::workers::proving::ProvingWorker; #[case(false)] #[tokio::test] async fn test_proving_worker(#[case] incomplete_runs: bool) -> Result<(), Box> { + use crate::queue::QueueType; + let server = MockServer::start(); let da_client = MockDaClient::new(); let mut db = MockDatabase::new(); @@ -62,7 +63,7 @@ async fn test_proving_worker(#[case] incomplete_runs: bool) -> Result<(), Box Result<(), Box Result<(), Box> { + use crate::queue::QueueType; + let server = MockServer::start(); let da_client = MockDaClient::new(); let mut db = MockDatabase::new(); @@ -92,7 +93,7 @@ async fn test_snos_worker(#[case] db_val: bool) -> Result<(), Box> { queue .expect_send_message_to_queue() .returning(|_, _, _| Ok(())) - .withf(|queue, _payload, _delay| queue == SNOS_JOB_PROCESSING_QUEUE); + .withf(|queue, _payload, _delay| *queue == QueueType::SnosJobProcessing); // mock block number (madara) : 5 let rpc_response_block_number = block; diff --git a/crates/orchestrator/src/workers/snos.rs b/crates/orchestrator/src/workers/snos.rs index ccff21ed..a346dd37 100644 --- a/crates/orchestrator/src/workers/snos.rs +++ b/crates/orchestrator/src/workers/snos.rs @@ -1,9 +1,9 @@ +use std::cmp::{max, min}; use std::collections::HashMap; use std::sync::Arc; use async_trait::async_trait; use starknet::providers::Provider; -use utils::env_utils::get_env_var_or_default; use crate::config::Config; use crate::jobs::create_job; @@ -21,21 +21,29 @@ impl Worker for SnosWorker { tracing::trace!(log_type = "starting", category = "SnosWorker", "SnosWorker started."); let provider = config.starknet_client(); - let block_number_provider = &provider.block_number().await?; + let block_number_provider = provider.block_number().await?; + + let latest_block_number = if let Some(max_block_to_process) = config.service_config().max_block_to_process { + min(max_block_to_process, block_number_provider) + } else { + block_number_provider + }; - let latest_block_number = - get_env_var_or_default_block_number("MAX_BLOCK_TO_PROCESS", &block_number_provider.to_string())?; tracing::debug!(latest_block_number = %latest_block_number, "Fetched latest block number from starknet"); let latest_job_in_db = config.database().get_latest_job_by_type(JobType::SnosRun).await?; let latest_job_id = match latest_job_in_db { - Some(job) => job.internal_id, - None => "0".to_string(), + Some(job) => job.internal_id.parse::().expect("Failed to parse job internal ID to u64"), + None => "0".to_string().parse::().expect("Failed to parse '0' to u64"), }; // To be used when testing in specific block range - let block_start = get_env_var_or_default_block_number("MIN_BLOCK_TO_PROCESS", &latest_job_id)?; + let block_start = if let Some(min_block_to_process) = config.service_config().min_block_to_process { + max(min_block_to_process, latest_job_id) + } else { + latest_job_id + }; for block_num in block_start..latest_block_number + 1 { match create_job(JobType::SnosRun, block_num.to_string(), HashMap::new(), config.clone()).await { @@ -49,11 +57,3 @@ impl Worker for SnosWorker { Ok(()) } } - -fn get_env_var_or_default_block_number(env_var_name: &str, default_block_number: &str) -> color_eyre::Result { - if get_env_var_or_default(env_var_name, default_block_number) == *"" { - Ok(default_block_number.to_string().parse::()?) - } else { - Ok(get_env_var_or_default(env_var_name, default_block_number).parse::()?) - } -} diff --git a/crates/prover-services/gps-fact-checker/src/lib.rs b/crates/prover-services/gps-fact-checker/src/lib.rs index 87f0252b..8189e653 100644 --- a/crates/prover-services/gps-fact-checker/src/lib.rs +++ b/crates/prover-services/gps-fact-checker/src/lib.rs @@ -1,3 +1,5 @@ +use std::str::FromStr as _; + use alloy::primitives::{Address, B256}; use alloy::providers::{ProviderBuilder, RootProvider}; use alloy::sol; @@ -25,9 +27,12 @@ type TransportT = Http; type ProviderT = RootProvider; impl FactChecker { - pub fn new(rpc_node_url: Url, verifier_address: Address) -> Self { - let provider = ProviderBuilder::new().on_http(rpc_node_url); - let fact_registry = FactRegistry::new(verifier_address, provider); + pub fn new(sharp_rpc_node_url: Url, gps_verifier_contract_address: String) -> Self { + let provider = ProviderBuilder::new().on_http(sharp_rpc_node_url); + let fact_registry = FactRegistry::new( + Address::from_str(gps_verifier_contract_address.as_str()).expect("Invalid GPS verifier contract address"), + provider, + ); Self { fact_registry } } diff --git a/crates/prover-services/prover-client-interface/src/lib.rs b/crates/prover-services/prover-client-interface/src/lib.rs index 62299740..84d611a7 100644 --- a/crates/prover-services/prover-client-interface/src/lib.rs +++ b/crates/prover-services/prover-client-interface/src/lib.rs @@ -33,8 +33,6 @@ pub enum TaskStatus { pub enum ProverClientError { #[error("Internal prover error: {0}")] Internal(#[source] Box), - #[error("Settings provider error: {0}")] - SettingsProvider(#[from] utils::settings::SettingsProviderError), #[error("Task is invalid: {0}")] TaskInvalid(String), #[error("Fact checker error: {0}")] diff --git a/crates/prover-services/sharp-service/src/client.rs b/crates/prover-services/sharp-service/src/client.rs index c652e23d..25c1b068 100644 --- a/crates/prover-services/sharp-service/src/client.rs +++ b/crates/prover-services/sharp-service/src/client.rs @@ -5,11 +5,11 @@ use base64::Engine; use reqwest::{Certificate, ClientBuilder, Identity}; use url::Url; use utils::env_utils::get_env_var_or_panic; -use utils::settings::Settings; use uuid::Uuid; use crate::error::SharpError; use crate::types::{SharpAddJobResponse, SharpGetStatusResponse}; +use crate::SharpValidatedArgs; /// SHARP API async wrapper pub struct SharpClient { @@ -20,31 +20,31 @@ pub struct SharpClient { impl SharpClient { /// We need to set up the client with the provided certificates. /// We need to have three secrets : - /// - base64(SHARP_USER_CRT) - /// - base64(SHARP_USER_KEY) - /// - base64(SHARP_SERVER_CRT) + /// - base64(MADARA_ORCHESTRATOR_SHARP_USER_CRT) + /// - base64(MADARA_ORCHESTRATOR_SHARP_USER_KEY) + /// - base64(MADARA_ORCHESTRATOR_SHARP_SERVER_CRT) /// /// You can run this command in terminal to convert a file output into base64 /// and then copy it and paste it into .env file : /// /// `cat | base64` - pub fn new_with_settings(url: Url, settings: &impl Settings) -> Self { + pub fn new_with_args(url: Url, sharp_params: &SharpValidatedArgs) -> Self { // Getting the cert files from the .env and then decoding it from base64 let cert = general_purpose::STANDARD - .decode(settings.get_settings_or_panic("SHARP_USER_CRT")) + .decode(sharp_params.sharp_user_crt.clone()) .expect("Failed to decode certificate"); let key = general_purpose::STANDARD - .decode(settings.get_settings_or_panic("SHARP_USER_KEY")) + .decode(sharp_params.sharp_user_key.clone()) .expect("Failed to decode sharp user key"); let server_cert = general_purpose::STANDARD - .decode(settings.get_settings_or_panic("SHARP_SERVER_CRT")) + .decode(sharp_params.sharp_server_crt.clone()) .expect("Failed to decode sharp server certificate"); // Adding Customer ID to the url let mut url_mut = url.clone(); - let customer_id = settings.get_settings_or_panic("SHARP_CUSTOMER_ID"); + let customer_id = sharp_params.sharp_customer_id.clone(); url_mut.query_pairs_mut().append_pair("customer_id", customer_id.as_str()); Self { @@ -69,7 +69,7 @@ impl SharpClient { let cairo_key = Uuid::new_v4(); let cairo_key_string = cairo_key.to_string(); - let proof_layout = get_env_var_or_panic("SHARP_PROOF_LAYOUT"); + let proof_layout = get_env_var_or_panic("MADARA_ORCHESTRATOR_SHARP_PROOF_LAYOUT"); // Params for sending the PIE file to the prover // for temporary reference you can check this doc : diff --git a/crates/prover-services/sharp-service/src/config.rs b/crates/prover-services/sharp-service/src/config.rs deleted file mode 100644 index b4e2cc8d..00000000 --- a/crates/prover-services/sharp-service/src/config.rs +++ /dev/null @@ -1,34 +0,0 @@ -use alloy::primitives::Address; -use serde::{Deserialize, Serialize}; -use url::Url; -use utils::settings::Settings; - -/// SHARP proving service configuration -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct SharpConfig { - /// SHARP service url - pub service_url: Url, - /// EVM RPC node url - pub rpc_node_url: Url, - /// GPS verifier contract address (implements FactRegistry) - pub verifier_address: Address, -} - -impl SharpConfig { - pub fn new_with_settings(settings: &impl Settings) -> color_eyre::Result { - Ok(Self { - service_url: settings - .get_settings_or_panic("SHARP_URL") - .parse() - .expect("Failed to parse to service url for SharpConfig"), - rpc_node_url: settings - .get_settings_or_panic("SETTLEMENT_RPC_URL") - .parse() - .expect("Failed to parse to rpc_node_url for SharpConfig"), - verifier_address: settings - .get_settings_or_panic("GPS_VERIFIER_CONTRACT_ADDRESS") - .parse() - .expect("Failed to parse to verifier_address for SharpConfig"), - }) - } -} diff --git a/crates/prover-services/sharp-service/src/lib.rs b/crates/prover-services/sharp-service/src/lib.rs index 6cdb8cbb..783d4324 100644 --- a/crates/prover-services/sharp-service/src/lib.rs +++ b/crates/prover-services/sharp-service/src/lib.rs @@ -1,5 +1,4 @@ pub mod client; -pub mod config; pub mod error; mod types; @@ -10,14 +9,26 @@ use async_trait::async_trait; use gps_fact_checker::FactChecker; use prover_client_interface::{ProverClient, ProverClientError, Task, TaskStatus}; use starknet_os::sharp::CairoJobStatus; -use utils::settings::Settings; use uuid::Uuid; use crate::client::SharpClient; -use crate::config::SharpConfig; pub const SHARP_SETTINGS_NAME: &str = "sharp"; +use url::Url; + +#[derive(Debug, Clone)] +pub struct SharpValidatedArgs { + pub sharp_customer_id: String, + pub sharp_url: Url, + pub sharp_user_crt: String, + pub sharp_user_key: String, + pub sharp_rpc_node_url: Url, + pub sharp_server_crt: String, + pub sharp_proof_layout: String, + pub gps_verifier_contract_address: String, +} + /// SHARP (aka GPS) is a shared proving service hosted by Starkware. pub struct SharpProverService { sharp_client: SharpClient, @@ -134,22 +145,24 @@ impl SharpProverService { Self { sharp_client, fact_checker } } - pub fn new_with_settings(settings: &impl Settings) -> Self { - let sharp_config = SharpConfig::new_with_settings(settings) - .expect("Not able to create SharpProverService from given settings."); - let sharp_client = SharpClient::new_with_settings(sharp_config.service_url, settings); - let fact_checker = FactChecker::new(sharp_config.rpc_node_url, sharp_config.verifier_address); + pub fn new_with_args(sharp_params: &SharpValidatedArgs) -> Self { + let sharp_client = SharpClient::new_with_args(sharp_params.sharp_url.clone(), sharp_params); + let fact_checker = FactChecker::new( + sharp_params.sharp_rpc_node_url.clone(), + sharp_params.gps_verifier_contract_address.clone(), + ); Self::new(sharp_client, fact_checker) } - pub fn with_test_settings(settings: &impl Settings, port: u16) -> Self { - let sharp_config = SharpConfig::new_with_settings(settings) - .expect("Not able to create SharpProverService from given settings."); - let sharp_client = SharpClient::new_with_settings( + pub fn with_test_params(port: u16, sharp_params: &SharpValidatedArgs) -> Self { + let sharp_client = SharpClient::new_with_args( format!("http://127.0.0.1:{}", port).parse().expect("Failed to create sharp client with the given params"), - settings, + sharp_params, + ); + let fact_checker = FactChecker::new( + sharp_params.sharp_rpc_node_url.clone(), + sharp_params.gps_verifier_contract_address.clone(), ); - let fact_checker = FactChecker::new(sharp_config.rpc_node_url, sharp_config.verifier_address); Self::new(sharp_client, fact_checker) } } diff --git a/crates/prover-services/sharp-service/tests/lib.rs b/crates/prover-services/sharp-service/tests/lib.rs index cfc4eb6d..1434ab79 100644 --- a/crates/prover-services/sharp-service/tests/lib.rs +++ b/crates/prover-services/sharp-service/tests/lib.rs @@ -1,14 +1,17 @@ use cairo_vm::vm::runners::cairo_pie::CairoPie; +use constants::CAIRO_PIE_PATH; use httpmock::MockServer; -use prover_client_interface::{ProverClient, Task, TaskStatus}; +// ProverClient +use prover_client_interface::ProverClient; +use prover_client_interface::{Task, TaskStatus}; use rstest::rstest; use serde_json::json; -use sharp_service::SharpProverService; +use sharp_service::{SharpProverService, SharpValidatedArgs}; use starknet_os::sharp::CairoJobStatus; +use url::Url; use utils::env_utils::get_env_var_or_panic; -use utils::settings::env::EnvSettingsProvider; -use crate::constants::{CAIRO_PIE_PATH, TEST_FACT}; +use crate::constants::TEST_FACT; mod constants; @@ -17,8 +20,19 @@ mod constants; async fn prover_client_submit_task_works() { dotenvy::from_filename("../.env.test").expect("Failed to load the .env file"); + let sharp_params = SharpValidatedArgs { + sharp_customer_id: get_env_var_or_panic("MADARA_ORCHESTRATOR_SHARP_CUSTOMER_ID"), + sharp_url: Url::parse(&get_env_var_or_panic("MADARA_ORCHESTRATOR_SHARP_URL")).unwrap(), + sharp_user_crt: get_env_var_or_panic("MADARA_ORCHESTRATOR_SHARP_USER_CRT"), + sharp_user_key: get_env_var_or_panic("MADARA_ORCHESTRATOR_SHARP_USER_KEY"), + sharp_rpc_node_url: Url::parse(&get_env_var_or_panic("MADARA_ORCHESTRATOR_SHARP_RPC_NODE_URL")).unwrap(), + sharp_server_crt: get_env_var_or_panic("MADARA_ORCHESTRATOR_SHARP_SERVER_CRT"), + sharp_proof_layout: get_env_var_or_panic("MADARA_ORCHESTRATOR_SHARP_PROOF_LAYOUT"), + gps_verifier_contract_address: get_env_var_or_panic("MADARA_ORCHESTRATOR_GPS_VERIFIER_CONTRACT_ADDRESS"), + }; + let server = MockServer::start(); - let sharp_service = SharpProverService::with_test_settings(&EnvSettingsProvider {}, server.port()); + let sharp_service = SharpProverService::with_test_params(server.port(), &sharp_params); let cairo_pie_path = env!("CARGO_MANIFEST_DIR").to_string() + CAIRO_PIE_PATH; let cairo_pie = CairoPie::read_zip_file(cairo_pie_path.as_ref()).unwrap(); @@ -27,7 +41,7 @@ async fn prover_client_submit_task_works() { "code" : "JOB_RECEIVED_SUCCESSFULLY" } ); - let customer_id = get_env_var_or_panic("SHARP_CUSTOMER_ID"); + let customer_id = get_env_var_or_panic("MADARA_ORCHESTRATOR_SHARP_CUSTOMER_ID"); let sharp_add_job_call = server.mock(|when, then| { when.path_contains("/add_job").query_param("customer_id", customer_id.as_str()); then.status(200).body(serde_json::to_vec(&sharp_response).unwrap()); @@ -51,9 +65,20 @@ async fn prover_client_submit_task_works() { async fn prover_client_get_task_status_works(#[case] cairo_job_status: CairoJobStatus) { dotenvy::from_filename("../.env.test").expect("Failed to load the .env file"); + let sharp_params = SharpValidatedArgs { + sharp_customer_id: get_env_var_or_panic("MADARA_ORCHESTRATOR_SHARP_CUSTOMER_ID"), + sharp_url: Url::parse(&get_env_var_or_panic("MADARA_ORCHESTRATOR_SHARP_URL")).unwrap(), + sharp_user_crt: get_env_var_or_panic("MADARA_ORCHESTRATOR_SHARP_USER_CRT"), + sharp_user_key: get_env_var_or_panic("MADARA_ORCHESTRATOR_SHARP_USER_KEY"), + sharp_rpc_node_url: Url::parse(&get_env_var_or_panic("MADARA_ORCHESTRATOR_SHARP_RPC_NODE_URL")).unwrap(), + sharp_server_crt: get_env_var_or_panic("MADARA_ORCHESTRATOR_SHARP_SERVER_CRT"), + sharp_proof_layout: get_env_var_or_panic("MADARA_ORCHESTRATOR_SHARP_PROOF_LAYOUT"), + gps_verifier_contract_address: get_env_var_or_panic("MADARA_ORCHESTRATOR_GPS_VERIFIER_CONTRACT_ADDRESS"), + }; + let server = MockServer::start(); - let sharp_service = SharpProverService::with_test_settings(&EnvSettingsProvider {}, server.port()); - let customer_id = get_env_var_or_panic("SHARP_CUSTOMER_ID"); + let sharp_service = SharpProverService::with_test_params(server.port(), &sharp_params); + let customer_id = get_env_var_or_panic("MADARA_ORCHESTRATOR_SHARP_CUSTOMER_ID"); let sharp_add_job_call = server.mock(|when, then| { when.path_contains("/get_status").query_param("customer_id", customer_id.as_str()); diff --git a/crates/settlement-clients/ethereum/Cargo.toml b/crates/settlement-clients/ethereum/Cargo.toml index a458bb1d..9dc43b69 100644 --- a/crates/settlement-clients/ethereum/Cargo.toml +++ b/crates/settlement-clients/ethereum/Cargo.toml @@ -4,9 +4,8 @@ version.workspace = true edition.workspace = true [dependencies] -alloy-primitives = { version = "0.7.7", default-features = false } - alloy = { workspace = true, features = ["full", "node-bindings"] } +alloy-primitives = { workspace = true, default-features = false } async-trait = { workspace = true } bytes = "1.7.2" c-kzg = { workspace = true } diff --git a/crates/settlement-clients/ethereum/src/config.rs b/crates/settlement-clients/ethereum/src/config.rs deleted file mode 100644 index cd20d356..00000000 --- a/crates/settlement-clients/ethereum/src/config.rs +++ /dev/null @@ -1,23 +0,0 @@ -use std::str::FromStr; - -use serde::{Deserialize, Serialize}; -use url::Url; -use utils::settings::Settings; - -pub const SETTLEMENT_RPC_URL: &str = "SETTLEMENT_RPC_URL"; -pub const L1_CORE_CONTRACT_ADDRESS: &str = "L1_CORE_CONTRACT_ADDRESS"; - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct EthereumSettlementConfig { - pub rpc_url: Url, - pub core_contract_address: String, -} - -impl EthereumSettlementConfig { - pub fn new_with_settings(settings: &impl Settings) -> Self { - let rpc_url = settings.get_settings_or_panic(SETTLEMENT_RPC_URL); - let rpc_url = Url::from_str(&rpc_url).unwrap_or_else(|_| panic!("Failed to parse {}", SETTLEMENT_RPC_URL)); - let core_contract_address = settings.get_settings_or_panic(L1_CORE_CONTRACT_ADDRESS); - Self { rpc_url, core_contract_address } - } -} diff --git a/crates/settlement-clients/ethereum/src/lib.rs b/crates/settlement-clients/ethereum/src/lib.rs index f7c4cf5c..55cc3fe9 100644 --- a/crates/settlement-clients/ethereum/src/lib.rs +++ b/crates/settlement-clients/ethereum/src/lib.rs @@ -23,16 +23,14 @@ use color_eyre::eyre::{bail, eyre, Ok}; use color_eyre::Result; use conversion::{get_input_data_for_eip_4844, prepare_sidecar}; use settlement_client_interface::{SettlementClient, SettlementVerificationStatus}; -#[cfg(feature = "testing")] use url::Url; +#[cfg(feature = "testing")] use utils::env_utils::get_env_var_or_panic; use crate::clients::interfaces::validity_interface::StarknetValidityContractTrait; use crate::clients::StarknetValidityContractClient; -use crate::config::EthereumSettlementConfig; use crate::conversion::{slice_u8_to_u256, vec_u8_32_to_vec_u256}; pub mod clients; -pub mod config; pub mod conversion; pub mod tests; pub mod types; @@ -42,11 +40,10 @@ use lazy_static::lazy_static; use mockall::automock; use reqwest::Client; use tokio::time::sleep; -use utils::settings::Settings; use crate::types::{bytes_be_to_u128, convert_stark_bigint_to_u256}; -pub const ENV_PRIVATE_KEY: &str = "ETHEREUM_PRIVATE_KEY"; +pub const ENV_PRIVATE_KEY: &str = "MADARA_ORCHESTRATOR_ETHEREUM_PRIVATE_KEY"; const X_0_POINT_OFFSET: usize = 10; const Y_LOW_POINT_OFFSET: usize = 14; const Y_HIGH_POINT_OFFSET: usize = Y_LOW_POINT_OFFSET + 1; @@ -64,6 +61,17 @@ lazy_static! { .expect("Error loading trusted setup file"); } +#[derive(Clone, Debug)] +pub struct EthereumSettlementValidatedArgs { + pub ethereum_rpc_url: Url, + + pub ethereum_private_key: String, + + pub l1_core_contract_address: Address, + + pub starknet_operator_address: Address, +} + #[allow(dead_code)] pub struct EthereumSettlementClient { core_contract_client: StarknetValidityContractClient, @@ -74,34 +82,31 @@ pub struct EthereumSettlementClient { } impl EthereumSettlementClient { - pub fn new_with_settings(settings: &impl Settings) -> Self { - let settlement_cfg = EthereumSettlementConfig::new_with_settings(settings); - let private_key = get_env_var_or_panic(ENV_PRIVATE_KEY); + pub fn new_with_args(settlement_cfg: &EthereumSettlementValidatedArgs) -> Self { + let private_key = settlement_cfg.ethereum_private_key.clone(); let signer: PrivateKeySigner = private_key.parse().expect("Failed to parse private key"); let wallet_address = signer.address(); let wallet = EthereumWallet::from(signer); // provider without wallet - let provider = Arc::new(ProviderBuilder::new().on_http(settlement_cfg.rpc_url.clone())); + let provider = Arc::new(ProviderBuilder::new().on_http(settlement_cfg.ethereum_rpc_url.clone())); // provider with wallet let filler_provider = Arc::new( - ProviderBuilder::new().with_recommended_fillers().wallet(wallet.clone()).on_http(settlement_cfg.rpc_url), + ProviderBuilder::new() + .with_recommended_fillers() + .wallet(wallet.clone()) + .on_http(settlement_cfg.ethereum_rpc_url.clone()), ); - let core_contract_client = StarknetValidityContractClient::new( - Address::from_str(&settlement_cfg.core_contract_address) - .expect("Failed to convert the validity contract address.") - .0 - .into(), - filler_provider, - ); + let core_contract_client = + StarknetValidityContractClient::new(settlement_cfg.l1_core_contract_address, filler_provider); EthereumSettlementClient { provider, core_contract_client, wallet, wallet_address, impersonate_account: None } } #[cfg(feature = "testing")] - pub fn with_test_settings( + pub fn with_test_params( provider: RootProvider>, core_contract_address: Address, rpc_url: Url, diff --git a/crates/settlement-clients/ethereum/src/tests/mod.rs b/crates/settlement-clients/ethereum/src/tests/mod.rs index a61bdcb4..bbd4a8e5 100644 --- a/crates/settlement-clients/ethereum/src/tests/mod.rs +++ b/crates/settlement-clients/ethereum/src/tests/mod.rs @@ -3,12 +3,11 @@ use std::path::PathBuf; use std::str::FromStr; use alloy::node_bindings::{Anvil, AnvilInstance}; +use alloy::primitives::Address; use alloy::providers::ext::AnvilApi; use alloy::providers::ProviderBuilder; use alloy::sol; -use alloy_primitives::Address; use utils::env_utils::get_env_var_or_panic; - // Using the Pipe trait to write chained operations easier #[allow(dead_code)] trait Pipe: Sized { @@ -32,13 +31,13 @@ lazy_static! { .to_str() .expect("Path contains invalid Unicode") .to_string(); - static ref ETH_RPC: String = get_env_var_or_panic("SETTLEMENT_RPC_URL"); - pub static ref STARKNET_OPERATOR_ADDRESS: Address = - Address::from_str(get_env_var_or_panic("STARKNET_OPERATOR_ADDRESS").as_str()) - .expect("Could not parse STARKNET_OPERATOR_ADDRESS"); + static ref ETH_RPC: String = get_env_var_or_panic("MADARA_ORCHESTRATOR_ETHEREUM_SETTLEMENT_RPC_URL"); + pub static ref MADARA_ORCHESTRATOR_STARKNET_OPERATOR_ADDRESS: Address = + Address::from_str(get_env_var_or_panic("MADARA_ORCHESTRATOR_STARKNET_OPERATOR_ADDRESS").as_str()) + .expect("Could not parse MADARA_ORCHESTRATOR_STARKNET_OPERATOR_ADDRESS"); static ref STARKNET_CORE_CONTRACT_ADDRESS: Address = - Address::from_str(get_env_var_or_panic("L1_CORE_CONTRACT_ADDRESS").as_str()) - .expect("Could not parse L1_CORE_CONTRACT_ADDRESS"); + Address::from_str(get_env_var_or_panic("MADARA_ORCHESTRATOR_L1_CORE_CONTRACT_ADDRESS").as_str()) + .expect("Could not parse MADARA_ORCHESTRATOR_L1_CORE_CONTRACT_ADDRESS"); pub static ref TEST_NONCE: u64 = 666068; } @@ -127,6 +126,7 @@ mod settlement_client_tests { use std::time::Duration; use alloy::eips::eip4844::BYTES_PER_BLOB; + use alloy::primitives::Address; use alloy::providers::Provider; use alloy::sol_types::private::U256; use alloy_primitives::FixedBytes; @@ -134,15 +134,16 @@ mod settlement_client_tests { use rstest::rstest; use settlement_client_interface::{SettlementClient, SettlementVerificationStatus}; use tokio::time::sleep; + use utils::env_utils::get_env_var_or_panic; use super::{BLOCK_TIME, ENV_FILE_PATH}; use crate::conversion::to_padded_hex; use crate::tests::{ - DummyCoreContract, EthereumTestBuilder, Pipe, CURRENT_PATH, STARKNET_CORE_CONTRACT, - STARKNET_CORE_CONTRACT_ADDRESS, STARKNET_OPERATOR_ADDRESS, + DummyCoreContract, EthereumTestBuilder, Pipe, CURRENT_PATH, MADARA_ORCHESTRATOR_STARKNET_OPERATOR_ADDRESS, + STARKNET_CORE_CONTRACT, STARKNET_CORE_CONTRACT_ADDRESS, }; use crate::types::{bytes_be_to_u128, convert_stark_bigint_to_u256}; - use crate::{EthereumSettlementClient, Y_HIGH_POINT_OFFSET, Y_LOW_POINT_OFFSET}; + use crate::{EthereumSettlementClient, EthereumSettlementValidatedArgs, Y_HIGH_POINT_OFFSET, Y_LOW_POINT_OFFSET}; #[rstest] #[tokio::test] @@ -153,14 +154,29 @@ mod settlement_client_tests { /// And hence to test the signature and transaction via a dummy contract that has same function /// selector as `updateStateKzgDa`. and anvil is for testing on fork Eth. async fn update_state_blob_with_dummy_contract_works() { + dotenvy::from_filename(&*ENV_FILE_PATH).expect("Could not load .env.test file."); + let setup = EthereumTestBuilder::new().build().await; + let ethereum_settlement_params = EthereumSettlementValidatedArgs { + ethereum_rpc_url: setup.rpc_url, + ethereum_private_key: get_env_var_or_panic("MADARA_ORCHESTRATOR_ETHEREUM_PRIVATE_KEY"), + l1_core_contract_address: Address::from_str(&get_env_var_or_panic( + "MADARA_ORCHESTRATOR_L1_CORE_CONTRACT_ADDRESS", + )) + .expect("Invalid L1 core contract address"), + starknet_operator_address: Address::from_str(&get_env_var_or_panic( + "MADARA_ORCHESTRATOR_STARKNET_OPERATOR_ADDRESS", + )) + .expect("Invalid Starknet operator address"), + }; + // Deploying a dummy contract let contract = DummyCoreContract::deploy(&setup.provider).await.expect("Unable to deploy address"); - let ethereum_settlement_client = EthereumSettlementClient::with_test_settings( + let ethereum_settlement_client = EthereumSettlementClient::with_test_params( setup.provider.clone(), *contract.address(), - setup.rpc_url, + ethereum_settlement_params.ethereum_rpc_url, None, ); @@ -213,23 +229,38 @@ mod settlement_client_tests { /// contract Here signature checks are bypassed and anvil is for testing on fork Eth. async fn update_state_blob_with_impersonation_works(#[case] fork_block_no: u64) { dotenvy::from_filename(&*ENV_FILE_PATH).expect("Could not load .env.test file."); + let setup = EthereumTestBuilder::new() .with_fork_block(fork_block_no) - .with_impersonator(*STARKNET_OPERATOR_ADDRESS) + .with_impersonator(*MADARA_ORCHESTRATOR_STARKNET_OPERATOR_ADDRESS) .build() .await; - let ethereum_settlement_client = EthereumSettlementClient::with_test_settings( + + let ethereum_settlement_params = EthereumSettlementValidatedArgs { + ethereum_rpc_url: setup.rpc_url, + ethereum_private_key: get_env_var_or_panic("MADARA_ORCHESTRATOR_ETHEREUM_PRIVATE_KEY"), + l1_core_contract_address: Address::from_str(&get_env_var_or_panic( + "MADARA_ORCHESTRATOR_L1_CORE_CONTRACT_ADDRESS", + )) + .expect("Invalid L1 core contract address"), + starknet_operator_address: Address::from_str(&get_env_var_or_panic( + "MADARA_ORCHESTRATOR_STARKNET_OPERATOR_ADDRESS", + )) + .expect("Invalid Starknet operator address"), + }; + + let ethereum_settlement_client = EthereumSettlementClient::with_test_params( setup.provider.clone(), - *STARKNET_CORE_CONTRACT_ADDRESS, - setup.rpc_url, - Some(*STARKNET_OPERATOR_ADDRESS), + ethereum_settlement_params.l1_core_contract_address, + ethereum_settlement_params.ethereum_rpc_url, + Some(ethereum_settlement_params.starknet_operator_address), ); // let nonce = ethereum_settlement_client.get_nonce().await.expect("Unable to fetch nonce"); let nonce = setup .provider - .get_transaction_count(*STARKNET_OPERATOR_ADDRESS) + .get_transaction_count(*MADARA_ORCHESTRATOR_STARKNET_OPERATOR_ADDRESS) .await .unwrap() .to_string() @@ -274,11 +305,26 @@ mod settlement_client_tests { #[tokio::test] #[case::typical(6806847)] async fn get_last_settled_block_typical_works(#[case] fork_block_no: u64) { + dotenvy::from_filename(&*ENV_FILE_PATH).expect("Could not load .env.test file."); let setup = EthereumTestBuilder::new().with_fork_block(fork_block_no).build().await; - let ethereum_settlement_client = EthereumSettlementClient::with_test_settings( + + let ethereum_settlement_params = EthereumSettlementValidatedArgs { + ethereum_rpc_url: setup.rpc_url, + ethereum_private_key: get_env_var_or_panic("MADARA_ORCHESTRATOR_ETHEREUM_PRIVATE_KEY"), + l1_core_contract_address: Address::from_str(&get_env_var_or_panic( + "MADARA_ORCHESTRATOR_L1_CORE_CONTRACT_ADDRESS", + )) + .expect("Invalid L1 core contract address"), + starknet_operator_address: Address::from_str(&get_env_var_or_panic( + "MADARA_ORCHESTRATOR_STARKNET_OPERATOR_ADDRESS", + )) + .expect("Invalid Starknet operator address"), + }; + + let ethereum_settlement_client = EthereumSettlementClient::with_test_params( setup.provider.clone(), - *STARKNET_CORE_CONTRACT_ADDRESS, - setup.rpc_url, + ethereum_settlement_params.l1_core_contract_address, + ethereum_settlement_params.ethereum_rpc_url, None, ); assert_eq!( diff --git a/crates/settlement-clients/starknet/Cargo.toml b/crates/settlement-clients/starknet/Cargo.toml index 861df735..bfd9d5a4 100644 --- a/crates/settlement-clients/starknet/Cargo.toml +++ b/crates/settlement-clients/starknet/Cargo.toml @@ -4,6 +4,8 @@ version.workspace = true edition.workspace = true [dependencies] +alloy = { workspace = true, features = ["full", "node-bindings"] } +alloy-primitives = { workspace = true, default-features = false } appchain-core-contract-client = { workspace = true } async-trait = { workspace = true } c-kzg = { workspace = true } diff --git a/crates/settlement-clients/starknet/src/config.rs b/crates/settlement-clients/starknet/src/config.rs index f7f595c9..8b137891 100644 --- a/crates/settlement-clients/starknet/src/config.rs +++ b/crates/settlement-clients/starknet/src/config.rs @@ -1,33 +1 @@ -use std::str::FromStr; -use serde::{Deserialize, Serialize}; -use url::Url; -use utils::env_utils::get_env_var_or_default; -use utils::settings::Settings; - -pub const ENV_STARKNET_RPC_URL: &str = "STARKNET_RPC_URL"; -pub const ENV_CORE_CONTRACT_ADDRESS: &str = "STARKNET_CAIRO_CORE_CONTRACT_ADDRESS"; - -pub const ENV_STARKNET_FINALITY_RETRY_DELAY_IN_SECS: &str = "STARKNET_FINALITY_RETRY_WAIT_IN_SECS"; -pub const DEFAULT_FINALITY_RETRY_DELAY: &str = "60"; - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct StarknetSettlementConfig { - pub rpc_url: Url, - pub core_contract_address: String, - pub tx_finality_retry_delay_in_seconds: u64, -} - -impl StarknetSettlementConfig { - /// Should create a new instance of the DaConfig from the environment variables - pub fn new_with_settings(settings: &impl Settings) -> Self { - let rpc_url = settings.get_settings_or_panic(ENV_STARKNET_RPC_URL); - let rpc_url = Url::from_str(&rpc_url).unwrap_or_else(|_| panic!("Failed to parse {}", ENV_STARKNET_RPC_URL)); - let core_contract_address = settings.get_settings_or_panic(ENV_CORE_CONTRACT_ADDRESS); - let tx_finality_retry_delay_in_seconds: u64 = - get_env_var_or_default(ENV_STARKNET_FINALITY_RETRY_DELAY_IN_SECS, DEFAULT_FINALITY_RETRY_DELAY) - .parse() - .expect("STARKNET_FINALITY_RETRY_WAIT_IN_SECS should be a delay in seconds"); - StarknetSettlementConfig { rpc_url, core_contract_address, tx_finality_retry_delay_in_seconds } - } -} diff --git a/crates/settlement-clients/starknet/src/lib.rs b/crates/settlement-clients/starknet/src/lib.rs index c3bae338..ade0d17b 100644 --- a/crates/settlement-clients/starknet/src/lib.rs +++ b/crates/settlement-clients/starknet/src/lib.rs @@ -22,9 +22,7 @@ use starknet::providers::jsonrpc::HttpTransport; use starknet::providers::{JsonRpcClient, Provider}; use starknet::signers::{LocalWallet, SigningKey}; use tokio::time::{sleep, Duration}; -use utils::settings::Settings; -use crate::config::StarknetSettlementConfig; use crate::conversion::{slice_slice_u8_to_vec_field, slice_u8_to_field, u64_from_felt}; pub type LocalWalletSignerMiddleware = Arc>, LocalWallet>>; @@ -36,30 +34,39 @@ pub struct StarknetSettlementClient { pub tx_finality_retry_delay_in_seconds: u64, } -pub const ENV_ACCOUNT_ADDRESS: &str = "STARKNET_ACCOUNT_ADDRESS"; -pub const ENV_PRIVATE_KEY: &str = "STARKNET_PRIVATE_KEY"; +pub const ENV_ACCOUNT_ADDRESS: &str = "MADARA_ORCHESTRATOR_STARKNET_ACCOUNT_ADDRESS"; +pub const ENV_PRIVATE_KEY: &str = "MADARA_ORCHESTRATOR_STARKNET_PRIVATE_KEY"; const MAX_RETRIES_VERIFY_TX_FINALITY: usize = 10; -// Assumed the contract called for settlement l ooks like: +use url::Url; +#[derive(Clone, Debug)] +pub struct StarknetSettlementValidatedArgs { + pub starknet_rpc_url: Url, + pub starknet_private_key: String, + pub starknet_account_address: String, + pub starknet_cairo_core_contract_address: String, + pub starknet_finality_retry_wait_in_secs: u64, +} + +// Assumed the contract called for settlement looks like: // https://github.com/keep-starknet-strange/piltover impl StarknetSettlementClient { - pub async fn new_with_settings(settings: &impl Settings) -> Self { - let settlement_cfg = StarknetSettlementConfig::new_with_settings(settings); + pub async fn new_with_args(settlement_cfg: &StarknetSettlementValidatedArgs) -> Self { let provider: Arc> = - Arc::new(JsonRpcClient::new(HttpTransport::new(settlement_cfg.rpc_url.clone()))); + Arc::new(JsonRpcClient::new(HttpTransport::new(settlement_cfg.starknet_rpc_url.clone()))); - let public_key = settings.get_settings_or_panic(ENV_ACCOUNT_ADDRESS); + let public_key = settlement_cfg.starknet_account_address.clone().to_string(); let signer_address = Felt::from_hex(&public_key).expect("invalid signer address"); // TODO: Very insecure way of building the signer. Needs to be adjusted. - let private_key = settings.get_settings_or_panic(ENV_PRIVATE_KEY); + let private_key = settlement_cfg.starknet_private_key.clone(); let signer = Felt::from_hex(&private_key).expect("Invalid private key"); let signer = LocalWallet::from(SigningKey::from_secret_scalar(signer)); - let core_contract_address = - Felt::from_hex(&settlement_cfg.core_contract_address).expect("Invalid core contract address"); + let core_contract_address = Felt::from_hex(&settlement_cfg.starknet_cairo_core_contract_address.to_string()) + .expect("Invalid core contract address"); let account: Arc>, LocalWallet>> = Arc::new(SingleOwnerAccount::new( @@ -77,7 +84,7 @@ impl StarknetSettlementClient { account, core_contract_address, starknet_core_contract_client, - tx_finality_retry_delay_in_seconds: settlement_cfg.tx_finality_retry_delay_in_seconds, + tx_finality_retry_delay_in_seconds: settlement_cfg.starknet_finality_retry_wait_in_secs, } } } diff --git a/crates/settlement-clients/starknet/src/tests/setup.rs b/crates/settlement-clients/starknet/src/tests/setup.rs index ae75a622..ec68dc53 100644 --- a/crates/settlement-clients/starknet/src/tests/setup.rs +++ b/crates/settlement-clients/starknet/src/tests/setup.rs @@ -153,7 +153,7 @@ impl MadaraCmdBuilder { } pub fn run(self) -> MadaraCmd { - let target_bin = env::var("MADARA_BINARY_PATH").expect("failed to get binary path"); + let target_bin = env::var("MADARA_ORCHESTRATOR_MADARA_BINARY_PATH").expect("failed to get binary path"); let target_bin = PathBuf::from(target_bin); if !target_bin.exists() { diff --git a/crates/settlement-clients/starknet/src/tests/test.rs b/crates/settlement-clients/starknet/src/tests/test.rs index f5eedad2..d4860e40 100644 --- a/crates/settlement-clients/starknet/src/tests/test.rs +++ b/crates/settlement-clients/starknet/src/tests/test.rs @@ -17,11 +17,10 @@ use starknet::macros::{felt, selector}; use starknet::providers::jsonrpc::HttpTransport; use starknet::providers::{JsonRpcClient, Provider, ProviderError, Url}; use starknet::signers::{LocalWallet, SigningKey}; -use utils::settings::env::EnvSettingsProvider; -use utils::settings::Settings; +use utils::env_utils::get_env_var_or_panic; use super::setup::{wait_for_cond, MadaraCmd, MadaraCmdBuilder}; -use crate::{LocalWalletSignerMiddleware, StarknetSettlementClient}; +use crate::{LocalWalletSignerMiddleware, StarknetSettlementClient, StarknetSettlementValidatedArgs}; #[fixture] pub async fn spin_up_madara() -> MadaraCmd { @@ -79,16 +78,28 @@ async fn wait_for_tx(account: &LocalWalletSignerMiddleware, transaction_hash: Fe #[fixture] async fn setup(#[future] spin_up_madara: MadaraCmd) -> (LocalWalletSignerMiddleware, MadaraCmd) { let madara_process = spin_up_madara.await; - env::set_var("STARKNET_RPC_URL", madara_process.rpc_url.to_string()); - let env_settings = EnvSettingsProvider::default(); - let rpc_url = Url::parse(&env_settings.get_settings_or_panic("STARKNET_RPC_URL")).unwrap(); + let starknet_settlement_params: StarknetSettlementValidatedArgs = StarknetSettlementValidatedArgs { + starknet_rpc_url: Url::parse(madara_process.rpc_url.as_ref()).unwrap(), + starknet_private_key: get_env_var_or_panic("MADARA_ORCHESTRATOR_STARKNET_PRIVATE_KEY"), + starknet_account_address: get_env_var_or_panic("MADARA_ORCHESTRATOR_STARKNET_ACCOUNT_ADDRESS"), + starknet_cairo_core_contract_address: get_env_var_or_panic( + "MADARA_ORCHESTRATOR_STARKNET_CAIRO_CORE_CONTRACT_ADDRESS", + ), + starknet_finality_retry_wait_in_secs: get_env_var_or_panic( + "MADARA_ORCHESTRATOR_STARKNET_FINALITY_RETRY_WAIT_IN_SECS", + ) + .parse::() + .unwrap(), + }; + + let rpc_url = Url::parse(starknet_settlement_params.starknet_rpc_url.as_ref()).unwrap(); let provider = Arc::new(JsonRpcClient::new(HttpTransport::new(rpc_url))); let signer = LocalWallet::from(SigningKey::from_secret_scalar( - Felt::from_hex(&env_settings.get_settings_or_panic("STARKNET_PRIVATE_KEY")).expect("Invalid private key"), + Felt::from_hex(&starknet_settlement_params.starknet_private_key).expect("Invalid private key"), )); - let address = Felt::from_hex(&env_settings.get_settings_or_panic("STARKNET_ACCOUNT_ADDRESS")).unwrap(); + let address = Felt::from_hex(&starknet_settlement_params.starknet_account_address.to_string()).unwrap(); let chain_id = provider.chain_id().await.unwrap(); let mut account = SingleOwnerAccount::new(provider, signer, address, chain_id, ExecutionEncoding::New); @@ -102,7 +113,23 @@ async fn setup(#[future] spin_up_madara: MadaraCmd) -> (LocalWalletSignerMiddlew #[rstest] #[tokio::test] async fn test_settle(#[future] setup: (LocalWalletSignerMiddleware, MadaraCmd)) { - let (account, _madara_process) = setup.await; + dotenvy::from_filename_override(".env.test").expect("Failed to load the .env file"); + + let (account, madara_process) = setup.await; + + let mut starknet_settlement_params: StarknetSettlementValidatedArgs = StarknetSettlementValidatedArgs { + starknet_rpc_url: madara_process.rpc_url.clone(), + starknet_private_key: get_env_var_or_panic("MADARA_ORCHESTRATOR_STARKNET_PRIVATE_KEY"), + starknet_account_address: get_env_var_or_panic("MADARA_ORCHESTRATOR_STARKNET_ACCOUNT_ADDRESS"), + starknet_cairo_core_contract_address: get_env_var_or_panic( + "MADARA_ORCHESTRATOR_STARKNET_CAIRO_CORE_CONTRACT_ADDRESS", + ), + starknet_finality_retry_wait_in_secs: get_env_var_or_panic( + "MADARA_ORCHESTRATOR_STARKNET_FINALITY_RETRY_WAIT_IN_SECS", + ) + .parse::() + .unwrap(), + }; let project_root = Path::new(env!("CARGO_MANIFEST_DIR")).ancestors().nth(3).unwrap(); let contract_path = project_root.join("crates/settlement-clients/starknet/src/tests/mock_contracts/target/dev"); @@ -126,21 +153,22 @@ async fn test_settle(#[future] setup: (LocalWalletSignerMiddleware, MadaraCmd)) tracing::debug!("declare tx hash {:?}", declare_tx_hash); let is_success = wait_for_tx(&account, declare_tx_hash, Duration::from_secs(2)).await; - assert!(is_success, "Declare trasactiion failed"); + assert!(is_success, "Declare transaction failed"); let contract_factory = ContractFactory::new(flattened_class.class_hash(), account.clone()); let deploy_v1 = contract_factory.deploy_v1(vec![], felt!("1122"), false); let deployed_address = deploy_v1.deployed_address(); - env::set_var("STARKNET_CAIRO_CORE_CONTRACT_ADDRESS", deployed_address.to_hex_string()); + // env::set_var("STARKNET_CAIRO_CORE_CONTRACT_ADDRESS", deployed_address.to_hex_string()); + starknet_settlement_params.starknet_cairo_core_contract_address = deployed_address.to_hex_string(); + let InvokeTransactionResult { transaction_hash: deploy_tx_hash } = deploy_v1.send().await.expect("Unable to deploy contract"); let is_success = wait_for_tx(&account, deploy_tx_hash, Duration::from_secs(2)).await; assert!(is_success, "Deploy trasaction failed"); - let env_settings = EnvSettingsProvider {}; - let settlement_client = StarknetSettlementClient::new_with_settings(&env_settings).await; + let settlement_client = StarknetSettlementClient::new_with_args(&starknet_settlement_params).await; let onchain_data_hash = [1; 32]; let mut program_output = Vec::with_capacity(32); program_output.fill(onchain_data_hash); diff --git a/crates/utils/Cargo.toml b/crates/utils/Cargo.toml index 59a5f9c5..ab351dee 100644 --- a/crates/utils/Cargo.toml +++ b/crates/utils/Cargo.toml @@ -8,8 +8,9 @@ edition.workspace = true [dependencies] color-eyre = { workspace = true } serde.workspace = true +serde_json = { workspace = true } thiserror.workspace = true - +url = { workspace = true } #Instrumentation opentelemetry = { workspace = true, features = ["metrics", "logs"] } opentelemetry-appender-tracing = { workspace = true, default-features = false } diff --git a/crates/utils/src/env_utils.rs b/crates/utils/src/env_utils.rs index e62d32ce..11771f08 100644 --- a/crates/utils/src/env_utils.rs +++ b/crates/utils/src/env_utils.rs @@ -14,6 +14,8 @@ pub fn get_env_var_or_default(key: &str, default: &str) -> String { pub fn get_env_var_optional(key: &str) -> Result, VarError> { match get_env_var(key) { + // if value is empty string, return None + Ok(s) if s.is_empty() => Ok(None), Ok(s) => Ok(Some(s)), Err(VarError::NotPresent) => Ok(None), Err(e) => Err(e), diff --git a/crates/utils/src/lib.rs b/crates/utils/src/lib.rs index e374f6c5..4d922e85 100644 --- a/crates/utils/src/lib.rs +++ b/crates/utils/src/lib.rs @@ -1,7 +1,6 @@ pub mod collections; pub mod env_utils; pub mod metrics; -pub mod settings; /// Evaluate `$x:expr` and if not true return `Err($y:expr)`. /// diff --git a/crates/utils/src/settings/env.rs b/crates/utils/src/settings/env.rs deleted file mode 100644 index 6a5962f0..00000000 --- a/crates/utils/src/settings/env.rs +++ /dev/null @@ -1,11 +0,0 @@ -use crate::env_utils::get_env_var_or_panic; -use crate::settings::Settings; - -#[derive(Debug, Clone, Default)] -pub struct EnvSettingsProvider {} - -impl Settings for EnvSettingsProvider { - fn get_settings_or_panic(&self, name: &'static str) -> String { - get_env_var_or_panic(name) - } -} diff --git a/crates/utils/src/settings/mod.rs b/crates/utils/src/settings/mod.rs deleted file mode 100644 index b6fa3bba..00000000 --- a/crates/utils/src/settings/mod.rs +++ /dev/null @@ -1,11 +0,0 @@ -pub mod env; - -#[derive(Debug, thiserror::Error)] -pub enum SettingsProviderError { - #[error("Internal settings error: {0}")] - Internal(#[source] Box), -} - -pub trait Settings: Send { - fn get_settings_or_panic(&self, name: &'static str) -> String; -} diff --git a/docker-compose.yml b/docker-compose.yml index e82ac9c1..e4b1fd07 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,17 +2,17 @@ services: app: build: . ports: - - "${PORT}:3000" + - "${MADARA_ORCHESTRATOR_PORT}:3000" environment: - - HOST=${HOST:-127.0.0.1} - - PORT=${PORT:-3000} + - MADARA_ORCHESTRATOR_HOST=${MADARA_ORCHESTRATOR_HOST:-127.0.0.1} + - MADARA_ORCHESTRATOR_PORT=${MADARA_ORCHESTRATOR_PORT:-3000} - AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} - AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY} - AWS_REGION=${AWS_REGION:-us-east-1} - AWS_ENDPOINT_URL=${AWS_ENDPOINT_URL} - AWS_DEFAULT_REGION=${AWS_DEFAULT_REGION:-localhost} - DATA_STORAGE=${DATA_STORAGE:-s3} - - AWS_S3_BUCKET_NAME=${AWS_S3_BUCKET_NAME} + - MADARA_ORCHESTRATOR_AWS_S3_BUCKET_NAME=${MADARA_ORCHESTRATOR_AWS_S3_BUCKET_NAME} - QUEUE_PROVIDER=${QUEUE_PROVIDER:-sqs} - SQS_SNOS_JOB_PROCESSING_QUEUE_URL=${SQS_SNOS_JOB_PROCESSING_QUEUE_URL} - SQS_SNOS_JOB_VERIFICATION_QUEUE_URL=${SQS_SNOS_JOB_VERIFICATION_QUEUE_URL} @@ -27,32 +27,32 @@ services: - SQS_JOB_HANDLE_FAILURE_QUEUE_URL=${SQS_JOB_HANDLE_FAILURE_QUEUE_URL} - SQS_WORKER_TRIGGER_QUEUE_URL=${SQS_WORKER_TRIGGER_QUEUE_URL} - ALERTS=${ALERTS:-sns} - - AWS_SNS_ARN=${AWS_SNS_ARN} - - AWS_SNS_ARN_NAME=${AWS_SNS_ARN_NAME} + - MADARA_ORCHESTRATOR_AWS_SNS_ARN=${MADARA_ORCHESTRATOR_AWS_SNS_ARN} + - MADARA_ORCHESTRATOR_AWS_SNS_ARN=${MADARA_ORCHESTRATOR_AWS_SNS_ARN} - DATABASE=${DATABASE:-mongodb} - - MONGODB_CONNECTION_STRING=${MONGODB_CONNECTION_STRING} + - MADARA_ORCHESTRATOR_MONGODB_CONNECTION_URL=${MADARA_ORCHESTRATOR_MONGODB_CONNECTION_URL} - PROVER_SERVICE=${PROVER_SERVICE:-sharp} - - SHARP_CUSTOMER_ID=${SHARP_CUSTOMER_ID} - - SHARP_URL=${SHARP_URL} - - SHARP_USER_CRT=${SHARP_USER_CRT} - - SHARP_USER_KEY=${SHARP_USER_KEY} - - SHARP_SERVER_CRT=${SHARP_SERVER_CRT} - - SHARP_PROOF_LAYOUT=${SHARP_PROOF_LAYOUT:-small} + - MADARA_ORCHESTRATOR_SHARP_CUSTOMER_ID=${MADARA_ORCHESTRATOR_SHARP_CUSTOMER_ID} + - MADARA_ORCHESTRATOR_SHARP_URL=${MADARA_ORCHESTRATOR_SHARP_URL} + - MADARA_ORCHESTRATOR_SHARP_USER_CRT=${MADARA_ORCHESTRATOR_SHARP_USER_CRT} + - MADARA_ORCHESTRATOR_SHARP_USER_KEY=${MADARA_ORCHESTRATOR_SHARP_USER_KEY} + - MADARA_ORCHESTRATOR_SHARP_SERVER_CRT=${MADARA_ORCHESTRATOR_SHARP_SERVER_CRT} + - MADARA_ORCHESTRATOR_SHARP_PROOF_LAYOUT=${MADARA_ORCHESTRATOR_SHARP_PROOF_LAYOUT:-small} - DA_LAYER=${DA_LAYER:-ethereum} - SETTLEMENT_LAYER=${SETTLEMENT_LAYER:-ethereum} - - SETTLEMENT_RPC_URL=${SETTLEMENT_RPC_URL} - - MADARA_RPC_URL=${MADARA_RPC_URL} + - MADARA_ORCHESTRATOR_ETHEREUM_SETTLEMENT_RPC_URL=${MADARA_ORCHESTRATOR_ETHEREUM_SETTLEMENT_RPC_URL} + - MADARA_ORCHESTRATOR_MADARA_RPC_URL=${MADARA_ORCHESTRATOR_MADARA_RPC_URL} - MEMORY_PAGES_CONTRACT_ADDRESS=${MEMORY_PAGES_CONTRACT_ADDRESS} - - ETHEREUM_PRIVATE_KEY=${ETHEREUM_PRIVATE_KEY} - - L1_CORE_CONTRACT_ADDRESS=${L1_CORE_CONTRACT_ADDRESS} - - RPC_FOR_SNOS=${RPC_FOR_SNOS} - - STARKNET_PRIVATE_KEY=${STARKNET_PRIVATE_KEY} - - STARKNET_ACCOUNT_ADDRESS=${STARKNET_ACCOUNT_ADDRESS} - - MADARA_BINARY_PATH=${MADARA_BINARY_PATH} - - OTEL_SERVICE_NAME=${OTEL_SERVICE_NAME:-madara_orchestrator} - - OTEL_COLLECTOR_ENDPOINT=${OTEL_COLLECTOR_ENDPOINT} + - MADARA_ORCHESTRATOR_ETHEREUM_PRIVATE_KEY=${MADARA_ORCHESTRATOR_ETHEREUM_PRIVATE_KEY} + - MADARA_ORCHESTRATOR_L1_CORE_CONTRACT_ADDRESS=${MADARA_ORCHESTRATOR_L1_CORE_CONTRACT_ADDRESS} + - MADARA_ORCHESTRATOR_RPC_FOR_SNOS=${MADARA_ORCHESTRATOR_RPC_FOR_SNOS} + - MADARA_ORCHESTRATOR_STARKNET_PRIVATE_KEY=${MADARA_ORCHESTRATOR_STARKNET_PRIVATE_KEY} + - MADARA_ORCHESTRATOR_STARKNET_ACCOUNT_ADDRESS=${MADARA_ORCHESTRATOR_STARKNET_ACCOUNT_ADDRESS} + - MADARA_ORCHESTRATOR_MADARA_BINARY_PATH=${MADARA_ORCHESTRATOR_MADARA_BINARY_PATH} + - MADARA_ORCHESTRATOR_OTEL_SERVICE_NAME=${MADARA_ORCHESTRATOR_OTEL_SERVICE_NAME:-madara_orchestrator} + - MADARA_ORCHESTRATOR_OTEL_COLLECTOR_ENDPOINT=${MADARA_ORCHESTRATOR_OTEL_COLLECTOR_ENDPOINT} - TRACING_LEVEL=${TRACING_LEVEL:-info} - - STARKNET_OPERATOR_ADDRESS=${STARKNET_OPERATOR_ADDRESS} + - MADARA_ORCHESTRATOR_STARKNET_OPERATOR_ADDRESS=${MADARA_ORCHESTRATOR_STARKNET_OPERATOR_ADDRESS} depends_on: - mongodb - localstack diff --git a/e2e-tests/Cargo.toml b/e2e-tests/Cargo.toml index f7396b77..547301a1 100644 --- a/e2e-tests/Cargo.toml +++ b/e2e-tests/Cargo.toml @@ -13,6 +13,7 @@ aws-sdk-s3.workspace = true aws-sdk-sqs.workspace = true bytes.workspace = true chrono = { workspace = true } +clap.workspace = true color-eyre.workspace = true dotenvy.workspace = true ethereum-settlement-client.workspace = true @@ -25,6 +26,8 @@ rstest.workspace = true serde.workspace = true serde_json.workspace = true starknet.workspace = true +strum = { workspace = true } +strum_macros = { workspace = true } testcontainers.workspace = true tokio = { workspace = true, features = ["full"] } tokio-stream.workspace = true diff --git a/e2e-tests/src/anvil.rs b/e2e-tests/src/anvil.rs index 5d939f88..0a5ff759 100644 --- a/e2e-tests/src/anvil.rs +++ b/e2e-tests/src/anvil.rs @@ -50,8 +50,9 @@ impl AnvilSetup { } pub async fn deploy_contracts(&self) -> (Address, Address) { - let wallet = - EthereumWallet::from(PrivateKeySigner::from_str(&get_env_var_or_panic("ETHEREUM_PRIVATE_KEY")).unwrap()); + let wallet = EthereumWallet::from( + PrivateKeySigner::from_str(&get_env_var_or_panic("MADARA_ORCHESTRATOR_ETHEREUM_PRIVATE_KEY")).unwrap(), + ); let provider = ProviderBuilder::new().with_recommended_fillers().wallet(wallet).on_http(self.rpc_url.clone()); let starknet_core_contract_client = StarknetCoreContract::deploy(&provider).await.unwrap(); diff --git a/e2e-tests/src/lib.rs b/e2e-tests/src/lib.rs index ea1c326f..a68276c8 100644 --- a/e2e-tests/src/lib.rs +++ b/e2e-tests/src/lib.rs @@ -1,5 +1,4 @@ pub mod anvil; -pub mod localstack; pub mod mock_server; pub mod mongodb; pub mod node; diff --git a/e2e-tests/src/localstack.rs b/e2e-tests/src/localstack.rs deleted file mode 100644 index c93d6418..00000000 --- a/e2e-tests/src/localstack.rs +++ /dev/null @@ -1,190 +0,0 @@ -use std::collections::HashMap; -use std::sync::Arc; - -use aws_config::environment::EnvironmentVariableCredentialsProvider; -use aws_config::meta::region::RegionProviderChain; -use aws_config::{from_env, Region}; -use aws_sdk_eventbridge::types::{InputTransformer, RuleState, Target}; -use aws_sdk_s3::config::ProvideCredentials; -use aws_sdk_sqs::types::QueueAttributeName; -use aws_sdk_sqs::types::QueueAttributeName::VisibilityTimeout; -use orchestrator::config::ProviderConfig; -use orchestrator::data_storage::aws_s3::AWSS3; -use orchestrator::data_storage::DataStorage; -use orchestrator::queue::job_queue::{ - JobQueueMessage, WorkerTriggerMessage, WorkerTriggerType, DATA_SUBMISSION_JOB_PROCESSING_QUEUE, - DATA_SUBMISSION_JOB_VERIFICATION_QUEUE, JOB_HANDLE_FAILURE_QUEUE, PROOF_REGISTRATION_JOB_PROCESSING_QUEUE, - PROOF_REGISTRATION_JOB_VERIFICATION_QUEUE, PROVING_JOB_PROCESSING_QUEUE, PROVING_JOB_VERIFICATION_QUEUE, - SNOS_JOB_PROCESSING_QUEUE, SNOS_JOB_VERIFICATION_QUEUE, UPDATE_STATE_JOB_PROCESSING_QUEUE, - UPDATE_STATE_JOB_VERIFICATION_QUEUE, WORKER_TRIGGER_QUEUE, -}; -use utils::env_utils::get_env_var_or_panic; -use utils::settings::env::EnvSettingsProvider; - -/// LocalStack struct -pub struct LocalStack { - pub sqs_client: aws_sdk_sqs::Client, - pub s3_client: Box, - event_bridge_client: aws_sdk_eventbridge::Client, -} - -impl LocalStack { - pub async fn new() -> Self { - let region_provider = Region::new(get_env_var_or_panic("AWS_REGION")); - - let creds = EnvironmentVariableCredentialsProvider::new().provide_credentials().await.unwrap(); - let config = from_env().region(region_provider).credentials_provider(creds).load().await; - let provider_config = Arc::new(ProviderConfig::AWS(Box::from(config.clone()))); - - Self { - sqs_client: aws_sdk_sqs::Client::new(&config), - s3_client: Box::new(AWSS3::new_with_settings(&EnvSettingsProvider {}, provider_config).await), - event_bridge_client: aws_sdk_eventbridge::Client::new(&config), - } - } - - #[allow(clippy::borrowed_box)] - pub fn s3_client(&self) -> &Box { - &self.s3_client - } - - /// To set up SQS on localstack instance - pub async fn setup_sqs(&self) -> color_eyre::Result<()> { - let list_queues_output = self.sqs_client.list_queues().send().await?; - let queue_urls = list_queues_output.queue_urls(); - println!("Found {} queues", queue_urls.len()); - for queue_url in queue_urls { - match self.sqs_client.delete_queue().queue_url(queue_url).send().await { - Ok(_) => println!("Successfully deleted queue: {}", queue_url), - Err(e) => eprintln!("Error deleting queue {}: {:?}", queue_url, e), - } - } - - // Creating SQS queues - let mut queue_attributes = HashMap::new(); - queue_attributes.insert(VisibilityTimeout, "10000".into()); - - let queue_names = vec![ - DATA_SUBMISSION_JOB_PROCESSING_QUEUE, - DATA_SUBMISSION_JOB_VERIFICATION_QUEUE, - PROOF_REGISTRATION_JOB_PROCESSING_QUEUE, - PROOF_REGISTRATION_JOB_VERIFICATION_QUEUE, - PROVING_JOB_PROCESSING_QUEUE, - PROVING_JOB_VERIFICATION_QUEUE, - SNOS_JOB_PROCESSING_QUEUE, - SNOS_JOB_VERIFICATION_QUEUE, - UPDATE_STATE_JOB_PROCESSING_QUEUE, - UPDATE_STATE_JOB_VERIFICATION_QUEUE, - JOB_HANDLE_FAILURE_QUEUE, - WORKER_TRIGGER_QUEUE, - ]; - - for queue_name in queue_names { - self.sqs_client - .create_queue() - .queue_name(queue_name) - .set_attributes(Some(queue_attributes.clone())) - .send() - .await?; - } - - println!("๐ŸŒŠ SQS queues creation completed."); - - Ok(()) - } - - /// Event Bridge setup - pub async fn setup_event_bridge(&self, worker_trigger_type: WorkerTriggerType) -> color_eyre::Result<()> { - let rule_name = "worker_trigger_scheduled"; - - self.event_bridge_client - .put_rule() - .name(rule_name) - .schedule_expression("rate(1 minute)") - .state(RuleState::Enabled) - .send() - .await?; - let queue_url = self.sqs_client.get_queue_url().queue_name(WORKER_TRIGGER_QUEUE).send().await?; - - let queue_attributes = self - .sqs_client - .get_queue_attributes() - .queue_url(queue_url.queue_url.unwrap()) - .attribute_names(QueueAttributeName::QueueArn) - .send() - .await?; - let queue_arn = queue_attributes.attributes().unwrap().get(&QueueAttributeName::QueueArn).unwrap(); - - // Create a sample WorkerTriggerMessage - let message = WorkerTriggerMessage { worker: worker_trigger_type.clone() }; - let event_detail = serde_json::to_string(&message)?; - - // Create the EventBridge target with the input transformer - let input_transformer = InputTransformer::builder() - .input_paths_map("$.time", "time") - .input_template(event_detail.to_string()) - .build()?; - - self.event_bridge_client - .put_targets() - .rule(rule_name) - .targets( - Target::builder() - .id(format!("worker-trigger-target-{:?}", worker_trigger_type)) - .arn(queue_arn) - .input_transformer(input_transformer) - .build()?, - ) - .send() - .await?; - - println!("๐ŸŒ‰ Event bridge setup completed. Trigger Type : {:?}", worker_trigger_type); - - Ok(()) - } - - /// Generic function to send message to any of the queues - pub async fn send_message_to_queue(&self, queue_url: &str, message_body: &str) -> color_eyre::Result<()> { - self.sqs_client.send_message().queue_url(queue_url).message_body(message_body).send().await?; - Ok(()) - } - - pub async fn delete_event_bridge_rule(&self, rule_name: &str) -> color_eyre::Result<()> { - let list_targets_output = self.event_bridge_client.list_targets_by_rule().rule(rule_name).send().await; - - match list_targets_output { - Ok(output) => { - let targets = output.targets(); - if !targets.is_empty() { - let target_ids: Vec = targets.iter().map(|t| t.id().to_string()).collect(); - - self.event_bridge_client.remove_targets().rule(rule_name).set_ids(Some(target_ids)).send().await?; - - println!("๐Ÿงน Removed targets from rule: {}", rule_name); - } - - // Step 2: Delete the rule - self.event_bridge_client.delete_rule().name(rule_name).send().await?; - - println!("๐Ÿงน Deleted EventBridge rule: {}", rule_name); - println!("๐Ÿงน Rule deleted successfully."); - - Ok(()) - } - Err(_) => Ok(()), - } - } - - pub async fn put_message_in_queue(&self, message: JobQueueMessage, queue_url: String) -> color_eyre::Result<()> { - let region_provider = RegionProviderChain::default_provider().or_else("us-east-1"); - let config = aws_config::from_env().region(region_provider).load().await; - let client = aws_sdk_sqs::Client::new(&config); - - let rsp = - client.send_message().queue_url(queue_url).message_body(serde_json::to_string(&message)?).send().await?; - - println!("Successfully sent message with ID: {:?}", rsp.message_id()); - - Ok(()) - } -} diff --git a/e2e-tests/src/mock_server.rs b/e2e-tests/src/mock_server.rs index 8597ef0b..aea13ed7 100644 --- a/e2e-tests/src/mock_server.rs +++ b/e2e-tests/src/mock_server.rs @@ -81,7 +81,7 @@ impl MockServerGlobal { then.json_body(return_val); }); - let snos_url = get_env_var_or_panic("RPC_FOR_SNOS"); + let snos_url = get_env_var_or_panic("MADARA_ORCHESTRATOR_RPC_FOR_SNOS"); let snos_host = snos_url.split("://").last().unwrap().split(":").next().unwrap(); let snos_port = snos_url.split("://").last().unwrap().split(":").last().unwrap(); proxy_server.proxy(|rule| { diff --git a/e2e-tests/src/mongodb.rs b/e2e-tests/src/mongodb.rs index ba653653..3f29c1cd 100644 --- a/e2e-tests/src/mongodb.rs +++ b/e2e-tests/src/mongodb.rs @@ -1,19 +1,16 @@ -use std::str::FromStr; - +use orchestrator::database::mongodb::MongoDBValidatedArgs; use url::Url; -use utils::env_utils::get_env_var_or_panic; - #[allow(dead_code)] pub struct MongoDbServer { endpoint: Url, } impl MongoDbServer { - pub async fn run() -> Self { - Self { endpoint: Url::from_str(&get_env_var_or_panic("MONGODB_CONNECTION_STRING")).unwrap() } + pub fn run(mongodb_params: MongoDBValidatedArgs) -> Self { + Self { endpoint: mongodb_params.connection_url } } pub fn endpoint(&self) -> Url { - Url::from_str(&get_env_var_or_panic("MONGODB_CONNECTION_STRING")).unwrap() + self.endpoint.clone() } } diff --git a/e2e-tests/src/node.rs b/e2e-tests/src/node.rs index ef4c626f..0603cb40 100644 --- a/e2e-tests/src/node.rs +++ b/e2e-tests/src/node.rs @@ -3,6 +3,7 @@ use std::process::{Child, Command, ExitStatus, Stdio}; use std::thread; use std::time::Duration; +use strum_macros::Display; use tokio::net::TcpStream; use url::Url; @@ -26,17 +27,25 @@ impl Drop for Orchestrator { } } +#[derive(Display, Debug, Clone, PartialEq, Eq)] +pub enum OrchestratorMode { + #[strum(serialize = "run")] + Run, + #[strum(serialize = "setup")] + Setup, +} impl Orchestrator { - pub fn run(envs: Vec<(String, String)>) -> Self { - let port = get_free_port(); - let address = format!("127.0.0.1:{}", port); + pub fn new(mode: OrchestratorMode, mut envs: Vec<(String, String)>) -> Option { let repository_root = &get_repository_root(); - + let mut address = String::new(); std::env::set_current_dir(repository_root).expect("Failed to change working directory"); - let port_str = format!("{}", port); - let envs = [envs, vec![("PORT".to_string(), port_str)]].concat(); + let is_run_mode = mode == OrchestratorMode::Run; + let mode_str = mode.to_string(); + println!("Running orchestrator in {} mode", mode_str); + + // Configure common command arguments let mut command = Command::new("cargo"); command .arg("run") @@ -45,36 +54,72 @@ impl Orchestrator { .arg("orchestrator") .arg("--features") .arg("testing") - .current_dir(repository_root) - .envs(envs) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()); + .arg(mode_str) + .arg("--aws") + .arg("--aws-s3") + .arg("--aws-sqs") + .arg("--aws-sns"); + + // Add event bridge arg only for setup mode + if is_run_mode { + command.arg("--settle-on-ethereum"); + command.arg("--da-on-ethereum"); + command.arg("--sharp"); + command.arg("--mongodb"); + + let port = get_free_port(); + let addr = format!("127.0.0.1:{}", port); + envs.push(("MADARA_ORCHESTRATOR_PORT".to_string(), port.to_string())); + address = addr; + + command.stdout(Stdio::piped()).stderr(Stdio::piped()); + } else { + command.arg("--aws-event-bridge"); + + // For setup mode, inherit the stdio to show output directly + command.stdout(Stdio::inherit()).stderr(Stdio::inherit()); + } + + command.current_dir(repository_root).envs(envs); let mut process = command.spawn().expect("Failed to start process"); - // Capture and print stdout - let stdout = process.stdout.take().expect("Failed to capture stdout"); - thread::spawn(move || { - let reader = BufReader::new(stdout); - reader.lines().for_each(|line| { - if let Ok(line) = line { - println!("STDOUT: {}", line); - } - }); - }); - - // Capture and print stderr - let stderr = process.stderr.take().expect("Failed to capture stderr"); - thread::spawn(move || { - let reader = BufReader::new(stderr); - reader.lines().for_each(|line| { - if let Ok(line) = line { - eprintln!("STDERR: {}", line); - } + if is_run_mode { + let stdout = process.stdout.take().expect("Failed to capture stdout"); + thread::spawn(move || { + let reader = BufReader::new(stdout); + reader.lines().for_each(|line| { + if let Ok(line) = line { + println!("STDOUT: {}", line); + } + }); }); - }); - Self { process, address } + let stderr = process.stderr.take().expect("Failed to capture stderr"); + thread::spawn(move || { + let reader = BufReader::new(stderr); + reader.lines().for_each(|line| { + if let Ok(line) = line { + eprintln!("STDERR: {}", line); + } + }); + }); + Some(Self { process, address }) + } else { + // Wait for the process to complete and get its exit status + let status = process.wait().expect("Failed to wait for process"); + if status.success() { + println!("Orchestrator cloud setup completed โœ…"); + } else { + // Get the exit code if available + if let Some(code) = status.code() { + println!("Orchestrator cloud setup failed with exit code: {}", code); + } else { + println!("Orchestrator cloud setup terminated by signal"); + } + } + None + } } pub fn endpoint(&self) -> Url { diff --git a/e2e-tests/tests.rs b/e2e-tests/tests.rs index 602e383c..89a63ee4 100644 --- a/e2e-tests/tests.rs +++ b/e2e-tests/tests.rs @@ -3,23 +3,28 @@ use std::fs::File; use std::io::Read; use std::time::{Duration, Instant}; +use aws_config::meta::region::RegionProviderChain; use chrono::{SubsecRound, Utc}; use e2e_tests::anvil::AnvilSetup; -use e2e_tests::localstack::LocalStack; use e2e_tests::mock_server::MockResponseBodyType; use e2e_tests::sharp::SharpClient; use e2e_tests::starknet_client::StarknetClient; use e2e_tests::utils::{get_mongo_db_client, read_state_update_from_file, vec_u8_to_hex_string}; use e2e_tests::{MongoDbServer, Orchestrator}; use mongodb::bson::doc; +use orchestrator::cli::database::DatabaseValidatedArgs; use orchestrator::data_storage::DataStorage; +use orchestrator::database::mongodb::MongoDBValidatedArgs; use orchestrator::jobs::constants::{JOB_METADATA_SNOS_BLOCK, JOB_METADATA_STATE_UPDATE_BLOCKS_TO_SETTLE_KEY}; use orchestrator::jobs::types::{ExternalId, JobItem, JobStatus, JobType}; -use orchestrator::queue::job_queue::{JobQueueMessage, WorkerTriggerType}; +use orchestrator::queue::job_queue::JobQueueMessage; +use orchestrator::queue::sqs::AWSSQSValidatedArgs; +use orchestrator::queue::QueueType; use rstest::rstest; use serde::{Deserialize, Serialize}; use serde_json::json; use starknet::core::types::{Felt, MaybePendingStateUpdate}; +use url::Url; use utils::env_utils::get_env_var_or_panic; use uuid::Uuid; @@ -40,14 +45,21 @@ struct Setup { mongo_db_instance: MongoDbServer, starknet_client: StarknetClient, sharp_client: SharpClient, - env_vector: Vec<(String, String)>, - localstack_instance: LocalStack, + env_vector: HashMap, } impl Setup { /// Initialise a new setup pub async fn new(l2_block_number: String) -> Self { - let mongo_db_instance = MongoDbServer::run().await; + let db_params = DatabaseValidatedArgs::MongoDB(MongoDBValidatedArgs { + connection_url: Url::parse(&get_env_var_or_panic("MADARA_ORCHESTRATOR_MONGODB_CONNECTION_URL")) + .expect("Invalid MongoDB connection URL"), + database_name: get_env_var_or_panic("MADARA_ORCHESTRATOR_DATABASE_NAME"), + }); + + let DatabaseValidatedArgs::MongoDB(mongodb_params) = db_params; + + let mongo_db_instance = MongoDbServer::run(mongodb_params); println!("โœ… Mongo DB setup completed"); let starknet_client = StarknetClient::new(); @@ -60,43 +72,40 @@ impl Setup { let (starknet_core_contract_address, verifier_contract_address) = anvil_setup.deploy_contracts().await; println!("โœ… Anvil setup completed"); - // Setting up LocalStack - let localstack_instance = LocalStack::new().await; - localstack_instance.setup_sqs().await.unwrap(); - localstack_instance.delete_event_bridge_rule("worker_trigger_scheduled").await.unwrap(); - localstack_instance.setup_event_bridge(WorkerTriggerType::Snos).await.unwrap(); - localstack_instance.setup_event_bridge(WorkerTriggerType::Proving).await.unwrap(); - localstack_instance.setup_event_bridge(WorkerTriggerType::DataSubmission).await.unwrap(); - localstack_instance.setup_event_bridge(WorkerTriggerType::UpdateState).await.unwrap(); + let mut env_vec: HashMap = HashMap::new(); - println!("โœ… Localstack instance setup completed"); + let env_vars = dotenvy::vars(); + for (key, value) in env_vars { + env_vec.insert(key, value); + } - let mut env_vec: Vec<(String, String)> = - vec![("MONGODB_CONNECTION_STRING".to_string(), mongo_db_instance.endpoint().to_string())]; + env_vec + .insert("MADARA_ORCHESTRATOR_MONGODB_CONNECTION_URL".to_string(), mongo_db_instance.endpoint().to_string()); // Adding other values to the environment variables vector - env_vec.push(("MADARA_RPC_URL".to_string(), get_env_var_or_panic("RPC_FOR_SNOS"))); - env_vec.push(("SETTLEMENT_RPC_URL".to_string(), anvil_setup.rpc_url.to_string())); - env_vec.push(("SHARP_URL".to_string(), sharp_client.url())); - - // Sharp envs - env_vec.push(("SHARP_CUSTOMER_ID".to_string(), get_env_var_or_panic("SHARP_CUSTOMER_ID"))); - env_vec.push(("SHARP_USER_CRT".to_string(), get_env_var_or_panic("SHARP_USER_CRT"))); - env_vec.push(("SHARP_USER_KEY".to_string(), get_env_var_or_panic("SHARP_USER_KEY"))); - env_vec.push(("SHARP_SERVER_CRT".to_string(), get_env_var_or_panic("SHARP_SERVER_CRT"))); + env_vec.insert("MADARA_ORCHESTRATOR_ETHEREUM_SETTLEMENT_RPC_URL".to_string(), anvil_setup.rpc_url.to_string()); + env_vec.insert("MADARA_ORCHESTRATOR_SHARP_URL".to_string(), sharp_client.url()); // Adding impersonation for operator as our own address here. // As we are using test contracts thus we don't need any impersonation. // But that logic is being used in integration tests so to keep that. We // add this address here. // Anvil.addresses[0] - env_vec - .push(("STARKNET_OPERATOR_ADDRESS".to_string(), "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266".to_string())); - env_vec.push(("GPS_VERIFIER_CONTRACT_ADDRESS".to_string(), verifier_contract_address.to_string())); - env_vec.push(("L1_CORE_CONTRACT_ADDRESS".to_string(), starknet_core_contract_address.to_string())); - env_vec.push(("MAX_BLOCK_TO_PROCESS".to_string(), l2_block_number)); + env_vec.insert( + "MADARA_ORCHESTRATOR_STARKNET_OPERATOR_ADDRESS".to_string(), + "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266".to_string(), + ); + env_vec.insert( + "MADARA_ORCHESTRATOR_GPS_VERIFIER_CONTRACT_ADDRESS".to_string(), + verifier_contract_address.to_string(), + ); + env_vec.insert( + "MADARA_ORCHESTRATOR_L1_CORE_CONTRACT_ADDRESS".to_string(), + starknet_core_contract_address.to_string(), + ); + env_vec.insert("MADARA_ORCHESTRATOR_MAX_BLOCK_NO_TO_PROCESS".to_string(), l2_block_number); - Self { mongo_db_instance, starknet_client, sharp_client, env_vector: env_vec, localstack_instance } + Self { mongo_db_instance, starknet_client, sharp_client, env_vector: env_vec } } pub fn mongo_db_instance(&self) -> &MongoDbServer { @@ -113,11 +122,7 @@ impl Setup { } pub fn envs(&self) -> Vec<(String, String)> { - self.env_vector.clone() - } - - pub fn localstack(&self) -> &LocalStack { - &self.localstack_instance + self.env_vector.iter().map(|(k, v)| (k.clone(), v.clone())).collect() } } @@ -127,16 +132,27 @@ impl Setup { async fn test_orchestrator_workflow(#[case] l2_block_number: String) { // Fetching the env vars from the test env file as these will be used in // setting up of the test and during orchestrator run too. + + use e2e_tests::node::OrchestratorMode; dotenvy::from_filename(".env.test").expect("Failed to load the .env file"); + let queue_params = AWSSQSValidatedArgs { + queue_base_url: Url::parse(&get_env_var_or_panic("MADARA_ORCHESTRATOR_SQS_BASE_QUEUE_URL")) + .expect("Invalid queue base URL"), + sqs_prefix: get_env_var_or_panic("MADARA_ORCHESTRATOR_SQS_PREFIX"), + sqs_suffix: get_env_var_or_panic("MADARA_ORCHESTRATOR_SQS_SUFFIX"), + }; + let mut setup_config = Setup::new(l2_block_number.clone()).await; - // Setup S3 - setup_s3(setup_config.localstack().s3_client()).await.unwrap(); + // Setup Cloud + // Setup orchestrator cloud + Orchestrator::new(OrchestratorMode::Setup, setup_config.envs()); + println!("โœ… Orchestrator cloud setup completed"); // Step 1 : SNOS job runs ========================================= // Updates the job in the db let job_id = put_job_data_in_db_snos(setup_config.mongo_db_instance(), l2_block_number.clone()).await; - put_snos_job_in_processing_queue(setup_config.localstack(), job_id).await.unwrap(); + put_snos_job_in_processing_queue(job_id, queue_params).await.unwrap(); // Step 2: Proving Job ============================================ // Mocking the endpoint @@ -153,9 +169,12 @@ async fn test_orchestrator_workflow(#[case] l2_block_number: String) { println!("โœ… Orchestrator setup completed."); // Run orchestrator - let mut orchestrator = Orchestrator::run(setup_config.envs()); + let mut orchestrator = + Orchestrator::new(OrchestratorMode::Run, setup_config.envs()).expect("Failed to start orchestrator"); orchestrator.wait_till_started().await; + println!("โœ… Orchestrator started"); + // Adding State checks in DB for validation of tests // Check 1 : After Proving Job state (15 mins. approx time) @@ -279,15 +298,37 @@ pub async fn put_job_data_in_db_snos(mongo_db: &MongoDbServer, l2_block_number: /// Adding SNOS job in JOB_PROCESSING_QUEUE so that the job is triggered /// as soon as it is picked up by orchestrator -pub async fn put_snos_job_in_processing_queue(local_stack: &LocalStack, id: Uuid) -> color_eyre::Result<()> { +pub async fn put_snos_job_in_processing_queue(id: Uuid, queue_params: AWSSQSValidatedArgs) -> color_eyre::Result<()> { let message = JobQueueMessage { id }; - local_stack.put_message_in_queue(message, get_env_var_or_panic("SQS_SNOS_JOB_PROCESSING_QUEUE_URL")).await?; + put_message_in_queue( + message, + format!( + "{}/{}_{}_{}", + queue_params.queue_base_url, + queue_params.sqs_prefix, + QueueType::SnosJobProcessing, + queue_params.sqs_suffix + ), + ) + .await?; + Ok(()) +} + +pub async fn put_message_in_queue(message: JobQueueMessage, queue_url: String) -> color_eyre::Result<()> { + let region_provider = RegionProviderChain::default_provider().or_else("us-east-1"); + let config = aws_config::from_env().region(region_provider).load().await; + let client = aws_sdk_sqs::Client::new(&config); + + let rsp = client.send_message().queue_url(queue_url).message_body(serde_json::to_string(&message)?).send().await?; + + println!("โœ… Successfully sent message with ID: {:?}", rsp.message_id()); + Ok(()) } /// Mocks the endpoint for sharp client pub async fn mock_proving_job_endpoint_output(sharp_client: &mut SharpClient) { - // Add job response + // Add job response, let add_job_response = json!( { "code" : "JOB_RECEIVED_SUCCESSFULLY" @@ -437,6 +478,6 @@ pub async fn put_job_data_in_db_proving(mongo_db: &MongoDbServer, l2_block_numbe /// To set up s3 files needed for e2e test (test_orchestrator_workflow) #[allow(clippy::borrowed_box)] pub async fn setup_s3(s3_client: &Box) -> color_eyre::Result<()> { - s3_client.create_bucket(&get_env_var_or_panic("AWS_S3_BUCKET_NAME")).await.unwrap(); + s3_client.create_bucket(&get_env_var_or_panic("MADARA_ORCHESTRATOR_AWS_S3_BUCKET_NAME")).await.unwrap(); Ok(()) } diff --git a/madara-bootstrapper b/madara-bootstrapper index b0b64750..f717bf17 160000 --- a/madara-bootstrapper +++ b/madara-bootstrapper @@ -1 +1 @@ -Subproject commit b0b647500c2ae3e3b0d99e345fa652989bca4726 +Subproject commit f717bf179581da53d68fee03b50ef78e0628ee20 diff --git a/migrate-mongo-config.js b/migrate-mongo-config.js index 9bb33951..52fa1e17 100644 --- a/migrate-mongo-config.js +++ b/migrate-mongo-config.js @@ -3,10 +3,13 @@ const config = { mongodb: { // TODO Change (or review) the url to your MongoDB: - url: process.env.MONGODB_CONNECTION_STRING || "mongodb://localhost:27017", + url: + process.env.MADARA_ORCHESTRATOR_MONGODB_CONNECTION_URLDB_CONNECTION_URL || + "mongodb://localhost:27017", // TODO Change this to your database name: - databaseName: process.env.DATABASE_NAME || "orchestrator", + databaseName: + process.env.MADARA_ORCHESTRATOR_DATABASE_NAME || "orchestrator", options: { useNewUrlParser: true, // removes a deprecation warning when connecting diff --git a/scripts/init_state.js b/scripts/init_state.js index cd2a7b0c..0c041972 100644 --- a/scripts/init_state.js +++ b/scripts/init_state.js @@ -13,10 +13,13 @@ const { MongoClient } = require("mongodb"); const { v4 } = require("uuid"); // using default anvil key which has funds -const ETHEREUM_PRIVATE_KEY = +const MADARA_ORCHESTRATOR_ETHEREUM_PRIVATE_KEY = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"; const eth_provider = new ethers.JsonRpcProvider("http://localhost:8545"); -const wallet = new ethers.Wallet(ETHEREUM_PRIVATE_KEY, eth_provider); +const wallet = new ethers.Wallet( + MADARA_ORCHESTRATOR_ETHEREUM_PRIVATE_KEY, + eth_provider, +); const starknet_provider = new starknet.RpcProvider({ nodeUrl: "http://localhost:9944",