From 056fab79d74782207b0f7039bb1482f6d97c3a9d Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Tue, 5 Jun 2018 11:33:11 -0400 Subject: [PATCH 1/2] Implement LIP-9. Add ServiceRegistry.sol with accompanying unit tests --- contracts/ServiceRegistry.sol | 45 +++++++++++++++++++++ test/unit/ServiceRegistry.js | 75 +++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 contracts/ServiceRegistry.sol create mode 100644 test/unit/ServiceRegistry.js diff --git a/contracts/ServiceRegistry.sol b/contracts/ServiceRegistry.sol new file mode 100644 index 00000000..5db6433c --- /dev/null +++ b/contracts/ServiceRegistry.sol @@ -0,0 +1,45 @@ +pragma solidity ^0.4.17; + +import "./ManagerProxyTarget.sol"; + + +/** + * @title ServiceRegistry + * @dev Maintains a registry of service metadata associated with service provider addresses (transcoders/orchestrators) + */ +contract ServiceRegistry is ManagerProxyTarget { + // Store service metadata + struct Record { + string serviceURI; // Service URI endpoint that can be used to send off-chain requests + } + + // Track records for addresses + mapping (address => Record) private records; + + // Event fired when a caller updates its service URI endpoint + event ServiceURIUpdate(address indexed addr, string serviceURI); + + /** + * @dev ServiceRegistry constructor. Only invokes constructor of base Manager contract with provided Controller address + * @param _controller Address of a Controller that this contract will be registered with + */ + function ServiceRegistry(address _controller) public Manager(_controller) {} + + /** + * @dev Stores service URI endpoint for the caller that can be used to send requests to the caller off-chain + * @param _serviceURI Service URI endpoint for the caller + */ + function setServiceURI(string _serviceURI) external { + records[msg.sender].serviceURI = _serviceURI; + + ServiceURIUpdate(msg.sender, _serviceURI); + } + + /** + * @dev Returns service URI endpoint stored for a given address + * @param _addr Address for which a service URI endpoint is desired + */ + function getServiceURI(address _addr) public view returns (string) { + return records[_addr].serviceURI; + } +} \ No newline at end of file diff --git a/test/unit/ServiceRegistry.js b/test/unit/ServiceRegistry.js new file mode 100644 index 00000000..097f9d04 --- /dev/null +++ b/test/unit/ServiceRegistry.js @@ -0,0 +1,75 @@ +import Fixture from "./helpers/fixture" + +const ServiceRegistry = artifacts.require("ServiceRegistry") + +contract("ServiceRegistry", accounts => { + describe("constructor", () => { + it("invokes base Manager contract constructor", async () => { + // Use dummy Controller + const controller = accounts[0] + const registry = await ServiceRegistry.new(controller) + + assert.equal(await registry.controller.call(), controller, "wrong Controller address") + }) + }) + + let fixture + let registry + + before(async () => { + fixture = new Fixture(web3) + + // Use dummy Controller in these unit tests + // We are testing the logic of ServiceRegistry directly so we do not + // interact with the contract via a proxy + // Thus, we do not need an actual Controller for the tests + const controller = accounts[0] + + registry = await ServiceRegistry.new(controller) + }) + + beforeEach(async () => { + await fixture.setUp() + }) + + afterEach(async () => { + await fixture.tearDown() + }) + + describe("setServiceURI", () => { + it("stores service URI endpoint for caller", async () => { + await registry.setServiceURI("foo", {from: accounts[0]}) + await registry.setServiceURI("bar", {from: accounts[1]}) + + assert.equal(await registry.getServiceURI(accounts[0]), "foo", "wrong service URI stored for caller 1") + assert.equal(await registry.getServiceURI(accounts[1]), "bar", "wrong service URI stored for caller 2") + }) + + it("fires ServiceURIUpdate event", async () => { + let e = registry.ServiceURIUpdate({}) + + e.watch(async (err, result) => { + e.stopWatching() + + assert.equal(result.args.addr, accounts[0], "wrong address in ServiceURIUpdate event") + assert.equal(result.args.serviceURI, "foo", "wrong service URI in ServiceURIUpdate event") + }) + + await registry.setServiceURI("foo", {from: accounts[0]}) + }) + }) + + describe("getServiceURI", () => { + it("returns service URI endpoint for provided address", async () => { + await registry.setServiceURI("foo", {from: accounts[0]}) + await registry.setServiceURI("bar", {from: accounts[1]}) + + assert.equal(await registry.getServiceURI(accounts[0]), "foo", "wrong service URI stored for caller 1") + assert.equal(await registry.getServiceURI(accounts[1]), "bar", "wrong service URI stored for caller 2") + }) + + it("returns empty string for address without stored service URI endpoint", async () => { + assert.equal(await registry.getServiceURI(accounts[5]), "", "should return empty string for address without service URI") + }) + }) +}) From 062a3b5e3e41e5a8390e218e41b0bc83cd16ec1f Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Tue, 5 Jun 2018 11:52:21 -0400 Subject: [PATCH 2/2] Add ServiceRegistry to migrations. Add registry related checks in job assignment integration tests --- migrations/3_deploy_contracts.js | 3 +++ test/integration/JobAssignment.js | 19 +++++++++++++++++++ test/unit/ServiceRegistry.js | 2 +- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/migrations/3_deploy_contracts.js b/migrations/3_deploy_contracts.js index b116d665..3dd848be 100644 --- a/migrations/3_deploy_contracts.js +++ b/migrations/3_deploy_contracts.js @@ -3,6 +3,7 @@ const ContractDeployer = require("../utils/contractDeployer") const Controller = artifacts.require("Controller") const Minter = artifacts.require("Minter") +const ServiceRegistry = artifacts.require("ServiceRegistry") const BondingManager = artifacts.require("BondingManager") const JobsManager = artifacts.require("JobsManager") const RoundsManager = artifacts.require("RoundsManager") @@ -38,6 +39,8 @@ module.exports = function(deployer, network) { roundsManager = await lpDeployer.deployProxyAndRegister(RoundsManager, "RoundsManager", controller.address) } + await lpDeployer.deployProxyAndRegister(ServiceRegistry, "ServiceRegistry", controller.address) + deployer.logger.log("Initializing contracts...") // Set BondingManager parameters diff --git a/test/integration/JobAssignment.js b/test/integration/JobAssignment.js index b941be00..22387b85 100644 --- a/test/integration/JobAssignment.js +++ b/test/integration/JobAssignment.js @@ -3,6 +3,7 @@ import {createTranscodingOptions} from "../../utils/videoProfile" import BigNumber from "bignumber.js" const Controller = artifacts.require("Controller") +const ServiceRegistry = artifacts.require("ServiceRegistry") const BondingManager = artifacts.require("BondingManager") const JobsManager = artifacts.require("JobsManager") const AdjustableRoundsManager = artifacts.require("AdjustableRoundsManager") @@ -12,6 +13,7 @@ contract("JobAssignment", accounts => { const TOKEN_UNIT = 10 ** 18 let controller + let registry let bondingManager let jobsManager let roundsManager @@ -33,6 +35,9 @@ contract("JobAssignment", accounts => { controller = await Controller.deployed() await controller.unpause() + const registryAddr = await controller.getContract(contractId("ServiceRegistry")) + registry = await ServiceRegistry.at(registryAddr) + const bondingManagerAddr = await controller.getContract(contractId("BondingManager")) bondingManager = await BondingManager.at(bondingManagerAddr) @@ -59,16 +64,22 @@ contract("JobAssignment", accounts => { await token.approve(bondingManager.address, 50000, {from: transcoder1}) await bondingManager.bond(50000, transcoder1, {from: transcoder1}) await bondingManager.transcoder(10, 5, 5, {from: transcoder1}) + // Set service URI for transcoder 1 + await registry.setServiceURI("transcoder1URI", {from: transcoder1}) // Register and bond transcoder 2 with 30% of the total active stake await token.approve(bondingManager.address, 30000, {from: transcoder2}) await bondingManager.bond(30000, transcoder2, {from: transcoder2}) await bondingManager.transcoder(10, 5, 1, {from: transcoder2}) + // Set service URI for transcoder 2 + await registry.setServiceURI("transcoder2URI", {from: transcoder2}) // Register and bond transcoder 3 with 20% of the total active stake await token.approve(bondingManager.address, 20000, {from: transcoder3}) await bondingManager.bond(20000, transcoder3, {from: transcoder3}) await bondingManager.transcoder(10, 5, 1, {from: transcoder3}) + // Set service URI for transcoder 3 + await registry.setServiceURI("transcoder3URI", {from: transcoder3}) // Initialize new round and initialize new active transcoder set await roundsManager.mineBlocks(roundLength.toNumber()) @@ -94,6 +105,7 @@ contract("JobAssignment", accounts => { let jobCreationRound let rand let electedTranscoder + let expServiceURI let jobsCreated = 0 @@ -111,19 +123,26 @@ contract("JobAssignment", accounts => { switch (electedTranscoder) { case transcoder1: + expServiceURI = "transcoder1URI" transcoder1JobCount++ break case transcoder2: + expServiceURI = "transcoder2URI" transcoder2JobCount++ break case transcoder3: + expServiceURI = "transcoder3URI" transcoder3JobCount++ break default: + expServiceURI = "" nullAddressJobCount++ break } + // Check for correct service URI + assert.equal(await registry.getServiceURI(electedTranscoder), expServiceURI, "wrong service URI") + await roundsManager.mineBlocks(1) if (!(await roundsManager.currentRoundInitialized())) { diff --git a/test/unit/ServiceRegistry.js b/test/unit/ServiceRegistry.js index 097f9d04..8241e354 100644 --- a/test/unit/ServiceRegistry.js +++ b/test/unit/ServiceRegistry.js @@ -1,4 +1,4 @@ -import Fixture from "./helpers/fixture" +import Fixture from "./helpers/Fixture" const ServiceRegistry = artifacts.require("ServiceRegistry")