瀏覽代碼

[fortuna] script to setup a provider for all chains (#1229)

* auto register script

* renaming

* add comments

* update abi file path

* pre commit

* pre commit

* resolved some

* better comment

* resolve comments

* fix bug

* pre commit

* fix

* update version
Dev Kalra 1 年之前
父節點
當前提交
ab10b9f5fa

+ 1 - 0
fortuna/.gitignore

@@ -1,3 +1,4 @@
 /target
 config.yaml
 *secret*
+*private-key*

+ 1 - 1
fortuna/Cargo.lock

@@ -1486,7 +1486,7 @@ dependencies = [
 
 [[package]]
 name = "fortuna"
-version = "3.2.0"
+version = "3.2.1"
 dependencies = [
  "anyhow",
  "axum",

+ 1 - 1
fortuna/Cargo.toml

@@ -1,6 +1,6 @@
 [package]
 name    = "fortuna"
-version = "3.2.0"
+version = "3.2.1"
 edition = "2021"
 
 [dependencies]

+ 1 - 0
fortuna/Dockerfile

@@ -9,6 +9,7 @@ RUN rustup default nightly-2023-07-23
 WORKDIR /src
 COPY fortuna fortuna
 COPY pythnet pythnet
+COPY target_chains/ethereum/entropy_sdk/solidity/abis target_chains/ethereum/entropy_sdk/solidity/abis
 
 WORKDIR /src/fortuna
 

+ 0 - 1020
fortuna/src/abi.json

@@ -1,1020 +0,0 @@
-[
-  {
-    "inputs": [],
-    "stateMutability": "nonpayable",
-    "type": "constructor"
-  },
-  {
-    "inputs": [],
-    "name": "AssertionFailure",
-    "type": "error"
-  },
-  {
-    "inputs": [],
-    "name": "BlockhashUnavailable",
-    "type": "error"
-  },
-  {
-    "inputs": [],
-    "name": "IncorrectRevelation",
-    "type": "error"
-  },
-  {
-    "inputs": [],
-    "name": "InsufficientFee",
-    "type": "error"
-  },
-  {
-    "inputs": [],
-    "name": "InvalidUpgradeMagic",
-    "type": "error"
-  },
-  {
-    "inputs": [],
-    "name": "NoSuchProvider",
-    "type": "error"
-  },
-  {
-    "inputs": [],
-    "name": "NoSuchRequest",
-    "type": "error"
-  },
-  {
-    "inputs": [],
-    "name": "OutOfRandomness",
-    "type": "error"
-  },
-  {
-    "inputs": [],
-    "name": "Unauthorized",
-    "type": "error"
-  },
-  {
-    "anonymous": false,
-    "inputs": [
-      {
-        "indexed": false,
-        "internalType": "address",
-        "name": "previousAdmin",
-        "type": "address"
-      },
-      {
-        "indexed": false,
-        "internalType": "address",
-        "name": "newAdmin",
-        "type": "address"
-      }
-    ],
-    "name": "AdminChanged",
-    "type": "event"
-  },
-  {
-    "anonymous": false,
-    "inputs": [
-      {
-        "indexed": true,
-        "internalType": "address",
-        "name": "beacon",
-        "type": "address"
-      }
-    ],
-    "name": "BeaconUpgraded",
-    "type": "event"
-  },
-  {
-    "anonymous": false,
-    "inputs": [
-      {
-        "indexed": false,
-        "internalType": "address",
-        "name": "oldImplementation",
-        "type": "address"
-      },
-      {
-        "indexed": false,
-        "internalType": "address",
-        "name": "newImplementation",
-        "type": "address"
-      }
-    ],
-    "name": "ContractUpgraded",
-    "type": "event"
-  },
-  {
-    "anonymous": false,
-    "inputs": [
-      {
-        "indexed": false,
-        "internalType": "address",
-        "name": "oldDefaultProvider",
-        "type": "address"
-      },
-      {
-        "indexed": false,
-        "internalType": "address",
-        "name": "newDefaultProvider",
-        "type": "address"
-      }
-    ],
-    "name": "DefaultProviderSet",
-    "type": "event"
-  },
-  {
-    "anonymous": false,
-    "inputs": [
-      {
-        "indexed": false,
-        "internalType": "uint8",
-        "name": "version",
-        "type": "uint8"
-      }
-    ],
-    "name": "Initialized",
-    "type": "event"
-  },
-  {
-    "anonymous": false,
-    "inputs": [
-      {
-        "indexed": false,
-        "internalType": "address",
-        "name": "oldAdmin",
-        "type": "address"
-      },
-      {
-        "indexed": false,
-        "internalType": "address",
-        "name": "newAdmin",
-        "type": "address"
-      }
-    ],
-    "name": "NewAdminAccepted",
-    "type": "event"
-  },
-  {
-    "anonymous": false,
-    "inputs": [
-      {
-        "indexed": false,
-        "internalType": "address",
-        "name": "oldAdmin",
-        "type": "address"
-      },
-      {
-        "indexed": false,
-        "internalType": "address",
-        "name": "newAdmin",
-        "type": "address"
-      }
-    ],
-    "name": "NewAdminProposed",
-    "type": "event"
-  },
-  {
-    "anonymous": false,
-    "inputs": [
-      {
-        "indexed": true,
-        "internalType": "address",
-        "name": "previousOwner",
-        "type": "address"
-      },
-      {
-        "indexed": true,
-        "internalType": "address",
-        "name": "newOwner",
-        "type": "address"
-      }
-    ],
-    "name": "OwnershipTransferStarted",
-    "type": "event"
-  },
-  {
-    "anonymous": false,
-    "inputs": [
-      {
-        "indexed": true,
-        "internalType": "address",
-        "name": "previousOwner",
-        "type": "address"
-      },
-      {
-        "indexed": true,
-        "internalType": "address",
-        "name": "newOwner",
-        "type": "address"
-      }
-    ],
-    "name": "OwnershipTransferred",
-    "type": "event"
-  },
-  {
-    "anonymous": false,
-    "inputs": [
-      {
-        "indexed": false,
-        "internalType": "uint256",
-        "name": "oldPythFee",
-        "type": "uint256"
-      },
-      {
-        "indexed": false,
-        "internalType": "uint256",
-        "name": "newPythFee",
-        "type": "uint256"
-      }
-    ],
-    "name": "PythFeeSet",
-    "type": "event"
-  },
-  {
-    "anonymous": false,
-    "inputs": [
-      {
-        "components": [
-          {
-            "internalType": "uint128",
-            "name": "feeInWei",
-            "type": "uint128"
-          },
-          {
-            "internalType": "uint128",
-            "name": "accruedFeesInWei",
-            "type": "uint128"
-          },
-          {
-            "internalType": "bytes32",
-            "name": "originalCommitment",
-            "type": "bytes32"
-          },
-          {
-            "internalType": "uint64",
-            "name": "originalCommitmentSequenceNumber",
-            "type": "uint64"
-          },
-          {
-            "internalType": "bytes",
-            "name": "commitmentMetadata",
-            "type": "bytes"
-          },
-          {
-            "internalType": "bytes",
-            "name": "uri",
-            "type": "bytes"
-          },
-          {
-            "internalType": "uint64",
-            "name": "endSequenceNumber",
-            "type": "uint64"
-          },
-          {
-            "internalType": "uint64",
-            "name": "sequenceNumber",
-            "type": "uint64"
-          },
-          {
-            "internalType": "bytes32",
-            "name": "currentCommitment",
-            "type": "bytes32"
-          },
-          {
-            "internalType": "uint64",
-            "name": "currentCommitmentSequenceNumber",
-            "type": "uint64"
-          }
-        ],
-        "indexed": false,
-        "internalType": "struct EntropyStructs.ProviderInfo",
-        "name": "provider",
-        "type": "tuple"
-      }
-    ],
-    "name": "Registered",
-    "type": "event"
-  },
-  {
-    "anonymous": false,
-    "inputs": [
-      {
-        "components": [
-          {
-            "internalType": "address",
-            "name": "provider",
-            "type": "address"
-          },
-          {
-            "internalType": "uint64",
-            "name": "sequenceNumber",
-            "type": "uint64"
-          },
-          {
-            "internalType": "uint32",
-            "name": "numHashes",
-            "type": "uint32"
-          },
-          {
-            "internalType": "bytes32",
-            "name": "commitment",
-            "type": "bytes32"
-          },
-          {
-            "internalType": "uint64",
-            "name": "blockNumber",
-            "type": "uint64"
-          },
-          {
-            "internalType": "address",
-            "name": "requester",
-            "type": "address"
-          },
-          {
-            "internalType": "bool",
-            "name": "useBlockhash",
-            "type": "bool"
-          }
-        ],
-        "indexed": false,
-        "internalType": "struct EntropyStructs.Request",
-        "name": "request",
-        "type": "tuple"
-      }
-    ],
-    "name": "Requested",
-    "type": "event"
-  },
-  {
-    "anonymous": false,
-    "inputs": [
-      {
-        "components": [
-          {
-            "internalType": "address",
-            "name": "provider",
-            "type": "address"
-          },
-          {
-            "internalType": "uint64",
-            "name": "sequenceNumber",
-            "type": "uint64"
-          },
-          {
-            "internalType": "uint32",
-            "name": "numHashes",
-            "type": "uint32"
-          },
-          {
-            "internalType": "bytes32",
-            "name": "commitment",
-            "type": "bytes32"
-          },
-          {
-            "internalType": "uint64",
-            "name": "blockNumber",
-            "type": "uint64"
-          },
-          {
-            "internalType": "address",
-            "name": "requester",
-            "type": "address"
-          },
-          {
-            "internalType": "bool",
-            "name": "useBlockhash",
-            "type": "bool"
-          }
-        ],
-        "indexed": false,
-        "internalType": "struct EntropyStructs.Request",
-        "name": "request",
-        "type": "tuple"
-      },
-      {
-        "indexed": false,
-        "internalType": "bytes32",
-        "name": "userRevelation",
-        "type": "bytes32"
-      },
-      {
-        "indexed": false,
-        "internalType": "bytes32",
-        "name": "providerRevelation",
-        "type": "bytes32"
-      },
-      {
-        "indexed": false,
-        "internalType": "bytes32",
-        "name": "blockHash",
-        "type": "bytes32"
-      },
-      {
-        "indexed": false,
-        "internalType": "bytes32",
-        "name": "randomNumber",
-        "type": "bytes32"
-      }
-    ],
-    "name": "Revealed",
-    "type": "event"
-  },
-  {
-    "anonymous": false,
-    "inputs": [
-      {
-        "indexed": true,
-        "internalType": "address",
-        "name": "implementation",
-        "type": "address"
-      }
-    ],
-    "name": "Upgraded",
-    "type": "event"
-  },
-  {
-    "inputs": [],
-    "name": "NUM_REQUESTS",
-    "outputs": [
-      {
-        "internalType": "uint8",
-        "name": "",
-        "type": "uint8"
-      }
-    ],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "NUM_REQUESTS_MASK",
-    "outputs": [
-      {
-        "internalType": "bytes1",
-        "name": "",
-        "type": "bytes1"
-      }
-    ],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "acceptAdmin",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "acceptOwnership",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "internalType": "bytes32",
-        "name": "userRandomness",
-        "type": "bytes32"
-      },
-      {
-        "internalType": "bytes32",
-        "name": "providerRandomness",
-        "type": "bytes32"
-      },
-      {
-        "internalType": "bytes32",
-        "name": "blockHash",
-        "type": "bytes32"
-      }
-    ],
-    "name": "combineRandomValues",
-    "outputs": [
-      {
-        "internalType": "bytes32",
-        "name": "combinedRandomness",
-        "type": "bytes32"
-      }
-    ],
-    "stateMutability": "pure",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "internalType": "bytes32",
-        "name": "userRandomness",
-        "type": "bytes32"
-      }
-    ],
-    "name": "constructUserCommitment",
-    "outputs": [
-      {
-        "internalType": "bytes32",
-        "name": "userCommitment",
-        "type": "bytes32"
-      }
-    ],
-    "stateMutability": "pure",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "getAccruedPythFees",
-    "outputs": [
-      {
-        "internalType": "uint128",
-        "name": "accruedPythFeesInWei",
-        "type": "uint128"
-      }
-    ],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "getAdmin",
-    "outputs": [
-      {
-        "internalType": "address",
-        "name": "",
-        "type": "address"
-      }
-    ],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "getDefaultProvider",
-    "outputs": [
-      {
-        "internalType": "address",
-        "name": "provider",
-        "type": "address"
-      }
-    ],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "internalType": "address",
-        "name": "provider",
-        "type": "address"
-      }
-    ],
-    "name": "getFee",
-    "outputs": [
-      {
-        "internalType": "uint128",
-        "name": "feeAmount",
-        "type": "uint128"
-      }
-    ],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "internalType": "address",
-        "name": "provider",
-        "type": "address"
-      }
-    ],
-    "name": "getProviderInfo",
-    "outputs": [
-      {
-        "components": [
-          {
-            "internalType": "uint128",
-            "name": "feeInWei",
-            "type": "uint128"
-          },
-          {
-            "internalType": "uint128",
-            "name": "accruedFeesInWei",
-            "type": "uint128"
-          },
-          {
-            "internalType": "bytes32",
-            "name": "originalCommitment",
-            "type": "bytes32"
-          },
-          {
-            "internalType": "uint64",
-            "name": "originalCommitmentSequenceNumber",
-            "type": "uint64"
-          },
-          {
-            "internalType": "bytes",
-            "name": "commitmentMetadata",
-            "type": "bytes"
-          },
-          {
-            "internalType": "bytes",
-            "name": "uri",
-            "type": "bytes"
-          },
-          {
-            "internalType": "uint64",
-            "name": "endSequenceNumber",
-            "type": "uint64"
-          },
-          {
-            "internalType": "uint64",
-            "name": "sequenceNumber",
-            "type": "uint64"
-          },
-          {
-            "internalType": "bytes32",
-            "name": "currentCommitment",
-            "type": "bytes32"
-          },
-          {
-            "internalType": "uint64",
-            "name": "currentCommitmentSequenceNumber",
-            "type": "uint64"
-          }
-        ],
-        "internalType": "struct EntropyStructs.ProviderInfo",
-        "name": "info",
-        "type": "tuple"
-      }
-    ],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "getPythFee",
-    "outputs": [
-      {
-        "internalType": "uint128",
-        "name": "feeAmount",
-        "type": "uint128"
-      }
-    ],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "internalType": "address",
-        "name": "provider",
-        "type": "address"
-      },
-      {
-        "internalType": "uint64",
-        "name": "sequenceNumber",
-        "type": "uint64"
-      }
-    ],
-    "name": "getRequest",
-    "outputs": [
-      {
-        "components": [
-          {
-            "internalType": "address",
-            "name": "provider",
-            "type": "address"
-          },
-          {
-            "internalType": "uint64",
-            "name": "sequenceNumber",
-            "type": "uint64"
-          },
-          {
-            "internalType": "uint32",
-            "name": "numHashes",
-            "type": "uint32"
-          },
-          {
-            "internalType": "bytes32",
-            "name": "commitment",
-            "type": "bytes32"
-          },
-          {
-            "internalType": "uint64",
-            "name": "blockNumber",
-            "type": "uint64"
-          },
-          {
-            "internalType": "address",
-            "name": "requester",
-            "type": "address"
-          },
-          {
-            "internalType": "bool",
-            "name": "useBlockhash",
-            "type": "bool"
-          }
-        ],
-        "internalType": "struct EntropyStructs.Request",
-        "name": "req",
-        "type": "tuple"
-      }
-    ],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "owner",
-    "outputs": [
-      {
-        "internalType": "address",
-        "name": "",
-        "type": "address"
-      }
-    ],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "pendingOwner",
-    "outputs": [
-      {
-        "internalType": "address",
-        "name": "",
-        "type": "address"
-      }
-    ],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "internalType": "address",
-        "name": "newAdmin",
-        "type": "address"
-      }
-    ],
-    "name": "proposeAdmin",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "proposedAdmin",
-    "outputs": [
-      {
-        "internalType": "address",
-        "name": "",
-        "type": "address"
-      }
-    ],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "proxiableUUID",
-    "outputs": [
-      {
-        "internalType": "bytes32",
-        "name": "",
-        "type": "bytes32"
-      }
-    ],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "internalType": "uint128",
-        "name": "feeInWei",
-        "type": "uint128"
-      },
-      {
-        "internalType": "bytes32",
-        "name": "commitment",
-        "type": "bytes32"
-      },
-      {
-        "internalType": "bytes",
-        "name": "commitmentMetadata",
-        "type": "bytes"
-      },
-      {
-        "internalType": "uint64",
-        "name": "chainLength",
-        "type": "uint64"
-      },
-      {
-        "internalType": "bytes",
-        "name": "uri",
-        "type": "bytes"
-      }
-    ],
-    "name": "register",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "renounceOwnership",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "internalType": "address",
-        "name": "provider",
-        "type": "address"
-      },
-      {
-        "internalType": "bytes32",
-        "name": "userCommitment",
-        "type": "bytes32"
-      },
-      {
-        "internalType": "bool",
-        "name": "useBlockHash",
-        "type": "bool"
-      }
-    ],
-    "name": "request",
-    "outputs": [
-      {
-        "internalType": "uint64",
-        "name": "assignedSequenceNumber",
-        "type": "uint64"
-      }
-    ],
-    "stateMutability": "payable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "internalType": "address",
-        "name": "provider",
-        "type": "address"
-      },
-      {
-        "internalType": "uint64",
-        "name": "sequenceNumber",
-        "type": "uint64"
-      },
-      {
-        "internalType": "bytes32",
-        "name": "userRandomness",
-        "type": "bytes32"
-      },
-      {
-        "internalType": "bytes32",
-        "name": "providerRevelation",
-        "type": "bytes32"
-      }
-    ],
-    "name": "reveal",
-    "outputs": [
-      {
-        "internalType": "bytes32",
-        "name": "randomNumber",
-        "type": "bytes32"
-      }
-    ],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "internalType": "address",
-        "name": "newDefaultProvider",
-        "type": "address"
-      }
-    ],
-    "name": "setDefaultProvider",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "internalType": "uint128",
-        "name": "newPythFee",
-        "type": "uint128"
-      }
-    ],
-    "name": "setPythFee",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "internalType": "address",
-        "name": "newOwner",
-        "type": "address"
-      }
-    ],
-    "name": "transferOwnership",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "internalType": "uint128",
-        "name": "amount",
-        "type": "uint128"
-      }
-    ],
-    "name": "withdraw",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "internalType": "address",
-        "name": "owner",
-        "type": "address"
-      },
-      {
-        "internalType": "address",
-        "name": "admin",
-        "type": "address"
-      },
-      {
-        "internalType": "uint128",
-        "name": "pythFeeInWei",
-        "type": "uint128"
-      },
-      {
-        "internalType": "address",
-        "name": "defaultProvider",
-        "type": "address"
-      },
-      {
-        "internalType": "bool",
-        "name": "prefillRequestStorage",
-        "type": "bool"
-      }
-    ],
-    "name": "initialize",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "internalType": "address",
-        "name": "newImplementation",
-        "type": "address"
-      }
-    ],
-    "name": "upgradeTo",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "internalType": "address",
-        "name": "newImplementation",
-        "type": "address"
-      },
-      {
-        "internalType": "bytes",
-        "name": "data",
-        "type": "bytes"
-      }
-    ],
-    "name": "upgradeToAndCall",
-    "outputs": [],
-    "stateMutability": "payable",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "entropyUpgradableMagic",
-    "outputs": [
-      {
-        "internalType": "uint32",
-        "name": "",
-        "type": "uint32"
-      }
-    ],
-    "stateMutability": "pure",
-    "type": "function"
-  }
-]

+ 4 - 1
fortuna/src/chain/ethereum.rs

@@ -50,7 +50,10 @@ use {
 
 // TODO: Programmatically generate this so we don't have to keep committed ABI in sync with the
 // contract in the same repo.
-abigen!(PythRandom, "src/abi.json");
+abigen!(
+    PythRandom,
+    "../target_chains/ethereum/entropy_sdk/solidity/abis/IEntropy.json"
+);
 
 pub type SignablePythContract = PythRandom<
     TransformerMiddleware<SignerMiddleware<Provider<Http>, LocalWallet>, LegacyTxTransformer>,

+ 2 - 0
fortuna/src/command.rs

@@ -3,6 +3,7 @@ mod get_request;
 mod register_provider;
 mod request_randomness;
 mod run;
+mod setup_provider;
 
 pub use {
     generate::generate,
@@ -10,4 +11,5 @@ pub use {
     register_provider::register_provider,
     request_randomness::request_randomness,
     run::run,
+    setup_provider::setup_provider,
 };

+ 3 - 6
fortuna/src/command/register_provider.rs

@@ -8,12 +8,9 @@ use {
         state::PebbleHashChain,
     },
     anyhow::Result,
-    ethers::{
-        signers::{
-            LocalWallet,
-            Signer,
-        },
-        types::Address,
+    ethers::signers::{
+        LocalWallet,
+        Signer,
     },
     std::sync::Arc,
 };

+ 117 - 0
fortuna/src/command/setup_provider.rs

@@ -0,0 +1,117 @@
+use {
+    crate::{
+        chain::ethereum::SignablePythContract,
+        command::{
+            register_provider,
+            register_provider::CommitmentMetadata,
+        },
+        config::{
+            Config,
+            RegisterProviderOptions,
+            SetupProviderOptions,
+        },
+        state::{
+            HashChainState,
+            PebbleHashChain,
+        },
+    },
+    anyhow::Result,
+    ethers::signers::{
+        LocalWallet,
+        Signer,
+    },
+    std::sync::Arc,
+};
+
+/// Setup provider for all the chains.
+/// 1. Register if there was no previous registration.
+/// 2. Re-register if there are no more random numbers to request on the contract.
+/// 3. Re-register if there is a mismatch in generated hash chain.
+/// 4. Update provider fee if there is a mismatch with the fee set on contract.
+/// 5. Update provider uri if there is a mismatch with the uri set on contract.
+pub async fn setup_provider(opts: &SetupProviderOptions) -> Result<()> {
+    let config = Config::load(&opts.config.config)?;
+    let private_key = opts.load_private_key()?;
+    let secret = opts.randomness.load_secret()?;
+    let provider_address = private_key.clone().parse::<LocalWallet>()?.address();
+
+    for (chain_id, chain_config) in &config.chains {
+        // Initialize a Provider to interface with the EVM contract.
+        let contract =
+            Arc::new(SignablePythContract::from_config(&chain_config, &private_key).await?);
+        let provider_info = contract.get_provider_info(provider_address).call().await?;
+        tracing::info!("Provider info: {:?}", provider_info);
+
+        let mut register = false;
+
+        // This condition satisfies for both when there is no registration and when there are no
+        // more random numbers left to request
+        if provider_info.end_sequence_number <= provider_info.sequence_number {
+            tracing::info!(
+                "endSequenceNumber <= sequenceNumber. endSequenceNumber={0}, sequenceNumber={1}",
+                provider_info.end_sequence_number,
+                provider_info.sequence_number
+            );
+            tracing::info!("Registering to {}", &chain_id);
+            register = true;
+        } else {
+            let metadata =
+                bincode::deserialize::<CommitmentMetadata>(&provider_info.commitment_metadata)?;
+
+            let hash_chain = PebbleHashChain::from_config(
+                &secret,
+                &chain_id,
+                &provider_address,
+                &chain_config.contract_addr,
+                &metadata.seed,
+                metadata.chain_length,
+            )?;
+            let chain_state = HashChainState {
+                offsets:     vec![provider_info
+                    .original_commitment_sequence_number
+                    .try_into()?],
+                hash_chains: vec![hash_chain],
+            };
+
+
+            if chain_state.reveal(provider_info.original_commitment_sequence_number)?
+                != provider_info.original_commitment
+            {
+                tracing::info!("The root of the generated hash chain for chain id {} does not match the commitment",  &chain_id);
+                tracing::info!("Registering to {}", &chain_id);
+                register = true;
+            }
+        }
+
+        if register {
+            register_provider(&RegisterProviderOptions {
+                config:      opts.config.clone(),
+                chain_id:    chain_id.clone(),
+                private_key: private_key.clone(),
+                randomness:  opts.randomness.clone(),
+                fee:         opts.fee,
+                uri:         opts.uri.clone(),
+            })
+            .await?;
+        } else {
+            if provider_info.fee_in_wei != opts.fee {
+                if let Some(r) = contract.set_provider_fee(opts.fee).send().await?.await? {
+                    tracing::info!("Updated provider fee: {:?}", r);
+                }
+            }
+
+            if bincode::deserialize::<String>(&provider_info.uri)? != opts.uri {
+                if let Some(receipt) = contract
+                    .set_provider_uri(bincode::serialize(&opts.uri)?.into())
+                    .send()
+                    .await?
+                    .log_msg("Pending transfer hash")
+                    .await?
+                {
+                    tracing::info!("Updated provider uri: {:?}", receipt);
+                }
+            }
+        }
+    }
+    Ok(())
+}

+ 6 - 0
fortuna/src/config.rs

@@ -27,6 +27,7 @@ pub use {
     register_provider::RegisterProviderOptions,
     request_randomness::RequestRandomnessOptions,
     run::RunOptions,
+    setup_provider::SetupProviderOptions,
 };
 
 mod generate;
@@ -34,6 +35,7 @@ mod get_request;
 mod register_provider;
 mod request_randomness;
 mod run;
+mod setup_provider;
 
 const DEFAULT_RPC_ADDR: &str = "127.0.0.1:34000";
 const DEFAULT_HTTP_ADDR: &str = "http://127.0.0.1:34000";
@@ -51,6 +53,10 @@ pub enum Options {
     /// Register a new provider with the Pyth Random oracle.
     RegisterProvider(RegisterProviderOptions),
 
+    /// Set up the provider for all the provided chains.
+    /// It registers, re-registers, or updates provider config on chain.
+    SetupProvider(SetupProviderOptions),
+
     /// Request a random number from the contract.
     RequestRandomness(RequestRandomnessOptions),
 

+ 43 - 0
fortuna/src/config/setup_provider.rs

@@ -0,0 +1,43 @@
+use {
+    crate::config::{
+        ConfigOptions,
+        RandomnessOptions,
+    },
+    anyhow::Result,
+    clap::Args,
+    std::fs,
+};
+
+#[derive(Args, Clone, Debug)]
+#[command(next_help_heading = "Setup Provider Options")]
+#[group(id = "SetupProviderOptions")]
+pub struct SetupProviderOptions {
+    #[command(flatten)]
+    pub config: ConfigOptions,
+
+    /// Path to a file containing a 20-byte (40 char) hex encoded Ethereum private key.
+    /// This key is required to submit transactions (such as registering with the contract).
+    #[arg(long = "private-key")]
+    #[arg(env = "PRIVATE_KEY")]
+    pub private_key_file: String,
+
+    #[command(flatten)]
+    pub randomness: RandomnessOptions,
+
+    /// The fee to charge (in wei) for each requested random number
+    #[arg(long = "pyth-contract-fee")]
+    #[arg(default_value = "100")]
+    pub fee: u128,
+
+    /// The URI where clients can retrieve random values from this provider,
+    /// i.e., wherever fortuna for this provider will be hosted.
+    #[arg(long = "uri")]
+    #[arg(default_value = "")]
+    pub uri: String,
+}
+
+impl SetupProviderOptions {
+    pub fn load_private_key(&self) -> Result<String> {
+        return Ok((fs::read_to_string(&self.private_key_file))?);
+    }
+}

+ 1 - 0
fortuna/src/main.rs

@@ -39,6 +39,7 @@ async fn main() -> Result<()> {
         config::Options::Generate(opts) => command::generate(&opts).await,
         config::Options::Run(opts) => command::run(&opts).await,
         config::Options::RegisterProvider(opts) => command::register_provider(&opts).await,
+        config::Options::SetupProvider(opts) => command::setup_provider(&opts).await,
         config::Options::RequestRandomness(opts) => command::request_randomness(&opts).await,
     }
 }