| 1 | """ |
|---|
| 2 | This is documentation (and doctest) file. It shows how the API is used, and |
|---|
| 3 | how the system is supposed to work. |
|---|
| 4 | |
|---|
| 5 | Please have a look at doc/message_dump.txt, or turn on the transports.printmessages |
|---|
| 6 | below, and generate the output for yourself. The section headers in the output |
|---|
| 7 | are generated by the 'printSection' commands in this file. |
|---|
| 8 | |
|---|
| 9 | >>> import time |
|---|
| 10 | >>> start = time.time() |
|---|
| 11 | >>> import transports |
|---|
| 12 | >>> from transports import DirectTransport as DT |
|---|
| 13 | >>> from transports import printSection |
|---|
| 14 | |
|---|
| 15 | toggle printing of the messages |
|---|
| 16 | >>> transports.printmessages = 0 |
|---|
| 17 | |
|---|
| 18 | |
|---|
| 19 | |
|---|
| 20 | ############################################################################### |
|---|
| 21 | Setup an issuer |
|---|
| 22 | |
|---|
| 23 | >>> from issuer import Issuer |
|---|
| 24 | >>> issuer = Issuer({}) |
|---|
| 25 | >>> issuer.createMasterKeys() |
|---|
| 26 | |
|---|
| 27 | |
|---|
| 28 | |
|---|
| 29 | issuer sets up "currency description document" = CDD (like a root certificate) |
|---|
| 30 | |
|---|
| 31 | >>> port = 9090 |
|---|
| 32 | >>> denominations = [0,1,2,5,10,20] |
|---|
| 33 | >>> cdd = issuer.makeCDD('OpenCentA','oca',[str(d) for d in denominations],'http://localhost:%s/' % port,'') |
|---|
| 34 | >>> issuer.getMasterPubKey().verifyContainerSignature(cdd) |
|---|
| 35 | True |
|---|
| 36 | |
|---|
| 37 | |
|---|
| 38 | |
|---|
| 39 | ############################################################################### |
|---|
| 40 | mint (regularily) creates keypairs (pP,sP) for all denominations and id(p). |
|---|
| 41 | Master key holder generates keys certificate |
|---|
| 42 | |
|---|
| 43 | >>> from mint import Mint |
|---|
| 44 | >>> mint = Mint({}) |
|---|
| 45 | >>> mint.setCDD(cdd) |
|---|
| 46 | >>> keys = mint.newMintKeys() |
|---|
| 47 | >>> mkcs = issuer.signMintKeys(keys=keys,cdd = cdd) |
|---|
| 48 | >>> issuer.getMasterPubKey().verifyContainerSignature(mkcs['20']) |
|---|
| 49 | True |
|---|
| 50 | |
|---|
| 51 | |
|---|
| 52 | >>> import storage |
|---|
| 53 | |
|---|
| 54 | Wallet fetches cdd from issuer |
|---|
| 55 | |
|---|
| 56 | >>> printSection('Basic Setup') |
|---|
| 57 | |
|---|
| 58 | we could use the method directly |
|---|
| 59 | >>> from wallet import Wallet |
|---|
| 60 | >>> wallet = Wallet(storage.EmptyStorage()) |
|---|
| 61 | >>> cdd == wallet.askLatestCDD(issuer.giveLatestCDD) |
|---|
| 62 | True |
|---|
| 63 | |
|---|
| 64 | but its better to use more real transports |
|---|
| 65 | >>> import testutils |
|---|
| 66 | >>> transport = transports.HTTPTransport('http://localhost:%s/' % port) |
|---|
| 67 | >>> testutils.run_once(port,issuer) |
|---|
| 68 | >>> cdd2 = wallet.askLatestCDD(transport) |
|---|
| 69 | >>> cdd2.toString(True) == cdd.toString(True) |
|---|
| 70 | True |
|---|
| 71 | |
|---|
| 72 | |
|---|
| 73 | |
|---|
| 74 | ############################################################################### |
|---|
| 75 | Wallet: fetches current public minting keys for denomination |
|---|
| 76 | |
|---|
| 77 | |
|---|
| 78 | >>> printSection('Prepare Blinds') |
|---|
| 79 | |
|---|
| 80 | >>> testutils.run_once(port,issuer) |
|---|
| 81 | >>> mkcs = wallet.fetchMintKeys(transport,cdd,denominations=['1','5']) |
|---|
| 82 | >>> mkcs[0].toString() == issuer.getCurrentMKCs()['1'].toString() |
|---|
| 83 | True |
|---|
| 84 | |
|---|
| 85 | |
|---|
| 86 | |
|---|
| 87 | Wallet creates blank and blinds it |
|---|
| 88 | |
|---|
| 89 | >>> mkc = mkcs[1] |
|---|
| 90 | >>> cdd.masterPubKey.verifyContainerSignature(mkc) |
|---|
| 91 | True |
|---|
| 92 | >>> blank = wallet._makeBlank(cdd,mkc) |
|---|
| 93 | >>> blank.denomination == '5' |
|---|
| 94 | True |
|---|
| 95 | >>> key = mkc.publicKey |
|---|
| 96 | >>> secret, blind = key.blindBlank(blank) |
|---|
| 97 | >>> tid = wallet.makeSerial() |
|---|
| 98 | >>> int(mkc.denomination) |
|---|
| 99 | 5 |
|---|
| 100 | |
|---|
| 101 | |
|---|
| 102 | |
|---|
| 103 | ############################################################################### |
|---|
| 104 | Lets try to get a coin minted |
|---|
| 105 | |
|---|
| 106 | We first need to setup an authorizer, to (surpise) authorize the request. Nils says |
|---|
| 107 | the mint should just mint |
|---|
| 108 | |
|---|
| 109 | >>> from authorizer import Authorizer |
|---|
| 110 | >>> authorizer = Authorizer({}) |
|---|
| 111 | >>> authpub = authorizer.createKeys() |
|---|
| 112 | >>> mint.addAuthKey(authpub) |
|---|
| 113 | >>> authorizer.setMKCs(mkcs) |
|---|
| 114 | |
|---|
| 115 | Lets have the authorizer denying the request |
|---|
| 116 | |
|---|
| 117 | >>> printSection('Blinding I') |
|---|
| 118 | |
|---|
| 119 | >>> authorizer.deny = True |
|---|
| 120 | >>> testutils.run_once(port,issuer=issuer,mint=mint,authorizer=authorizer) |
|---|
| 121 | >>> wallet.requestTransfer(transport,tid,'foo',[[mkc.keyId,blind]],[]).header |
|---|
| 122 | 'TransferReject' |
|---|
| 123 | |
|---|
| 124 | Now have a well working one |
|---|
| 125 | |
|---|
| 126 | >>> printSection('Blinding II') |
|---|
| 127 | |
|---|
| 128 | >>> authorizer.deny = False |
|---|
| 129 | >>> testutils.run_once(port,issuer=issuer,mint=mint,authorizer=authorizer) |
|---|
| 130 | >>> response = wallet.requestTransfer(transport,tid,'foo',[[mkc.keyId,blind]],[]) |
|---|
| 131 | >>> response.header |
|---|
| 132 | 'TransferAccept' |
|---|
| 133 | |
|---|
| 134 | And check it |
|---|
| 135 | |
|---|
| 136 | >>> blindsign = response.signatures[0] |
|---|
| 137 | >>> blank.signature = key.unblind(secret,blindsign) |
|---|
| 138 | >>> coin = blank |
|---|
| 139 | >>> key.verifyContainerSignature(coin) |
|---|
| 140 | True |
|---|
| 141 | |
|---|
| 142 | We don't have a transport between mint and issuer yet. Lets have the mint |
|---|
| 143 | stuff coins directly into the issuer |
|---|
| 144 | |
|---|
| 145 | >>> mint.addToTransactions = issuer.addToTransactions |
|---|
| 146 | |
|---|
| 147 | The mint can also be a bit slow |
|---|
| 148 | |
|---|
| 149 | >>> printSection('Delay I') |
|---|
| 150 | |
|---|
| 151 | >>> mint.delay = True |
|---|
| 152 | >>> testutils.run_once(port,issuer=issuer,mint=mint,authorizer=authorizer) |
|---|
| 153 | >>> wallet.requestTransfer(transport,tid,'foo',[[mkc.keyId,blind]],[]).header |
|---|
| 154 | 'TransferDelay' |
|---|
| 155 | |
|---|
| 156 | >>> mint.delay = False |
|---|
| 157 | |
|---|
| 158 | Or the issuer is slow |
|---|
| 159 | |
|---|
| 160 | >>> printSection('Delay II') |
|---|
| 161 | |
|---|
| 162 | >>> issuer.delay = True |
|---|
| 163 | >>> testutils.run_once(port,issuer=issuer) |
|---|
| 164 | >>> wallet.resumeTransfer(transport,tid).header |
|---|
| 165 | 'TransferDelay' |
|---|
| 166 | >>> issuer.delay = False |
|---|
| 167 | |
|---|
| 168 | So we need to resume |
|---|
| 169 | |
|---|
| 170 | >>> printSection('Resume') |
|---|
| 171 | |
|---|
| 172 | >>> testutils.run_once(port,issuer=issuer) |
|---|
| 173 | >>> wallet.resumeTransfer(transport,tid).header |
|---|
| 174 | 'TransferAccept' |
|---|
| 175 | |
|---|
| 176 | And we have a valid coin |
|---|
| 177 | |
|---|
| 178 | >>> blindsign = response.signatures[0] |
|---|
| 179 | >>> blank.signature = key.unblind(secret,blindsign) |
|---|
| 180 | >>> coin = blank |
|---|
| 181 | >>> key.verifyContainerSignature(coin) |
|---|
| 182 | True |
|---|
| 183 | |
|---|
| 184 | |
|---|
| 185 | |
|---|
| 186 | ############################################################################### |
|---|
| 187 | Now, wallet to wallet. We setup an alice and a bob side. Alice announces |
|---|
| 188 | a sum, and bob dedices if he wants to accept it |
|---|
| 189 | |
|---|
| 190 | >>> printSection('W2W: Announce') |
|---|
| 191 | >>> alice = wallet |
|---|
| 192 | >>> bob = Wallet(storage.EmptyStorage()) |
|---|
| 193 | >>> bob.approval = "I don't like odd sums" |
|---|
| 194 | >>> alicetid = alice.makeSerial() |
|---|
| 195 | >>> alice.announceSum(DT(bob.listenSum), alicetid, 5, 'foobar') |
|---|
| 196 | "I don't like odd sums" |
|---|
| 197 | |
|---|
| 198 | >>> bob.approval = True |
|---|
| 199 | >>> alice.announceSum(DT(bob.listenSum), alicetid, 5, 'foobar') |
|---|
| 200 | True |
|---|
| 201 | |
|---|
| 202 | |
|---|
| 203 | Wallet Alice sends tokens to Wallet Bob (this time including their clear |
|---|
| 204 | serial and signature) |
|---|
| 205 | |
|---|
| 206 | For testing this we need to have a special transport that actually |
|---|
| 207 | allows the two consecutive requests that bob needs to make |
|---|
| 208 | |
|---|
| 209 | >>> tht = transports.TestingHTTPTransport(port,issuer=issuer,mint=mint) |
|---|
| 210 | |
|---|
| 211 | >>> printSection('W2W: Rejected I') |
|---|
| 212 | |
|---|
| 213 | Lets have first a wrong transactionId |
|---|
| 214 | >>> alice.requestSpend(DT(bob.listenSpend,tht),'foobar',[coin]) |
|---|
| 215 | Traceback (most recent call last): |
|---|
| 216 | .... |
|---|
| 217 | SpendReject: unknown transactionId |
|---|
| 218 | |
|---|
| 219 | Or lets try to send a wrong amount |
|---|
| 220 | |
|---|
| 221 | >>> printSection('W2W: Rejected II') |
|---|
| 222 | >>> alice.announceSum(DT(bob.listenSum), alicetid, 5, 'foobar') |
|---|
| 223 | True |
|---|
| 224 | >>> alice.requestSpend(DT(bob.listenSpend,tht),alicetid,[]) |
|---|
| 225 | Traceback (most recent call last): |
|---|
| 226 | .... |
|---|
| 227 | SpendReject: amount of coins does not match announced one. Announced: 5, got 0 |
|---|
| 228 | |
|---|
| 229 | And now, finally |
|---|
| 230 | |
|---|
| 231 | >>> printSection('W2W: Success') |
|---|
| 232 | >>> alice.announceSum(DT(bob.listenSum), alicetid, 5, 'foobar') |
|---|
| 233 | True |
|---|
| 234 | >>> alice.requestSpend(DT(bob.listenSpend,tht), alicetid, [coin]) |
|---|
| 235 | True |
|---|
| 236 | |
|---|
| 237 | That was so fun, lets do it again |
|---|
| 238 | |
|---|
| 239 | >>> printSection('W2W: Double Spend') |
|---|
| 240 | >>> alicetid = alice.makeSerial() |
|---|
| 241 | >>> bob.approval = True |
|---|
| 242 | >>> alice.announceSum(DT(bob.listenSum), alicetid, 5, 'foobar') |
|---|
| 243 | True |
|---|
| 244 | >>> alice.requestSpend(DT(bob.listenSpend,tht), alicetid, [coin]) |
|---|
| 245 | Traceback (most recent call last): |
|---|
| 246 | .... |
|---|
| 247 | SpendReject: did not go through |
|---|
| 248 | |
|---|
| 249 | |
|---|
| 250 | >>> printSection('Redeem') |
|---|
| 251 | >>> bobtid = wallet.makeSerial() |
|---|
| 252 | >>> testutils.run_once(port,issuer=issuer,mint=mint) |
|---|
| 253 | >>> bobscoins = bob.storage[cdd.currencyId]['coins'] |
|---|
| 254 | >>> bob.requestTransfer(transport,tid,target='foo', coins = bobscoins).header |
|---|
| 255 | 'TransferAccept' |
|---|
| 256 | |
|---|
| 257 | >>> #print time.time()-start |
|---|
| 258 | """ |
|---|
| 259 | |
|---|
| 260 | |
|---|
| 261 | if __name__ == "__main__": |
|---|
| 262 | import doctest |
|---|
| 263 | doctest.testmod(optionflags=doctest.ELLIPSIS) |
|---|