Skip to content
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

Feat/eng 483 create jobs #19

Merged
merged 16 commits into from
Jul 25, 2018
61 changes: 57 additions & 4 deletions contracts/JobManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,74 @@ import "zeppelin-solidity/contracts/math/SafeMath.sol";
contract JobManager {
using SafeMath for uint;

enum MineralCategory { Compute, Storage }

event MineralAdded (
uint id,
string name
string name,
address producer,
MineralCategory category
);

event JobAdded (
uint id,
uint mineralId,
uint minPricePerMineral,
uint expirationBlock
);

struct Mineral {
string name;
address producer;
MineralCategory category;
}

struct Job {
uint mineralId;
uint minPricePerMineral;
// TODO: Add consumer address
uint expirationBlock;
}

uint public numberOfMinerals;
mapping(uint => Mineral) public minerals;

function submitMineral(string _name) external {
minerals[numberOfMinerals] = Mineral(_name);
emit MineralAdded(numberOfMinerals, _name);
uint public numberOfJobs;
mapping(uint => Job) public jobs;

function submitMineral(string _name, uint _category) external {
// name should be non null
require(bytes(_name).length > 0);
// Mineral has to be one of two categories: Compute or Storage
MineralCategory mc;
if(_category == uint(MineralCategory.Compute)) {
mc = MineralCategory.Compute;
} else if (_category == uint(MineralCategory.Storage)) {
mc = MineralCategory.Storage;
} else {
revert();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, given that you both cannot create a mineral with category Null and cannot update a mineral to have category Null, I would say that this enum option can be removed.

}
minerals[numberOfMinerals] = Mineral(_name, msg.sender, mc);
emit MineralAdded(numberOfMinerals, _name, msg.sender, mc);
numberOfMinerals = numberOfMinerals.add(1);
}

function mineralIsValid(uint _mineralId) public view returns (bool) {
Mineral memory m = minerals[_mineralId];
return m.producer != address(0);
}

function submitJob(uint _mineralId, uint _minPricePerMineral, uint _expirationBlock) external {
// mineralId must correspond to an existing mineral
require(mineralIsValid(_mineralId));
// expirationBlock must be in the future
require(_expirationBlock > block.number);

Job storage j = jobs[numberOfJobs];
j.mineralId = _mineralId;
j.minPricePerMineral = _minPricePerMineral;
j.expirationBlock = _expirationBlock;
emit JobAdded(numberOfJobs, _mineralId, _minPricePerMineral, _expirationBlock);
numberOfJobs = numberOfJobs.add(1);
}
}
21 changes: 13 additions & 8 deletions contracts/TransmuteDPOS.sol
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ contract TransmuteDPOS is TransmuteToken, RoundManager, ProviderPool {
enum ProviderStatus { Unregistered, Registered }

struct Provider {
ProviderStatus status;
uint pricePerStorageMineral;
uint pricePerComputeMineral;
uint blockRewardCut;
Expand Down Expand Up @@ -86,22 +85,21 @@ contract TransmuteDPOS is TransmuteToken, RoundManager, ProviderPool {
Delegator storage d = delegators[msg.sender];
// Provider has to be a Delegator to himself
require(d.delegateAddress == msg.sender);
if (p.status == ProviderStatus.Unregistered) {
if (providerStatus(msg.sender) == ProviderStatus.Unregistered) {
numberOfProviders = numberOfProviders.add(1);
addProvider(msg.sender, p.totalAmountBonded);
emit ProviderAdded(msg.sender, _pricePerStorageMineral, _pricePerComputeMineral, _blockRewardCut, _feeShare);
} else {
emit ProviderUpdated(msg.sender, _pricePerStorageMineral, _pricePerComputeMineral, _blockRewardCut, _feeShare);
}
p.status = ProviderStatus.Registered;
p.pricePerStorageMineral = _pricePerStorageMineral;
p.pricePerComputeMineral = _pricePerComputeMineral;
p.blockRewardCut = _blockRewardCut;
p.feeShare = _feeShare;
}

function resignAsProvider(address _provider) internal {
require(providers[_provider].status != ProviderStatus.Unregistered);
require(providerStatus(_provider) == ProviderStatus.Registered);
removeProvider(_provider);
delete providers[_provider];
emit ProviderResigned(_provider);
Expand All @@ -112,14 +110,15 @@ contract TransmuteDPOS is TransmuteToken, RoundManager, ProviderPool {
Provider storage p = providers[_provider];
// A delegator is only allowed to bond to himself (in which case he wants to be a Provider)
// or to a Registered Provider
require(_provider == msg.sender || p.status == ProviderStatus.Registered);
ProviderStatus pStatus = providerStatus(_provider);
require(_provider == msg.sender || pStatus == ProviderStatus.Registered);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why have a temp variable here and not just use the function in the equality check like you did on line 102?

// Check if delegator has not already bonded to some address
require(delegators[msg.sender].delegateAddress == address(0));
this.transferFrom(msg.sender, this, _amount);
delegators[msg.sender] = Delegator(_provider, _amount);
p.totalAmountBonded = p.totalAmountBonded.add(_amount);
// Update the bonded amount of the provider in the pool
if (p.status == ProviderStatus.Registered) {
if (pStatus == ProviderStatus.Registered) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I suppose you reuse this variable - fair enough. Disregard the above.

updateProvider(_provider, p.totalAmountBonded);
}
emit DelegatorBonded(msg.sender, _provider, _amount);
Expand Down Expand Up @@ -157,8 +156,14 @@ contract TransmuteDPOS is TransmuteToken, RoundManager, ProviderPool {
delete withdrawInformations[msg.sender];
}

// TODO: Create the same function for Providers
// This will remove the need for ProviderStatus inside the Provider Struct
function providerStatus(address _provider) public view returns (ProviderStatus) {
if (this.containsProvider(_provider)) {
return ProviderStatus.Registered;
} else {
return ProviderStatus.Unregistered;
}
}

function delegatorStatus(address _delegator) public view returns (DelegatorStatus) {
if (delegators[_delegator].amountBonded != 0) {
// If _delegator is in the mapping, he is Bonded
Expand Down
94 changes: 87 additions & 7 deletions test/JobManager.spec.js
Original file line number Diff line number Diff line change
@@ -1,37 +1,117 @@
const JobManager = artifacts.require('./JobManager.sol');
const { assertFail } = require('./utils.js');
require('truffle-test-utils').init();

contract('JobManager', accounts => {

let jm;
const MINERAL_COMPUTE = 0;
const MINERAL_STORAGE = 1;

describe('submitMineral', () => {

before(async () => {
jm = await JobManager.deployed();
});

it('should fail if category is not MINERAL_COMPUTE or MINERAL_STORAGE', async () => {
await jm.submitMineral("test", MINERAL_COMPUTE);
await jm.submitMineral("test", MINERAL_STORAGE);
await assertFail( jm.submitMineral("test", 2) );
});

it('should store the Mineral in the minerals mapping', async () => {
await jm.submitMineral('multiplication');
const mineral = await jm.minerals.call(0);
assert.equal('multiplication', mineral);
const mineralId = (await jm.numberOfMinerals.call()).toNumber();
await jm.submitMineral('multiplication', MINERAL_COMPUTE, {from: accounts[0]});
const mineral = await jm.minerals.call(mineralId);
let [name, producer, category] = mineral;
assert.equal('multiplication', name);
assert.equal(accounts[0], producer);
assert.equal(MINERAL_COMPUTE, category);
});

it('should increase the value of numberOfMinerals', async () => {
it('should increment numberOfMinerals', async () => {
const numberOfMinerals = await jm.numberOfMinerals.call();
await jm.submitMineral('addition');
await jm.submitMineral('addition', MINERAL_COMPUTE);
assert.deepEqual(numberOfMinerals.add(1), await jm.numberOfMinerals.call())
});

it('should emit a MineralAdded event', async () => {
let result = await jm.submitMineral('division');
const mineralId = (await jm.numberOfMinerals.call()).toNumber();
let result = await jm.submitMineral('division', MINERAL_COMPUTE, {from: accounts[0]});
assert.web3Event(result, {
event: 'MineralAdded',
args: {
id: 2,
id: mineralId,
name: 'division',
producer: accounts[0],
category: MINERAL_COMPUTE,
}
});
});

it('should fail if name is an empty string', async () => {
const mineralId = (await jm.numberOfMinerals.call()).toNumber();
await assertFail( jm.submitMineral("", MINERAL_COMPUTE) );
await jm.submitMineral("non empty string", MINERAL_COMPUTE);
const mineral = await jm.minerals.call(mineralId);
let name = mineral[0];
assert.equal("non empty string", name);
});
});

describe('submitJob', () => {

let expirationBlock = web3.eth.blockNumber + 1000;

before(async () => {
jm = await JobManager.new();
await jm.submitMineral("multiplication", MINERAL_COMPUTE);
await jm.submitMineral("addition", MINERAL_COMPUTE);
});

it('should fail if mineralId is not the id of a valid Mineral', async () => {
await assertFail( jm.submitJob(2, 10, expirationBlock) );
await jm.submitJob(0, 10, expirationBlock);
});

it('should fail if expiration block is not in the future', async () => {
const blockInThePast = web3.eth.blockNumber;
await assertFail( jm.submitJob(0, 10, blockInThePast) );
const presentBlock = web3.eth.blockNumber + 1;
await assertFail( jm.submitJob(0, 10, presentBlock) );
const blockInTheFuture = web3.eth.blockNumber + 2;
await jm.submitJob(0, 10, blockInTheFuture);
});

it('should store the Job parameters in the jobs mapping', async () => {
const jobId = await jm.numberOfJobs.call();
await jm.submitJob(1, 11, expirationBlock + 42);
const job = await jm.jobs.call(jobId);
const [mineralId, minPricePerMineral, expBlock] = job;
assert.equal(1, mineralId);
assert.equal(11, minPricePerMineral);
assert.equal(expirationBlock + 42, expBlock);
});

it('should emit a JobAdded event', async () => {
const jobId = await jm.numberOfJobs.call();
let result = await jm.submitJob(1, 12, expirationBlock);
assert.web3Event(result, {
event: 'JobAdded',
args: {
id: jobId.toNumber(),
mineralId: 1,
minPricePerMineral: 12,
expirationBlock: expirationBlock
}
});
});

it('should increment numberOfJobs', async () => {
const numberOfJobs = await jm.numberOfJobs.call();
await jm.submitJob(1, 12, expirationBlock);
assert.deepEqual(numberOfJobs.add(1), await jm.numberOfJobs.call())
});
});
});
Loading