parent
fd67304da5
commit
5bcc264d10
@ -0,0 +1,76 @@ |
||||
pragma solidity 0.4.20; |
||||
|
||||
|
||||
contract DonationBag { |
||||
|
||||
// dontaion-signatures must be created by the owner |
||||
address public owner; |
||||
|
||||
// each donation contains this amount of wei |
||||
uint public amountPerDonation = 1000 * 1000; |
||||
|
||||
// one address can recieve only one donation |
||||
// the ones already recieved one, are stored here |
||||
mapping (address => bool) public alreadyRecieved; |
||||
|
||||
// constructor |
||||
function DonationBag(address _owner) public { |
||||
owner = _owner; |
||||
} |
||||
|
||||
/** |
||||
* to ensure the signatures for this contract cannot be |
||||
* replayed somewhere else, we add this prefix to the signed hash |
||||
*/ |
||||
string public signPrefix = "Signed for DonationBag:"; |
||||
|
||||
/** |
||||
* validates if the signature is valid |
||||
* by checking if the correct message was signed |
||||
* which consists of <signPrefix><contractAddress><receiverAddress> |
||||
*/ |
||||
function isSignatureValid( |
||||
address receiver, |
||||
uint8 v, |
||||
bytes32 r, |
||||
bytes32 s |
||||
) public constant returns (bool correct) { |
||||
bytes32 mustBeSigned = keccak256( |
||||
signPrefix, |
||||
this, |
||||
receiver |
||||
); |
||||
address signer = ecrecover( |
||||
mustBeSigned, |
||||
v, r, s |
||||
); |
||||
|
||||
return (signer == owner && receiver == msg.sender); |
||||
} |
||||
|
||||
/** |
||||
* checks if the signature and message is valid |
||||
* if yes we send some wei to the submitter |
||||
*/ |
||||
function recieveDonation( |
||||
address receiver, |
||||
uint8 v, |
||||
bytes32 r, |
||||
bytes32 s |
||||
) public { |
||||
|
||||
// already recieved donation -> revert |
||||
if (alreadyRecieved[msg.sender] == true) revert(); |
||||
|
||||
// signature not valid -> revert |
||||
if (isSignatureValid( |
||||
receiver, |
||||
v, r, s |
||||
) == false) { |
||||
revert(); |
||||
} |
||||
|
||||
// all valid -> send wei |
||||
msg.sender.transfer(amountPerDonation); |
||||
} |
||||
} |
@ -1,84 +1,80 @@ |
||||
# Sign and validate data with solidity |
||||
|
||||
In this tutorial we will sign data with javascript and later validate the signatur at a solidity smart-contract. |
||||
In this tutorial we will sign data with javascript and later validate the signatur in a solidity smart-contract. |
||||
|
||||
|
||||
## sign the data |
||||
First we create an identity and sign a message with it. |
||||
## prerequisites |
||||
|
||||
First we create two identities, `creator` and `receiver`. |
||||
|
||||
```javascript |
||||
const EthCrypto = require('eth-crypto'); |
||||
const creatorIdentity = EthCrypto.createIdentity(); |
||||
const recieverIdentity = EthCrypto.createIdentity(); |
||||
const message = 'Give me the money:' + recieverIdentity.address; |
||||
``` |
||||
|
||||
const signature = EthCrypto.sign( |
||||
identity.privateKey, |
||||
message |
||||
); |
||||
Then we start a local testnet to use later. At the testnet, we givbe the creatorIdentity a balance of 10 ether. |
||||
|
||||
console.dir(signature); |
||||
/* > { |
||||
v: '0x1b', |
||||
r: '0xc04b809d8f33c46ff80c44ba58e866...', |
||||
s: '0x757a3393b695ba83b2aba0c35c1503...' |
||||
} |
||||
*/ |
||||
```javascript |
||||
const Web3 = require('web3'); |
||||
const ganache = require('ganache-cli'); |
||||
|
||||
// create a web3-instance |
||||
const web3 = new Web3(); |
||||
|
||||
// create a ganache-provider |
||||
const ganacheProvider = ganache.provider({ |
||||
// we preset the balance of our identity to 10 ether |
||||
accounts: { |
||||
// we have to remove the trailing '0x' so ganache accepts our key |
||||
secretKey: creatorIdentity.privateKey.replace(/^.{2}/g, ''), |
||||
balance: web3.utils.toWei('10', 'ether') |
||||
} |
||||
}); |
||||
|
||||
// set ganache to web3 as provider |
||||
web3.setProvider(ganacheProvider); |
||||
``` |
||||
|
||||
|
||||
## create a smart-contract that validate the signature |
||||
## create a smart-contract that can validate signatures |
||||
Lets create an example-contract. The contract will be a donation-bag which contains some ether and has an owner. |
||||
Whenever someone submits a valid donation-signature, he recieves a part of the contracts value. |
||||
Whenever someone submits a valid donation-signature, he recieves a part of the contracts value. This allows the creator of the contract to give signed data to people **off-chain** which they can later use to claim the value **on-chain**. |
||||
|
||||
Write this in a file called `DonationBag.sol`. |
||||
Write the contracts code in a file called `DonationBag.sol`. See the content [here](../contracts/DonationBag.sol). |
||||
|
||||
```javascript |
||||
pragma solidity 0.4.20; |
||||
As you can see, the contract has some methods: |
||||
|
||||
contract DonationBag { |
||||
- DonationBag(): The constructor which is called when the contract is created. Here we set the owner of the DonationBag |
||||
- isSignatureValid(): checks if a given signature is really signed by the sender and contains the correct content. |
||||
- recieveDonation(): This is called by the receiver when the donation is claimed. |
||||
|
||||
// dontaion-signatures must be created by the owner |
||||
address owner; |
||||
## deploy the contract |
||||
|
||||
// each donation contains this amount of wei |
||||
uint amountPerDonation = 1000 * 1000; |
||||
Before we can put the contract on our local blockchain. We have to compile the solidity-code to bytecode. |
||||
|
||||
// one address can recieve only one donation |
||||
// the ones already recieved one, are stored here |
||||
mapping (address => bool) alreadyRecieved; |
||||
|
||||
// constructor |
||||
function DonationBag(address _owner) public { |
||||
owner = _owner; |
||||
} |
||||
|
||||
/** |
||||
* checks if the signature and message is valid |
||||
* if yes we send some wei to the submitter |
||||
*/ |
||||
function recieveDonation( |
||||
bytes32 message, |
||||
uint8 v, |
||||
bytes32 r, |
||||
bytes32 s |
||||
) public { |
||||
```javascript |
||||
const solc = require('solc'); |
||||
const fs = require('fs'); |
||||
const path = require('path'); |
||||
const contractPath = path.join(__dirname, '../contracts/DonationBag.sol'); |
||||
|
||||
// already recieved donation -> revert |
||||
if(alreadyRecieved[msg.sender] == true) revert(); |
||||
// read solidity-code from file |
||||
const contractCode = fs.readFileSync(contractPath, 'utf8'); |
||||
|
||||
address signer = ecrecover( |
||||
message, |
||||
v, r, s |
||||
); |
||||
// compile the code into an object |
||||
const compiled = solc.compile(contractCode, 1).contracts[':DonationBag']; |
||||
|
||||
// signature not valid -> revert |
||||
if(signer != owner) revert(); |
||||
console.dir(compiled); |
||||
/* > { |
||||
interface: [...], |
||||
bytecode: '10390f35b34156101ef57600...' |
||||
} |
||||
*/ |
||||
``` |
||||
|
||||
Now that we have the bytecode of the contract, we can submit a transaction to create a new instance of it at our local blockchain. |
||||
|
||||
// all valid -> send wei |
||||
msg.sender.transfer(amountPerDonation); |
||||
} |
||||
} |
||||
```javascript |
||||
|
||||
``` |
||||
|
Loading…
Reference in new issue