-
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
claimWork and verify without transcoding verification #35
Conversation
…Need to refactor to deploy due to gas costs
@@ -665,7 +682,7 @@ contract LivepeerProtocol { | |||
* for its time window | |||
* @param _transcoder Address of transcoder | |||
*/ | |||
function validRewardTimeWindow(address _transcoder) internal returns (bool) { | |||
function validRewardTimeWindow(address _transcoder) internal constant returns (bool) { |
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.
No state changes in this function so should be marked constant
test/LivepeerProtocol.js
Outdated
}); | ||
|
||
it("should fail if the segment is not eligible for verification", async function() { | ||
|
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.
This is left blank for now because still figuring out the best way to test segment verification eligibility
const d3 = Buffer.from("4ce8765e720c576f6f5a34ca380b3de5f0912e6e3cc5355542c363891e54594b", "hex"); | ||
|
||
// Segment hashes (streamId, segmentSequenceNumber, dataHash) | ||
const s0 = abi.soliditySHA3(["uint256", "uint256", "bytes"], [streamId, 0, d0]); |
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.
Solidity's built-in sha3
function accepts an arbitrary of number of arguments and tightly packs them together meaning they are concatenated without padding. The soliditySHA3
function from ethereumjs-abi
tightly packs arguments before hashing thereby following the behavior of Solidity's sha3
in Javascript.
contracts/TranscodeJobs.sol
Outdated
// Check if sender is assigned transcoder | ||
if (self.jobs[_jobId].transcoderAddress != msg.sender) throw; | ||
// Check if previous verification period over | ||
if (block.number < self.jobs[_jobId].endVerificationBlock) throw; |
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.
For now we force transcoders to call verify a call to claimWork()
before they can make a subsequent call to claimWork()
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.
In the future, if verification finishes, this also should be able to be called even if endVerificationBLock
hasn't been reached.
contracts/TranscodeJobs.sol
Outdated
// Check if job is active | ||
if (jobStatus(self, _jobId) != JobStatus.Active) throw; | ||
// Check if sender is assigned transcoder | ||
if (self.jobs[_jobId].transcoderAddress != msg.sender) throw; |
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.
Only the assigned transcoder for a job can submit transcode claims for a job. Therefore, the transcode claims used to form the submitted Merkle root do not need to be signed by the transcoder
contracts/TranscodeJobs.sol
Outdated
* @param _segmentSequenceNumber Sequence number of segment in stream | ||
* @param _verificationRate Rate at which a particular segment should be verified | ||
*/ | ||
function shouldVerifySegment(uint256 _segmentSequenceNumber, |
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.
Rather than access state (i.e. a Jobs
struct), all dependencies are passed in as parameters in order to make this helper function easier to test.
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.
Awesome work. A lot to dig into here. I think we should err on the side of overcommenting for now because it's a lot to wrap your head around if you're trying to understand the protocol and tests from the outside.
contracts/TranscodeJobs.sol
Outdated
} | ||
} | ||
|
||
function newJob(Jobs storage self, uint256 _streamId, bytes32 _transcodingOptions, uint256 _maxPricePerSegment, address _electedTranscoder) returns (bool) { |
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.
might need to do validation on _transcodingOptions, but we'll leave that for the future.
contracts/TranscodeJobs.sol
Outdated
); | ||
} | ||
|
||
function getJobWorkDetails(Jobs storage self, uint256 _jobId) constant returns (uint256, uint256, bytes32) { |
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.
At the minimum let's add a comment on what this function is used for and how it's different than getJob()
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.
or perhaps rename to getJobTranscodeClaimDetails()
contracts/TranscodeJobs.sol
Outdated
// Check if sender is assigned transcoder | ||
if (self.jobs[_jobId].transcoderAddress != msg.sender) throw; | ||
// Check if previous verification period over | ||
if (block.number < self.jobs[_jobId].endVerificationBlock) throw; |
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.
In the future, if verification finishes, this also should be able to be called even if endVerificationBLock
hasn't been reached.
contracts/TranscodeJobs.sol
Outdated
return true; | ||
} | ||
|
||
function verify(Jobs storage self, uint256 _jobId, uint256 _segmentSequenceNumber, bytes32 _dataHash, bytes32 _transcodedDataHash, bytes _broadcasterSig, bytes _proof, uint64 _verificationRate) constant returns (bool) { |
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.
Should we label this (and other functions) "internal", or whatever the proper scoping is to prevent it from being called externally by users?
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.
Yondon mentioned that marking it internal possibly causes issues with deployment of the protocol contract. We can double check.
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.
I would also consider changing the name of this function since "Verification" is a term that means verifying the offchain work, whereas this is just validating that the claim was correct.
contracts/TranscodeJobs.sol
Outdated
_verificationRate | ||
)) return false; | ||
// Check if segment was signed by broadcaster | ||
if (!ECVerify.ecverify(sha3(self.jobs[_jobId].streamId, _segmentSequenceNumber, _dataHash), _broadcasterSig, self.jobs[_jobId].broadcasterAddress)) return false; |
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.
Let's consider using the keccak256
version of the alias since the hash is actually not sha3
according to spec. This is minor. Don't need to do it, but we could consider being trailblazers of truth.
* @param _broadcasterSig Broadcaster's signature over segment | ||
* @param _proof Merkle proof for the signed transcode claim | ||
*/ | ||
function verify(uint256 _jobId, uint256 _segmentSequenceNumber, bytes32 _dataHash, bytes32 _transcodedDataHash, bytes _broadcasterSig, bytes _proof) returns (bool) { |
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.
Will possibly be payable
since someone needs to pay Oraclize or Truebit. TBD whether we can use LPT for this or need to use Ether.
test/LivepeerProtocol.js
Outdated
const proof = merkleTree.getHexProof(tClaim0); | ||
|
||
// Account 1 calls verify | ||
await instance.verify(0, 0, utils.bufferToHex(d0), utils.bufferToHex(tD0), utils.bufferToHex(bSig0), proof, {from: accounts[1]}); |
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.
Should check that this does not throw.
test/LivepeerProtocol.js
Outdated
|
||
try { | ||
// Account 1 calls verify | ||
await instance.verify(0, 2, utils.bufferToHex(d2), utils.bufferToHex(tD2), utils.bufferToHex(bSig3), proof, {from: accounts[1]}); |
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.
Fails because of bSig3 when it should be bSig2.
let hashNum = new BigNumber(hash, 16); | ||
let res = hashNum.mod(verificationRate); | ||
|
||
while (res != 0) { |
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.
Let's add a comment about why we're iterating here to find a suitable candidate segment number.
let hashNum = new BigNumber(hash, 16); | ||
let res = hashNum.mod(verificationRate); | ||
|
||
while (res == 0) { |
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.
Same comment as above about why we're looping here.
In response to the comment on marking library functions in If many internal library functions are called by a contract, the bytecode size of the contract will increase along with the gas costs of deploying the contract. I tested this out locally and marking all the relevant functions in I don't think its too big of an issue to have the functions in |
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.
Looks good. 🚢
Addresses #23 and #24
ECVerify.sol
andMerkleProof.sol
libraries. The functionality of both these libraries are both being added to OpenZeppelin - once the relevant PRs are merged in we can remove these pasted in libraries and directly use the libraries in OpenZeppelin.TranscodeJobs.sol
library. This was needed to deploy theLivepeerProtocol.sol
contract without exceeding gas limits. Should also be a +1 for code modularity