Skip to content
This repository has been archived by the owner on Apr 25, 2023. It is now read-only.

Commit

Permalink
signature tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Christophe Le Bars committed Jul 24, 2018
1 parent 9e9fecb commit 0d15f68
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 64 deletions.
4 changes: 4 additions & 0 deletions contracts/RougeRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ contract RougeRegistry is RougeRegistryInterface {
return campaigns[issuer][index];
}

function get_last_campaign(address issuer) public view returns(address) {
return campaigns[issuer][campaigns[issuer].length - 1];
}

function get_mycount() public view returns(uint count) {
return campaigns[msg.sender].length;
}
Expand Down
80 changes: 41 additions & 39 deletions contracts/SimpleRougeCampaign.sol
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ contract SimpleRougeCampaign {
}

// web3.eth.sign compat prefix XXX mv to lib
function prefixed(bytes32 hash) internal pure returns (bytes32) {
return keccak256("\x19Ethereum Signed Message:\n32", hash);
function prefixed(bytes32 _message) internal pure returns (bytes32) {
return keccak256("\x19Ethereum Signed Message:\n32", _message);
}

string public name;
Expand Down Expand Up @@ -80,46 +80,48 @@ contract SimpleRougeCampaign {
return acquisitionRegister[_bearer];
}

/* low level transfer of a note between bearers */
function transfer(address _from, address _to) CampaignOpen private {
require(_to != issuer); /* RULE issuer and bearer need to be diffrent */
require(hasNote(_from));
acquisitionRegister[_from] = false;
acquisitionRegister[_to] = true;
}
event Acquisition(address indexed bearer);

function distributeNote(address _to) CampaignOpen private returns (bool success) {
require(_to != issuer); /* RULE: issuer and bearer need to be diffrent */
require(!hasNote(_to)); /* RULE: only one note per address (but not bearer) */
function acquisition(address _bearer) CampaignOpen private returns (bool success) {
require(_bearer != issuer); /* RULE: issuer and bearer need to be diffrent */
require(!hasNote(_bearer)); /* RULE: only one note per address */
require(!transferRegister[_bearer]); /* RULE transfer is not reversible */
if (available > 0) {
available -= 1;
acquired += 1;
acquisitionRegister[_to] = true;
acquisitionRegister[_bearer] = true;
emit Acquisition(_bearer);
return true;
} else {
return false;
}
}
/* ********** ********** ********** */
/* Demo functions that manage the acquisition process for notes */

function askForNote() CampaignOpen public returns (bool success) {

event Log(bytes32 hash);

// _hash is any hashed msg that confirm issuer authorisation for the note acquisition
function acquire(bytes32 _hash, uint8 v, bytes32 r, bytes32 s) CampaignOpen public returns (bool success) {
require(msg.sender != issuer);
require(!hasNote(msg.sender)); /* duplicate test. remove ? */

/* TODO send to approval contract => return always ok in these tests */

return distributeNote(msg.sender);
require(_hash == keccak256('acceptAcquisition', this, msg.sender));
require(ecrecover(prefixed(_hash), v, r, s) == issuer);
return acquisition(msg.sender);
}

function giveNote(address _to) onlyBy(issuer) CampaignOpen public returns (bool success) {
return distributeNote(_to);
function distributeNote(address _bearer) onlyBy(issuer) CampaignOpen public returns (bool success) {
return acquisition(_bearer);
}

/* ********** ********** ********** */
/* the redemtion Register (track notes effectively redeemed by Bearers) */
mapping (address => bool) public transferRegister;

/* low level transfer of a note between bearers */
function transfer(address _from, address _to) CampaignOpen private {
require(_to != issuer); /* RULE issuer and bearer need to be diffrent */
require(hasNote(_from));
acquisitionRegister[_from] = false;
transferRegister[_from] = true; /* RULE transfer is not reversible */
acquisitionRegister[_to] = true;
}

mapping (address => bool) redemptionRegister;

function hasRedeemed(address _bearer) constant public returns (bool yes) {
Expand All @@ -128,30 +130,30 @@ contract SimpleRougeCampaign {
return redemptionRegister[_bearer];
}

event Redeem(address indexed bearer);
event Redemption(address indexed bearer);

function redeemNote(address _bearer) CampaignOpen private returns (bool success) {
function redemption(address _bearer) CampaignOpen private returns (bool success) {
require(_bearer != issuer);
require(!hasRedeemed(_bearer));
redeemed += 1;
redemptionRegister[_bearer] = true;
emit Redeem(_bearer);
emit Redemption(_bearer);
return true;
}

// _hash is any hashed msg agreed between issuer and bearer
// WARNING: replay protection not implemented at protocol level
function acceptRedemption(address _bearer, bytes32 _hash, uint8 v, bytes32 r, bytes32 s)
CampaignOpen onlyBy(issuer) public returns (bool success) {
bytes32 message = prefixed(_hash);
require(ecrecover(message, v, r, s) == _bearer);
return redeemNote(_bearer);
function redeem(bytes32 _hash, uint8 v, bytes32 r, bytes32 s) CampaignOpen public returns (bool success) {
require(_hash == keccak256('acceptRedemption', this, msg.sender));
require(ecrecover(prefixed(_hash), v, r, s) == issuer);
return redemption(msg.sender);
}

function confirmRedemption(bytes32 _hash, uint8 v, bytes32 r, bytes32 s) CampaignOpen public returns (bool success) {
bytes32 message = prefixed(_hash);
require(ecrecover(message, v, r, s) == issuer);
return redeemNote(msg.sender);
function acceptRedemption(address _bearer, bytes32 _hash, uint8 v, bytes32 r, bytes32 s)
CampaignOpen onlyBy(issuer) public returns (bool success) {
require(_hash == keccak256('acceptRedemption', this, _bearer));
require(ecrecover(prefixed(_hash), v, r, s) == _bearer);
return redemption(_bearer);
}

function kill() onlyBy(issuer) public {
Expand Down
8 changes: 7 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,17 @@
"description": "The Rouge Project - Note and Voucher Protocol",
"main": "",
"keywords": [
"ethereum", "coupon", "voucher", "ticket"
"ethereum",
"coupon",
"voucher",
"ticket"
],
"authors": [
"Christophe Le Bars <[email protected]>",
"Valentin D. Guillois <[email protected]>"
],
"license": "AGPL",
"dependencies": {
"ethereumjs-abi": "^0.6.5"
}
}
103 changes: 79 additions & 24 deletions test/SimpleRougeCampaign.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,41 @@

var abi = require('ethereumjs-abi')
var BN = require('bn.js')

const RGEToken = artifacts.require("./TestRGEToken.sol");
const Factory = artifacts.require("./RougeFactory.sol");
const SimpleRougeCampaign = artifacts.require("./SimpleRougeCampaign.sol");

const tare = 0.1 * 10**6; /* tare price is 0.1 rge in beta phase */
const tare = 0.1 * 10**6; /* tare price is 0.1 rge in beta phase */
const tokens = 1000 * 10**6; /* issuer RGE tokens before campaign start */

const new_campaign = async function(rge, issuer, tokens, issuance, deposit) {
const new_campaign = async function(rge, issuer, issuance, deposit) {

const factory = await Factory.deployed();

await rge.giveMeRGE(tokens, {from: issuer});
const issuer_balance_before = await rge.balanceOf.call(issuer);

/* refill issuer tokens to test starting value */
await rge.giveMeRGE(tokens - issuer_balance_before, {from: issuer});

await rge.newCampaign(issuance, deposit, {from: issuer, gas: 2000000, gasPrice: web3.toWei(1, "gwei")})
const campaign_address = await factory.get_campaign.call(issuer, 0);
const campaign_address = await factory.get_last_campaign.call(issuer);

return SimpleRougeCampaign.at(campaign_address);

}

const get_signature = function(account, message) {
const create_auth_hash = function(msg, campaign, account) {

return '0x' + abi.soliditySHA3(
[ "string", "address", "address" ], [ msg, new BN(campaign, 16), new BN(account, 16) ]
).toString('hex')

}

const get_signature = function(account, hash) {

const signature = web3.eth.sign(account, web3.sha3(message)).substr(2)
const signature = web3.eth.sign(account, hash).substr(2)
return {
r: '0x' + signature.slice(0, 64),
s: '0x' + signature.slice(64, 128),
Expand All @@ -30,20 +46,19 @@ const get_signature = function(account, message) {

contract('SimpleRougeCampaign', function(accounts) {

it("simple tare burning test with no redemption", async function() {
it("no acquisition/noredemtion campaign", async function() {

const rge = await RGEToken.deployed();

const issuer = accounts[2];
const tokens = 1000 * 10**6;
const issuance = 10;
const deposit = 50 * 10**6;

const campaign = await new_campaign(rge, issuer, tokens, issuance, deposit);
const campaign = await new_campaign(rge, issuer, issuance, deposit);

// expiration of the campaign in 2 days
const expiration = Math.trunc((new Date()).getTime() / 1000) + 60*60*24*2
await campaign.issue('tare burning', expiration, {from: issuer});
await campaign.issue('no acquisition/noredemtion campaign', expiration, {from: issuer});

const available = await campaign.available.call();
assert.equal(available.toNumber(), issuance, "check notes available after issuance");
Expand All @@ -60,39 +75,36 @@ contract('SimpleRougeCampaign', function(accounts) {

});

it("acceptRedemption from issuer", async function() {
it("one distribution/one redemption by issuer", async function() {

const rge = await RGEToken.deployed();

const issuer = accounts[3];
const bearer = accounts[4];
const tokens = 1000 * 10**6;
const issuance = 10;
const deposit = 50 * 10**6;

const campaign = await new_campaign(rge, issuer, tokens, issuance, deposit);
const campaign = await new_campaign(rge, issuer, issuance, deposit);

const expiration = Math.trunc((new Date()).getTime() / 1000) + 60*60*24*2
await campaign.issue('acceptRedemption Test', expiration, {from: issuer});
await campaign.issue('issuer test', expiration, {from: issuer});

await campaign.giveNote(bearer, {from: issuer});
await campaign.distributeNote(bearer, {from: issuer});

const acquired = await campaign.acquired.call();
assert.equal(acquired.toNumber(), 1, "check notes acquired after giveNote");
assert.equal(acquired.toNumber(), 1, "check notes acquired after distributeNote");

// bearer signature that to be used by issuer
// at minimum, msg needs to include the campaign address to protect against replay
const msg = 'this is a valid note of ' + campaign.address
const sign = get_signature(bearer, msg)

await campaign.acceptRedemption(bearer, web3.sha3(msg), sign.v, sign.r, sign.s, {from: issuer});
// call acceptRedemption with auth message and bearer signature
const auth2 = create_auth_hash('acceptRedemption', campaign.address, bearer)
const sign2 = get_signature(bearer, auth2)
await campaign.acceptRedemption(bearer, auth2, sign2.v, sign2.r, sign2.s, {from: issuer});

const redeemed = await campaign.redeemed.call();
assert.equal(redeemed.toNumber(), 1, "notes redeemed after confirmRedemption");
assert.equal(redeemed.toNumber(), 1, "note(s) redeemed after confirmRedemption");

await campaign.kill({from: issuer});

const burned = tare * (issuance - 1);
const burned = tare * (issuance - redeemed);

const campaign_balance_after = await rge.balanceOf.call(campaign.address);
assert.equal(campaign_balance_after.toNumber(), 0, "the campaign has no more rge after kill");
Expand All @@ -101,6 +113,49 @@ contract('SimpleRougeCampaign', function(accounts) {
assert.equal(issuer_balance_after.toNumber(), tokens - burned, "the issuer has his tokens back less tare for unredeemed notes");

});

it("one acquisition/one redemption by bearer", async function() {

const rge = await RGEToken.deployed();

const issuer = accounts[3];
const bearer = accounts[4];
const issuance = 10;
const deposit = 50 * 10**6;

const campaign = await new_campaign(rge, issuer, issuance, deposit);

const expiration = Math.trunc((new Date()).getTime() / 1000) + 60*60*24*2
await campaign.issue('acceptRedemption Test', expiration, {from: issuer});

// call acquire with auth message and issuer signature
const auth1 = create_auth_hash('acceptAcquisition', campaign.address, bearer)
const sign1 = get_signature(issuer, auth1)
await campaign.acquire(auth1, sign1.v, sign1.r, sign1.s, {from: bearer});

const acquired = await campaign.acquired.call();
assert.equal(acquired.toNumber(), 1, "check notes acquired after distributeNote");

// call acceptRedemption with auth message and bearer signature
const auth2 = create_auth_hash('acceptRedemption', campaign.address, bearer)
const sign2 = get_signature(issuer, auth2)
await campaign.redeem(auth2, sign2.v, sign2.r, sign2.s, {from: bearer});

const redeemed = await campaign.redeemed.call();
assert.equal(redeemed.toNumber(), 1, "note(s) redeemed after confirmRedemption");

await campaign.kill({from: issuer});

const burned = tare * (issuance - redeemed);

const campaign_balance_after = await rge.balanceOf.call(campaign.address);
assert.equal(campaign_balance_after.toNumber(), 0, "the campaign has no more rge after kill");

const issuer_balance_after = await rge.balanceOf.call(issuer);
assert.equal(issuer_balance_after.toNumber(), tokens - burned, "the issuer has his tokens back less tare for unredeemed notes");

});



});
Expand Down

0 comments on commit 0d15f68

Please sign in to comment.