Merge pull request #3 from status-im/add-block-num

add block number and hash
This commit is contained in:
Bitgamma 2020-04-03 08:57:48 +03:00 committed by GitHub
commit 46ee4862b4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 69 additions and 8 deletions

View File

@ -132,6 +132,13 @@ interface SignRedeemResponse {
async function signRedeem(web3Type: Web3Type, contractAddress: string, signer: string, message: RedeemMessage): Promise<SignRedeemResponse> {
const chainId = await config.web3!.eth.net.getId();
const block = await config.web3!.eth.getBlock("latest");
const finalMessage = {
...message,
blockNumber: block.number,
blockHash: block.hash
};
const domain = [
{ name: "name", type: "string" },
{ name: "version", type: "string" },
@ -140,6 +147,8 @@ async function signRedeem(web3Type: Web3Type, contractAddress: string, signer: s
];
const redeem = [
{ name: "blockNumber", type: "uint256" },
{ name: "blockHash", type: "bytes32" },
{ name: "receiver", type: "address" },
{ name: "code", type: "bytes32" },
];
@ -158,7 +167,7 @@ async function signRedeem(web3Type: Web3Type, contractAddress: string, signer: s
},
primaryType: ("Redeem" as const),
domain: domainData,
message: message
message: finalMessage
};
if (web3Type === Web3Type.Status) {

View File

@ -13,6 +13,8 @@ contract GiftBucket {
uint256 public expirationTime;
uint256 constant maxTxDelayInBlocks = 10;
struct Gift {
address recipient;
uint256 amount;
@ -22,6 +24,8 @@ contract GiftBucket {
mapping(address => Gift) public gifts;
struct Redeem {
uint256 blockNumber;
bytes32 blockHash;
address receiver;
bytes32 code;
}
@ -29,7 +33,7 @@ contract GiftBucket {
uint256 public redeemableSupply;
bytes32 constant EIP712DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
bytes32 constant REDEEM_TYPEHASH = keccak256("Redeem(address receiver,bytes32 code)");
bytes32 constant REDEEM_TYPEHASH = keccak256("Redeem(uint256 blockNumber,bytes32 blockHash,address receiver,bytes32 code)");
bytes32 DOMAIN_SEPARATOR;
modifier onlyOwner() {
@ -99,6 +103,10 @@ contract GiftBucket {
}
function redeem(Redeem calldata _redeem, bytes calldata sig) external {
require(_redeem.blockNumber < block.number, "transaction cannot be in the future");
require(_redeem.blockNumber >= (block.number - maxTxDelayInBlocks), "transaction too old");
require(_redeem.blockHash == blockhash(_redeem.blockNumber), "invalid block hash");
require(block.timestamp < expirationTime, "expired gift");
address recipient = recoverSigner(_redeem, sig);
@ -130,6 +138,8 @@ contract GiftBucket {
function hashRedeem(Redeem memory _redeem) internal pure returns (bytes32) {
return keccak256(abi.encode(
REDEEM_TYPEHASH,
_redeem.blockNumber,
_redeem.blockHash,
_redeem.receiver,
_redeem.code
));

View File

@ -54,6 +54,8 @@ async function signRedeem(contractAddress, signer, message) {
];
const redeem = [
{ name: "blockNumber", type: "uint256" },
{ name: "blockHash", type: "bytes32" },
{ name: "receiver", type: "address" },
{ name: "code", type: "bytes32" },
];
@ -256,7 +258,7 @@ contract("GiftBucket", function () {
}
});
async function testRedeem(receiver, recipient, signer, relayer, redeemCode) {
async function testRedeem(receiver, recipient, signer, relayer, redeemCode, blockNumber, blockHash) {
let initialBucketBalance = await TestToken.methods.balanceOf(GiftBucket._address).call();
let initialUserBalance = await TestToken.methods.balanceOf(user).call();
let initialRedeemableSupply = await GiftBucket.methods.redeemableSupply().call();
@ -265,6 +267,8 @@ contract("GiftBucket", function () {
const amount = parseInt(gift.amount);
const message = {
blockNumber: blockNumber,
blockHash: blockHash,
receiver: receiver,
code: redeemCode,
};
@ -293,9 +297,11 @@ contract("GiftBucket", function () {
}
it("cannot redeem after expiration date", async function() {
const block = await web3.eth.getBlock("latest");
await mineAt(EXPIRATION_TIME);
try {
await testRedeem(user, keycard_1, keycard_1, relayer, REDEEM_CODE);
await testRedeem(user, keycard_1, keycard_1, relayer, REDEEM_CODE, block.number, block.hash);
assert.fail("redeem should have failed");
} catch(e) {
assert.match(e.message, /expired/);
@ -303,9 +309,10 @@ contract("GiftBucket", function () {
});
it("cannot redeem with invalid code", async function() {
const block = await web3.eth.getBlock("latest");
await mineAt(NOW);
try {
await testRedeem(user, keycard_1, keycard_1, relayer, web3.utils.sha3("bad-code"));
await testRedeem(user, keycard_1, keycard_1, relayer, web3.utils.sha3("bad-code"), block.number, block.hash);
assert.fail("redeem should have failed");
} catch(e) {
assert.match(e.message, /invalid code/);
@ -313,18 +320,53 @@ contract("GiftBucket", function () {
});
it("cannot redeem with invalid recipient", async function() {
const block = await web3.eth.getBlock("latest");
await mineAt(NOW);
try {
await testRedeem(user, keycard_1, keycard_2, relayer, REDEEM_CODE);
await testRedeem(user, keycard_1, keycard_2, relayer, REDEEM_CODE, block.number, block.hash);
assert.fail("redeem should have failed");
} catch(e) {
assert.match(e.message, /not found/);
}
});
it("can redeem before expiration date", async function() {
it("cannot redeem with a block in the future", async function() {
const block = await web3.eth.getBlock("latest");
await mineAt(NOW);
await testRedeem(user, keycard_1, keycard_1, relayer, REDEEM_CODE);
try {
await testRedeem(user, keycard_1, keycard_1, relayer, REDEEM_CODE, (block.number + 2), "0x0000000000000000000000000000000000000000000000000000000000000000");
} catch (e) {
assert.match(e.message, /future/);
}
});
it("cannot redeem with an old block", async function() {
const currentBlock = await web3.eth.getBlock("latest");
const block = await web3.eth.getBlock(currentBlock.number - 10);
await mineAt(NOW);
try {
await testRedeem(user, keycard_1, keycard_1, relayer, REDEEM_CODE, block.number, block.hash);
} catch (e) {
assert.match(e.message, /too old/);
}
});
it("cannot redeem with an invalid hash", async function() {
const block = await web3.eth.getBlock("latest");
await mineAt(NOW);
try {
await testRedeem(user, keycard_1, keycard_1, relayer, REDEEM_CODE, block.number, "0x0000000000000000000000000000000000000000000000000000000000000000");
} catch (e) {
assert.match(e.message, /invalid block hash/);
}
});
it("can redeem before expiration date", async function() {
const block = await web3.eth.getBlock("latest");
await mineAt(NOW);
await testRedeem(user, keycard_1, keycard_1, relayer, REDEEM_CODE, block.number, block.hash);
});
async function testKill() {