A generic “Claim and Endorse” Contract

Did you already endorse someone at LinkedIn? For instance, someone claims that he knows C++ and you endorse this claim because you know it’s true.

A large number of processes can be modelled in this way:

A simple Solidity contract for managing claims and endorsements could look like below.

contract ClaimAndEndorse {
struct ENDORSEMENT {
uint creationTime;
}

struct CLAIM {
uint creationTime;
uint claimHash;
mapping (address => ENDORSEMENT) endorsements;
}

mapping (address =>
mapping (uint /* CLAIM GUID */ => CLAIM)) claims;

function setClaim(uint claimGuid, uint claimHash) {
CLAIM c = claims[msg.sender][claimGuid];
if(c.claimHash > 0) throw; // unset first!
c.creationTime = now;
c.claimHash = claimHash;
}

function unsetClaim(uint claimGuid) {
delete claims[msg.sender][claimGuid];
}

function setEndorsement(
address claimer, uint claimGuid, uint expectedClaimHash
) {
CLAIM c = claims[claimer][claimGuid];
if(c.claimHash != expectedClaimHash) throw;
ENDORSEMENT e = c.endorsements[msg.sender];
e.creationTime = now;
}

function unsetEndorsement(address claimer, uint claimGuid) {
delete claims[claimer][claimGuid]
.endorsements[msg.sender];
}

function checkClaim(
address claimer, uint claimGuid, uint expectedClaimHash
) constant returns (bool) {
return claims[claimer][claimGuid].claimHash
== expectedClaimHash;
}

function checkEndorsement(
address claimer, uint claimGuid, address endorsedBy
) constant returns (bool) {
return claims[claimer][claimGuid]
.endorsements[endorsedBy].creationTime > 0;
}
}

The fantastic thing about this very simple contract is that we now can answer the following question:

Who claims what and who endorses it?

Usecase – Skill/Degree Endorsements

John claims that he holds a PhD in computer science at the Stanford university.

contract.setClaim(
3000010 /* GUID for PhD in CS */,
HASH("PhD in Computer Science at Stanford University"))

The Stanford university confirms this fact.

contract.setEndorsement(JOHN, 3000010)
Usecase – Identity Verification

Step 1. John claims facts about his personal data by binding hashes to his ethereum address. The corresponding pseudo code is:

// RND is a random sequence introduced for making it impossible to restore the initial data from the hash by brute force lookups.

contract.setClaim(1000010 /*guid firstname*/,
HASH("John"+RND))

contract.setClaim(1000011 /*guid surename */,
HASH("Smith"+RND))

contract.setClaim(1000012 /*guid bday */,
HASH("1965-01-01"+RND))

contract.setClaim(1000013 /*gender */,
HASH("m"+RND"))

Step 2. John visits his bank, which endorses these facts about his identity:

contract.setEndorsement(JOHN, 1000010 /*guid firstname*/);
contract.setEndorsement(JOHN, 1000011 /*guid surename */);
contract.setEndorsement(JOHN, 1000012 /*guid bday */);
contract.setEndorsement(JOHN, 1000013 /*guid gender */);

Now consider that JOHN wants to open A) an  account at ACME Inc, B) buy alcohol in the bar and C) register at a dating site. If all three trust John’s bank, he’ll be able to digitally prove his claims on his personal data. Moreover, he only needs to present the relevant pieces of data. For instance at the bar, he only has to prove the claim that he’s older than 18.

Related: ShoCard, uPort 

Usecase – Approving existence of documents

ACME Inc. wants to publish a new financial product. The hashes of the required documents are stored on the blockchain. The authorities and the exchanges are confirming the existence and the correctness of these documents.

Related: Luxembourg Stock Exchange OAM

Usecase – Managing Memberships

John wants to become a member in his local bowling club. He stores this fact on the blockchain and the club confirms this fact. With the first step, John manifests his will to enter the club. In the second step, the club confirms that they are accepting John as a member.

Try it yourself on Ropsten Testnet

https://testnet.etherscan.io/address/0x65ec6e00e336a96972987ee25386a3090f38a27d#code

 

Rebuilding Ripple on Ethereum

Ripple is a P2P payment network with an integrated foreign exchange market. It supports any possible currency. In it’s core it is based on a public distributed ledger containing liabilities between individuals and organisations (IOUs). The network depends on the trust relations between its members. Transferring a value within the network between A and B requires a direct or indirect path in this web of trust. Moreover, the ledger  contains a distributed foreign exchange market, which makes it possible to convert between currencies in real-time.

In this blog post, I want to sketch how a Ripple-like implementation could look like in Ethereum.

Asset Contract

First of all we need a contract to represent an asset that network participants can agree on. This could be a fiat or a crypto currency, but it also could be bonus miles, loyalty points or similar.

contract Asset {
string public description;
string public id;
uint public decimalUnits;

mapping (address => bool) public accepted;

function Asset(string _description, string _id, uint _decimalUnits) {
description = _description;
id = _id;
decimalUnits = _decimalUnits;
}

function accept() {
accepted[msg.sender] = true;
}

function reject() {
delete accepted[msg.sender];
}
}

An asset has a description, an id, and how many decimal units are used. For instance, we would model US Dollar and European Euro as:

Asset USD = new Asset("USD Currency", "USD", 2)
Asset EUR = new Asset("EUR Currency", "EUR", 2)

With the accept function, network participants are agreeing upon a specific Asset instance. Network participants can only use assets that they have accepted.

EthRipple Contract

data model

After defining the Asset contract, we can now specify the data model for the EthRipple contract itself. We’ll need the following model elements:

ACCOUNT

Every participant in the network needs an ACCOUNT struct storing his ASSETs. The assets are identified with their contract addresses.

struct ACCOUNT {
mapping (address /* of an Asset */ => ASSET) assets;
}

mapping (address => ACCOUNT) accounts;
ASSET

An ASSET consists of all IOUs that a participant holds and of all his asset exchange offers (XCHG).

struct ASSET {
mapping (address => IOU) ious;
mapping (address /* of an target Asset */ => XCHG) xchgs;
}

xchgs – Offers for exchanging this asset for another asset.
ious – list of debtors for this asset.

IOU – “I owe you”
struct IOU {
uint amountOwed;
uint maxAllowed;
}

The IOU struct describes how much of a specific asset (e.g. USD) a debtor owes to the lender (amountOwed). Moreover, it describes how much a lender trusts that a potential debtor is going to pay him back (maxAllowed). During a transfer, amountOwed will always be less than or equal to maxAllowed.

E.g.:

IOU iou = accounts[JOHN].assets[EUR].ious[ANDY];
iou.maxAllowed = 100;
iou.amountOwed = 10;

iou.maxAllowed = 100 – JOHN trusts ANDY that he’ll pay his debts up to 100 units of the EUR asset.

iou.amountOwed = 10 – currently ANDY owes to JOHN 10 units of the EUR asset.

XCHG – Asset Exchange
struct XCHG {
uint validUntil;
uint exchangeRateInMillionth;
}

This struct represents the offer to exchange an Asset for another Asset at a specific exchangeRate which is equal to exchangeRateInMillionth/1,000,000. Note that here we have to work with unsigned integers since Ethereum’s Solidity Compiler has no support for decimals yet. validUntil is used to limit an offer to a specific period of time.

E.g.

XCHG xchg = accounts[JOHN].assets[EUR].xchgs[USD];
xchg.exchangeRateInMillionth = 1100000;

xchg.exchangeRateInMillionth = 1100000 – JOHN offers to exchange EUR for USD at a rate of 1100000/1000000 (1,10).

Operations

The minimal interface for the contract offers methods to modify IOUs and asset exchange offers. And finally, there is a ripple method for sending assets through the web of trust to a specific destination. Note that sending a value in this case means changing the IOU records along the path in the web of trust. If required, the sent asset can also be exchanged for another asset (e.g. converting EUR to USD).

 function modifyIOU(address debtor,
Asset asset,
uint newAmountOwed,
uint newMaxAllowed);

function modifyXCHG(Asset fromAsset,
Asset toAsset,
uint exchangeRateInMillionth,
uint validUntil);

function ripple(address[] chain,
Asset[] assetFlow,
uint amount);

function modifyIOU(address debtor, Asset asset, uint newAmountOwed, uint newMaxAllowed) – with this function the msg.sender can reduce the amount owed by a debtor or he can change the maxAllowed amount for this asset/debtor. The amountOwned can only be reduced, never increased.

function modifyXCHG(Asset fromAsset, Asset toAsset, uint exchangeRateInMillionth, uint validUntil) – with this function the msg.sender can publish new offers for converting fromAsset to toAsset at an exchangeRate which is exchangeRateInMillionth/1000000.

function ripple(address[] chain, Asset[] assetFlow, uint amount) – this function is the main workhorse. It allows the msg.sender to transfer an asset to a destination address, which is reachable within the web of trust that is encoded via IOU relations.

Considering the relations below, JOHN can send money to ALEX via ANDY.

// JOHN and ANDY trust each other that they'll be paying their debts up to 1000 units of the EUR asset.
accounts[JOHN].assets[EUR].ious[ANDY].maxAllowed = 1000;
accounts[ANDY].assets[EUR].ious[JOHN].maxAllowed = 1000;

// same for ANDY and ALEX
accounts[ANDY].assets[EUR].ious[ALEX].maxAllowed = 1000;
accounts[ALEX].assets[USD].ious[ALEX].maxAllowed = 1000;

If JOHN want to send 10 units of the EUR asset to ALEX, he would call the ripple function like this

ripple([JOHN, ANDY, ALEX], [EUR, EUR], 10) 

The second array parameter means that JOHN is transferring EUR to ANDY and that ANDY is also transferring  EUR to ALEX. There is no conversion between assets. After the transaction has been committed to the blockchain, we would see the following changes in the IOU records.

// JOHN and ANDY trust each other that they'll be paying their debts up to 1000 units of the EUR asset.
accounts[ANDY].assets[EUR].ious[JOHN].amountOwed = 10;
accounts[ALEX].assets[EUR].ious[ANDY].amountOwed = 10;

If JOHN wants to send EUR, but ALEX wants to receive USD, the transfer would work if ANDY would have an active asset exchange offer for exchanging EUR to USD. Moreover, there also has to be an established trust relation between ANDY and ALEX for the USD asset.

The function call would be:

ripple([JOHN, ANDY, ALEX], [EUR, USD], 10) 

Note that the path within the web of trust is calculated off-chain and passed as input to the ripple function. There is no need to do this expensive calculation on-chain.

Try it yourself

I deployed this contract on the Morden Testnet. Feel free to try it yourself.

Contract Addresses

EUR Asset 0x110c1b256c180ddBBFF384cA553Bf7683Ce8a02c
USD Asset 0xFa33639783B5ae93795A4aeCF86985eB95EA0B39
Ripple 0x33f03cea07586f42900fbf46df6a7f596345bec1

Asset Interface

[ { "constant": true, "inputs": [], "name": "decimalUnits", "outputs": [ { "name": "", "type": "uint256" } ], "payable": false, "type": "function" }, { "constant": false, "inputs": [], "name": "accept", "outputs": [], "payable": false, "type": "function" }, { "constant": false, "inputs": [], "name": "reject", "outputs": [], "payable": false, "type": "function" }, { "constant": true, "inputs": [], "name": "description", "outputs": [ { "name": "", "type": "string" } ], "payable": false, "type": "function" }, { "constant": true, "inputs": [], "name": "id", "outputs": [ { "name": "", "type": "string" } ], "payable": false, "type": "function" }, { "constant": true, "inputs": [ { "name": "a", "type": "address" } ], "name": "isAcceptedBy", "outputs": [ { "name": "", "type": "bool" } ], "payable": false, "type": "function" }, { "inputs": [ { "name": "_description", "type": "string" }, { "name": "_id", "type": "string" }, { "name": "_decimalUnits", "type": "uint256" } ], "type": "constructor" }, { "payable": false, "type": "fallback" } ] 

EthRipple Interface

[ { "constant": false, "inputs": [ { "name": "fromAsset", "type": "address" }, { "name": "toAsset", "type": "address" }, { "name": "exchangeRateInMillionth", "type": "uint256" }, { "name": "validUntil", "type": "uint256" } ], "name": "modifyXCHG", "outputs": [], "payable": false, "type": "function" }, { "constant": true, "inputs": [ { "name": "fxAddr", "type": "address" }, { "name": "fromAsset", "type": "address" }, { "name": "toAsset", "type": "address" } ], "name": "queryXCHG", "outputs": [ { "name": "", "type": "uint256" }, { "name": "", "type": "uint256" } ], "payable": false, "type": "function" }, { "constant": false, "inputs": [ { "name": "fromAsset", "type": "address" }, { "name": "toAsset", "type": "address" } ], "name": "deleteXCHG", "outputs": [], "payable": false, "type": "function" }, { "constant": false, "inputs": [ { "name": "debitor", "type": "address" }, { "name": "asset", "type": "address" } ], "name": "deleteIOU", "outputs": [], "payable": false, "type": "function" }, { "constant": true, "inputs": [ { "name": "lender", "type": "address" }, { "name": "asset", "type": "address" }, { "name": "debitor", "type": "address" } ], "name": "queryIOU", "outputs": [ { "name": "", "type": "uint256" }, { "name": "", "type": "uint256" } ], "payable": false, "type": "function" }, { "constant": false, "inputs": [ { "name": "debitor", "type": "address" }, { "name": "asset", "type": "address" }, { "name": "newAmountOwed", "type": "uint256" }, { "name": "newMaxAllowed", "type": "uint256" } ], "name": "modifyIOU", "outputs": [], "payable": false, "type": "function" }, { "constant": false, "inputs": [ { "name": "chain", "type": "address[]" }, { "name": "assetFlow", "type": "address[]" }, { "name": "expectedExchangeRateInMillionth", "type": "uint256[]" }, { "name": "amount", "type": "uint256" } ], "name": "ripple", "outputs": [], "payable": false, "type": "function" }, { "inputs": [], "type": "constructor" }, { "payable": false, "type": "fallback" }, { "anonymous": false, "inputs": [ { "indexed": false, "name": "lender", "type": "address" }, { "indexed": false, "name": "debitor", "type": "address" }, { "indexed": false, "name": "asset", "type": "address" }, { "indexed": false, "name": "newCurrent", "type": "uint256" }, { "indexed": false, "name": "newMax", "type": "uint256" } ], "name": "EventUpdateIOU", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": false, "name": "lender", "type": "address" }, { "indexed": false, "name": "debitor", "type": "address" }, { "indexed": false, "name": "asset", "type": "address" } ], "name": "EventDeleteIOU", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": false, "name": "xchgAddr", "type": "address" }, { "indexed": false, "name": "fromAsset", "type": "address" }, { "indexed": false, "name": "toAsset", "type": "address" }, { "indexed": false, "name": "exchangeRateInMillionth", "type": "uint256" }, { "indexed": false, "name": "validUntil", "type": "uint256" } ], "name": "EventUpdateXCHG", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": false, "name": "xchgAddr", "type": "address" }, { "indexed": false, "name": "fromAsset", "type": "address" }, { "indexed": false, "name": "toAsset", "type": "address" } ], "name": "EventDeleteXCHG", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": false, "name": "xchgAddr", "type": "address" }, { "indexed": false, "name": "fromAsset", "type": "address" }, { "indexed": false, "name": "toAsset", "type": "address" }, { "indexed": false, "name": "exchangeRateInMillionth", "type": "uint256" } ], "name": "EventExecuteXCHG", "type": "event" } ]

 

Full Source Code

Ethereum Usecase: Identity Management (Take 2)

Identity verification is one of the hottest usecases for the blockchain. I already wrote on this topic few months ago with the idea of a fictive government binding hashed identity data to citizen’s ethereum address.

Recently, I ran into ShoCard, a mobile app which is able to locally store user’s identities (driver’s license, passport, tickets, credits cards, online accounts, …) on the mobile phone and seal this data by putting the hashes via the BlockCypher API on the blockchain. Furthermore, institutions, like banks for instance, can verify user’s identities and store this fact on blockchain too, effectively confirming that the sealed id record is correct.

I did the experiment of implementing ShoCard’s concept on the Ethereum blockchain. A very interesting point is that we only need one simple contract for the implementation of the concept. It simply binds a hash value to an address:

contract DataSeal {
address owner;
uint256 dataHash;
function DataSeal(uint256 _dataHash) {
owner = msg.sender;
dataHash = _dataHash;
}
}

First, for every user’s identity record of the form

idRecord = {idData_1, ..., idData_n, randomValue} 

we create in Ethereum a DataSeal instance storing idRecord‘s hash value.

idRecordSeal = new DataSeal(<idRecord hash>)

From now on, idRecord can not be modified without breaking idRecordSeal.

If we want to prove to X that our idRecord has been sealed by us, we will send to X the idRecordSeal address and idRecord signed with the private key of the Ethereum account used to instantiate idRecordSeal. Having this informaton, X can verify that idRecord matches the hash value in idRecordSeal contract and that the signature matches its owner.

So far, we have the proof that idRecord was created and sealed by us, but we have no proof yet that idRecord matches our real identity as documented on our id card. For instance, we could steal the id card from someone else and  seal it on the blockchain. In order to make the idRecord trustworthy, we need a trustworthy witness verifying our idRecord and committing the proof to the blockchain.

The most direct witness for this proof would be the public authority issuing the id cards to the citizens. The next best instance, could be a commonly accepted institution like the mailing company (see POSTIDENT solution of Deutsche Post AG) or a bank.

If the user has been successfully authenticated, the witness will produce

witnessRecord = {idRecordSeal, secretKey}

and create a new instance of the DataSeal contract with the hash of it:

witnessRecordSeal = new DataSeal(<witnessRecord hash>)

Finally the witness shares the following record with the user:

{witnessPublicAddress, witnessRecordSeal, secretKey}

Assuming that X is trusting W, and that we already were authenticated by W, we can pass to X

  1. the witness data {witnessPublicAddress, witnessRecordSeal, secretKey}
  2. our idRecord signed with the corresponding private key
  3. our idRecordSeal address

Now X, can check that idRecord hasn’t been modified, that we’re the owner of the record and it’s blockchain seal, and that we already were successfully authenticated by W. If X trusts W, then he doesn’t need any further verification of our identity and he can do business with us.

The concept is universal and it works with any kind of document. There are also usecases where no witness is needed at all. For instance I can seal my credit card data like this:

creditCardDataSeal = new DataSeal(<hashed credit card data>)

Every time I purchase something, I also sign my purchase with my Ethereum private key and the merchant can verify that credit card data is in my ownership. So even if someone steals my credit card, he won’t be able to purchase something with it, because the thief can not prove he’s the owner of the credit card.

Steem – community building and social interaction with cryptocurrency rewards

This week I came across Steem and I instantly got sucked into it. In this post am trying to summarize what I learned about it so far. 

You can think of Steem as a Facebook-like platform where authors and curators can earn cryptocurrency rewards by writing posts, up-voting posts, commenting on posts and up-voting comments.

To understand how this process works, first of all we need to understand the currencies circulating in this process. According to the whitepaper we have:

Steem – the fundamental unit of account on the Steem blockchain. All other tokens derive their value from the value of STEEM. Generally speaking STEEM should be held for short periods of time when liquidity is needed. Someone looking to enter or exit the Steem platform will have to buy or sell STEEM. Once STEEM has been purchased it should be converted into Steem Power (SP) or Steem Dollar (SMD) to mitigate the impact of dilution over the long-term (100% inflation annually). 

Steem Power (SP) – Steem can be instantly converted to Steem Power. It is a long-term investment and can only be converted back to Steem via 104 weekly payments over a period of 2 years. This long-term commitment gives the owner two advantages:

  1. the user can participate on the platform by voting or posting articles/comments. This is rewarded by more Steem Power (SP) and Steem Dollars (SMD). How much everyone gets, depends on his Steem Power and the voting algorithm.
  2. The inflation is not 100% anymore like with Steem, but only ~10% per year because for every 1 Steem generated as reward, 9 new Steem are distributed among Steem Power holders. If the platform grows faster than the inflation, investors make a win, otherwise they’ll be losing money.
 

Steem Dollar (SMD) – represents a number of Steem tokens having a value of ~1 USD. It can be used to trade goods/services with the stability of the USD. SMD pays holders interest (currently 10% per year). The interest rate is set by the same people who publish the price feed so that it can adapt to changing market conditions.

The two main reward mechanisms in Steem are curation and author rewards. Both are equally important in Steem.

Author Rewards are gained by posting articles that are upvoted by others. Curation Awards are gained by early upvoting articles that grow popular later. The rewards are payed out in Steem Power and Steem Dollars. In general the more Steem Power someone holds, the more weight his vote has. If one of developers is upvoting a post, it can instantly bring more than $100 value to the post. A new account on the other side with ~3 Steem Power will only bring a fraction of a cent. The final value of a post is distributed among the author and curators depending on how early they participated in the voting process and how much Steem Power their accounts hold.

The money for the reward pool is coming from the inflation. Instead of taking a tax on existing accounts for generating the reward pools, the protocol generates continuously new Steem where 1 is going to the reward pool and  9 are redistributed to Steem Power holders. This basically results in a ~10% inflation annually.

Lot of people are asking the question if this model is sustainable. One could expect that the model can only be profitable as long as more investors are jumping in. At the moment where supply gets greater than demand the whole system could break together like a Ponzi-Schema. This could be very painful for late-adoptors because of the 2 year payout mechanism. One of the main critical voices claiming that Steem is actually scam is Tone Vays.

My personal impression so far is that this could be the first DLT application with good user experience and the potential to go viral. It reminds me a little bit on 2007 and the gold rush with the AppStore. At the time of writing this post, the platform has around 70k accounts.

Try it yourself at https://steemit.com or read more about it at https://steem.io. Currently every new account gets Steem Power worth ~$4 for free.

Further reading:

 

Ethereum Usecase: Online Identitätsprüfung

Die Identitätsprüfung des Kunden bei Onlinegeschäften ist in der Regel ein Prozess, der mehrere Tage in Anspruch nimmt. Die Identifizierung wird zum großen Teil über das PostIdent Verfahren abgewickelt, das von dem Kunden verlangt zur Postfiliale zu fahren, um sich vor Ort identifizieren zu lassen. Die schnellste (mir bekannte) Onlineidentifizierungsmethode bietet idnow, das z.B. von Number26 bei der Kontoeröffnung benutzt wird. Dabei wird in einem Video-Chat die Identität des Kunden überprüft. Der Prozess nimmt etwa 5 Minuten in Anspruch und ist mit gängigen Smartphones durchführbar.

Was wäre aber, wenn man den Identitfikationsprozess noch schneller und mit noch weniger Intermediären durchführen könnte? Das Zauberwort heißt Ethereum.

Der große Anspruch von Ethereum ist es einen offen zugägnlichen Welt-Computer zu schaffen, der in der Lage ist die Programme, die er ausführt und den Laufzeitzustand, der sich aus der Ausführung dieser Programme ergibt, in eine Blockchain so zu verpacken, dass es keine Möglichkeit gibt die Programme oder deren Laufzeitzustand zu verfälschen.

Jeder Benutzer und jedes Programm in Ethereum haben eine oder mehrere öffentlich bekannte Adressen. Zusätzlich zu der öffentlichen Adresse hat jeder Benutzer einen privaten (geheimen) Schlüssel mit dem er Transaktionen in Ethereum signieren kann.

Welche Möglichkeiten würden sich ergeben, wenn die Behörde bei der Ausstellung eines neuen Personalausweises für alle Ausweisdaten (Name, Vorname, Geburtsdatum, Adresse, …), einen Hash-Wert berechnen und in einem öffentlich zugänglichen Identitätsverzeichnis an die Ethereum-Adresse des Kartenbesitzers binden würde?

In Ethereum kann man so ein Identitätsverzeichnis mit wenigen Codezeilen durch den folgenden Contract abbilden:

contract IdentityRegistry {
    address owner; // Der Besitzerer der Registry (die Behörde)
    string name;   // Der Name der Registry
    
    // Abbildung von Ethereum Adresse
    // zu dem Hash-Wert der Personalausweisdaten
    mapping(address => uint256) idHashes;  
    
    // der Konstruktor wird einmalig beim Anlegen des Contracts ausgeführt
    function IdentityRegistry(string _name) {
        owner = msg.sender;   // hier merken wir uns den Contract-Ersteller als Besitzer
        name = _name;         // den Namen der Registry setzen
    }
    
    // Liefert den Namen der Registry, verändert nicht den Zustand
    function getName() constant returns (string) {
        return name;
    }
    
    // Beschreibt wie der Hash-Wert für die Daten gebildet wird
    // Alle Informationen werden durch "/" getrennt, hintereinander abgelegt
    // Es wird SHA-1 Hashwert der Zeichenkette berechnet
    function getIdHashFormat() constant returns (string) {
        return "sha-1(NAME/GIVENNAMES/DATEOFBIRTH/PLACEOFBIRTH/NATIONALIY/ADDRESS1/ADDRESS2/ADDRESS3)";
    }
    
    // Diese Methode kann nur von dem Besitzer (Bürgeramt) aufgerufen werden.
    // das "throw" rollt die Transaktion zurück, wenn die Transaktion
    // nicht vom Besitzer der Registry (Bürgeramt) aufgerufen wurde.
    // Die Funktion bindet den Hash-Wert an die Ethereum Adresse des Ausweisbesitzers
    function registerIdHash(address who, uint256 hash) {
        if(owner != msg.sender) throw;
        idHashes[who] = hash;
    }
    
    // Wird vom Gegenüber (z.b. Bank) aufgerufen, um den Hash-Wert einer
    // Ethereum Adresse zu überprüfen
    function verifyIdentity(address who, uint256 hash)
    constant returns(bool) {
        return idHashes[who] == hash;
    }
    
    // Falls keine der oben definierten Methoden aufgerufen wurde,
    // sorgt diese Funktion, dass die Transaktion zurückgerollt wird.
    function() {
        throw;
    }
}

Beantrag beispielsweise Max Mustermann, geboren am 01.01.1990 aus Musterstrasse 12, Musterhausen den Ausweis, berechnet das Amt zunächst den Hash-Wert aus allen oben genannten Daten, z.B.:

sha1("MUSTERMANN/MAX/01.01.1990/MUSTERHAUSEN/DEUTSCH/MUSTERHAUSEN/MUSTERSTRASSE 12") = 1417400950720517943593210833135938663929334829119

Dieser Hash-Wert wird anschliessend vom Amt im Identitätsverzeichnis an Maxs öffentliche Ethereum-Adresse (0xDBEc37…) gebunden, z.B.:

identityRegistry.registerIdHash(
    0xDBEc371775c57f59fDA5BBa2c9FC8876968F34E3, 
    1417400950720517943593210833135938663929334829119)

Hierbei ist es wichtig anzumerken, dass aus dem Hash-Wert der Daten keine Rückschlüsse auf die Daten selbst gezogen werden können.

Möchte Max ein Konto bei der Musterbank eröffnen, könnte er nun über ein Onlineformular der Musterbank seine persönlichen Daten eingeben und zusätzlich seine öffentliche Ethereum-Adresse senden. Mit diesen Informationen kann die Musterbank bereits überprüfen, ob der Datensatz korrekt ist. Genauso wie das Bürgeramt, berechnet die Musterbank den Hash-Wert und vergleicht diesen mit dem Hash-Wert aus dem Identitätsverzeichnis des Bürgeramts. Z.B.:

bool res = identityRegistry.verifyIdentity(
    0xDBEc371775c57f59fDA5BBa2c9FC8876968F34E3,
    1417400950720517943593210833135938663929334829119)

Damit ist überprüft, ob der Datensatz valide ist, aber noch nicht ob Max auch der tatsächliche Absender der Information ist. Um auch das zu überprüfen, instantiiert die Musterbank einen weiteren Contract auf Ethereum und wartet darauf, dass Max mit seinem privaten (geheimen) Schlüssel die Bestätigungsmethode aus dem Contract aufruft und damit beweist, dass er tatsächlich der Besitzer des Ethereum Kontos mit der öffentlichen Adresse 0xDBEc37… ist. Der Contract sieht wie folgt aus:

contract IdentityConfirmation {
    address createdBy;    // Ersteller des Contracts (Musterbank)
    address who;          // Wer soll sich identifizieren (Max)
    bool confirmed;       // wurde die Identität bestätigt?
    
    // dieses Event wird verschickt,
    // wenn Max die Funktion "confirm" aufruft
    event IdentityConfirmed(address who);
    
    function IdentityConfirmation(address _who) {
        who = _who;
        createdBy = msg.sender;
        confirmed = false;
    }
    
    // wenn die Methode von Max aufgerufen wird, d.h.,
    // der Aufruf mit dem privaten Schlüssel von Max signiert wurde
    // bekommt die Musterbank die Bestätigung über
    // die erfolgreiche Authentifizierung
    function confirm() {
        if(who == msg.sender) {
            confirmed = true;
            IdentityConfirmed(msg.sender);    
            suicide(createdBy);
        }
    }
    
    function() {
        throw;
    }
}

Anbei der gesamte Vorgang zusammengefasst in einem Sequenzdiagramm:

Sequence Diagram

Weitere Überlegungen

Das vorgestellte Konzept ist eine grobe Skizze. Ein produktives System bräuchte noch weitere Feature wie z.B Aktualisierung / Sperrung / Entsperrung des Eintrags im Umzugsfall oder beim Verlust des privaten Schlüssels bzw. beim Ablauf des Ausweises.

Falls die Behörden nicht die Blockchain-Technologie adaptieren sollten, kann man sich auch folgende Alternativen vorstellen:

  • Es existiert ein Intermediär vergleichbar zu idnow, der den Aufbau des Identitätsverzeichnisses übernimmt und den Dienst gegen Gebühr anbietet.
  • Das Identitätsverzeichnis wird innerhalb des Konsortiums/Verbands aufgebaut und benutzt.

Schließlich wäre noch zu untersuchen, ob man mit dem privaten Ethereum Schlüssel außerhalb von Ethereum Daten signieren kann (public/private-key Verfahren). Das würde den zweiten Contract überflüssig machen.

Ethereum – der neue große Rivale des Bitcoin

Das Thema Ethereum ist in der deutschen Mainstream-Presse angekommen. In dem Artikel “Das ist der neue große Rivale des Bitcoin” schreibt Die Welt:

Der Bitcoin war das Nonplusultra, wenn es um digitales Geld ging. Der Konkurrent Ether könnte das aber nun ändern – auch weil er mehr kann als Bitcoin. Ether übersprang jetzt eine magische Hürde.

Die magische Hürde war die Marktkapitalisierung von mehr als 1 Milliarde Dollar, die aus den Berechnungen von Coinmarketcap.com hervorgeht. Im Vergleich dazu liegt Bitcoin bei einer Marktkapitalisierung von rund 6,3 Milliarden Dollar.

Des Weiteren schreibt Die Welt:

Experten gestehen Buterins Erfindung ein großes Potential zu, von einer revolutionären Technik ist die Rede. Unternehmen wie UBS, Microsoft, Samsung und seit neuestem auch der deutsche Energieversorger RWE experimentieren mit der Währung.

Auch auf den Börsen, die den Kryptowährungshandel anbieten, entkoppelt sich Ethereum immer mehr von Bitcoin. Kraken bietet seit längerem die Möglichkeit Ether direkt in Euro zu kaufen. Laut Welt, möchte auch die größte deutsche Bitcoinbörse Bitcoin.de in den Ether-Handel einsteigen und Bitfinex hat bereits offiziell angekündigt, dass sie ab dem 14.3. den Ether/Dollar Handel anbieten werden.

Der 14.3. ist nicht zufällig gewählt. An diesem Tag, bei Ethereum-Block 1.150.000, wird das neue Ethereum Homestead-Release aktiviert. Dabei wechselt Ethereum in einen, den Entwicklern zufolge, stabilen Betriebsmodus. Die Ethereum Website wurde bereits aktualisiert und das durchgestrichene “Safe” wurde entfernt.