large_contract.sol 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901
  1. // This is a test for issue #1367: https://github.com/hyperledger/solang/issues/1367
  2. // This contract used to generate so many calls to 'vector_new' for allocating error strings that it caused a LLVM ERROR: Branch target out of insn range
  3. import "solana";
  4. /// @title Service Registry Solana - Smart contract for registering services on the Solana chain.
  5. /// @dev Underlying canonical agents and components are not checked for their validity since they are set up on the L1 mainnet.
  6. /// The architecture is optimistic, in the sense that service owners are assumed to reference existing and relevant agents.
  7. /// @author Aleksandr Kuperman - <aleksandr.kuperman@valory.xyz>
  8. /// @author Andrey Lebedev - <andrey.lebedev@valory.xyz>
  9. contract ServiceRegistrySolana {
  10. event OwnerUpdated(address indexed owner);
  11. event BaseURIChanged(string baseURI);
  12. event DrainerUpdated(address indexed drainer);
  13. event Deposit(address indexed sender, uint64 amount);
  14. event Refund(address indexed receiver, uint64 amount);
  15. event CreateService(uint32 indexed serviceId, bytes32 configHash);
  16. event UpdateService(uint32 indexed serviceId, bytes32 configHash);
  17. event RegisterInstance(address indexed operator, uint32 indexed serviceId, address indexed agentInstance, uint32 agentId);
  18. event CreateMultisigWithAgents(uint32 indexed serviceId, address indexed multisig);
  19. event ActivateRegistration(uint32 indexed serviceId);
  20. event TerminateService(uint32 indexed serviceId);
  21. event OperatorSlashed(uint64 amount, address indexed operator, uint32 indexed serviceId);
  22. event OperatorUnbond(address indexed operator, uint32 indexed serviceId);
  23. event DeployService(uint32 indexed serviceId);
  24. event Drain(address indexed drainer, uint64 amount);
  25. enum ServiceState {
  26. NonExistent,
  27. PreRegistration,
  28. ActiveRegistration,
  29. FinishedRegistration,
  30. Deployed,
  31. TerminatedBonded
  32. }
  33. // Service parameters
  34. struct Service {
  35. address serviceOwner;
  36. // Registration activation deposit
  37. // This is enough for 1b+ ETH or 1e27
  38. uint64 securityDeposit;
  39. // Multisig address for agent instances
  40. address multisig;
  41. // IPFS hashes pointing to the config metadata
  42. bytes32 configHash;
  43. // Agent instance signers threshold: must no less than ceil((n * 2 + 1) / 3) of all the agent instances combined
  44. // This number will be enough to have ((2^32 - 1) * 3 - 1) / 2, which is bigger than 6.44b
  45. uint32 threshold;
  46. // Total number of agent instances. We assume that the number of instances is bounded by 2^32 - 1
  47. uint32 maxNumAgentInstances;
  48. // Actual number of agent instances. This number is less or equal to maxNumAgentInstances
  49. uint32 numAgentInstances;
  50. // Service state
  51. ServiceState state;
  52. // Canonical agent Ids for the service. Individual agent Id is bounded by the max number of agent Id
  53. uint32[] agentIds;
  54. uint32[] slots;
  55. uint64[] bonds;
  56. address[] operators;
  57. address[] agentInstances;
  58. uint32[] agentIdForAgentInstances;
  59. }
  60. // The public key for the storage authority (owner) that should sign every change to the contract
  61. address public owner;
  62. address public escrow;
  63. // Base URI
  64. string public baseURI;
  65. // Service counter
  66. uint32 public totalSupply;
  67. // Reentrancy lock
  68. uint32 internal _locked = 1;
  69. // To better understand the CID anatomy, please refer to: https://proto.school/anatomy-of-a-cid/05
  70. // CID = <multibase_encoding>multibase_encoding(<cid-version><multicodec><multihash-algorithm><multihash-length><multihash-hash>)
  71. // CID prefix = <multibase_encoding>multibase_encoding(<cid-version><multicodec><multihash-algorithm><multihash-length>)
  72. // to complement the multibase_encoding(<multihash-hash>)
  73. // multibase_encoding = base16 = "f"
  74. // cid-version = version 1 = "0x01"
  75. // multicodec = dag-pb = "0x70"
  76. // multihash-algorithm = sha2-256 = "0x12"
  77. // multihash-length = 256 bits = "0x20"
  78. string public constant CID_PREFIX = "f01701220";
  79. // The amount of funds slashed. This is enough for 1b+ ETH or 1e27
  80. uint64 public slashedFunds;
  81. // Drainer address: set by the government and is allowed to drain ETH funds accumulated in this contract
  82. address public drainer;
  83. // Service registry version number
  84. string public constant VERSION = "1.0.0";
  85. // Map of hash(operator address and serviceId) => agent instance bonding / escrow balance
  86. mapping(bytes32 => uint64) public mapOperatorAndServiceIdOperatorBalances;
  87. // Map of agent instance address => service id it is registered with and operator address that supplied the instance
  88. mapping (address => address) public mapAgentInstanceOperators;
  89. // Map of policy for multisig implementations
  90. mapping (address => bool) public mapMultisigs;
  91. // Set of services
  92. Service[type(uint32).max] public services;
  93. /// @dev Service registry constructor.
  94. /// @param _owner Agent contract symbol.
  95. /// @param _baseURI Agent registry token base URI.
  96. constructor(address _owner, string memory _baseURI)
  97. {
  98. owner = _owner;
  99. baseURI = _baseURI;
  100. }
  101. /// Requires the signature of the metadata authority.
  102. function requireSigner(address authority) internal view {
  103. for(uint32 i=0; i < tx.accounts.length; i++) {
  104. if (tx.accounts[i].key == authority) {
  105. require(tx.accounts[i].is_signer, "the authority account must sign the transaction");
  106. return;
  107. }
  108. }
  109. revert("The authority is missing");
  110. }
  111. /// @dev Changes the owner address.
  112. /// @param newOwner Address of a new owner.
  113. function changeOwner(address newOwner) external {
  114. // Check for the metadata authority
  115. requireSigner(owner);
  116. // Check for the zero address
  117. if (newOwner == address(0)) {
  118. revert("Zero Address");
  119. }
  120. owner = newOwner;
  121. emit OwnerUpdated(newOwner);
  122. }
  123. /// @dev Changes the drainer.
  124. /// @param newDrainer Address of a drainer.
  125. function changeDrainer(address newDrainer) external {
  126. // Check for the metadata authority
  127. requireSigner(owner);
  128. // Check for the zero address
  129. if (newDrainer == address(0)) {
  130. revert("ZeroAddress");
  131. }
  132. drainer = newDrainer;
  133. emit DrainerUpdated(newDrainer);
  134. }
  135. function transfer(uint32 serviceId, address newServiceOwner) public {
  136. // Check for the service authority
  137. address serviceOwner = services[serviceId].serviceOwner;
  138. requireSigner(serviceOwner);
  139. services[serviceId].serviceOwner = newServiceOwner;
  140. }
  141. /// @dev Going through basic initial service checks.
  142. /// @param configHash IPFS hash pointing to the config metadata.
  143. /// @param agentIds Canonical agent Ids.
  144. /// @param slots Set of agent instances number for each agent Id.
  145. /// @param bonds Corresponding set of required bonds to register an agent instance in the service.
  146. function _initialChecks(
  147. bytes32 configHash,
  148. uint32[] memory agentIds,
  149. uint32[] memory slots,
  150. uint64[] memory bonds
  151. ) private pure
  152. {
  153. // Check for the non-zero hash value
  154. if (configHash == 0) {
  155. revert("ZeroValue");
  156. }
  157. // Checking for non-empty arrays and correct number of values in them
  158. if (agentIds.length == 0 || agentIds.length != slots.length || agentIds.length != bonds.length) {
  159. revert("WrongArrayLength");
  160. }
  161. // Check for duplicate canonical agent Ids
  162. uint32 lastId = 0;
  163. for (uint32 i = 0; i < agentIds.length; i++) {
  164. if (agentIds[i] < (lastId + 1)) {
  165. revert("WrongAgentId");
  166. }
  167. lastId = agentIds[i];
  168. }
  169. // Check that there are no zero number of slots for a specific canonical agent id and no zero registration bond
  170. for (uint32 i = 0; i < agentIds.length; i++) {
  171. if (slots[i] == 0 || bonds[i] == 0) {
  172. revert("ZeroValue");
  173. }
  174. }
  175. }
  176. /// @dev Sets the service data.
  177. /// @param service A service instance to fill the data for.
  178. /// @param agentIds Canonical agent Ids.
  179. /// @param slots Set of agent instances number for each agent Id.
  180. /// @param bonds Corresponding set of required bonds to register an agent instance in the service.
  181. function _setServiceData(
  182. Service memory service,
  183. uint32[] memory agentIds,
  184. uint32[] memory slots,
  185. uint64[] memory bonds
  186. ) private
  187. {
  188. // Security deposit
  189. uint64 securityDeposit = 0;
  190. // Add canonical agent Ids for the service and the slots map
  191. service.agentIds = agentIds;
  192. service.slots = slots;
  193. service.bonds = bonds;
  194. for (uint32 i = 0; i < agentIds.length; i++) {
  195. service.maxNumAgentInstances += slots[i];
  196. // Security deposit is the maximum of the canonical agent registration bond
  197. if (bonds[i] > securityDeposit) {
  198. securityDeposit = bonds[i];
  199. }
  200. }
  201. service.securityDeposit = securityDeposit;
  202. // Check for the correct threshold: no less than ceil((n * 2 + 1) / 3) of all the agent instances combined
  203. uint32 checkThreshold = service.maxNumAgentInstances * 2 + 1;
  204. if (checkThreshold % 3 == 0) {
  205. checkThreshold = checkThreshold / 3;
  206. } else {
  207. checkThreshold = checkThreshold / 3 + 1;
  208. }
  209. if (service.threshold < checkThreshold || service.threshold > service.maxNumAgentInstances) {
  210. revert("WrongThreshold");
  211. }
  212. }
  213. /// @dev Creates a new service.
  214. /// @notice If agentIds are not sorted in ascending order then the function that executes initial checks gets reverted.
  215. /// @param serviceOwner Individual that creates and controls a service.
  216. /// @param configHash IPFS hash pointing to the config metadata.
  217. /// @param agentIds Canonical agent Ids in a sorted ascending order.
  218. /// @param slots Set of agent instances number for each agent Id.
  219. /// @param bonds Corresponding set of required bonds to register an agent instance in the service.
  220. /// @param threshold Signers threshold for a multisig composed by agent instances.
  221. /// @return serviceId Created service Id.
  222. function create(
  223. address serviceOwner,
  224. bytes32 configHash,
  225. uint32[] memory agentIds,
  226. uint32[] memory slots,
  227. uint64[] memory bonds,
  228. uint32 threshold
  229. ) external returns (uint32 serviceId)
  230. {
  231. // Reentrancy guard
  232. if (_locked > 1) {
  233. revert("ReentrancyGuard");
  234. }
  235. _locked = 2;
  236. // Check for the non-empty service owner address
  237. if (serviceOwner == address(0)) {
  238. revert("ZeroAddress");
  239. }
  240. // Execute initial checks
  241. _initialChecks(configHash, agentIds, slots, bonds);
  242. // Create a new service Id
  243. serviceId = totalSupply;
  244. serviceId++;
  245. // Set high-level data components of the service instance
  246. Service memory service;
  247. // Updating high-level data components of the service
  248. service.threshold = threshold;
  249. // Assigning the initial hash
  250. service.configHash = configHash;
  251. // Set the initial service state
  252. service.state = ServiceState.PreRegistration;
  253. // Set service data
  254. _setServiceData(service, agentIds, slots, bonds);
  255. // Mint the service instance to the service owner and record the service structure
  256. service.serviceOwner = serviceOwner;
  257. services[serviceId] = service;
  258. totalSupply = serviceId;
  259. emit CreateService(serviceId, configHash);
  260. _locked = 1;
  261. }
  262. /// @dev Updates a service in a CRUD way.
  263. /// @param configHash IPFS hash pointing to the config metadata.
  264. /// @param agentIds Canonical agent Ids in a sorted ascending order.
  265. /// @notice If agentIds are not sorted in ascending order then the function that executes initial checks gets reverted.
  266. /// @param slots Set of agent instances number for each agent Id.
  267. /// @param bonds Corresponding set of required bonds to register an agent instance in the service.
  268. /// @param threshold Signers threshold for a multisig composed by agent instances.
  269. /// @param serviceId Service Id to be updated.
  270. /// @return success True, if function executed successfully.
  271. function update(
  272. bytes32 configHash,
  273. uint32[] memory agentIds,
  274. uint32[] memory slots,
  275. uint64[] memory bonds,
  276. uint32 threshold,
  277. uint32 serviceId
  278. ) external returns (bool success)
  279. {
  280. Service memory service = services[serviceId];
  281. // Check for the service authority
  282. address serviceOwner = service.serviceOwner;
  283. requireSigner(serviceOwner);
  284. if (service.state != ServiceState.PreRegistration) {
  285. revert("WrongServiceState");
  286. }
  287. // Execute initial checks
  288. _initialChecks(configHash, agentIds, slots, bonds);
  289. // Updating high-level data components of the service
  290. service.threshold = threshold;
  291. service.maxNumAgentInstances = 0;
  292. // Check if the previous hash is the same / hash was not updated
  293. bytes32 lastConfigHash = service.configHash;
  294. if (lastConfigHash != configHash) {
  295. service.configHash = configHash;
  296. }
  297. // Set service data and record the modified service struct
  298. _setServiceData(service, agentIds, slots, bonds);
  299. services[serviceId] = service;
  300. emit UpdateService(serviceId, configHash);
  301. success = true;
  302. }
  303. /// @dev Activates the service.
  304. /// @param serviceId Correspondent service Id.
  305. /// @return success True, if function executed successfully.
  306. function activateRegistration(uint32 serviceId) external payable returns (bool success)
  307. {
  308. Service storage service = services[serviceId];
  309. // Check for the service authority
  310. address serviceOwner = service.serviceOwner;
  311. requireSigner(serviceOwner);
  312. // Service must be inactive
  313. if (service.state != ServiceState.PreRegistration) {
  314. revert("ServiceMustBeInactive");
  315. }
  316. // TODO: Need to check that the balance of the escrow for the serviceOwner has enough balance
  317. // if (msg.value != service.securityDeposit) {
  318. // revert IncorrectRegistrationDepositValue(msg.value, service.securityDeposit, serviceId);
  319. // }
  320. // Activate the agent instance registration
  321. service.state = ServiceState.ActiveRegistration;
  322. emit ActivateRegistration(serviceId);
  323. success = true;
  324. }
  325. /// @dev Registers agent instances.
  326. /// @param operator Address of the operator.
  327. /// @param serviceId Service Id to register agent instances for.
  328. /// @param agentInstances Agent instance addresses.
  329. /// @param agentIds Canonical Ids of the agent correspondent to the agent instance.
  330. /// @return success True, if function executed successfully.
  331. function registerAgents(
  332. address operator,
  333. uint32 serviceId,
  334. address[] memory agentInstances,
  335. uint32[] memory agentIds
  336. ) external payable returns (bool success)
  337. {
  338. // Check if the length of canonical agent instance addresses array and ids array have the same length
  339. if (agentInstances.length != agentIds.length) {
  340. revert("WrongArrayLength");
  341. }
  342. Service storage service = services[serviceId];
  343. // The service has to be active to register agents
  344. if (service.state != ServiceState.ActiveRegistration) {
  345. revert("WrongServiceState");
  346. }
  347. // Check for the sufficient amount of bond fee is provided
  348. uint32 numAgents = agentInstances.length;
  349. uint64 totalBond = 0;
  350. uint32[] memory serviceAgentIds = service.agentIds;
  351. for (uint32 i = 0; i < numAgents; ++i) {
  352. // Check if canonical agent Id exists in the service
  353. uint32 idx = 0;
  354. for (; idx < serviceAgentIds.length; ++idx) {
  355. if (serviceAgentIds[idx] == agentIds[i]) {
  356. break;
  357. }
  358. }
  359. // If the index reached the length of service agent Ids, the agent Id does not exist in the service
  360. if (idx == service.agentIds.length) {
  361. revert("AgentNotInService");
  362. }
  363. totalBond += service.bonds[idx];
  364. }
  365. // TODO: Check the escrow balance
  366. // if (msg.value != totalBond) {
  367. // revert("IncorrectAgentBondingValue");
  368. // }
  369. // Operator address must not be used as an agent instance anywhere else
  370. if (mapAgentInstanceOperators[operator] != address(0)) {
  371. revert("WrongOperator");
  372. }
  373. for (uint32 i = 0; i < numAgents; ++i) {
  374. address agentInstance = agentInstances[i];
  375. uint32 agentId = agentIds[i];
  376. // Operator address must be different from agent instance one
  377. if (operator == agentInstance) {
  378. revert("WrongOperator");
  379. }
  380. // Check if the agent instance is already engaged with another service
  381. if (mapAgentInstanceOperators[agentInstance] != address(0)) {
  382. revert("AgentInstanceRegistered");
  383. }
  384. // Check if there is an empty slot for the agent instance in this specific service
  385. uint32 numRegAgentInstances = 0;
  386. for (uint32 j = 0; j < service.agentIdForAgentInstances.length; ++j) {
  387. if (service.agentIdForAgentInstances[j] == agentId) {
  388. numRegAgentInstances++;
  389. }
  390. }
  391. if (numRegAgentInstances == service.slots[i]) {
  392. revert("AgentInstancesSlotsFilled");
  393. }
  394. // Copy new operator and agent instance information to the new one
  395. address[] memory oldOperators = service.operators;
  396. address[] memory oldAgentInstances = service.agentInstances;
  397. uint32[] memory oldAgentIdForAgentInstances = service.agentIdForAgentInstances;
  398. address[] memory newOperators = new address[](oldOperators.length + 1);
  399. address[] memory newAgentInstances = new address[](oldOperators.length + 1);
  400. uint32[] memory newAgentIdForAgentInstances = new uint32[](oldOperators.length + 1);
  401. for (uint32 j = 0; j < oldOperators.length; ++j) {
  402. newOperators[j] = oldOperators[j];
  403. newAgentInstances[j] = oldAgentInstances[j];
  404. newAgentIdForAgentInstances[j] = oldAgentIdForAgentInstances[j];
  405. }
  406. // Add agent instance and operator and set the instance engagement
  407. newOperators[oldOperators.length] = operator;
  408. newAgentInstances[oldOperators.length] = agentInstance;
  409. newAgentIdForAgentInstances[oldOperators.length] = agentId;
  410. // Update service arrays
  411. service.operators = newOperators;
  412. service.agentInstances = newAgentInstances;
  413. service.agentIdForAgentInstances = newAgentIdForAgentInstances;
  414. // Increase the total number of agent instances in the service
  415. service.numAgentInstances++;
  416. mapAgentInstanceOperators[agentInstance] = operator;
  417. emit RegisterInstance(operator, serviceId, agentInstance, agentId);
  418. }
  419. // If the service agent instance capacity is reached, the service becomes finished-registration
  420. if (service.numAgentInstances == service.maxNumAgentInstances) {
  421. service.state = ServiceState.FinishedRegistration;
  422. }
  423. // Update operator's bonding balance
  424. bytes32 operatorServiceIdHash = keccak256(abi.encode(operator, serviceId));
  425. mapOperatorAndServiceIdOperatorBalances[operatorServiceIdHash] += totalBond;
  426. emit Deposit(operator, totalBond);
  427. success = true;
  428. }
  429. /// @dev Creates multisig instance controlled by the set of service agent instances and deploys the service.
  430. /// @param serviceId Correspondent service Id.
  431. /// @param multisigImplementation Multisig implementation address.
  432. /// @param data Data payload for the multisig creation.
  433. /// @return multisig Address of the created multisig.
  434. function deploy(
  435. uint32 serviceId,
  436. address multisigImplementation,
  437. bytes memory data
  438. ) external returns (address multisig)
  439. {
  440. // Reentrancy guard
  441. if (_locked > 1) {
  442. revert("ReentrancyGuard");
  443. }
  444. _locked = 2;
  445. Service storage service = services[serviceId];
  446. // Check for the service authority
  447. address serviceOwner = service.serviceOwner;
  448. requireSigner(serviceOwner);
  449. // Check for the whitelisted multisig implementation
  450. if (!mapMultisigs[multisigImplementation]) {
  451. revert("UnauthorizedMultisig");
  452. }
  453. if (service.state != ServiceState.FinishedRegistration) {
  454. revert("WrongServiceState");
  455. }
  456. // Get all agent instances for the multisig
  457. address[] memory agentInstances = service.agentInstances;
  458. // TODO: Understand the multisig workflow
  459. // Create a multisig with agent instances
  460. multisig = address(0);//IMultisig(multisigImplementation).create(agentInstances, service.threshold, data);
  461. service.multisig = multisig;
  462. service.state = ServiceState.Deployed;
  463. emit CreateMultisigWithAgents(serviceId, multisig);
  464. emit DeployService(serviceId);
  465. _locked = 1;
  466. }
  467. /// @dev Slashes a specified agent instance.
  468. /// @param agentInstances Agent instances to slash.
  469. /// @param amounts Correspondent amounts to slash.
  470. /// @param serviceId Service Id.
  471. /// @return success True, if function executed successfully.
  472. function slash(address[] memory agentInstances, uint64[] memory amounts, uint32 serviceId) external
  473. returns (bool success)
  474. {
  475. // Check if the service is deployed
  476. // Since we do not kill (burn) services, we want this check to happen in a right service state.
  477. // If the service is deployed, it definitely exists and is running. We do not want this function to be abused
  478. // when the service was deployed, then terminated, then in a sleep mode or before next deployment somebody
  479. // could use this function and try to slash operators.
  480. Service storage service = services[serviceId];
  481. if (service.state != ServiceState.Deployed) {
  482. revert("WrongServiceState");
  483. }
  484. // Check for the array size
  485. if (agentInstances.length != amounts.length) {
  486. revert("WrongArrayLength");
  487. }
  488. // Only the multisig of a correspondent address can slash its agent instances
  489. requireSigner(service.multisig);
  490. // Loop over each agent instance
  491. uint32 numInstancesToSlash = agentInstances.length;
  492. for (uint32 i = 0; i < numInstancesToSlash; ++i) {
  493. // Get the service Id from the agentInstance map
  494. address operator = mapAgentInstanceOperators[agentInstances[i]];
  495. // Slash the balance of the operator, make sure it does not go below zero
  496. bytes32 operatorServiceIdHash = keccak256(abi.encode(operator, serviceId));
  497. uint64 balance = mapOperatorAndServiceIdOperatorBalances[operatorServiceIdHash];
  498. if ((amounts[i] + 1) > balance) {
  499. // We cannot add to the slashed amount more than the balance of the operator
  500. slashedFunds += balance;
  501. balance = 0;
  502. } else {
  503. slashedFunds += amounts[i];
  504. balance -= amounts[i];
  505. }
  506. mapOperatorAndServiceIdOperatorBalances[operatorServiceIdHash] = balance;
  507. emit OperatorSlashed(amounts[i], operator, serviceId);
  508. }
  509. success = true;
  510. }
  511. /// @dev Terminates the service.
  512. /// @param serviceId Service Id to be updated.
  513. /// @return success True, if function executed successfully.
  514. /// @return refund Refund to return to the service owner.
  515. function terminate(uint32 serviceId) external returns (bool success, uint64 refund)
  516. {
  517. // Reentrancy guard
  518. if (_locked > 1) {
  519. revert("ReentrancyGuard");
  520. }
  521. _locked = 2;
  522. Service storage service = services[serviceId];
  523. // Check for the service authority
  524. address serviceOwner = service.serviceOwner;
  525. requireSigner(serviceOwner);
  526. // Check if the service is already terminated
  527. if (service.state == ServiceState.PreRegistration || service.state == ServiceState.TerminatedBonded) {
  528. revert("WrongServiceState");
  529. }
  530. // Define the state of the service depending on the number of bonded agent instances
  531. if (service.numAgentInstances > 0) {
  532. service.state = ServiceState.TerminatedBonded;
  533. } else {
  534. service.state = ServiceState.PreRegistration;
  535. }
  536. // Return registration deposit back to the service owner
  537. refund = service.securityDeposit;
  538. // TODO: Figure out the escrow release
  539. // // By design, the refund is always a non-zero value, so no check is needed here fo that
  540. // (bool result, ) = serviceOwner.call{value: refund}("");
  541. // if (!result) {
  542. // revert TransferFailed(address(0), address(this), serviceOwner, refund);
  543. // }
  544. emit Refund(serviceOwner, refund);
  545. emit TerminateService(serviceId);
  546. success = true;
  547. _locked = 1;
  548. }
  549. /// @dev Unbonds agent instances of the operator from the service.
  550. /// @param operator Operator of agent instances.
  551. /// @param serviceId Service Id.
  552. /// @return success True, if function executed successfully.
  553. /// @return refund The amount of refund returned to the operator.
  554. function unbond(address operator, uint32 serviceId) external returns (bool success, uint64 refund) {
  555. // Reentrancy guard
  556. if (_locked > 1) {
  557. revert("ReentrancyGuard");
  558. }
  559. _locked = 2;
  560. // Checks if the operator address is not zero
  561. if (operator == address(0)) {
  562. revert("ZeroAddress");
  563. }
  564. Service storage service = services[serviceId];
  565. // Service can only be in the terminated-bonded state or expired-registration in order to proceed
  566. if (service.state != ServiceState.TerminatedBonded) {
  567. revert("WrongServiceState");
  568. }
  569. // Check for the operator and unbond all its agent instances
  570. uint32 numAgentsUnbond = 0;
  571. for (uint32 j = 0; j < service.operators.length; ++j) {
  572. if (operator == service.operators[j]) {
  573. // Get the agent Id of the agent instance
  574. uint32 agentId = service.agentIdForAgentInstances[j];
  575. // Remove the operator from the set of service operators
  576. service.operators[j] = address(0);
  577. numAgentsUnbond++;
  578. // Add the bond associated with the agent Id to the refund
  579. for (uint32 idx = 0; idx < service.agentIds.length; ++idx) {
  580. if (service.agentIds[idx] == agentId) {
  581. refund += uint64(service.bonds[idx]);
  582. break;
  583. }
  584. }
  585. }
  586. }
  587. if (numAgentsUnbond == 0) {
  588. revert("OperatorHasNoInstances");
  589. }
  590. // Subtract number of unbonded agent instances
  591. service.numAgentInstances -= numAgentsUnbond;
  592. // When number of instances is equal to zero, all the operators have unbonded and the service is moved into
  593. // the PreRegistration state, from where it can be updated / start registration / get deployed again
  594. if (service.numAgentInstances == 0) {
  595. delete service.operators;
  596. delete service.agentInstances;
  597. delete service.agentIdForAgentInstances;
  598. service.state = ServiceState.PreRegistration;
  599. }
  600. // else condition is redundant here, since the service is either in the TerminatedBonded state, or moved
  601. // into the PreRegistration state and unbonding is not possible before the new TerminatedBonded state is reached
  602. // Calculate the refund
  603. bytes32 operatorServiceIdHash = keccak256(abi.encode(operator, serviceId));
  604. uint64 balance = mapOperatorAndServiceIdOperatorBalances[operatorServiceIdHash];
  605. // This situation is possible if the operator was slashed for the agent instance misbehavior
  606. if (refund > balance) {
  607. refund = balance;
  608. }
  609. // Refund the operator
  610. if (refund > 0) {
  611. // Operator's balance is essentially zero after the refund
  612. mapOperatorAndServiceIdOperatorBalances[operatorServiceIdHash] = 0;
  613. // TODO: Figure out the escrow release
  614. // // Send the refund
  615. // (bool result, ) = operator.call{value: refund}("");
  616. // if (!result) {
  617. // revert("TransferFailed");
  618. // }
  619. emit Refund(operator, refund);
  620. }
  621. emit OperatorUnbond(operator, serviceId);
  622. success = true;
  623. _locked = 1;
  624. }
  625. /// @dev Gets the service instance.
  626. /// @param serviceId Service Id.
  627. /// @return service Corresponding Service struct.
  628. function getService(uint32 serviceId) external view returns (Service memory service) {
  629. service = services[serviceId];
  630. }
  631. /// @dev Gets service agent parameters: number of agent instances (slots) and a bond amount.
  632. /// @param serviceId Service Id.
  633. /// @return numAgentIds Number of canonical agent Ids in the service.
  634. /// @return slots Set of agent instances number for each agent Id.
  635. /// @return bonds Corresponding set of required bonds to register an agent instance in the service.
  636. function getAgentParams(uint32 serviceId) external view
  637. returns (uint32 numAgentIds, uint32[] memory slots, uint64[] memory bonds)
  638. {
  639. Service memory service = services[serviceId];
  640. numAgentIds = service.agentIds.length;
  641. slots = new uint32[](numAgentIds);
  642. bonds = new uint64[](numAgentIds);
  643. for (uint32 i = 0; i < numAgentIds; ++i) {
  644. slots[i] = service.slots[i];
  645. bonds[i] = service.bonds[i];
  646. }
  647. }
  648. // /// @dev Lists all the instances of a given canonical agent Id if the service.
  649. // /// @param serviceId Service Id.
  650. // /// @param agentId Canonical agent Id.
  651. // /// @return numAgentInstances Number of agent instances.
  652. // /// @return agentInstances Set of agent instances for a specified canonical agent Id.
  653. // function getInstancesForAgentId(uint32 serviceId, uint32 agentId) external view
  654. // returns (uint32 numAgentInstances, address[] memory agentInstances)
  655. // {
  656. // numAgentInstances = mapServiceAndAgentIdAgentInstances[serviceId][agentId].length;
  657. // agentInstances = new address[](numAgentInstances);
  658. // for (uint32 i = 0; i < numAgentInstances; i++) {
  659. // agentInstances[i] = mapServiceAndAgentIdAgentInstances[serviceId][agentId][i];
  660. // }
  661. // }
  662. /// @dev Gets service agent instances.
  663. /// @param serviceId ServiceId.
  664. /// @return numAgentInstances Number of agent instances.
  665. /// @return agentInstances Pre-allocated list of agent instance addresses.
  666. function getAgentInstances(uint32 serviceId) external view
  667. returns (uint32 numAgentInstances, address[] memory agentInstances)
  668. {
  669. Service memory service = services[serviceId];
  670. agentInstances = service.agentInstances;
  671. numAgentInstances = agentInstances.length;
  672. }
  673. /// @dev Gets the operator's balance in a specific service.
  674. /// @param operator Operator address.
  675. /// @param serviceId Service Id.
  676. /// @return balance The balance of the operator.
  677. function getOperatorBalance(address operator, uint32 serviceId) external view returns (uint64 balance)
  678. {
  679. bytes32 operatorServiceIdHash = keccak256(abi.encode(operator, serviceId));
  680. balance = mapOperatorAndServiceIdOperatorBalances[operatorServiceIdHash];
  681. }
  682. /// @dev Controls multisig implementation address permission.
  683. /// @param multisig Address of a multisig implementation.
  684. /// @param permission Grant or revoke permission.
  685. /// @return success True, if function executed successfully.
  686. function changeMultisigPermission(address multisig, bool permission) external returns (bool success) {
  687. // Check for the contract authority
  688. requireSigner(owner);
  689. if (multisig == address(0)) {
  690. revert("ZeroAddress");
  691. }
  692. mapMultisigs[multisig] = permission;
  693. success = true;
  694. }
  695. /// @dev Drains slashed funds.
  696. /// @return amount Drained amount.
  697. function drain() external returns (uint64 amount) {
  698. // Reentrancy guard
  699. if (_locked > 1) {
  700. revert("ReentrancyGuard");
  701. }
  702. _locked = 2;
  703. // Check for the drainer address
  704. requireSigner(drainer);
  705. // Drain the slashed funds
  706. amount = slashedFunds;
  707. if (amount > 0) {
  708. slashedFunds = 0;
  709. // TODO: Figure out the amount send
  710. // Send the amount
  711. // (bool result, ) = msg.sender.call{value: amount}("");
  712. // if (!result) {
  713. // revert TransferFailed(address(0), address(this), msg.sender, amount);
  714. // }
  715. emit Drain(drainer, amount);
  716. }
  717. _locked = 1;
  718. }
  719. function ownerOf(uint32 serviceId) public view returns (address) {
  720. return services[serviceId].serviceOwner;
  721. }
  722. /// @dev Checks for the service existence.
  723. /// @notice Service counter starts from 1.
  724. /// @param serviceId Service Id.
  725. /// @return true if the service exists, false otherwise.
  726. function exists(uint32 serviceId) public view returns (bool) {
  727. return serviceId > 0 && serviceId < (totalSupply + 1);
  728. }
  729. /// @dev Sets service base URI.
  730. /// @param bURI Base URI string.
  731. function setBaseURI(string memory bURI) external {
  732. requireSigner(owner);
  733. // Check for the zero value
  734. if (bytes(bURI).length == 0) {
  735. revert("Zero Value");
  736. }
  737. baseURI = bURI;
  738. emit BaseURIChanged(bURI);
  739. }
  740. /// @dev Gets the valid service Id from the provided index.
  741. /// @notice Service counter starts from 1.
  742. /// @param id Service counter.
  743. /// @return serviceId Service Id.
  744. function tokenByIndex(uint32 id) external view returns (uint32 serviceId) {
  745. serviceId = id + 1;
  746. if (serviceId > totalSupply) {
  747. revert("Overflow");
  748. }
  749. }
  750. // Open sourced from: https://stackoverflow.com/questions/67893318/solidity-how-to-represent-bytes32-as-string
  751. /// @dev Converts bytes16 input data to hex16.
  752. /// @notice This method converts bytes into the same bytes-character hex16 representation.
  753. /// @param data bytes16 input data.
  754. /// @return result hex16 conversion from the input bytes16 data.
  755. function _toHex16(bytes16 data) internal pure returns (bytes32 result) {
  756. result = bytes32 (data) & 0xFFFFFFFFFFFFFFFF000000000000000000000000000000000000000000000000 |
  757. (bytes32 (data) & 0x0000000000000000FFFFFFFFFFFFFFFF00000000000000000000000000000000) >> 64;
  758. result = result & 0xFFFFFFFF000000000000000000000000FFFFFFFF000000000000000000000000 |
  759. (result & 0x00000000FFFFFFFF000000000000000000000000FFFFFFFF0000000000000000) >> 32;
  760. result = result & 0xFFFF000000000000FFFF000000000000FFFF000000000000FFFF000000000000 |
  761. (result & 0x0000FFFF000000000000FFFF000000000000FFFF000000000000FFFF00000000) >> 16;
  762. result = result & 0xFF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 |
  763. (result & 0x00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000) >> 8;
  764. result = (result & 0xF000F000F000F000F000F000F000F000F000F000F000F000F000F000F000F000) >> 4 |
  765. (result & 0x0F000F000F000F000F000F000F000F000F000F000F000F000F000F000F000F00) >> 8;
  766. result = bytes32 (0x3030303030303030303030303030303030303030303030303030303030303030 +
  767. uint256 (result) +
  768. (uint256 (result) + 0x0606060606060606060606060606060606060606060606060606060606060606 >> 4 &
  769. 0x0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F) * 39);
  770. }
  771. /// @dev Returns service token URI.
  772. /// @notice Expected multicodec: dag-pb; hashing function: sha2-256, with base16 encoding and leading CID_PREFIX removed.
  773. /// @param serviceId Service Id.
  774. /// @return Service token URI string.
  775. function tokenURI(uint32 serviceId) public view returns (string memory) {
  776. bytes32 serviceHash = services[serviceId].configHash;
  777. // Parse 2 parts of bytes32 into left and right hex16 representation, and concatenate into string
  778. // adding the base URI and a cid prefix for the full base16 multibase prefix IPFS hash representation
  779. return string(abi.encodePacked(baseURI, CID_PREFIX, _toHex16(bytes16(serviceHash)),
  780. _toHex16(bytes16(serviceHash << 128))));
  781. }
  782. }
  783. // ---- Expect: diagnostics ----
  784. // warning: 204:5-209:14: function can be declared 'pure'
  785. // warning: 500:22-26: function parameter 'data' is unused
  786. // warning: 525:26-40: local variable 'agentInstances' is unused
  787. // warning: 867:31-35: function parameter 'data' is unused