-
Notifications
You must be signed in to change notification settings - Fork 45
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement LIP-9: Service Registry #223
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are there any potential negative externalities for the rest of the network if a bunch of non-transcoders set their service URI records here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm since a broadcaster client would only care about the service URI record for the transcoder address that was assigned to the broadcaster's transcode job if non-transcoder users set their own service URI record they would pay the tx cost, but at least in the current proposed networking architecture those records will never be used in practice unless the non-transcoder user becomes a transcoder in the future. Perhaps something to be wary about though is if a transcoder's key is compromised such that an attacker can change the service URI record right before a broadcaster looks it up to send requests to the transcoder There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, keeping the transcoder's keys keys cold would entail something like the discussion in livepeer/LIPs#7 which probably should be done separately from this change. I was mostly wondering if filling the record mapping with unneeded entries could lead to higher costs for the network down the road, eg during a migration or with any enactment of 'data rents', but that's all fairly speculative. LGTM. |
||
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; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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") | ||
}) | ||
}) | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we changed the Record struct in the future, the storage layout will be screwy right? If that's the case, I prefer to have the mapping from
address
tostring
, that way this potential mistake doesn't happen.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding additional fields to the
Record
struct in an upgrade is safe as long as 1) we do not remove/replace existing fields 2) theRecord
struct is not used in an array (it is only used in a mapping right now)