commit 90ff97c51aa41149b4e74f9916df33645342406f Author: MEUNIER Thibaud Date: Sun Sep 2 09:39:01 2018 +0200 first commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..1f68df1 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +topisto - www + +Il s'agit du contenu WEB présneté par le serveur https://www.topisto.net diff --git a/articles/00000000/content.html b/articles/00000000/content.html new file mode 100644 index 0000000..91a5cdd --- /dev/null +++ b/articles/00000000/content.html @@ -0,0 +1,14 @@ +
+
+
+
+

This QR Code is my PGP public key

+

Scan or click to get it

+
+ + + +
+
+
+
diff --git a/articles/00000000/public_key_qrcode.png b/articles/00000000/public_key_qrcode.png new file mode 100644 index 0000000..aa5dd78 Binary files /dev/null and b/articles/00000000/public_key_qrcode.png differ diff --git a/articles/20170200/content.html b/articles/20170200/content.html new file mode 100644 index 0000000..51b8638 --- /dev/null +++ b/articles/20170200/content.html @@ -0,0 +1,112 @@ +
+
+
+

A digital currency

+

+From Wikipedia, the free encyclopedia
+Bitcoin is a cryptocurrency and a digital payment system invented by an unknown programmer, or a group of programmers, under the name Satoshi Nakamoto.It was released as open-source software in 2009.
+The system is peer-to-peer, and transactions take place between users directly, without an intermediary.These transactions are verified by network nodes and recorded in a public distributed ledger called a blockchain. Since the system works without a central repository or single administrator, bitcoin is called the first decentralized digital currency.
+

+
+
+ +
+
+
+ +
+
+
+ +
+
+

WALLET

+

+ You can install a software on your computer or your phone to manage your bitcoins.
+ As Bitcoin is open source, there is a lot of such software avalaible.
+ It's called a wallet.
+ Those softwares are all talking with the same protocol. They are connected to the same network.
+ But, in fact, your true wallet is your 2 crypt keys : the public and the private one.
+ Everybody who knows those 2 values can spend your bitcoins.
+

+
+
+
+ +
+
+
+

Transaction

+

+When Bob is sending 42 to Alice, the bitcoin protocol is creating a transaction.
+A transaction is the balance between his Inputs and his Outputs. +

    +
  • First the network needs to be sure that Bob has got enough in his wallet.
    So Bob will include a list of transactions that he has received before.
    This is the inputs of the transactions.
    For example, previous transaction's amount are 10, 25 and another 10, so total inputs are 45
  • +
  • As the input's sum is superior to the amount of the transaction, an ouput is added.
    This output is the change to Bob.
    In the example, this 3
  • +
  • Fees are took by the network to process the transaction.
    Let say it will take 0.5.
    This is another ouput
  • +
  • So Alice will get only a part of the transaction's amount.
    this the last output, 41.5
  • +
+So the transaction is a made with : +
    +
  • Inputs : a list of previous transactions that bob has received
  • +
  • Outputs : a list of amounts to send to Alice, Bob, and fees
  • +
  • A timestamp
  • +
+Then the wallet will broadcast the transaction on the network.
+It will be sent to the area called "mempool".
+Nodes of the network will validate the transaction by verfying the Inputs and the Ouputs.
+A hash of the transaction is computed.
+So the transaction is sealed. +

+
+
+ +
+
+
+ +
+
+
+ +
+
+

Block

+

+Then the bitcoin protocol is grouping validated transactions in blocks.
+So a block is an array of transactions.
+A block is made with : +

    +
  • An array of transactions.
    +That's what my script is drawing.
    +Running the transactions array, it is computing the sum of each outputs of each transaction.
    +Then, it draws a rectangle for each sum.
    +
  • +
  • A "proof of work".
    The miner is computing a special hash that depends on the array of transaction and that will answer to a constraint.
    This hash is very difficult to compute. In fact it is not really computed, the miner must test every value until he find the solution.
    +That why the network will give him a reward.
    This reward is a special transaction that has no inputs.
    +As this reward is in bitcoin, this is also a mean to control the money stock.
    +The more you use the money, the more there is blocks, the more there is money.
    +
  • +
+

+
+ +
+
+ +
+
+
+

Blockchain

+

+Each block has a hash and a reference to the ihash of the previous block.
+So blocks are chained together.
+

+
+ +
+ +
+ +
+
diff --git a/articles/20170200/header.html b/articles/20170200/header.html new file mode 100644 index 0000000..2cd968d --- /dev/null +++ b/articles/20170200/header.html @@ -0,0 +1,17 @@ +
+
+
+
+

ARTICLE

+
+
+

Bitcoin and blockchain

+

This quickly explain what is Bitcoin and how it works

+

+ Everything is on bitcoin.org, but here you can find a quick intro to Bitcoin and the Blockchain.
+ The project here is to make datavisualization about the Blockchain. So I must explain what is the Blockchain. I'm not interesting in trading. The Bitcoin is only see as technical object. +

+ ####BUTTON#### +
+
+
diff --git a/articles/20170200/images/bitcoin.jpg b/articles/20170200/images/bitcoin.jpg new file mode 100644 index 0000000..f856e6e Binary files /dev/null and b/articles/20170200/images/bitcoin.jpg differ diff --git a/articles/20170200/images/blockchain.gif b/articles/20170200/images/blockchain.gif new file mode 100644 index 0000000..fe98c2e Binary files /dev/null and b/articles/20170200/images/blockchain.gif differ diff --git a/articles/20170200/images/bob_to_alice.gif b/articles/20170200/images/bob_to_alice.gif new file mode 100644 index 0000000..f753ad2 Binary files /dev/null and b/articles/20170200/images/bob_to_alice.gif differ diff --git a/articles/20170200/images/btcfond.jpg b/articles/20170200/images/btcfond.jpg new file mode 100644 index 0000000..cc0771b Binary files /dev/null and b/articles/20170200/images/btcfond.jpg differ diff --git a/articles/20170200/images/tx2block.gif b/articles/20170200/images/tx2block.gif new file mode 100644 index 0000000..f90679e Binary files /dev/null and b/articles/20170200/images/tx2block.gif differ diff --git a/articles/20170200/images/wallet.png b/articles/20170200/images/wallet.png new file mode 100644 index 0000000..46987fd Binary files /dev/null and b/articles/20170200/images/wallet.png differ diff --git a/articles/20170210/header.html b/articles/20170210/header.html new file mode 100644 index 0000000..188b7de --- /dev/null +++ b/articles/20170210/header.html @@ -0,0 +1,17 @@ +
+
+
+
+

ARTICLE

+
+
+

Install a full node

+

Want to be part of it !

+

+ But I don't have enought disk space ...
+ So it will be a pruned one. +

+ ####BUTTON#### +
+
+
diff --git a/articles/20170210/images/btc_node.jpg b/articles/20170210/images/btc_node.jpg new file mode 100644 index 0000000..089606d Binary files /dev/null and b/articles/20170210/images/btc_node.jpg differ diff --git a/articles/20170229/header.html b/articles/20170229/header.html new file mode 100644 index 0000000..24914ba --- /dev/null +++ b/articles/20170229/header.html @@ -0,0 +1,14 @@ +
+
+
+
+

ARTICLE

+
+
+

R. Topisto (Twitterbot)

+

This robot is listening to the bitcoin blockchain. Every new block it draw an image using one of the above methods.

+

This is not technic ... this is computationnal art. It uses PHP, blockchain.info API, abraham TwitterOAuth.

+ ####BUTTON#### +
+
+
diff --git a/articles/20170229/images/topisto_rouge.png b/articles/20170229/images/topisto_rouge.png new file mode 100644 index 0000000..9f22b10 Binary files /dev/null and b/articles/20170229/images/topisto_rouge.png differ diff --git a/articles/20170301/content.html b/articles/20170301/content.html new file mode 100644 index 0000000..06b0b08 --- /dev/null +++ b/articles/20170301/content.html @@ -0,0 +1,65 @@ + + +
+ +
+
+
+ Drawing the treemap +
+
+
+ +
+
+
+ Drawing the block hashes +
+
+
+ + + + diff --git a/articles/20170301/header.html b/articles/20170301/header.html new file mode 100644 index 0000000..d78efe8 --- /dev/null +++ b/articles/20170301/header.html @@ -0,0 +1,37 @@ +
+
+
+
+

ARTICLE

+
+
+

Blockchain as a Treemap

+

Draw a Treemap where each rectangle is a transaction's inputs sum. Then add block's hashes on top and previous block's invert hash on bottom. This way we can draw the blockchain by putting images last's one above the others.

+

That way, we can "see" the Blockchain...

+

You can choose an alternative drawing method. + +

+ ####BUTTON#### +
+
+
+ diff --git a/articles/20170315/content.html b/articles/20170315/content.html new file mode 100644 index 0000000..b955710 --- /dev/null +++ b/articles/20170315/content.html @@ -0,0 +1,207 @@ +
+
+
+ +
+
+
+ + + +
+
+
+
+
+
+
+
+
+
+
+ + + + + + + + diff --git a/articles/20170315/header.html b/articles/20170315/header.html new file mode 100644 index 0000000..2bc662a --- /dev/null +++ b/articles/20170315/header.html @@ -0,0 +1,17 @@ +
+
+
+
+

ARTICLE

+
+
+

Exploring D3 force driven graphs

+

This the Klingon starfleet organisation

+

Not really finished ...

+

+Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. +

+ ####BUTTON#### +
+
+
diff --git a/articles/20170315/images/D3-force-driven-graph.png b/articles/20170315/images/D3-force-driven-graph.png new file mode 100644 index 0000000..7bb37c1 Binary files /dev/null and b/articles/20170315/images/D3-force-driven-graph.png differ diff --git a/articles/20170315/styles.html b/articles/20170315/styles.html new file mode 100644 index 0000000..69edb73 --- /dev/null +++ b/articles/20170315/styles.html @@ -0,0 +1,12 @@ + + diff --git a/articles/20170425/content.html b/articles/20170425/content.html new file mode 100644 index 0000000..6deab1d --- /dev/null +++ b/articles/20170425/content.html @@ -0,0 +1,99 @@ +
+
+
+ Let explain how I do this
+ I will take the "Pizza Block" to illustrate.
+ On 2010, may the 22nd, a bitcoin developper (Laszlo Hanyecz) bought a pizza with 10 000 BTC.
+ This is widely recognised as the first real-world transaction with bitcoin.
+ As the Blockchain is public, we can find the block that contains this transaction.
+ I call it the "Pizza block", it is at height 57035.
+ It contains 2 transactions, the pizza and the reward for the mining.
+ You can explore the block here.
+
+
+
+ +
+
+
+ +
+
+ Fisrt, I draw a line for each transaction.
+ The heigth of a line depends of the amount of the transaction.
+ So, in the Pizza block, we have a line at 50 and a line at 10 000. +
+
+
+ +
+
+
+ +
+
+ It will be more beautiful with a gradient of 2 colors, from right to left.
+
+
+
+ +
+
+
+ +
+
+ Each transaction have a hash. In our case, those hashes are : +
    +
  • dc79b6d28309783a0aa2b47be2037626fbd19d93ad1338d187c27df0a1d5e1a4
  • +
  • 49d2adb6e476fa46d8357babf78b1b501fd39e177ac7833124b3f67b17c40c2a
  • +
+ A hash is a 64 characters string. So i will cut each line in 64 parts.
+ It's also an hexadecimal value, so each character is a value between 0 and f.
+ Each character will be a point. +
    +
  • above the line if the value is above 8
  • +
  • under the line if the value is under 8
  • +
+ Rather than draw a line, i will draw a spline which link each point.
+
+
+
+ +
+
+
+ +
+
+ It will be more beautiful with a growing factor from left to right.
+ And it is also a mean to see the "true" height of the transaction on the left side.
+
+
+
+ +
+
+
+ +
+
+ Then i will add iterations and hasard.
+ I will iterate on each spline.
+ At each point, i will substract or add a random value.
+ So the lines becomes ribbons. +
+
+
+ +
+
+
+ +
+
+ Then I will use transaprency.
+ And that's it ! +
+
+
diff --git a/articles/20170425/header.html b/articles/20170425/header.html new file mode 100644 index 0000000..f89c10c --- /dev/null +++ b/articles/20170425/header.html @@ -0,0 +1,14 @@ +
+
+
+
+

ARTICLE

+
+
+

Blockchain and Splines

+

Another block drawing method.

+

Each line is a transaction value. The line is then converted to spline using transaction's hash value. I'm following Inconvergent's blog to achieve this work.

+ ####BUTTON#### +
+
+
diff --git a/articles/20170920/content.html b/articles/20170920/content.html new file mode 100644 index 0000000..38b4053 --- /dev/null +++ b/articles/20170920/content.html @@ -0,0 +1,55 @@ + +
+ +
+
+
+ Drawing the splines +
+
+
+ + + + diff --git a/articles/20170920/header.html b/articles/20170920/header.html new file mode 100644 index 0000000..d053bc3 --- /dev/null +++ b/articles/20170920/header.html @@ -0,0 +1,14 @@ +
+
+
+
+

ARTICLE

+
+
+

fuzzy Treemap

+

Another Treemap drawing method.

+

Drawing with 2 colors, fuzzy is using to draw the rectangle ...

+ ####BUTTON#### +
+
+
diff --git a/articles/20170921/content.html b/articles/20170921/content.html new file mode 100644 index 0000000..38b4053 --- /dev/null +++ b/articles/20170921/content.html @@ -0,0 +1,55 @@ + +
+ +
+
+
+ Drawing the splines +
+
+
+ + + + diff --git a/articles/20170921/header.html b/articles/20170921/header.html new file mode 100644 index 0000000..d053bc3 --- /dev/null +++ b/articles/20170921/header.html @@ -0,0 +1,14 @@ +
+
+
+
+

ARTICLE

+
+
+

fuzzy Treemap

+

Another Treemap drawing method.

+

Drawing with 2 colors, fuzzy is using to draw the rectangle ...

+ ####BUTTON#### +
+
+
diff --git a/articles/20171031/content.html b/articles/20171031/content.html new file mode 100644 index 0000000..4505a8b --- /dev/null +++ b/articles/20171031/content.html @@ -0,0 +1,24 @@ + +
+ +
+
+
+ Drawing the splines +
+
+
diff --git a/articles/20171031/header.html b/articles/20171031/header.html new file mode 100644 index 0000000..13af6c5 --- /dev/null +++ b/articles/20171031/header.html @@ -0,0 +1,34 @@ +
+
+
+
+

ARTICLE

+
+
+

Draw the "Full" Block

+

Use the drawing methods from below, but drawing each part of the block : inputs, outputs, fees and reward

+

You can choose an alternative drawing method. + +

+ ####BUTTON#### +
+
+
+ + diff --git a/articles/20180224/content.html b/articles/20180224/content.html new file mode 100644 index 0000000..4505a8b --- /dev/null +++ b/articles/20180224/content.html @@ -0,0 +1,24 @@ + +
+ +
+
+
+ Drawing the splines +
+
+
diff --git a/articles/20180224/fluid.js b/articles/20180224/fluid.js new file mode 100644 index 0000000..7b8648b --- /dev/null +++ b/articles/20180224/fluid.js @@ -0,0 +1,584 @@ +// shim layer with setTimeout fallback (Paul Irish) +window.requestAnimFrame = (function(){ + return window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function( callback ){ + window.setTimeout(callback, 1000 / 60); + }; + })(); + + + // Based on http://www.dgp.toronto.edu/people/stam/reality/Research/pdf/GDC03.pdf + var NavierStokes = function(settings){ + this.init(settings); + }; + + NavierStokes.prototype = { + + init : function(settings){ + var defaultSettings = { + resolution : 64, + iterations : 10, + fract : 1/4, + diffusion : 1, + gridmodify : 0, + dt : 0.1, + callbackUser : function(D, U, V, size){}, + callbackDisplay : function(D, U, V, size){} + }; + + if (this.settings === undefined){ + this.settings = defaultSettings; + } + + this._mergeRecursive(this.settings, settings); + + this.rows = this.settings.resolution + 2; + this.arraySize = (this.settings.resolution+2)*(this.settings.resolution+2); + + + this.U = new Float32Array(this.arraySize); + this.V = new Float32Array(this.arraySize); + this.D = new Float32Array(this.arraySize); + + this.U_prev = new Float32Array(this.arraySize); + this.V_prev = new Float32Array(this.arraySize); + this.D_prev = new Float32Array(this.arraySize); + + this.NullArray = new Float32Array(this.arraySize); + + + // Precalculate lookup table for 2D > 1D array. + this.IX = new Array(this.rows); + for (var i = 0; i < this.rows; i++){ + this.IX[i] = new Array(this.rows); + for (var b = 0; b < this.rows; b++){ + this.IX[i][b] = i+b*this.rows; + } + } + // Init all Arrays. + for (i = 0; i < this.arraySize; i++){ + this.D_prev[i] = this.U_prev[i] = this.V_prev[i] = this.D[i] = this.U[i] = this.V[i] = this.NullArray[i] = 0.0; + } + + //Init some vars based on the Resolution settings: + this.calculateSettings(); + }, + clear : function(){ + this.D.set(this.NullArray); + this.U.set(this.NullArray); + this.V.set(this.NullArray); + }, + // Getter Setter + getResolution : function(){ + return this.settings.resolution; + }, + + getSettings : function(){ + return this.settings; + }, + + update : function(){ + // Add user Action + this.userAction(); + + // Cals velosity & density + this.vel_step (this.U, this.V, this.U_prev, this.V_prev, this.settings.dt ); + this.dens_step (this.D, this.D_prev, this.U, this.V, this.settings.dt ); + + this.settings.callbackDisplay(this.D, this.U, this.V, this.settings.resolution); + }, + + calculateSettings : function(){ + this.centerPos = (-0.5/this.settings.resolution) * (1 + this.settings.gridmodify); + this.scale = this.settings.resolution * 0.5; + this.dt0 = this.settings.dt * this.settings.resolution; + this.p5 = this.settings.resolution + 0.5; + }, + + userAction : function (){ + this.D_prev.set(this.NullArray); + this.U_prev.set(this.NullArray); + this.V_prev.set(this.NullArray); + + this.settings.callbackUser(this.D_prev, this.U_prev, this.V_prev, this.settings.resolution); + }, + vel_step : function (u, v, u0, v0, dt ){ + var tmp; + + this.add_source(u, u0, dt ); + this.add_source(v, v0, dt ); + + tmp = u0; + u0 = u; + u = tmp; + this.diffuse ( 1, u, u0, dt ); + + + tmp = v0; + v0 = v; + v = tmp; + this.diffuse ( 2, v, v0, dt ); + + this.project(u, v, u0, v0); + + tmp = u0; + u0 = u; + u = tmp; + + tmp = v0; + v0 = v; + v = tmp; + this.advect(1, u, u0, u0, v0, dt); + this.advect(2, v, v0, u0, v0, dt); + + this.project(u, v, u0, v0 ); + }, + + dens_step : function (x, x0, u, v, dt){ + var tmp; + this.add_source (x, x0, dt); + //SWAP ( x0, x ); + this.diffuse (0, x0, x, dt ); + //SWAP ( x0, x ); + this.advect ( 0, x, x0, u, v, dt ); + }, + + add_source : function (x, s, dt){ + for (var i=0; i this.p5) x = this.p5; + + var i0 = x | 0; + var i1 = i0 + 1; + + if (y < 0.5) y = 0.5 + if (y > this.p5) y = this.p5; + + var j0 = y | 0; + var j1 = j0 + 1; + var s1 = x - i0; + var s0 = 1 - s1; + var t1 = y - j0; + var t0 = 1 - t1; + var toR1 = this.IX[0][j0]; + var toR2 = this.IX[0][j1]; + d[pos] = s0 * (t0 * d0[i0 + toR1] + t1 * d0[i0 + toR2]) + s1 * (t0 * d0[i1 + toR1] + t1 * d0[i1 + toR2]); + } + } + this.set_bnd(b, d); + }, + // Calculate Boundary's + set_bnd : function (b, x ){ + var i, j; + switch (b){ + case 1 : + for (i = 1; i <= this.settings.resolution; i++) { + x[i] = x[i + this.rows]; + x[this.IX[i][(this.settings.resolution+1)]] = x[this.IX[i][(this.settings.resolution)]]; + x[this.IX[0][i]] = -x[this.IX[1][i]]; + x[this.IX[(this.settings.resolution+1)][i]] = -x[this.IX[(this.settings.resolution)][i]]; + } + break; + case 2 : + for ( i = 1; i <= this.settings.resolution; i++) { + x[i] = -x[i + this.rows]; + x[this.IX[i][(this.settings.resolution+1)]] = -x[this.IX[i][(this.settings.resolution)]]; + x[this.IX[0][i]] = x[this.IX[1][i]]; + x[this.IX[(this.settings.resolution+1)][i]] = x[this.IX[(this.settings.resolution)][i]]; + } + break; + default : + for ( i = 1; i <= this.settings.resolution; i++) { + x[i] = x[i + this.rows]; + x[this.IX[i][(this.settings.resolution+1)]] = x[this.IX[i][(this.settings.resolution)]]; + x[this.IX[0][i]] = x[this.IX[1][i]]; + x[this.IX[(this.settings.resolution+1)][i]] = x[this.IX[(this.settings.resolution)][i]]; + } + } + // Boundes of the Canvas + var topPos = this.IX[0][this.settings.resolution+1]; + x[0] = (x[1] + x[this.rows]) / 2; + x[topPos] = (x[1 + topPos] + x[this.IX[this.settings.resolution][0]]) / 2; + x[(this.settings.resolution+1)] = (x[this.settings.resolution] + x[(this.settings.resolution + 1) + this.rows]) / 2; + x[(this.settings.resolution+1)+topPos] = (x[this.settings.resolution + topPos] + x[this.IX[this.settings.resolution+1][this.settings.resolution]]); + + }, + // Merge Settings. + _mergeRecursive : function(obj1, obj2) { + for (var p in obj2) { + if ( obj2[p].constructor==Object ) { + obj1[p] = this._mergeRecursive(obj1[p], obj2[p]); + } else { + obj1[p] = obj2[p]; + } + } + return obj1; + } + }; + + var Display = function(canvas){ + this.colorFunctions = { + "BW" : this.calcColorBW , + "Color": this.calcColor, + "User" : this.calcUserColor + }; + this.currColorFunc = "BW"; + this.canvas = canvas; + this.context = canvas.getContext("2d", {alpha : false}); + this.supportImageData = !!this.context.getImageData; + + this.colorUser = { + R : 0, + G : 255, + B : 0 + } + }; + + Display.prototype = { + + init : function (res){ + this.calcResolution = res; + this.imageData = null; + this.showColors = false; + this.line = null; + }, + + density : function(D, U, V){ + var r,g,b, x, y, d; + + if (this.supportImageData){ + // Get Image Data + if (this.imageData === null){ + this.imageData = this.context.getImageData(0, 0, this.calcResolution, this.calcResolution); + } + + + for (x = 0; x < this.calcResolution; x++) { + for (y = 0; y < this.calcResolution; y++){ + var posC = (x + y * this.calcResolution) * 4; + var pos = fluid.IX[x][y]; + + var cArray = this.colorFunctions[this.currColorFunc].call(this, D, U, V, pos); + + this.imageData.data[posC] = cArray[0]; // R + this.imageData.data[posC + 1] = cArray[1]; // G + this.imageData.data[posC + 2] = cArray[2]; // B + this.imageData.data[posC + 3] = 255; //A + } + } + this.context.putImageData(this.imageData, 0, 0); + }else{ + // Slow fallback for oldie + for (x = 0; x < this.calcResolution; x++) { + for (y = 0; y < this.calcResolution; y++) { + var pos = fluid.IX[x][y]; + var c =(D[pos] / 2); + this.context.setFillColor(c , c, c , 1); + this.context.fillRect(x, y, 1, 1); + } + } + } + // Draw the line for creating an Emitter + if (this.line != null){ + this.context.beginPath(); + this.context.lineWidth = 1; + this.context.strokeStyle = "rgb(255,255,255)"; + this.context.moveTo(this.line[0][0],this.line[0][1]); + this.context.lineTo(this.line[1][0],this.line[1][1]); + this.context.stroke(); + } + }, + + setColorFunction: function(colorFuncName){ + this.currColorFunc = "BW"; + }, + + calcColorBW : function(D, U, V, pos){ + var bw = (D[pos] * 255 / 6) | 0; + return [bw, bw, bw]; + }, + + calcColor : function(D, U, V, pos){ + var r = Math.abs((U[pos] * 1300 ) | 0); + var b = Math.abs((V[pos] * 1300 ) | 0); + var g = (D[pos] * 255 / 6) | 0; + + return [r, g, b]; + }, + + calcUserColor : function(D, U, V, pos){ + var r = Math.abs((U[pos] *500* this.colorUser.R) | 0); + var g = (D[pos] * this.colorUser.G) | 0; + var b = Math.abs((V[pos] *500* this.colorUser.B ) | 0); + + return [r, g, b]; + }, + + drawLine : function (a0, a1, scale){ + var l0 = [a0[0]*scale, a0[1]*scale]; + var l1 = [a1[0]*scale, a1[1]*scale]; + + this.line = [l0, l1]; + }, + + removeLine : function(){this.line = null}, + }; + + + +var user = { + displaySize : 500, + canvas : null, + canvasOffset : null, + scale : 0, + + mouseStart: [], + mouseEnd : [], + mouseLeftDown : false, + mousePath : [], + + mouseRightDown : false, + mouseRightStart : [], + + forceEmitters : [], + + insertedDensity : 50, + + init : function(canvas){ + this.canvas = $(canvas); + + this.canvasOffset = this.canvas.offset(); + + var that = this; + window.ontouchend = window.onmouseup = function(e){that.handleInputEnd(e);}; + canvas.ontouchstart = canvas.onmousedown = function(e){that.handleInput(e);}; + canvas.ontouchmove = canvas.onmousemove = function(e){that.handleInputMove(e);}; + canvas.oncontextmenu = function(e){e.preventDefault();}; + }, + + interact : function(D, U, V, size){ + var x, y, pos, i ; + + if (this.mouseLeftDown){ + var dx = this.mouseStart[0] - this.mouseEnd[0]; + var dy = this.mouseStart[1] - this.mouseEnd[1]; + + var mousePathLength = Math.sqrt(dx * dx + dy * dy) | 0; + mousePathLength = (mousePathLength < 1) ? 1 : mousePathLength; + for ( i = 0; i < mousePathLength; i++) { + x = (((this.mouseStart[0] - (i / mousePathLength) * dx)) * this.scale) | 0; + y = (((this.mouseStart[1] - (i / mousePathLength) * dy)) * this.scale) | 0; + + pos = fluid.IX[x][y]; + + U[pos] = -dx / 6; + V[pos] = -dy / 6; + D[pos] = this.insertedDensity; + } + this.mouseStart[0] = this.mouseEnd[0]; + this.mouseStart[1] = this.mouseEnd[1]; + } + + for (i = 0;i +
+
+
+ +

ARTICLE

+
+
+

Test Fluid animation

+

Try a new kind of visualisation

+

+ Inspired from Fabrice Weinberg's code +

+ ####BUTTON#### +
+
+ + + diff --git a/articles/20180225/content.html b/articles/20180225/content.html new file mode 100644 index 0000000..60750b5 --- /dev/null +++ b/articles/20180225/content.html @@ -0,0 +1,26 @@ + +
+ +
+
+
+ OK ... Here i don't really understand anything.
+ That's a copy/passte of Jeff's Thomas code.
+ And i just replace mouse move by a path computed from the block HASH. +
+
+
diff --git a/articles/20180225/fluid2.js b/articles/20180225/fluid2.js new file mode 100644 index 0000000..727ced5 --- /dev/null +++ b/articles/20180225/fluid2.js @@ -0,0 +1,780 @@ +/* +Comments were requested, here we go :) + +Here's the rundown: + +This script creates a grid of cells and a separate layer of particles that +float on top of the grid. Each cell of the grid holds X and Y velocity +(direction and magnitude) values and a pressure value. + +Whenever the user holds down and moves their mouse over the canvas, the velocity +of the mouse is calculated and is used to influence the velocity and pressure in +each cell that was within the defined range of the mouse coordinates. Then, the +pressure change is communicated to all of the neighboring cells of those affected, +adjusting their velocity and pressure, and this is repeated over and over until +the change propogates to all of the cells in the path of the direction of movement. + +The particles are randomly placed on the canvas and move according to the +velocity of the grid cells below, similar to grass seed floating on the surface +of water as it's moving. Whenever the particles move off the edge of the canvas, +they are "dropped" back on to the canvas in a random position. The velocity, +however, is "wrapped" around to the opposite edge of the canvas. The slowing +down of the movement is simulated viscosity, which is basically frictional drag +in the liquid. + + +Let's get started: +-------- + +This is a self-invoking function. Basically, that means that it runs itself +automatically. The reason for wrapping the script in this is to isolate the +majority of the variables that I define inside from the global scope and +only reveal specific functions and values. It looks like this: + +(function(argument) { + + alert(argument); + +})("Yo."); + +and it does the same thing as this: + +function thing(argument) { + + alert(argument); + +} + +thing("Yo."); + +*/ + +/* added by TOPISTO */ +function myGetRandom(max) +{ + return Math.floor(Math.random() * max); +} + +(function(w) { + + var canvas, ctx; + + /* + These are the variable definitions for the values that will be used + throughout the rest of the script. + */ + var canvas_width = 500; //Needs to be a multiple of the resolution value below. + var canvas_height = 250; //This too. + + /* + This is an associative array to hold the status of the mouse cursor + Whenever the mouse is moved or pressed, there are event handlers that + update the values in this array. + */ + var mouse = { + x: Math.floor(canvas_width / 2), + y: Math.floor(canvas_height / 2), + px: Math.floor(canvas_width / 2), + py: Math.floor(canvas_height / 2), + down: false + }; + + //var resolution = 10; //Width and height of each cell in the grid. + var resolution = 5; + + var pen_size = 20; //Radius around the mouse cursor coordinates to reach when stirring + + var num_cols = canvas_width / resolution; //This value is the number of columns in the grid. + var num_rows = canvas_height / resolution; //This is number of rows. + //var speck_count = 5000; //This determines how many particles will be made. + var speck_count = 5000; + + var vec_cells = []; //The array that will contain the grid cells + var particles = []; //The array that will contain the particles + + /* added by topisto */ + var current_block_hash = []; + var current_block_height = myGetRandom(canvas_height); + var current_block_hash_ndx = 0; + var current_block_hash_pos = 0; + var current_topisto_way = 0; // 0 : left 2 right, 1 : right 2 left, 2 : top 2 bottom, 3 : bottom 2 top + + /* + This is the main function. It is triggered to start the process of constructing the + the grid and creating the particles, attaching event handlers, and starting the + animation loop. + */ + function init() { + vec_cells = []; + particles = []; + + /* added by topisto */ + if (current_block_hash.length == 0) for(i=0;i<64;i++) current_block_hash[i] = myGetRandom(16); + + //These lines get the canvas DOM element and canvas context, respectively. + canvas = document.getElementById("c"); + ctx = canvas.getContext("2d"); + + //These two set the width and height of the canvas to the defined values. + canvas.width = canvas_width; + canvas.height = canvas_height; + + /* + This loop begins at zero and counts up to the defined number of particles, + less one, because array elements are numbered beginning at zero. + */ + for (i = 0; i < speck_count; i++) { + /* + This calls the function particle() with random X and Y values. It then + takes the returned object and pushes it into the particles array at the + end. + */ + particles.push(new particle(Math.random() * canvas_width, Math.random() * canvas_height)); + } + + //This loops through the count of columns. + for (col = 0; col < num_cols; col++) { + + //This defines the array element as another array. + vec_cells[col] = []; + + //This loops through the count of rows. + for (row = 0; row < num_rows; row++) { + + /* + This line calls the cell() function, which creates an individual grid cell + and returns it as an object. The X and Y values are multiplied by the + resolution so that when the loops are referring to "column 2, row 2", the + width and height of "column 1, row 1" are counted in so that the top-left + corner of the new grid cell is at the bottom right of the other cell. + */ + var cell_data = new cell(col * resolution, row * resolution, resolution) + + //This pushes the cell object into the grid array. + vec_cells[col][row] = cell_data; + + /* + These two lines set the object's column and row values so the object knows + where in the grid it is positioned. + */ + vec_cells[col][row].col = col; + vec_cells[col][row].row = row; + + } + } + + + /* + These loops move through the rows and columns of the grid array again and set variables + in each cell object that will hold the directional references to neighboring cells. + For example, let's say the loop is currently on this cell: + + OOOOO + OOOXO + OOOOO + + These variables will hold the references to neighboring cells so you only need to + use "up" to refer to the cell above the one you're currently on. + */ + for (col = 0; col < num_cols; col++) { + + for (row = 0; row < num_rows; row++) { + + /* + This variable holds the reference to the current cell in the grid. When you + refer to an element in an array, it doesn't copy that value into the new + variable; the variable stores a "link" or reference to that spot in the array. + If the value in the array is changed, the value of this variable would change + also, and vice-versa. + */ + var cell_data = vec_cells[col][row]; + + /* + Each of these lines has a ternary expression. A ternary expression is similar + to an if/then clause and is represented as an expression (e.g. row - 1 >= 0) + which is evaluated to either true or false. If it's true, the first value after + the question mark is used, and if it's false, the second value is used instead. + + If you're on the first row and you move to the row above, this wraps the row + around to the last row. This is done so that momentum that is pushed to the edge + of the canvas is "wrapped" to the opposite side. + */ + var row_up = (row - 1 >= 0) ? row - 1 : num_rows - 1; + var col_left = (col - 1 >= 0) ? col - 1 : num_cols - 1; + var col_right = (col + 1 < num_cols) ? col + 1 : 0; + + //Get the reference to the cell on the row above. + var up = vec_cells[col][row_up]; + var left = vec_cells[col_left][row]; + var up_left = vec_cells[col_left][row_up]; + var up_right = vec_cells[col_right][row_up]; + + /* + Set the current cell's "up", "left", "up_left" and "up_right" attributes to the + respective neighboring cells. + */ + cell_data.up = up; + cell_data.left = left; + cell_data.up_left = up_left; + cell_data.up_right = up_right; + + /* + Set the neighboring cell's opposite attributes to point to the current cell. + */ + up.down = vec_cells[col][row]; + left.right = vec_cells[col][row]; + up_left.down_right = vec_cells[col][row]; + up_right.down_left = vec_cells[col][row]; + + } + } + + + /* + These lines create triggers that fire when certain events happen. For + instance, when you move your mouse, the mouse_move_handler() function + will run and will be passed the event object reference into it's "e" + variable. Something to note, the mousemove event doesn't necessarily + fire for *every* mouse coordinate position; the mouse movement is + sampled at a certain rate, meaning that it's checked periodically, and + if the mouse has moved, the event is fired and the current coordinates + are sent. That's why you'll see large jumps from one pair of coordinates + to the next if you move your mouse very fast across the screen. That's + also how I measure the mouse's velocity. + */ +/* + w.addEventListener("mousedown", mouse_down_handler); + w.addEventListener("touchstart", mouse_down_handler); + + w.addEventListener("mouseup", mouse_up_handler); + w.addEventListener("touchend", touch_end_handler); + + canvas.addEventListener("mousemove", mouse_move_handler); + canvas.addEventListener("touchmove", touch_move_handler); +*/ + + //When the page is finished loading, run the draw() function. + w.onload = draw; + + } + + + /* + This function updates the position of the particles according to the velocity + of the cells underneath, and also draws them to the canvas. + */ + function update_particle() { + + //Loops through all of the particles in the array + for (i = 0; i < particles.length; i++) { + //Sets this variable to the current particle so we can refer to the particle easier. + var p = particles[i]; + + //If the particle's X and Y coordinates are within the bounds of the canvas... + if (p.x >= 0 && p.x < canvas_width && p.y >= 0 && p.y < canvas_height) { + + /* + These lines divide the X and Y values by the size of each cell. This number is + then parsed to a whole number to determine which grid cell the particle is above. + */ + var col = parseInt(p.x / resolution); + var row = parseInt(p.y / resolution); + + //Same as above, store reference to cell + var cell_data = vec_cells[col][row]; + + /* + These values are percentages. They represent the percentage of the distance across + the cell (for each axis) that the particle is positioned. To give an example, if + the particle is directly in the center of the cell, these values would both be "0.5" + + The modulus operator (%) is used to get the remainder from dividing the particle's + coordinates by the resolution value. This number can only be smaller than the + resolution, so we divide it by the resolution to get the percentage. + */ + var ax = (p.x % resolution) / resolution; + var ay = (p.y % resolution) / resolution; + + /* + These lines subtract the decimal from 1 to reverse it (e.g. 100% - 75% = 25%), multiply + that value by the cell's velocity, and then by 0.05 to greatly reduce the overall change in velocity + per frame (this slows down the movement). Then they add that value to the particle's velocity + in each axis. This is done so that the change in velocity is incrementally made as the + particle reaches the end of it's path across the cell. + */ + p.xv += (1 - ax) * cell_data.xv * 0.05; + p.yv += (1 - ay) * cell_data.yv * 0.05; + + /* + These next four lines are are pretty much the same, except the neighboring cell's + velocities are being used to affect the particle's movement. If you were to comment + them out, the particles would begin grouping at the boundary between cells because + the neighboring cells wouldn't be able to pull the particle into their boundaries. + */ + p.xv += ax * cell_data.right.xv * 0.05; + p.yv += ax * cell_data.right.yv * 0.05; + + p.xv += ay * cell_data.down.xv * 0.05; + p.yv += ay * cell_data.down.yv * 0.05; + + //This adds the calculated velocity to the position coordinates of the particle. + p.x += p.xv; + p.y += p.yv; + + ctx.strokeStyle = p.color; + + //For each axis, this gets the distance between the old position of the particle and it's new position. + var dx = p.px - p.x; + var dy = p.py - p.y; + + //Using the Pythagorean theorum (A^2 + B^2 = C^2), this determines the distance the particle travelled. + var dist = Math.sqrt(dx * dx + dy * dy); + + //This line generates a random value between 0 and 0.5 + var limit = Math.random() * 0.5; + + //If the distance the particle has travelled this frame is greater than the random value... + if (dist > limit) { + ctx.lineWidth = 1; + //ctx.lineWidth = 3; + ctx.beginPath(); //Begin a new path on the canvas + ctx.moveTo(p.x, p.y); //Move the drawing cursor to the starting point + ctx.lineTo(p.px, p.py); //Describe a line from the particle's old coordinates to the new ones + ctx.stroke(); //Draw the path to the canvas + }else{ + //If the particle hasn't moved further than the random limit... + + ctx.beginPath(); + ctx.moveTo(p.x, p.y); + + /* + Describe a line from the particle's current coordinates to those same coordinates + plus the random value. This is what creates the shimmering effect while the particles + aren't moving. + */ + ctx.lineTo(p.x + limit, p.y + limit); + + ctx.stroke(); + } + + //This updates the previous X and Y coordinates of the particle to the new ones for the next loop. + p.px = p.x; + p.py = p.y; + } + else { + //If the particle's X and Y coordinates are outside the bounds of the canvas... + + //Place the particle at a random location on the canvas + p.x = p.px = Math.random() * canvas_width; + p.y = p.py = Math.random() * canvas_height; + + //Set the particles velocity to zero. + p.xv = 0; + p.yv = 0; + } + + //These lines divide the particle's velocity in half everytime it loops, slowing them over time. + p.xv *= 0.5; + p.yv *= 0.5; + } + } + + /* + This is the main animation loop. It is run once from the init() function when the page is fully loaded and + uses RequestAnimationFrame to run itself again and again. + */ + function draw() { + + if (current_block_hash_ndx == 64) + { + current_block_hash_ndx = 0; + current_block_height = myGetRandom(canvas_height); + current_topisto_way = myGetRandom(2); + } + + //if ((!mouse.down)&&(30 < myGetRandom(100))) + if ((!mouse.down)&&(current_block_hash_ndx < 64)) + { + /* + v1 = 5 - myGetRandom(10); + v2 = 5 - myGetRandom(10); + mouse.x += v1; + mouse.y += v2; + */ + var pos = -1; + + // if (current_block_hash_ndx == 64) current_block_hash_ndx = 0; + local_height = (12+(current_block_height-8)); + + mouse.x = Math.floor(((3+current_block_hash_ndx) * canvas_width) / 70); + if (current_topisto_way == 1) mouse.x = canvas_width - mouse.x; + + mouse.y = Math.floor(local_height + (1.2 * (8 - current_block_hash[current_block_hash_ndx++]))); + + /* + if (current_block_hash_ndx == 0) + { + current_topisto_way = myGetRandom(4); + current_block_hash_pos = myGetRandom(100); + + current_block_hash_pos = current_block_hash_pos / (canvas_height / 100); + mouse.y = current_block_hash_pos + (8 - current_block_hash[current_block_hash_ndx++]); + + if (current_topisto_way > 10) // ! impossible + { + current_block_hash_pos = current_block_hash_pos / (canvas_width / 100); + mouse.x = current_block_hash_pos + (8 - current_block_hash[current_block_hash_ndx++]); + } + } + + switch(current_topisto_way) + { + case 0 : + case 1 : + mouse.x = current_block_hash_ndx * ( canvas_width / 64); + if (current_topisto_way == 1) mouse.x = canvas_width - mouse.x; + break; + case 2 : + case 3 : + mouse.y = current_block_hash_ndx * ( canvas_height / 64); + if (current_topisto_way == 3) mouse.y = canvas_height - mouse.y; + break; + } +*/ + + if (mouse.x < 0) mouse.x = 0; + if (mouse.y < 0) mouse.y = 0; + + if (mouse.x > canvas_width) mouse.x = canvas_width; + if (mouse.y > canvas_height) mouse.y = canvas_height; + } else { + mouse.x = mouse.xp; + mouse.y = mouse.yp; + } + + /* + This calculates the velocity of the mouse by getting the distance between the last coordinates and + the new ones. The coordinates will be further apart depending on how fast the mouse is moving. + var mouse_xv = mouse.x - mouse.px; + var mouse_yv = mouse.y - mouse.py; + */ + var topisto_facteur_velocite = 3; // Entre 1 et 5 en focntion du montant du bloc ? + var mouse_xv = (mouse.x - mouse.px) / (topisto_facteur_velocite * 2); + var mouse_yv = (mouse.y - mouse.py) / (topisto_facteur_velocite * 2); + + //Loops through all of the columns + for (i = 0; i < vec_cells.length; i++) { + var cell_datas = vec_cells[i]; + + //Loops through all of the rows + for (j = 0; j < cell_datas.length; j++) { + + //References the current cell + var cell_data = cell_datas[j]; + + //If the mouse button is down, updates the cell velocity using the mouse velocity + //if (mouse.down) { + change_cell_velocity(cell_data, mouse_xv, mouse_yv, pen_size); + //} + + //This updates the pressure values for the cell. + update_pressure(cell_data); + } + } + + /* + This line clears the canvas. It needs to be cleared every time a new frame is drawn + so the particles move. Otherwise, the particles would just look like long curvy lines. + */ + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.fillRect(0, 0, canvas.width, canvas.height); + + //This sets the color to draw with. + ctx.strokeStyle = "#00FFFF"; + //ctx.strokeStyle = "#FFFFFF"; + + // TOPISTO : draw the path of the HASH + /* + ctx.strokeStyle = "#999999"; + ctx.lineWidth = 2; + + x1 = Math.floor((3*canvas_width) / 70); + y1 = Math.floor((canvas_height / 2) + (1.2 * (8 - current_block_hash[0]))); + ctx.beginPath(); //Begin a new path on the canvas + ctx.moveTo(x1, y1); //Move the drawing cursor to the starting point + for(i=1;i +
+
+
+ +

ARTICLE

+
+
+

Fluid animation 2

+

Last Block Hash is touching a Fluid surface

+

+ Inspired from Jeff Thomas's code +

+ + + + + + + ####BUTTON#### +
+
+ + + + diff --git a/articles/20180225/header.js b/articles/20180225/header.js new file mode 100644 index 0000000..bc829ad --- /dev/null +++ b/articles/20180225/header.js @@ -0,0 +1,36 @@ +var flag_20180225_animation = true; +function switch_20180225_animation() +{ + btn = document.getElementById('20180225_control_btn'); + ctrl = document.getElementById('20180225_control_icon'); + if(ctrl.className == "glyphicon glyphicon-pause") + { + ctrl.title = "PLAY"; + ctrl.className = "glyphicon glyphicon-play"; + } else { + ctrl.title = "PAUSE"; + ctrl.className = "glyphicon glyphicon-pause"; + } + flag_20180225_animation = ! flag_20180225_animation; + if (flag_20180225_animation) Fluid.draw(); +} + +function init_20180225_animation() +{ + ctrl = document.getElementById('20180225_control_icon'); + ctrl.title = "PAUSE"; + ctrl.className = "glyphicon glyphicon-pause"; + flag_20180225_animation = true; + Fluid.initialize(); + setTimeout(switch_20180225_animation, 60000); +} + +function init_2018025(leblock) +{ + current_block_hash = leblock.hash; + init_20180225_animation(); +} + +$(document).ready(function(){ + last_block_hooks.push(init_2018025); +}); diff --git a/articles/20180806/header.html b/articles/20180806/header.html new file mode 100644 index 0000000..45cc464 --- /dev/null +++ b/articles/20180806/header.html @@ -0,0 +1,13 @@ +
+
+
+
+

ARTICLE

+
+
+

Refactoring my code

+

Trying to rewrite some code, starting on new basis

+ ####BUTTON#### +
+
+
diff --git a/articles/20180815/content.html b/articles/20180815/content.html new file mode 100644 index 0000000..4a2b4d0 --- /dev/null +++ b/articles/20180815/content.html @@ -0,0 +1,70 @@ +
+
+
+ +
+
+

+


+ Structure de Quadrilatères (Square Structures)​​​​​​​, 1985, ink on paper, 11.81 x 16.54 inches (30 x 42 cm) +

+
+
+
+ +
+
+
+ +
+
+

+


+ Structure de Quadrilatères (Square Structures)​​​​​​​, 1985. +

+
+
+
+ +
+
+
+ +
+
+

+


+ Structure de Quadrilatères (Square Structures)​​​​​​​, 1987. +

+
+
+
+ +
+
+
+ +
+
+

+


+ Structure de Quadrilatères (Square Structures)​​​​​​​, 1988. +

+
+
+
+ +
+
+
+ +
+
+

+


+ Affiche promo exposition.
+ "1 % de désordre, ou la vulnérabilité de l'angle droit" +

+
+
+
diff --git a/articles/20180815/header.html b/articles/20180815/header.html new file mode 100644 index 0000000..940860a --- /dev/null +++ b/articles/20180815/header.html @@ -0,0 +1,14 @@ +
+
+
+
+

ARTICLE

+
+
+

Tribute to Vera Molnar

+

In fact, my "fuzzy treemap" is a true Vera Molnar legacy.

+

As usual, see wikipedia for more information

+ ####BUTTON#### +
+
+
diff --git a/articles/20180815/images/1522026225650.JPEG b/articles/20180815/images/1522026225650.JPEG new file mode 100644 index 0000000..a3179cb Binary files /dev/null and b/articles/20180815/images/1522026225650.JPEG differ diff --git a/articles/20180815/images/Molnar_1.jpeg b/articles/20180815/images/Molnar_1.jpeg new file mode 100644 index 0000000..83b5c16 Binary files /dev/null and b/articles/20180815/images/Molnar_1.jpeg differ diff --git a/articles/20180815/images/VeraMolnar.jpg b/articles/20180815/images/VeraMolnar.jpg new file mode 100644 index 0000000..8a730e0 Binary files /dev/null and b/articles/20180815/images/VeraMolnar.jpg differ diff --git a/articles/20180815/images/quadrilateres1985.jpg b/articles/20180815/images/quadrilateres1985.jpg new file mode 100644 index 0000000..1ac73da Binary files /dev/null and b/articles/20180815/images/quadrilateres1985.jpg differ diff --git a/articles/20180815/images/veramolnar2.jpg b/articles/20180815/images/veramolnar2.jpg new file mode 100644 index 0000000..7ec261f Binary files /dev/null and b/articles/20180815/images/veramolnar2.jpg differ diff --git a/articles/20180823/content.html b/articles/20180823/content.html new file mode 100644 index 0000000..6f47a7a --- /dev/null +++ b/articles/20180823/content.html @@ -0,0 +1,667 @@ + + +
+
+
+

+ Bitcoin est vraiment cool. Bien sûr il y a des remarques à faire à son sujet, qu'il s'agisse de savoir si c'est une technologie interessante, si nous sommes dans une bulle des cryptomonnaies ou bien si le problème de gouvernance auquel nous sommes confrontés va trouver une solution. Mais d'un point de vue purement technique, le mystique Satoshi Nakamoto a créé une technologie impressionnante. +

Malheureusement, bien qu'il y ait de nombreuses ressources résumant comment Bitcoin fonctionne à un haut niveau (je recommanderai par exemple la fantastique vidéo de Anders Brownworth, Blockchain visual 101), il n'y a pas beaucoup d'informations sur ce qui se passe dans les couches basses et, à mon avis, on ne peut pas toujours se contenter de la vue à 10 000 pieds d'altitude. +

+ En tant que néophyte, j'ai eu beaucoup de mal à comprendre la mécanique du foncitonnement de Bitcoin. Heureusement, parce que bitcoin est décentralisé et pair à pair par nature, tout à chacun est capable de développer un client respectant le protocole. Afin de mieux comprendre la façon dont bitcoin foncionne, j'ai donc décidé d'écrire mon propre petit client Bitcoin, capable de publier une transaction sur la Blockchain Bitcoin. +

+ Cet article décrit le processus de création d'un client minimal, capable de créer une transaction, de la soumettre au réseau pair à pair Bitcoin afin qu'il soit inscrit dans sa Blockchain. Si vous préférez juste lire le code brut, vous pouvez sans problème vous rendre sur mon repo GITHUB. +

+

Génération d'une adresse

+

+ Pour participer au réseau bitcoin, il faut disposer d'une adresse à partir de laquelle il est possible d'envoyer et de recevoir des fonds. Bitcoin utilise la cryptographie à clé publique et une adresse est simplement un hash de la clé publique qui, elle-même, dérive de la clé privée. + Etonnamment, et contrairement à la plupart des autres systèmes de cryptographie à clé publique, la clé publique est égalment gardée secrète jusqu'à ce que les fonds soient dépenses depuis cette adresse - mais nous décrirons cela plus tard. +

+ En aparté, un point de terminologie : Le terme de "porte-monnaie (wallet)" est utilisé par les clients Bitcoin pour désigner une liste d'adresses. Ce concept de porte-monnaie n'existe pas dans le protocole technique qui ne connait que des adresses. +
+ Bitcoin utilise la cryptographie à courbe elliptique pour définir ses adresses. Au niveau le plus fin, la cryptographie à courbe elliptique sert à générer une clé publique à partir d'une clé privée, un peu comme le ferait RSA, mais avec une empreinte plus légère. Si vous souhaitez appronfondir l'aspect mathématique de ce fonctionnement, Cloudflare's primer est une ressource fantastique. +

+ Le schéma ci-dessous montre le processus de génération d'une adresse Bitcoin à partir d'une clé privée de 256 bits. +
+
+
+ Sous Python, j'utilise la bibliothèque ecsda pour faire le gros du travail de cryptographie à courbe elliptique. + Le bout de code suivant crée une clé publique à partir de la très mémorable (et très peu sûre) clé privée 0xFEEDB0BDEADBEEF (En ajoutant assez de zéros à l'avant pour faire 64 caractères hexadécimaux, soit 256 bis). + Utilisez une méthode de création de clé privée plus sûre si vous souhaitez stocker de vrai montants sur une adresse ! +
+ Aparté amusante. + J'avais initialemment créé une adresse en utilisant la clé privé OxFACEBEFF en y stockant 0.0005 BTC. Après seulement un mois quelqu'un avait volé mes 0.0005 BTC ! + J'imagine que certains doivent de temps en temps aller à la pêche sur les adresses issues des clés privées les plus simples/usuelles. + Vous devez vraiment utiliser une technique fiable pour définir vos clés privées ! +
+

+
+      from ecdsa import SECP256k1, SigningKey
+
+        def get_private_key(hex_string):
+            return bytes.fromhex(hex_string.zfill(64)) # pad the hex string to the required 64 characters
+          
+        def get_public_key(private_key):
+            # this returns the concatenated x and y coordinates for the supplied private address
+            # the prepended 04 is used to signify that it's uncompressed
+            return (bytes.fromhex("04") + SigningKey.from_string(private_key, curve=SECP256k1).verifying_key.to_string())
+          
+        private_key = get_private_key("FEEDB0BDEADBEEF")
+        public_key = get_public_key(private_key)
+ +

Ce code produit la clé privée suivante (en hexadécimal) :

+
0000000000000000000000000000000000000000000000000feedb0bdeadbeef
+ +

Et la clé publique (toujours en hexadécimal) :

+
04d077e18fd45c031e0d256d75dfa8c3c21c589a861c4c33b99e64cf613113fcff9fc9d90a9d81346bcac64d3c01e6e0ef0828543edad73c0e257b845812cc8d28
+ +

+ Le 0x04 au début de la clé publique indique qu'il s'agit d'une clé publiquenon compressée, cela signifie que les coordonnées x et le y sur la courbe ECSDA sont simplement concaténés. + De part la structure de la courbe ECSDA, si vous connaissez la valeur de l'abcisse x, l'ordonnée y ne peut prendre que deux valeurs, uen paire et une impaire. En utilisant cette caractéristique, il est possible d'exprimer une clé publique avec seulement son abcisse et la polarité de son ordonnée. + Cela réduit la taille de la clé de 65 à 33 bits, la clé (et par conséquent l'adresse qui en découle) est dite compressée. + Pour les clés publiques compressées, la valeur de début est 0x02 ou 0x03 en fonction de la polarité de l'abcisse. Bitcoin utilise principalement les clés non compressées, c'est donc ce que nous ferons également par la suite. +

+ A partir de là, afin de générer l'adresse Bitcoin depuis la clé publique, cette dernière est "hashée" par sha256 puis par "ripemd160". + Cette double empreinte produit une sur-couche de sécurité et le hash ripemd160 réduit la longueur de l'adresse, qui passe de 256 bits à 160 bits. + Une conséquence remarquable est qu'il alors possible que deux clés publiques produisent la même adresse ! + Toutefois, avec 2^160 adresses possibles, ce n'est pas susceptible de se produire de si tôt. +

+
+      import hashlib
+        def get_public_address(public_key):
+            address = hashlib.sha256(public_key).digest()
+            h = hashlib.new('ripemd160')
+            h.update(address)
+            address = h.digest()
+            return address
+        public_address = get_public_address(public_key)
+

+ Le code ci-dessus génère l'adresse c8db639c24f6dc026378225e40459ba8a9e54d1a. + Ceci est parfois appelé l'adresse hash 160. +

+ Comme évoqué plus tôt, un point intéressant est que les conversions de la clé privée en clé publique puis de la clé publique vers l'adresse ne sont pas réversibles. + Si vous diposez d'une adresse la seule possibilité de remonter à la clé publique est de résoudre le hash sha256. + C'est un peu différent de la plupart des cryptographies à clés publiques dans lesquelles votre clé publique est connue et votre clé privée cachée. + Dans ce cas, les deux clés sont cachées et seule l'adresse (qui est une empreinte de la clé publique) est publiée. +

+
+ Les clés publiques sont cachées pour uen bonne raison. Bien qu'il soit normalement impossible de remonter d'une clé publique vers sa clé privée, si la méthode de génération de clé privée est corrompue, alors l'accès aux clés publiques permet un peu plus facilement de déduire la clé privée. + En 2013, c'est malheureusement arrivé dans les porte-monnaies Bitcoin pour Androïd. + Androïd avait une faiblesse critique dans sa génération de nombres aléatoires, créant ainsi un vecteur d'attaque pour trouver les clés privées depuis les clés publiques. + C'est également la raison pour laquelle la réutilisation d'adresses publiques n'est pas encouragée. + La signature d'une transaction oblige à produire sa clé publique. + si vous ne réutilisez pas une clé après l'envoi d'une transaction depuis son adresse, vous n'avez pas à vous inquiéter d'avoir exposé cette clé publique. +
+

+ La façon standard d'exrpimer une adresse Bitcoin est de l'encoder via Base58Check. + Cet encodage n'est qu'une représentation de l'adresse (il peut être décodé). + Base58Check génère des adresses de la forme 1661HxZpSy5jhcJ2k6av2dxuspa8aafDac. + L'encodage Base58Check produit des adresses plus courtes et embarque une somme de contrôle, cela permet de détecter les adresses malformées. + Dans à peu près tous les clients Bitcoin les adresses encodées en Base58Check sont les seules que vous verrez. + Base58Check comporte également un numéro de version, que je positionne à 0 dans le code suivant pour indiquer qu'il s'agit du hash d'une clé publique. +

+
+        # 58 character alphabet used
+        BASE58_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
+        
+        def base58_encode(version, public_address):
+            """
+            Gets a Base58Check string
+            See https://en.bitcoin.it/wiki/base58Check_encoding
+            """
+            version = bytes.fromhex(version)
+            checksum = hashlib.sha256(hashlib.sha256(version + public_address).digest()).digest()[:4]
+            payload = version + public_address + checksum
+        
+            result = int.from_bytes(payload, byteorder="big")
+        
+            print(result)
+        
+            # count the leading 0s
+            padding = len(payload) - len(payload.lstrip(b'\0'))
+            encoded = []
+        
+            while result != 0:
+                result, remainder = divmod(result, 58)
+                encoded.append(BASE58_ALPHABET[remainder])
+        
+            return padding*"1" + "".join(encoded)[::-1]
+        
+        bitcoin_address = base58_encode("00", public_address)
+

+ Au final, à partir de ma clé privée feedb0bdeadbeef (préfixée par des zéros), je suis arrivé à l'adresse Bitcoin 1KK2xni6gmTtdnSGRiuAf94jciFgRjDj7W ! +

+ Muni d'une adresse, il est maintenant possible d'obtenir queqlues bitcoins ! + Pour ce faire, je me suis acheté sur 0.0045 BTC (à peu près 11 USD au moment où j'écris) depuis btcmarkets en utilisant des dollars australiens. + Depuis le portail de trading de btcmarkets, je les ai transférés sur l'adresse ci-dessus, perdant 0.0005 BTC de frais durant la procédure. Vous pouvez voir ce mouvement sur la blockchain à la transaction 95855ba9f46c6936d7b5ee6733c81e715ac92199938ce30ac3e1214b8c2cd8d7 +

+

Se connecter au réseau P2P

+

+ Maintenant que j'ai une adresse avec quelques bitcoinc dessus, les choses deviennent plus intéressantes. + Pour envoyer ces bitcoins ailleurs, il est nécessaire de se connecter au réseau P2P de Bitcoin. +

+
Bootstrapping
+

+ Une des difficultés que j'ai eu à la première approche de Bitcoin, étant donné la nature décentralisée du réseau, fût de déterminer comment les pairs du réseau trouvaient les autres pairs. + Sans autorité centrale, comment un client peut-il démarrer et commencer le dialogue avec le reste du réseau ? +

+ Il s'est avéré que l'idéalism se soumet au réalisme et qu'il s'avère subsister un reliquat de centralisation dans le processus de découverte initiale des autres pairs. + La principale manière pour un nouveau client de trouver des pairs est d'utiliser une requête DNS vers un certain nombre de serveurs DNS graines maintenus par des membres de la communauté Bitcoin. +

+ Il s'avère que le DNS est bien équipé pour ce type d'initialisation, ce protocole, qui utilise UDP et est trés léger, est difficilement sujet aux attauqes par Déni de Service. + Précédemment, c'est IRC qui était utilisé pour cette phase, mais cela a cessé justement pour sa faiblesse aux attaques DDOS. +

+ Les graines sont codées en dutr dans le source du noyau Bitcoin mais peuvent être changés par le noyau de développeurs. +

+ Le code Python ci-dessous se connecte à une graine DNS et choisit arbitrairement le premier des pairs pour se connecter. + Il utilise la bibliothèque socket, et réalise un nslookup pour retourner l'adresse IPV4 du premier résultat sur la requête vers la graine seed.bitcoin.sipa.be. +

+
+        import socket
+          # use a dns request to a seed bitcoin DNS server to find a node
+          nodes = socket.getaddrinfo("seed.bitcoin.sipa.be", None)
+          
+          # arbitrarily choose the first node
+          node = nodes[0][4][0]
+

+ Après avoir lancé ceci, l'adresse retournée fût 208.67.251.126 qui semble un pair amical auquel je peux me connecter ! +

+
Dire coucou à mon nouvel ami
+

+ Les connexions bitcoin entre pairs se font via TCP. + Afin de se connecter à pair, la poignée de main initiale du protocole Bitcoin est un message de type Version. + Tant que les pairs n'ont pas échangé un message de type Version, aucun autre message ne sera accepté. +

+ Les messages Bitcoin sont bien documentés dans La référence du développeur Bitcoin + En utilisant cette référence comme un guide, le message de type version peut être construit en Python comme le bout de code suivant le montre. + La plupart des données sont des données administratives peu intéressantes qui sont utilisées pour établir la connexion. + si vous souhaitez plus de détails que ceux présents dans les commentaires, consultez la référence du développeur. +

+
+        version = 70014
+        services = 1 # not a full node, cant provide any data
+        timestamp = int(time.time())
+        addr_recvservices = 1
+        addr_recvipaddress = socket.inet_pton(socket.AF_INET6, "::ffff:127.0.0.1") #ip address of receiving node in big endian
+        addr_recvport = 8333
+        addr_transservices = 1
+        addr_transipaddress = socket.inet_pton(socket.AF_INET6, "::ffff:127.0.0.1")
+        addr_transport = 8333
+        nonce = 0
+        user_agentbytes = 0
+        start_height = 329167
+        relay = 0
+

+ En utilisant la bibliothèque struct de Python les données sont empaquetées dans le bon format, en prenant une attention particulière sur la taille en octets des données et à leur sens de lecture (endianness). + Le bon empaquetage des données est important, dans le cas contraire, l'interlocuteur ne sera pas capable de comprendre les octets bruts qu'il reçoit. +

+
+        payload = struct.pack("<I", version)
+        payload += struct.pack("<Q", services)
+        payload += struct.pack("<Q", timestamp)
+        payload += struct.pack("<Q", addr_recvservices)
+        payload += struct.pack("16s", addr_recvipaddress)
+        payload += struct.pack(">H", addr_recvport)
+        payload += struct.pack("<Q", addr_transservices)
+        payload += struct.pack("16s", addr_transipaddress)
+        payload += struct.pack(">H", addr_transport)
+        payload += struct.pack("<Q", nonce)
+        payload += struct.pack("<H", user_agentbytes)
+        payload += struct.pack("<I", start_height)
+

+ A nouveau, on trouvera dans le guide du développeur la description de la façon dont les données doivent être empaquetées. + Enfin, chaque donnée transmise sur le réseau doit être préfixée par une entête, qui contient la longueur des données, une somme de contrôle et le type de message dont il s'agit. + L'entête contient également la constante magique 0xF9BEB4D9 qui doit être positionnée pour tous les messages du réseau principal de Bitcoin. + La fonction suivante retourne un message Bitcoin contenant les données attachées à leur entête. +

+
+      def get_bitcoin_message(message_type, payload):
+        header = struct.pack(">L", 0xF9BEB4D9)
+        header += struct.pack("12s", bytes(message_type, 'utf-8'))
+        header += struct.pack("<L", len(payload))
+        header += hashlib.sha256(hashlib.sha256(payload).digest()).digest()[:4]
+    
+        return header + payload
+

+ Avec des données empaquetées dans le bon format et le header attaché, on peut les envoyer à notre pair ! +

+
+        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        s.connect((node, 8333))
+        s.send(get_bitcoin_message("version", payload))
+        print(s.recv(1024))
+

+ Le protocole Bitcoin prévoit qu'en réponse à un message de type version, unpair doit répondre avec un message de type Verack. + Comme je développe un petit client pour le fun, et parce que cela ne me sera pas préjudiciable si je ne le fait pas, j'ignorerai les messages de type Version et je ne leur renverrai pas d'acquittement. + Se connecter avec un message de type Version suffit pour m'autoriser à envoyer d'autres messages par la suite. +

+ Après exécution, le code précédent affiche ce qui suit. Cela est sûrement prometteur. "Satoshi" et "Verack" sont de bons mots à retrouver dans le dump de sortie ! + Si mon message de type Version avait été malformé, le pair ne m'aurait pas répondu du tout. +

+
+        b'\xf9\xbe\xb4\xd9version\x00\x00\x00\x00\x00f\x00\x00\x00\xf8\xdd\x9aL\x7f\x11\x01\x00
+        \r\x00\x00\x00\x00\x00\x00\x00\xddR1Y\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
+        \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xcb\xce\x1d\xfc\xe9j\r\x00\x00\x00
+        \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
+        \x00\x06\xb8>*\x88@I\x8e\x10/Satoshi:0.14.0/t)\x07\x00\x01\xf9\xbe\xb4\xd9verack\x00
+        \x00\x00\x00\x00\x00\x00\x00\x00\x00]\xf6\xe0\xe2'
+

+
+

+

Les transactions Bitcoin

+

+ Pour transférer des bitcoins, il faut diffuser une transaction sur le réseau Bitcoin. +

+ D'une manière critique, l'idée la plus importante à comprendre est que la balance d'une adresse Bitcoin est seulement constituée par le nombre de sorties non dépensées (UTXO, "Unspent Transaction Outputs") que cette adresse possède. + Lorsque Bob envoie des bitcoins à Alice, il ne fait en fait que créer une UTXO qu'Alice (et seulement Alice) pour utiliser plus tard pour créer une autre UTXO et envoyer des bitcoins dessus. + La balance d'une adresse est donc le nombre de bitcoins qu'elle peut transférer à une autre adresse plutôt que le nombre de bitcoins qu'elle possède. +

+ J'insite encore, quand quelqqu'un dit qu'il possède X bitcoins, il dit en réalité que la somme de ses UTXOs fait X bitcoins. + La différence est subtile mais importante, la balance d'une adresse Bitcoin n'est enregistrée nulle part directement mais elle doit être trouvée en faisant la somme des UTXOs qu'elle peut encore dépenser. + Quand j'ai réalisé cela, ce fut un moment de "Oh, c'est comme ça que ça marche !". +

+ Un effet de bord est qu'une sortie ne peut être que entièrement dépensée ou non dépensée. + Il n'est pas possible de ne dépenser qu'une moitié d'une sortie que l'on vous a envoyé, puis de dépenser le reste plus tard. + Si vous souhaitez dépenser une partie d'une sortie que vous avez reçu, vous devez envoyer la partie que vous souhaitez dépenser tout en vous renvoyant le reste. + Une version simplifiée de ceci est schématisée ci-dessous. +
+

+
+ Lorsqu'une sortie de transaction est créée, elle comporte une condition verrou qui autorisera quelqu'un à la dépenser dans le futur, à travers ce que l'on appelle un script de transaction. + La plupart du temps, ce verrou est du type : "pour dépenser cette sortie, vous devez prouver que vous possédez la clé privée correspondant à une adresse particulière". + C'est ce que l'on appelle un script "Pay-to-Public-Key-Hash". + Toutefois rappelez vous que d'autres types de scripts Bitcoin sont possibles. + Par exemple, une sortie peut être créée qui pourrait être dépensée par toute personne pouvant résoudre une certaine empreinte, ou une transaction peut être créée que que tout le monde pourrait dépenser. +

+ A travers le langage Script, il est possible de créer des transactions basées sur des contrats simples. + Script est un langage à pile rudimentaire avec un certain nombre d'opérations centrées sur le contrôle d'égalité d'empreinte ou de vérification de signatures. + Script n'est pas un langage "Turing complet" et il ne propose pas la possibilité de faire des boucles. + La cryptomonnaie concurrente Ethereum a été conçue pour être capable de traiter des "contrats intelligents", et possède un langage "Turing complet". + On peut débattre longuement de l'utilité, la nécessité et la sécurité de disposer d'un langage "Turing complet" dans les cryptomonnaies, mais je laisse ce débat à d'autres ! +

+ Dans la terminologie standard, une transaction Bitcoin est faite d'entrées et de sorties. + Une entrée est une UTXO (une sortie maintenant dépensée) et une sortie est une nouvelle UTXO. + Il peut y avoir plusieurs sorties pour une entrée mais une sortie doit être complètement dépensée dans uen trasaction. + Toute partie non dépensée d'une entrée est considérée comme un pourboire de minage par les mineurs. +

+ Pour mon petit client, je souhaite être capable d'envoyer les bitcoins précédemment transférés depuis une place de marché vers mon adresse FEEDB0BDEADBEEF. + En utilisant la même procédure que précédemment, j'ai généré une autre adresse à partir de la clé privée BADCAFEFABC0FFEE. + Cette adresse est 1QGNXLzGXhWTKF3HTSjuBMpQyUYFkWfgVC. +
+

+
Création d'une transaction brute
+

+ La création d'une transaction est une affaire d'empaquetage d'une "transaction brute", puis de signature de cette transaction brute. + Encore une fois, la référence du développeur contient une description de ce que doit contenir uen transaction. + Ce qui constitue une transaction est montré ci-dessous, mais tout d'abord : +

    +
  • + Le vocabulaire Bitcoin utilise les termes de "signature script" et "pubkey script" que je trouve un peu confus. + Le script de signature est utilisé pour définir les conditions de l'UTXO que l'on souhaite utiliser dans la transaction, et le script de clé publique est utilisé pour donner les conditions nécessaires pour dépenser l'UTXO que nous sommes en train de créer. + Il serait préférable d'appeler le script de signature le script de déverrouillage et d'appeler le le script de clé publique le script de verrouillage. +
  • +
  • + Le montant d'une transaction Bitcoin est donnée en Satoshis. + Le Satoshi représente la plus petite partie non divisible d'un bitcoin, soit le cent millionnième d'un bitcoin. +
  • +
+

+ Afin de rester simple, ce qui est indiqué ci-dessous correspond à une transaction comportant une entrée et une sortie. + Des transactions plus complexes, avec plusieurs entrées et plusieurs sorties, sont créées de la même façon. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ChampDescription
VersionTransaction version (actuellement toujours 1)
Number of inputsNombre d'entrées à dépenser
Transaction IDID de la transaction à dépenser
Output numberNuméro de la sortie à dépenser
Signature script lengthLongueur en octets du script de signature
Signature scriptScript de signature dans le langage Script
Sequence numberToujours 0xffffffff sauf si vous souhaitez positionner un Lock Time
Number of outputsNombre de sorties à créer
ValueMontant à dépenser en Satoshis
Pubkey script lengthLongueur en octets du script de clé publique
Pubkey scriptScript de clé publique en langage Script
Lock timeProchaine date ou numéro de bloc où il sera possible d'inclure la transaction dans la blockchain
+

+ Exceptés les script de signatur et de clé publique, il est assez facile de voir ce qui doit aller dans les autres champs de la trasaction brute. + Pour envoyer mes fonds de mon adresse FEEDB0BDEADBEEF à mon adresse BADCAFEFABC0FFEE, j'ai recherché la transaction qui avait été crée par la place de marché. + Cela m'a donné :

+
    +
  • L'ID de la transaction est 95855ba9f46c6936d7b5ee6733c81e715ac92199938ce30ac3e1214b8c2cd8d7.
  • +
  • La sortie qui était envoyée à mon adresse était la deuxième sortie, soit la sortie 1 (le tableau des sorties commence à 0)
  • +
  • Le nombre de sortie est 1, car je souhaite tout transférer de FEEDB0BDEADBEEF à BADCAFEFABC0FFEE
  • +
  • + Le montant doit être au maximum de 400 000 Satoshis.
    + Cela doit être moins que cela pour accorder un pourboire.
    + J'accorde 20 000 Satoshis à être pris en tant que pourboire, le montant sera donc fixé à 380 000 Satoshis. +
  • +
  • Le locktime sera mis à 0, autorisant la transaction a être prise en compte n'importe quand.
  • +
+

+ Le script de clé publique sera un script Pay to Pubkey hash (ou p2pk). + Ce script garantit que la seule personne capable de dépenser la sortie qui sera créée sera celle qui connait la clé publique dont l'empreinte coïncide avec l'adresse Bitcoin donnée et qui détient également la clé privée correspondant à cette la clé publique. +

+ Pour débloquer une transaction verrouillée par un script P2PK, l'utilisateur doit fournir sa clé publique ainsi qu'une signature de l'empreinte de la transaction brute. + L'empreinte de la clé publique est comparée à l'adresse avec laquelle le script a été créé et la signature est vérifiée avec la clé publique fournie. + Si l'empreinte coïncide et que la signature est vérifiée, la sortie peut être dépensée. +

+ Avec les opérateurs de script Bitcoin, le script P2PK ressemble à ce qui suit. +

+ OP_DUP
+ OP_HASH160
+ -- Longueur en octets de l'adresse --
+ -- adresse Bitcoin --
+ OP_EQUALVERIFY
+ OP_CHECKSIG +

+ Après conversion des opérateurs dans leur valeurs (fournies par le WIKI) et en rajoutant l'adresse publique (avant encodage en Base58Check), cela donne le script hexadecimal suivant. +

+ 0x76
+ 0xA9
+ 0x14
+ 0xFF33195EC053D6E58D5FD3CC67747D3E1C71B280
+ 0x88
+ 0xAC +

+ L'adresse a été trouvée en utilisant le bout de code montré précédemment pour dérivé une adresse depuis une clé privée, en l'appliquant à la clé privée de destination, 0xBADCAFEFABC0FFEE. +
+

+
Signer la transaction
+

+ Il y a 2 usages séparés, mais quelque part reliés, du script de signature dans une transaction P2PK. +
+

    +
  • Le script vérifie (déverrouille) l'UTXO que l'on essaie de dépenser en fournissant notre clé publique qui créée l'empreinte de l'adresse à laquelle l'UTXO a été envoyée.
  • +
  • Le sicript effectue également une signature de ce nous transmettons au réseau, de façon à ce personne ne puisse modifier la transaction sans corrompre la signature.
  • +
+
+ Ainsi, la transaction brute contient le script de signature qui contient uen signature de la transaction brute ! + Ce problème de l'oeuf et la poule est résolu en plaçant le script de clé publique de notre UTXO dans l'emplacement du script de signature avant de signer la transaction brute. + Pour autant, il ne semble pas de bonne raison pour utiliser l'emplacement du script de clé publique, cela pourrait être arbitraimenent n'importe quelle autre donnée. +

+ Avant de calculer l'empreinte de la transaction brute, on doit d'abord ajouter la valeur du type d'empreinte (Hashtype). + Le type d'empreinte le plus commun est SIGHASH_ALL, qui signe toute la structure pour qu'aucune entrée ni aucune sortie ne puisse être modifiée. + Le lien vers le WIKI liste les autres types d'empreinte, qui peuvent autoriser la combinaison de la modification des entrées et des sorties après que la transaction ait été signée. +

+ Les fonctions Python ci-dessous placent ensemble le répertoire des valeurs de transaction brute +

+
def get_p2pkh_script(pub_key):
+          """
+          This is the standard 'pay to pubkey hash' script
+          """
+          # OP_DUP then OP_HASH160 then 20 bytes (pub address length)
+          script = bytes.fromhex("76a914")
+      
+          # The address to pay to
+          script += pub_key
+      
+          # OP_EQUALVERIFY then OP_CHECKSIG
+          script += bytes.fromhex("88ac")
+      
+          return script
+      
+      def get_raw_transaction(from_addr, to_addr, transaction_hash, output_index, satoshis_spend):
+          """
+          Gets a raw transaction for a one input to one output transaction
+          """
+          transaction = {}
+          transaction["version"] = 1
+          transaction["num_inputs"] = 1
+      
+          # transaction byte order should be reversed:
+          # https://bitcoin.org/en/developer-reference#hash-byte-order
+          transaction["transaction_hash"] = bytes.fromhex(transaction_hash)[::-1]
+          transaction["output_index"] = output_index
+      
+          # temporarily make the signature script the old pubkey script
+          # this will later be replaced. I'm assuming here that the previous
+          # pubkey script was a p2pkh script here
+          transaction["sig_script_length"] = 25
+          transaction["sig_script"] = get_p2pkh_script(from_addr)
+      
+          transaction["sequence"] = 0xffffffff
+          transaction["num_outputs"] = 1
+          transaction["satoshis"] = satoshis_spend
+          transaction["pubkey_length"] = 25
+          transaction["pubkey_script"] = get_p2pkh_script(to_addr)
+          transaction["lock_time"] = 0
+          transaction["hash_code_type"] = 1
+      
+          return transaction
+

+ Appeler ce code avec les valeurs ci-dessous crée la transaction brute que je me suis fixé. +

+
+        private_key = address_utils.get_private_key("FEEDB0BDEADBEEF")
+        public_key = address_utils.get_public_key(private_key)
+        from_address = address_utils.get_public_address(public_key)
+        to_address = address_utils.get_public_address(address_utils.get_public_key(address_utils.get_private_key("BADCAFEFABC0FFEE")))
+        
+        transaction_id = "95855ba9f46c6936d7b5ee6733c81e715ac92199938ce30ac3e1214b8c2cd8d7"  
+        satoshis = 380000
+        output_index = 1
+        
+        raw = get_raw_transaction(from_address, to_address, transaction_id, output_index, satoshis)        
+

+ Il peut paraître perturbant d'utliser la clé privée pour générer la valeur to_address. + C'est fait uniquement par commodité et pour montrer comment ce champ est trouvé. + Lorsque vous faites une transaction avec un tiers, vous devez lui demander son adresse et y effectuer le transfert, sans en connaître la clé privée. +

+ Pour être capable de signer, et éventuellement de transmettre cette transaction au réseau, la trasaction brute doit être empaquetée correctement. + Ceci est implémenté dans la focntion get_packed_transaction que je ne vais pas copier ici, puisqu'il s'agit essentiellement d'un nouvel usage de la bibliothèque struct. + Si cela vous intéresse, voyus pouvez la trouver dans le fichier Python bitcoin_transaction_utils.py de mon dépôt GITHUB. +

+ Cela me permet de définir la fonction qui va produire le script de signature. + Une fois le script de signature généré, il va remplacer le contenu du champs signature script. +

+
+        def get_transaction_signature(transaction, private_key):
+        """
+            Gets the sigscript of a raw transaction
+            private_key should be in bytes form
+            """
+        packed_raw_transaction = get_packed_transaction(transaction)
+        hash = hashlib.sha256(hashlib.sha256(packed_raw_transaction).digest()).digest()
+        public_key = address_utils.get_public_key(private_key)
+        key = SigningKey.from_string(private_key, curve=SECP256k1)
+        signature = key.sign_digest(hash, sigencode=util.sigencode_der)
+        signature += bytes.fromhex("01") #hash code type
+    
+        sigscript = struct.pack("<B", len(signature))
+        sigscript += signature
+        sigscript += struct.pack("<B", len(public_key))
+        sigscript += public_key
+    
+        return sigscript
+

+ Pour l'essentiel, le script de signature est fourni comme une entrée du script de clé publique de la transaction précédente que je tente d'utiliser, de cetet façon je peux prouver que je suis autorisé à en dépenser la sortie que j'utilise maintenant comme entrée. + La mécanique de focnitonnement est montrée ci-dessous, extrait du WIKI Bitcoin. + Cela se lit de haut en bas, chaque ligne est une itération du script. + Cela décrit le script P2PK qui comme je l'ai déjà indiqué, est le plus utilisé. + C'est aussi celui que j'utilise pour ma transaction ainsi que pour la transaction dont j'utilise la sortie. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
StackScript                 Description
Emptysignature
publicKey
OP_DUP
OP_HASH160
pubKeyHash
OP_EQUALVERIFY
OP_CHECKSIG
La signature et la clé publique du script de signature sont combinées avec le script de clé publique.
publicKey
signature
OP_DUP
OP_HASH160
pubKeyHash
OP_EQUALVERIFY
OP_CHECKSIG
La signature et la clé publique sont ajouées sur la pile.
publicKey
publicKey
signature
OP_HASH160
pubKeyHash
OP_EQUALVERIFY
OP_CHECKSIG
L'item du haut de la pile est dupliqué par OP_DUP
pubHashA
publicKey
signature
pubKeyHash
OP_EQUALVERIFY
OP_CHECKSIG
On calcule l'empreinte de l'item du haut de la pile (publicKey) via OP_HASH160, le résultat pubHashA remplace le haut de la pile.
pubHash
pubHashA
publicKey
signature
OP_EQUALVERIFY
OP_CHECKSIG
pubKeyHash est ajouté au haut de la pile.
publicKey
signature
OP_CHECKSIGContrôle de l'égalité entre pubHashA et pubKeyHash. Le script s'arrête si ce n'est pas égal.
TrueLa publicKey est utiliséee pour signer la transaction, le résultat est comparé à la signature fournie par le script de signature.
+ +

+ Ce script échouera si la clé fournie n'a pas une empreinte équivalente à celle fournie dans le script ou si la signature donnée ne correspond pas à la clé publique. + Cela garantit que seule la personne détenant la clé privée pour l'adresse donnée est capable de dépenser la sortie. +

+ Vous pouvez voir que c'est ici la première fois que j'ai à produire ma clé publique. + Jusqu'ici, seule mon adresse avait été publiée. + Ici, il est nécessaire de montrer sa clé publique pour permettre de vérifier la signature utilisée dans la transaction. +

+ En utilisant la fonction get_transaction_signature, nous pouvons maintenant signer et empaqueter notre transaction pour la transmettre ! + Cela implique de remplacer l'emplacement du script de signature par le vraie script de signature et d'enlever le hash_code_type de la transaction comme montré ci-dessous. +

+
+        signature = get_transaction_signature(raw, private_key )
+
+        raw["sig_script_length"] = len(signature)
+        raw["sig_script"] = signature
+        del raw["hash_code_type"]
+        
+        transaction = get_packed_transaction(raw)
+
Publier la transaction
+

+ Avec une transaction empaquetée et signée, il s'agit de dire au réseau qu'elle existe. + Utilisant les quelques fonctions définies dans cet article, placés dans bitcoin_p2p_message_utils.py, le bout de code suivant place l'entête Bitcoin sur la transmission et l'envoie à un pair. + Comme indiqué précédemment, il est nécessaire d'envoyer un message de version au préalable à ce pair afin qu'il accepte les messages suivants. +

+
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        s.connect((get_bitcoin_peer(), 8333))
+        s.send(get_bitcoin_message("version", get_version_payload())
+        s.send(get_bitcoin_message("tx", transaction)
+

+ Envoyer la transaction fût la partie la plus ennuyeuse de ce travail. + Lorsque je soumettais une transaction qui était mal formée ou mal signée, souvent le pair se contentait de couper la connexion, ou dans le meilleur des cas, renvoyait un message d'erreur déroutant. + L'un de ces (vraiment incompréhensible) messages était "La valeur S est inutilement haute" qui était dûe à la signature de l'empreinte de la transaction par un encodage suivant la methode ECSDA nommée sigencode_der. + Bien que la signature soit valide, apparemment les mineurs Bitcoin n'aiment pas les sigantures ECSDA formatées de cette façon car elles autorisent le spam sur le réseau. + La solution fût d'utiliser la function sigencode_der_canonize qui prend soin de formter la signature d'une autre façon. + Un problème simple mais très difficile à débugguer ! +

+ Quoiqu'il en soit, j'ai fini par tout faire fonctionner et je fût trés exité lorsque je vis que ma transaction faisait son chemin dans la blockchain. + Ce fût un grand sentiment d'accomplissement de savoir que ma ravissante petite transaction faite à la main ferait partie du registre Bitcoin à tout jamais. +
+

+
+ Lorsque ma transaction fût soumise, le montant du pourboire était un peu faible par rapport à la moyenne (j'ai utilisé bitcoin fees pour vérifier) et ainsi il fallu 5 heures pour qu'un mineur se décide à l'inclure dans un bloc. + J'ai contrôlé cela en regardant le nombre de confirmations que la transaction avait - c'est une mesure du nombre de blocs suivants celui qui contient la transaction. + Au moment où j'écris ces lignes, il y a 190 confirmations, cela signifie que 190 blocs ont été produits depuis celui qui contient ma transaction. + Cela peut être considéré comme relativement sûrement confirmée, puisqu'il faudrait une attaque impressionnante sur le réseau pour réécrire 190 blocs afin d'effacer ma transaction. +

+

Conclusion

+

+ J'espère que vous avez gagné une petite idée de comment fonctionne Bitcoin en lisant cet article, pour ma part je suis certain de l'avoir fait pendant les mois que j'ai passé à mettre tout ceci ensemble ! + Bien que la plupart des informations présentées ici ne sont pas applicables de façon pragmatiques - normalement vous utilisez un client qui fait tout cela pour vous - je pense que comprendre un peu mieux comment les choses fonctionnent sous la couverture ont fait de vous un utilisateur plus confiant dans la technologie. +

+ Si vous souhaitez utiliser le code, ou bien vous amuser plus en avant avec le petit jouet, consultez mon dépôt GITHUB. + Il y a de nombreuses pièces à découvrir dans le monde Bitcoin, je n'en ai exploré que les plus communes. + Certaines pièces permettent probablement de faire des choses plus cool que le simple transfert de bitcoins entre deux adresses ! + Je n'ai même pas effleuré le minnage, le processus pour ajouter des blocks dans la blockchain. + Du travail et un nouveau trou de lapin en perspective ... +

+ Si vous avez tout lu jusqu'ici, vous avez sans doute réalisé que les 380 000 Satoshis que j'ai transféré sur l'adresse 1QGNXLzGXhWTKF3HTSjuBMpQyUYFkWfgVC peuvent, si on est malin, être récupérés par tout à chacun ... puisque la clé privée de cette adresse est indiquée dans l'article. + Je suis vraiment curieux de savoir combien de temps cela pour être transféré ailleurs et j'espère que celui qui le fera aura la décence de le faire en utilisant les techniques que j'ai décrit ici ! + Je serai vraiment dommage que la clé privée soit juste chargée dans un porte-monnaie pour les prendre, mais je ne pense vous en empêcher ! + Au moment où j'écris ces lignes, cela représente environ 10 USD, mais si Bitcoin "s'envole vers la lune", qui sait combien cela pourrait être plus ! + (Edit : Et c'est déjà retiré ! Pris par 1KgoPFVDNcx7H2VY9bB2dxxP9yNM2Nar1N quelques heures après la publication de ces lignes. Bien joué !) +

+ Au cas où vous chercheriez une adresse pour envoyer des bitcoins lorsque vous jouerez avec ce travail, ou bien si vous pensez que cet article était suffisemment interessant pour le récompenser, mon adresse 18uKa5c9S84tkN1ktuG568CR23vmeU7F5H sera heureuse de prendre de petits dons ! + Par ailleurs, si vous souhaitez me crier dessus pour signaler des erreurs, j'en serai très heureux. +

+

D'autres ressources

+

+ Si vous avez trouvé cet article intéressant, voici quelques ressources pour aller plus loin :
+

    +
  • Le livre Mastering Bitcoin explique les détails techniques de Bitcoin. Je ne l'ai pas lu complètement mais il a l'air d'être riche en bonnes informations
  • +
  • L'artcile du blog de Ken Sheriff à l'air de couvrir le même sujet que mon article. Je ne l'ai malheureusement découvert que lorsque mon travail était déjà très avancé.Si vous ne comprenez pas quelque chose ici, consultez son article.
  • +
  • Déjà mentionnée, la fantastique vidéo d'Anders Brownworth est une excellente première approche de comment fonctionne les technologies Blockchain
  • +
  • A moins d'être complètement masochiste, je ne vous recommande pas de faire les choses à partir de rien, sauf dans un but pédagogique. La bibliothèque Python Pycoin vous économisera bien des maux de têtes
  • +
  • Pour économiser votre peine, il est sans doute préférable d'utiliser le réseau de test plutôt que le résau principal comme je l'ai fait. Ceci dit, c'est plus fun quand un code erroné vous fait perdre de l'argent réel !
  • +
  • Enfin je répète à nouveau que le code qui accompagne cet article est disponible sur mon dépôt GITHUB
  • +
+

+
+
+
+ + + + diff --git a/articles/20180823/header.html b/articles/20180823/header.html new file mode 100644 index 0000000..2e40839 --- /dev/null +++ b/articles/20180823/header.html @@ -0,0 +1,15 @@ +
+
+
+
+

ARTICLE

+
+
+

Bitcoin : un coup d'oeil coup le capot

+

Un post pour traduire un article de 2017 sur le fonctionnement des transactions Bitcoin

+

Pour une fois, un article en français. comme on ne peut pas toujours se contenter de la surface, cette fois-ci, on plonge plus en profondeur...  L'article original a été écrit par Sam Lewis et se trouve ici.

+ ####BUTTON#### +
+
+
diff --git a/articles/20180823/images/BobAlice.png b/articles/20180823/images/BobAlice.png new file mode 100644 index 0000000..82bb446 Binary files /dev/null and b/articles/20180823/images/BobAlice.png differ diff --git a/articles/20180823/images/bitcoin_address_generation.png b/articles/20180823/images/bitcoin_address_generation.png new file mode 100644 index 0000000..d44981e Binary files /dev/null and b/articles/20180823/images/bitcoin_address_generation.png differ diff --git a/articles/20180823/images/final_transaction.png b/articles/20180823/images/final_transaction.png new file mode 100644 index 0000000..6925612 Binary files /dev/null and b/articles/20180823/images/final_transaction.png differ diff --git a/articles/20180823/images/illustration.png b/articles/20180823/images/illustration.png new file mode 100644 index 0000000..8b0c749 Binary files /dev/null and b/articles/20180823/images/illustration.png differ diff --git a/blog.php b/blog.php new file mode 100644 index 0000000..868e023 --- /dev/null +++ b/blog.php @@ -0,0 +1,159 @@ + + + + TOPISTO + + + + + + + + + + + + + + + + + + + + +
+
+
+

+
+
+
+ +'; + echo '$(document).ready(function(){'; + echo ' $("#'.$article.'_button").on("click", function(event) {'; + echo ' window.location="page.php?id='.$article.'"'; + echo ' })'; + echo '})'; + echo ''; + $header = str_replace('####BUTTON####','',$header); + } + + $liste = $header.PHP_EOL.$liste; +} +echo $liste; +?> + +
+

CONTACT

+
+
+

Contact me

+

Shambala

+

Employer : Mutiny

+

+00 666 666 666

+

+ + + + +

+
+ +
+ PGP :
+ +
+
+
+ + + + + + + diff --git a/css/fonts.css b/css/fonts.css new file mode 100644 index 0000000..8fa1aea --- /dev/null +++ b/css/fonts.css @@ -0,0 +1,8 @@ +@font-face { + font-family: Klingon; + src: url('/fonts/klingon.ttf'); +} +@font-face { + font-family: Twengar; + src: url('/fonts/twengar.ttf'); +} diff --git a/css/topisto.css b/css/topisto.css new file mode 100644 index 0000000..d04b5ef --- /dev/null +++ b/css/topisto.css @@ -0,0 +1,200 @@ + body { + font: 400 15px Lato, sans-serif; + line-height: 1.8; + color: #818181; + } + h2 { + font-size: 24px; + text-transform: uppercase; + color: #303030; + font-weight: 600; + margin-bottom: 30px; + } + h4 { + font-size: 19px; + line-height: 1.375em; + color: #303030; + font-weight: 400; + margin-bottom: 30px; + } + + .btn-secondary { + background-color: #74716c; + color:#fff; + } + .jumbotron { + background-color: #74716c; + color: #fff; + padding: 100px 25px; + font-family: Bangers, sans-serif; + } + .container-fluid { + padding: 60px 50px; + } + .bg-grey { + background-color: #f6f6f6; + } + .bg-grey-odd { + background-color: #fff; + margin : 0px; + padding-top: 5px; + padding-bottom: 5px; + box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19); + } + .bg-grey-even { + background-color: #fff; + margin : 0px; + padding-top: 5px; + padding-bottom: 5px; + box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19); + } + .logo-small { + color: #f4511e; + font-size: 50px; + } + .logo { + color: #f4511e; + font-size: 200px; + } + .thumbnail { + padding: 0 0 15px 0; + border: none; + border-radius: 0; + } + .thumbnail img { + width: 100%; + height: 100%; + margin-bottom: 10px; + } + .carousel-control.right, .carousel-control.left { + background-image: none; + color: #f4511e; + } + .carousel-indicators li { + border-color: #f4511e; + } + .carousel-indicators li.active { + background-color: #f4511e; + } + .item h4 { + font-size: 19px; + line-height: 1.375em; + font-weight: 400; + font-style: italic; + margin: 70px 0; + } + .item span { + font-style: normal; + } + .panel { + border: 1px solid #f4511e; + border-radius:0 !important; + transition: box-shadow 0.5s; + } + .panel:hover { + box-shadow: 5px 0px 40px rgba(0,0,0, .2); + } + .panel-footer .btn:hover { + border: 1px solid #f4511e; + background-color: #fff !important; + color: #f4511e; + } + .panel-heading { + color: #fff !important; + background-color: #f4511e !important; + padding: 25px; + border-bottom: 1px solid transparent; + border-top-left-radius: 0px; + border-top-right-radius: 0px; + border-bottom-left-radius: 0px; + border-bottom-right-radius: 0px; + } + .panel-footer { + background-color: white !important; + } + .panel-footer h3 { + font-size: 32px; + } + .panel-footer h4 { + color: #aaa; + font-size: 14px; + } + .panel-footer .btn { + margin: 15px 0; + background-color: #f4511e; + color: #fff; + } + .navbar { + margin-bottom: 0; + background-color: rgb(93, 152, 167); + z-index: 9999; + border: 0; + font-size: 12px !important; + line-height: 1.42857143 !important; + letter-spacing: 4px; + border-radius: 0; + font-family: Montserrat, sans-serif; + box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19); + } + .navbar li a, .navbar .navbar-brand { + color: #fff !important; + font-family: Bangers, sans-serif; + } + .navbar-nav li a:hover { + color: #666 !important; + background-color: rgb(93, 152, 167)!important; + } + .navbar-nav li.active a { + color: #000 !important; + background-color: #fff !important; + } + .navbar-default .navbar-toggle { + border-color: transparent; + color: #fff !important; + } + footer .glyphicon { + font-size: 20px; + margin-bottom: 20px; + color: #f4511e; + } + .slideanim {visibility:hidden;} + .slide { + animation-name: slide; + -webkit-animation-name: slide; + animation-duration: 1s; + -webkit-animation-duration: 1s; + visibility: visible; + } + @keyframes slide { + 0% { + opacity: 0; + transform: translateY(70%); + } + 100% { + opacity: 1; + transform: translateY(0%); + } + } + @-webkit-keyframes slide { + 0% { + opacity: 0; + -webkit-transform: translateY(70%); + } + 100% { + opacity: 1; + -webkit-transform: translateY(0%); + } + } + @media screen and (max-width: 768px) { + .col-sm-4 { + text-align: center; + } + .btn-lg { + width: 100%; + } + } + @media screen and (max-width: 480px) { + .logo { + font-size: 150px; + } + } diff --git a/data/getBlockInfo.php b/data/getBlockInfo.php new file mode 100644 index 0000000..aeb0cdb --- /dev/null +++ b/data/getBlockInfo.php @@ -0,0 +1,55 @@ +hash); +if ($the_name == $the_block->hash) $the_name =''; + +// --- +// --- Si on a passé un hash, le navigateur peut le mettre en cache +// --- +if (isset($_REQUEST['block_hash'])) +{ + $seconds_to_cache = 180; // 3 minutes de cache HTTP + $ts = gmdate("D, d M Y H:i:s", time() + $seconds_to_cache) . " GMT"; + header("Expires: $ts"); + header("Pragma: cache"); + header("Cache-Control: max-age=$seconds_to_cache"); + header('Content-Type: application/json'); +} + +$message = '{"hash":"'.$the_block->hash.'", "block_index":"'.$the_block->block_index.'", "time":"'.date('Y/m/d H:i:s', $the_block->time).'", "height":"'.$the_block->height.'", "topisto_outputs":"'.$the_block->topisto_outputs.'", "prev":"'.$the_block->prev_block.'", "topisto_inputs":"'.$the_block->topisto_inputs.'", "topisto_fees":"'.$the_block->topisto_fees.'", "topisto_reward":"'.$the_block->topisto_reward.'", "nonce":"'.$the_block->nonce.'", "n_tx":"'.$the_block->n_tx.'"}'; + +if ($_REQUEST['FULL'] == 'OK') $message = json_encode($the_block); + +header('Content-Type: application/json'); +die($message); + +?> diff --git a/full.php b/full.php new file mode 100644 index 0000000..39c057b --- /dev/null +++ b/full.php @@ -0,0 +1,427 @@ + + + + TOPISTO + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ avatar dragon gargoyle +
+
+


+

This site is a hobby.
It's also a testing place where i can do things that are impossible at work.

+

I like surfing and sailing on the ocean. But during longs winter nights, computing is fun. I'm interesting into cryptocurrencies, computationnal art, datavisualisation. I know that 42 is the answer to the Life, Universe and Everything Else. As Eto Demerzel said to me, the Seldon's Plan will save the Galaxy. My favorites books are the House of leaves, the Necronomicron and the DarkHold. I also know that great power comes with great responsibility. I'm still in the search of Captain Zero, because the truth is out there.

+
+
+
+ + +
+
+
+

A kind of Blockchain Explorer +
+ +
+ +
+ +
+
+ +
+ +
+
+
 
+
+ Blog +
+
+
+ +'; + echo '$(document).ready(function(){'; + echo ' $("#'.$article.'_button").on("click", function(event) {'; + echo ' window.location="page.php?id='.$article.'"'; + echo ' })'; + echo '})'; + echo ''; + $header = str_replace('####BUTTON####','',$header); + } + + $liste = $header.PHP_EOL.$liste; +} +echo $liste; +?> + +
+

CONTACT

+
+
+

Contact me

+

Shambala

+

Employer : Mutiny

+

+00 666 666 666

+

+ + + + +

+
+ +
+ PGP :
+ +
+
+
+ + + + diff --git a/images/20170200-content.html b/images/20170200-content.html new file mode 100644 index 0000000..12ea4a4 --- /dev/null +++ b/images/20170200-content.html @@ -0,0 +1,106 @@ +
+
+
+

A digital currency

+

+ Use cryptography.
+ No "central bank".
+ P2P network.
+

+
+
+ +
+
+
+ +
+
+
+ +
+
+

WALLET

+

+ Use cryptography.
+ No "central bank".
+ P2P network.
+

+
+
+
+ +
+
+
+

Transaction

+

+When Bob is sending 42 to Alice, the bitcoin protocol is creating a transaction.
+A transaction is the balance between his Inputs and his Outputs. +

    +
  • First the network needs to be sure that Bob has got enough in his wallet.
    So Bob will include a list of transactions that he has received before.
    This is the inputs of the transactions.
    For example, previous transaction's amount are 10, 25 and another 10, so total inputs are 45
  • +
  • As the input's sum is superior to the amount of the transaction, an ouput is added.
    This output is the change to Bob.
    In the example, this 3
  • +
  • Fees are took by the network to process the transaction.
    Let say it will take 0.5.
    This is another ouput
  • +
  • So Alice will get only a part of the transaction's amount.
    this the last output, 41.5
  • +
+So the transaction is a made with : +
    +
  • Inputs : a list of previous transactions that bob has received
  • +
  • Outputs : a list of amounts to send to Alice, Bob, and fees
  • +
  • A timestamp
  • +
+Then the wallet will broadcast the transaction on the network.
+It will be sent to the area called "mempool".
+Nodes of the network will validate the transaction by verfying the Inputs and the Ouputs.
+A hash of the transaction is computed.
+So the transaction is sealed. +

+
+
+ +
+
+
+ +
+
+
+ +
+
+

Block

+

+Then the bitcoin protocol is grouping validated transactions in blocks.
+So a block is an array of transactions.
+A block is made with : +

    +
  • An array of transactions.
    +That's what my script is drawing.
    +Running the transactions array, it is computing the sum of each outputs of each transaction.
    +Then, it draws a rectangle for each sum.
    +
  • +
  • A "proof of work".
    The miner is computing a special hash that depends on the array of transaction and that will answer to a constraint.
    This hash is very difficult to compute. In fact it is not really computed, the miner must test every value until he find the solution.
  • +
  • That why the network will give him a reward.
    This is a special transaction that has no inputs.
  • +
+

+
+ +
+
+ +
+
+
+

Blockchain

+

+Each block has a hash and a reference to the ihash of the previous block.
+So blocks are chained together.
+

+
+ +
+ +
+ +
+
diff --git a/images/Bitcoin_Network_Blue.jpg b/images/Bitcoin_Network_Blue.jpg new file mode 100644 index 0000000..5cf34c4 Binary files /dev/null and b/images/Bitcoin_Network_Blue.jpg differ diff --git a/images/D3-force-driven-graph.png b/images/D3-force-driven-graph.png new file mode 100644 index 0000000..7bb37c1 Binary files /dev/null and b/images/D3-force-driven-graph.png differ diff --git a/images/R_TOPISTO_image.php b/images/R_TOPISTO_image.php new file mode 100644 index 0000000..e2d32a0 --- /dev/null +++ b/images/R_TOPISTO_image.php @@ -0,0 +1,21 @@ + filemtime($b); +}); + +$block_image = @imagecreatefrompng($files[0]); + +// --- +// --- envoyer l'image au navigateur +// --- +header("Content-Type: image/png"); +imagepng($block_image); + +imagedestroy($block_image); + +?> diff --git a/images/bandeau.png b/images/bandeau.png new file mode 100644 index 0000000..5157aae Binary files /dev/null and b/images/bandeau.png differ diff --git a/images/bitcoin.jpg b/images/bitcoin.jpg new file mode 100644 index 0000000..f856e6e Binary files /dev/null and b/images/bitcoin.jpg differ diff --git a/images/block_image.php b/images/block_image.php new file mode 100644 index 0000000..d3f754a --- /dev/null +++ b/images/block_image.php @@ -0,0 +1,231 @@ +hash); + if ($the_name == $the_block->hash) $the_name =''; + + $bandeau = 50; + $marge = 25; + $text_border = 20; + $width = GRAPH_WIDTH; + $height = GRAPH_HEIGHT; + + // Pour que l'image simple ait les proportions que l'image full + $width = $marge + ($width*2) + (2*$text_border); + $height = $marge + ($height*2); + + $img_w = $width; + $img_h = $height+(2*$bandeau); + + $type=2; + if (count($the_block->tx)==1) $type=4; + + // création d'une image plus haute pour inclure bandeaux haut et bas + $img = imagecreatetruecolor($img_w, $img_h); + + $param0 = blockchain::DrawBlockHeaderFooter($the_block, $img, $bandeau); + + $parametres = []; + $parametres['x'] = 0; + $parametres['y'] = $bandeau; + $parametres['width'] = $width; + $parametres['height'] = $height; + $parametres['methode'] = $mode; + $parametres['type'] = $type; + $parametres['transparent_color'] = $param0[0]; + $parametres['background_color'] = $param0[1]; + $parametres['font_color'] = $param0[2]; + $parametres['fontname'] = $param0[3]; + $parametres['font_RGB'] = $param0[4]; + $parametres['background_RGB'] = $param0[5]; + + switch($methode) + { + case 'veraMolnar': + require_once APP_PATH.'/methode/veraMolnar/inc/treemap.php'; + topisto_veraMolnar::DrawBlock($the_block, $img, $parametres); + break; + + case 'treemapV2': + require_once APP_PATH.'/methode/treemapV2/inc/treemap.php'; + topisto_treemap::DrawBlock($the_block, $img, $parametres); + break; + + case 'treemap_fuzzy': + require_once APP_PATH.'/methode/treemap_fuzzy/inc/treemap.php'; + topisto_treemap_fuzzy::DrawBlock($the_block, $img, 0, $bandeau, $width, $height, $mode, $type); + break; + + case 'spline': + require_once APP_PATH.'/methode/spline/inc/splines.php'; + topisto_spline::DefaultDrawBlock($the_block, $img, 0, $bandeau, $width, $height, $mode, $type); + break; + + case 'spline_2': + require_once APP_PATH.'/methode/spline/inc/splines.php'; + topisto_spline::DrawBlock($the_block, $img, 0, $bandeau, $width, $height, $mode, 200, $type); + break; + + case 'treemap' : + default: + require_once APP_PATH.'/methode/treemap/inc/treemap.php'; + topisto_treemap::DrawBlock($the_block, $img, 0, $bandeau, $width, $height, $mode, $type); + break; + } + + // Les textes + /* + * INUTILE depuis que blockchain::DrawBlockHeaderFooter ajoute elle-même les textes + * + putenv('GDFONTPATH='.RESS_PATH.'/fonts/'); + $font = 'DS-DIGIB.TTF'; + + $fontColor = imagecolorallocate($img, 227,227,153); + $the_texte = "Height : ".$the_block->height; + imagettftext($img,18, 0, 5, $bandeau-5, $fontColor, $font, $the_texte); + $the_texte = "Inputs : ".$the_block->topisto_inputs; + if ($type == 4) $the_texte = "Reward : ".$the_block->topisto_reward; + imagettftext($img,15, 0, 5, $bandeau+$height+18, $fontColor, $font, $the_texte); + + if ($the_name == '') $the_name = date('Ymd H:m:s', $the_block->time); + $bbox = imagettfbbox(14, 0, $font, $the_name); + imagettftext($img, 14, 0, ($img_w-3)-($bbox[2]-$bbox[0]), ($bandeau-5), $fontColor, $font, $the_name); + */ + + // Sauvegarder l'image et ajouter + // - un lien sur le mode + // - un lien dans le dossier "hasard" + $imagefilename2 = str_replace(".png","-$mode.png", $imagefilename); + imagepng($img,$imagefilename2); + + if (!file_exists($imagefilename)) link($imagefilename2, $imagefilename); + + $imagefilename2 = str_replace("methode/$methode/","methode/hasard/", $imagefilename); + if (!file_exists($imagefilename2)) link($imagefilename, $imagefilename2); +} + +$seconds_to_cache = 7200; +$ts = gmdate("D, d M Y H:i:s", time() + $seconds_to_cache) . " GMT"; +header("Expires: $ts"); +header("Pragma: cache"); +header("Cache-Control: max-age=$seconds_to_cache"); +header('Content-Type: image/png'); + +imagepng($img); + +imagedestroy($img); + +?> diff --git a/images/blockchain.gif b/images/blockchain.gif new file mode 100644 index 0000000..fe98c2e Binary files /dev/null and b/images/blockchain.gif differ diff --git a/images/bob_to_alice.gif b/images/bob_to_alice.gif new file mode 100644 index 0000000..f753ad2 Binary files /dev/null and b/images/bob_to_alice.gif differ diff --git a/images/bob_to_alice.jpg b/images/bob_to_alice.jpg new file mode 100644 index 0000000..d3bce68 Binary files /dev/null and b/images/bob_to_alice.jpg differ diff --git a/images/btcfond.jpg b/images/btcfond.jpg new file mode 100644 index 0000000..cc0771b Binary files /dev/null and b/images/btcfond.jpg differ diff --git a/images/cached_block_image.php b/images/cached_block_image.php new file mode 100644 index 0000000..6c5759f --- /dev/null +++ b/images/cached_block_image.php @@ -0,0 +1,53 @@ + \ No newline at end of file diff --git a/images/debug_block_image.php b/images/debug_block_image.php new file mode 100644 index 0000000..aaf3d00 --- /dev/null +++ b/images/debug_block_image.php @@ -0,0 +1,109 @@ +OK
'.PHP_EOL; +else echo ' KO
'.PHP_EOL; + +// +// "strict" est un flag qui indique que l'on veut précisément +// ce que les paramètres demandent +// +if (($img==null)&&(!isset($_REQUEST['strict']))) +{ + // + // On n'a pas passé de HASH + // + if (($img==null)&&(!isset($_REQUEST['hash']))) + { + // + // On prend le dernier connu pour la méthode et le mode + // + $myarray = glob(DATA_PATH.'/'.$methode.'/*-'.$mode.'.png'); + if (isset($myarray[0])) + { + usort( $myarray, function( $a, $b ) { return filemtime($a) - filemtime($b); } ); + echo $myarray[0].' OK
'.PHP_EOL; + $img=true; + } + // + // On prend le dernier connu pour la méthode tout mode confondu + // + if ($img==null) + { + $myarray = glob(DATA_PATH.'/'.$methode.'/*.png'); + if (isset($myarray[0])) + { + usort( $myarray, function( $a, $b ) { return filemtime($a) - filemtime($b); } ); + echo $myarray[0].' OK
'.PHP_EOL; + $img=true; + } + } + } + + // + // On a passé un HASH + // + if (($img==null)&&(isset($_REQUEST['hash']))) + { + // + // On cherche le bon HASH et la bonne méthode + // + $imagefilename = DATA_PATH.'/'.$methode.'/'.$block_hash.'.png'; + if (($img==null)&&(file_exists($imagefilename))) + { + echo $imagefilename.' OK
'.PHP_EOL; + $img=true; + } + // + // En fait on s'en fiche de la méthode tant qu'on a le bon HASH + // + $imagefilename = DATA_PATH.'/hasard/'.$block_hash.'.png'; + if (($img==null)&&(file_exists($imagefilename))) + { + echo $imagefilename.' OK
'.PHP_EOL; + $img=true; + } + } +} + +// +// On n'a rien trouvé, donc on dessine ce qui est demandé +// +if ($img==null) echo 'KO
'; + +?> diff --git a/images/full_block_image.php b/images/full_block_image.php new file mode 100644 index 0000000..13ccc9b --- /dev/null +++ b/images/full_block_image.php @@ -0,0 +1,237 @@ +hash); + if ($the_name == $the_block->hash) $the_name =''; + + $bandeau = 50; + $marge = 25; + $text_border = 20; + $width = GRAPH_WIDTH; + $height = GRAPH_HEIGHT; + + $img_w = $marge + ($width*2) + (2*$text_border); + $img_h = $marge + ($height*2) + (2*$bandeau); + + // création d'une image plus haute pour inclure bandeaux haut et bas + $img = imagecreatetruecolor( $img_w, $img_h); + + // Les parties du block : inputs, outputs, fees, reward + switch($methode) + { + case 'full_treemap_fuzzy': + topisto_treemap_fuzzy::DrawBlock($the_block, $img, $text_border + 0, $bandeau, $width, $height, $mode, 2); + topisto_treemap_fuzzy::DrawBlock($the_block, $img, $text_border+$marge+$width, $bandeau, $width, $height, $mode, 1); + topisto_treemap_fuzzy::DrawBlock($the_block, $img, $text_border+0, $marge+$height + $bandeau, $width, $height, $mode, 3); + topisto_treemap_fuzzy::DrawBlock($the_block, $img, $text_border+$marge+$width, $marge+$height + $bandeau, $width, $height, $mode, 4); + break; + + case 'full_spline': + $type=2; + $x0 = $text_border; $y0 = $bandeau; + topisto_spline::DefaultDrawBlock($the_block, $img, $x0, $y0, $width, $height, $type); + + $type=1; + $x0 = $text_border+$marge+$width; $y0 = $bandeau; + topisto_spline::DefaultDrawBlock($the_block, $img, $x0, $y0, $width, $height, $type); + + $type=3; + $x0 = $text_border; $y0 = $marge+$height+$bandeau; + topisto_spline::DefaultDrawBlock($the_block, $img, $x0, $y0, $width, $height, $type); + + $type=4; + $x0 = $text_border+$marge+$width; $y0 = $marge+$height+$bandeau; + topisto_spline::DefaultDrawBlock($the_block, $img, $x0, $y0, $width, $height, $type); + break; + + case 'full_treemap': + default: + topisto_treemap::DrawBlock($the_block, $img, $text_border + 0, $bandeau, $width, $height, $mode, 2); + topisto_treemap::DrawBlock($the_block, $img, $text_border+$marge+$width, $bandeau, $width, $height, $mode, 1); + topisto_treemap::DrawBlock($the_block, $img, $text_border+0, $marge+$height + $bandeau, $width, $height, $mode, 3); + topisto_treemap::DrawBlock($the_block, $img, $text_border+$marge+$width, $marge+$height + $bandeau, $width, $height, $mode, 4); + break; + } + + blockchain::DrawBlockHeaderFooter($the_block, $img, $bandeau); + + $fond = imagecolorallocate($img, 40, 40, 40); + imagefilledrectangle($img, 0, $bandeau, $text_border, $img_h-$bandeau, $fond); + imagefilledrectangle($img, $img_w-$text_border, $bandeau, $img_w, $img_h-$bandeau, $fond); + imagefilledrectangle($img, $text_border+$width, $bandeau, $text_border+$marge+$width, $img_h-$bandeau, $fond); + imagefilledrectangle($img, 0, $bandeau+$height, $img_w - $text_border, $bandeau+$marge+$height, $fond); + + // Les textes + putenv('GDFONTPATH='.RESS_PATH.'/fonts/'); + $font = 'DS-DIGIB.TTF'; + + $fontColor = imagecolorallocate($img, 158,227,253); + $fontColor = imagecolorallocate($img, 227,253,158); + $fontColor = imagecolorallocate($img, 227,227,153); + + $the_texte = "Height : ".$the_block->height; + imagettftext($img,18, 0, 5, $bandeau-5, $fontColor, $font, $the_texte); + $the_texte = "Inputs : ".$the_block->topisto_inputs; + imagettftext($img,15, 90, $text_border-3, $bandeau+$height, $fontColor, $font, $the_texte); + $the_texte = "Outputs : ".$the_block->topisto_outputs; + imagettftext($img,15, 90, $text_border+$width+$marge-3, $bandeau+$height, $fontColor, $font, $the_texte); + $the_texte = "Fees : ".$the_block->topisto_fees; + imagettftext($img,15, 90, $text_border-3, $bandeau+(2*$height)+$marge-3, $fontColor, $font, $the_texte); + $the_texte = "Reward : ".$the_block->topisto_reward; + imagettftext($img,15, 90, $text_border+$width+$marge-3, $bandeau+(2*$height)+$marge-3, $fontColor, $font, $the_texte); + + if ($the_name == '') $the_name = date('Ymd H:m:s', $the_block->time); + $bbox = imagettfbbox(14, 0, $font, $the_name); + imagettftext($img, 14, 0, ($img_w-3)-($bbox[2]-$bbox[0]), ($bandeau-5), $fontColor, $font, $the_name); + + // Sauvegarder l'image et ajouter + // - un lien sur le mode + // - un lien dans le dossier "hasard" + $imagefilename2 = str_replace(".png","-$mode.png", $imagefilename); + imagepng($img,$imagefilename2); + + if (!file_exists($imagefilename)) link($imagefilename2, $imagefilename); + + $imagefilename2 = str_replace("methode/$methode/","methode/hasard/", $imagefilename); + if (!file_exists($imagefilename2)) link($imagefilename, $imagefilename2); +} + +$seconds_to_cache = 7200; +$ts = gmdate("D, d M Y H:i:s", time() + $seconds_to_cache) . " GMT"; +header("Expires: $ts"); +header("Pragma: cache"); +header("Cache-Control: max-age=$seconds_to_cache"); +header('Content-Type: image/png'); + +imagepng($img); +imagedestroy($img); + +?> diff --git a/images/loading.gif b/images/loading.gif new file mode 100644 index 0000000..89bfa55 Binary files /dev/null and b/images/loading.gif differ diff --git a/images/logo.php b/images/logo.php new file mode 100644 index 0000000..ac00d2a --- /dev/null +++ b/images/logo.php @@ -0,0 +1,29 @@ + 30) +{ + $files = glob('logo/medium/*/topisto_vert.png'); + usort($files, function($a, $b) { + return filemtime($a) > filemtime($b); + }); + + shuffle($files); + + $logo = $files[0]; +} + +$block_image = @imagecreatefrompng($logo); + +// --- +// --- envoyer l'image au navigateur +// --- +header("Content-Type: image/png"); +imagepng($block_image); + +imagedestroy($block_image); + +?> diff --git a/images/logo/big/big_boxfitting/topisto_rouge.png b/images/logo/big/big_boxfitting/topisto_rouge.png new file mode 100644 index 0000000..2471659 Binary files /dev/null and b/images/logo/big/big_boxfitting/topisto_rouge.png differ diff --git a/images/logo/big/big_boxfitting/topisto_vert.png b/images/logo/big/big_boxfitting/topisto_vert.png new file mode 100644 index 0000000..2acb57a Binary files /dev/null and b/images/logo/big/big_boxfitting/topisto_vert.png differ diff --git a/images/logo/big/big_delaunay/topisto_rouge.png b/images/logo/big/big_delaunay/topisto_rouge.png new file mode 100644 index 0000000..aa1f39b Binary files /dev/null and b/images/logo/big/big_delaunay/topisto_rouge.png differ diff --git a/images/logo/big/big_delaunay/topisto_vert.png b/images/logo/big/big_delaunay/topisto_vert.png new file mode 100644 index 0000000..293c254 Binary files /dev/null and b/images/logo/big/big_delaunay/topisto_vert.png differ diff --git a/images/logo/big/big_op_art/topisto_rouge.png b/images/logo/big/big_op_art/topisto_rouge.png new file mode 100644 index 0000000..08af8e7 Binary files /dev/null and b/images/logo/big/big_op_art/topisto_rouge.png differ diff --git a/images/logo/big/big_op_art/topisto_vert.png b/images/logo/big/big_op_art/topisto_vert.png new file mode 100644 index 0000000..43a9bea Binary files /dev/null and b/images/logo/big/big_op_art/topisto_vert.png differ diff --git a/images/logo/big/big_rodillus1/topisto_rouge.png b/images/logo/big/big_rodillus1/topisto_rouge.png new file mode 100644 index 0000000..8f3f428 Binary files /dev/null and b/images/logo/big/big_rodillus1/topisto_rouge.png differ diff --git a/images/logo/big/big_rodillus1/topisto_vert.png b/images/logo/big/big_rodillus1/topisto_vert.png new file mode 100644 index 0000000..1429565 Binary files /dev/null and b/images/logo/big/big_rodillus1/topisto_vert.png differ diff --git a/images/logo/big/big_rodillus2/topisto_rouge.png b/images/logo/big/big_rodillus2/topisto_rouge.png new file mode 100644 index 0000000..f4f6f18 Binary files /dev/null and b/images/logo/big/big_rodillus2/topisto_rouge.png differ diff --git a/images/logo/big/big_rodillus2/topisto_vert.png b/images/logo/big/big_rodillus2/topisto_vert.png new file mode 100644 index 0000000..06ba95e Binary files /dev/null and b/images/logo/big/big_rodillus2/topisto_vert.png differ diff --git a/images/logo/big/big_shapeism/topisto_rouge.png b/images/logo/big/big_shapeism/topisto_rouge.png new file mode 100644 index 0000000..524e553 Binary files /dev/null and b/images/logo/big/big_shapeism/topisto_rouge.png differ diff --git a/images/logo/big/big_shapeism/topisto_vert.png b/images/logo/big/big_shapeism/topisto_vert.png new file mode 100644 index 0000000..5bf631a Binary files /dev/null and b/images/logo/big/big_shapeism/topisto_vert.png differ diff --git a/images/logo/big/big_shockwave/topisto_rouge.png b/images/logo/big/big_shockwave/topisto_rouge.png new file mode 100644 index 0000000..9c1548e Binary files /dev/null and b/images/logo/big/big_shockwave/topisto_rouge.png differ diff --git a/images/logo/big/big_shockwave/topisto_vert.png b/images/logo/big/big_shockwave/topisto_vert.png new file mode 100644 index 0000000..60bc76e Binary files /dev/null and b/images/logo/big/big_shockwave/topisto_vert.png differ diff --git a/images/logo/big/big_stainglass/topisto_rouge.png b/images/logo/big/big_stainglass/topisto_rouge.png new file mode 100644 index 0000000..32939a1 Binary files /dev/null and b/images/logo/big/big_stainglass/topisto_rouge.png differ diff --git a/images/logo/big/big_stainglass/topisto_vert.png b/images/logo/big/big_stainglass/topisto_vert.png new file mode 100644 index 0000000..8f24697 Binary files /dev/null and b/images/logo/big/big_stainglass/topisto_vert.png differ diff --git a/images/logo/big/topisto_rouge.png b/images/logo/big/topisto_rouge.png new file mode 100644 index 0000000..3fae363 Binary files /dev/null and b/images/logo/big/topisto_rouge.png differ diff --git a/images/logo/big/topisto_vert.png b/images/logo/big/topisto_vert.png new file mode 100644 index 0000000..49b94bd Binary files /dev/null and b/images/logo/big/topisto_vert.png differ diff --git a/images/logo/gmic.sh b/images/logo/gmic.sh new file mode 100755 index 0000000..8c5a3b4 --- /dev/null +++ b/images/logo/gmic.sh @@ -0,0 +1,4 @@ +for toto in rouge vert +do + gmic ../topisto_$toto.png $@ -output topisto_$toto.png +done diff --git a/images/logo/medium/boxfitting/topisto_rouge.png b/images/logo/medium/boxfitting/topisto_rouge.png new file mode 100644 index 0000000..d508395 Binary files /dev/null and b/images/logo/medium/boxfitting/topisto_rouge.png differ diff --git a/images/logo/medium/boxfitting/topisto_vert.png b/images/logo/medium/boxfitting/topisto_vert.png new file mode 100644 index 0000000..9764811 Binary files /dev/null and b/images/logo/medium/boxfitting/topisto_vert.png differ diff --git a/images/logo/medium/delaunay/topisto_rouge.png b/images/logo/medium/delaunay/topisto_rouge.png new file mode 100644 index 0000000..43f3f8f Binary files /dev/null and b/images/logo/medium/delaunay/topisto_rouge.png differ diff --git a/images/logo/medium/delaunay/topisto_vert.png b/images/logo/medium/delaunay/topisto_vert.png new file mode 100644 index 0000000..60db7cf Binary files /dev/null and b/images/logo/medium/delaunay/topisto_vert.png differ diff --git a/images/logo/medium/dream_smooth/topisto_rouge.png b/images/logo/medium/dream_smooth/topisto_rouge.png new file mode 100644 index 0000000..322cd17 Binary files /dev/null and b/images/logo/medium/dream_smooth/topisto_rouge.png differ diff --git a/images/logo/medium/dream_smooth/topisto_vert.png b/images/logo/medium/dream_smooth/topisto_vert.png new file mode 100644 index 0000000..9ca273f Binary files /dev/null and b/images/logo/medium/dream_smooth/topisto_vert.png differ diff --git a/images/logo/medium/felt_pen/topisto_rouge.png b/images/logo/medium/felt_pen/topisto_rouge.png new file mode 100644 index 0000000..c2ea5ce Binary files /dev/null and b/images/logo/medium/felt_pen/topisto_rouge.png differ diff --git a/images/logo/medium/felt_pen/topisto_vert.png b/images/logo/medium/felt_pen/topisto_vert.png new file mode 100644 index 0000000..9acf3d9 Binary files /dev/null and b/images/logo/medium/felt_pen/topisto_vert.png differ diff --git a/images/logo/medium/fire_edges/topisto_rouge_000000.png b/images/logo/medium/fire_edges/topisto_rouge_000000.png new file mode 100644 index 0000000..8fb95e8 Binary files /dev/null and b/images/logo/medium/fire_edges/topisto_rouge_000000.png differ diff --git a/images/logo/medium/fire_edges/topisto_rouge_000001.png b/images/logo/medium/fire_edges/topisto_rouge_000001.png new file mode 100644 index 0000000..f910b3f Binary files /dev/null and b/images/logo/medium/fire_edges/topisto_rouge_000001.png differ diff --git a/images/logo/medium/fire_edges/topisto_rouge_000002.png b/images/logo/medium/fire_edges/topisto_rouge_000002.png new file mode 100644 index 0000000..f998a66 Binary files /dev/null and b/images/logo/medium/fire_edges/topisto_rouge_000002.png differ diff --git a/images/logo/medium/fire_edges/topisto_rouge_000003.png b/images/logo/medium/fire_edges/topisto_rouge_000003.png new file mode 100644 index 0000000..3075fea Binary files /dev/null and b/images/logo/medium/fire_edges/topisto_rouge_000003.png differ diff --git a/images/logo/medium/fire_edges/topisto_rouge_000004.png b/images/logo/medium/fire_edges/topisto_rouge_000004.png new file mode 100644 index 0000000..0762e3f Binary files /dev/null and b/images/logo/medium/fire_edges/topisto_rouge_000004.png differ diff --git a/images/logo/medium/fire_edges/topisto_rouge_000005.png b/images/logo/medium/fire_edges/topisto_rouge_000005.png new file mode 100644 index 0000000..0c0cde3 Binary files /dev/null and b/images/logo/medium/fire_edges/topisto_rouge_000005.png differ diff --git a/images/logo/medium/fire_edges/topisto_rouge_000006.png b/images/logo/medium/fire_edges/topisto_rouge_000006.png new file mode 100644 index 0000000..8bbdbaa Binary files /dev/null and b/images/logo/medium/fire_edges/topisto_rouge_000006.png differ diff --git a/images/logo/medium/fire_edges/topisto_rouge_000007.png b/images/logo/medium/fire_edges/topisto_rouge_000007.png new file mode 100644 index 0000000..e6a894c Binary files /dev/null and b/images/logo/medium/fire_edges/topisto_rouge_000007.png differ diff --git a/images/logo/medium/fire_edges/topisto_rouge_000008.png b/images/logo/medium/fire_edges/topisto_rouge_000008.png new file mode 100644 index 0000000..d496501 Binary files /dev/null and b/images/logo/medium/fire_edges/topisto_rouge_000008.png differ diff --git a/images/logo/medium/fire_edges/topisto_rouge_000009.png b/images/logo/medium/fire_edges/topisto_rouge_000009.png new file mode 100644 index 0000000..b18919a Binary files /dev/null and b/images/logo/medium/fire_edges/topisto_rouge_000009.png differ diff --git a/images/logo/medium/fire_edges/topisto_rouge_000010.png b/images/logo/medium/fire_edges/topisto_rouge_000010.png new file mode 100644 index 0000000..a0d2921 Binary files /dev/null and b/images/logo/medium/fire_edges/topisto_rouge_000010.png differ diff --git a/images/logo/medium/fire_edges/topisto_rouge_000011.png b/images/logo/medium/fire_edges/topisto_rouge_000011.png new file mode 100644 index 0000000..7124c4d Binary files /dev/null and b/images/logo/medium/fire_edges/topisto_rouge_000011.png differ diff --git a/images/logo/medium/fire_edges/topisto_rouge_000012.png b/images/logo/medium/fire_edges/topisto_rouge_000012.png new file mode 100644 index 0000000..57010f6 Binary files /dev/null and b/images/logo/medium/fire_edges/topisto_rouge_000012.png differ diff --git a/images/logo/medium/fire_edges/topisto_rouge_000013.png b/images/logo/medium/fire_edges/topisto_rouge_000013.png new file mode 100644 index 0000000..e6728db Binary files /dev/null and b/images/logo/medium/fire_edges/topisto_rouge_000013.png differ diff --git a/images/logo/medium/fire_edges/topisto_rouge_000014.png b/images/logo/medium/fire_edges/topisto_rouge_000014.png new file mode 100644 index 0000000..9055b7d Binary files /dev/null and b/images/logo/medium/fire_edges/topisto_rouge_000014.png differ diff --git a/images/logo/medium/fire_edges/topisto_rouge_000015.png b/images/logo/medium/fire_edges/topisto_rouge_000015.png new file mode 100644 index 0000000..f1eb43b Binary files /dev/null and b/images/logo/medium/fire_edges/topisto_rouge_000015.png differ diff --git a/images/logo/medium/fire_edges/topisto_rouge_000016.png b/images/logo/medium/fire_edges/topisto_rouge_000016.png new file mode 100644 index 0000000..7042808 Binary files /dev/null and b/images/logo/medium/fire_edges/topisto_rouge_000016.png differ diff --git a/images/logo/medium/fire_edges/topisto_rouge_000017.png b/images/logo/medium/fire_edges/topisto_rouge_000017.png new file mode 100644 index 0000000..6486612 Binary files /dev/null and b/images/logo/medium/fire_edges/topisto_rouge_000017.png differ diff --git a/images/logo/medium/fire_edges/topisto_rouge_000018.png b/images/logo/medium/fire_edges/topisto_rouge_000018.png new file mode 100644 index 0000000..a8c31dd Binary files /dev/null and b/images/logo/medium/fire_edges/topisto_rouge_000018.png differ diff --git a/images/logo/medium/fire_edges/topisto_rouge_000019.png b/images/logo/medium/fire_edges/topisto_rouge_000019.png new file mode 100644 index 0000000..ba6dc9d Binary files /dev/null and b/images/logo/medium/fire_edges/topisto_rouge_000019.png differ diff --git a/images/logo/medium/illustration_look/topisto.png b/images/logo/medium/illustration_look/topisto.png new file mode 100644 index 0000000..d3cf060 Binary files /dev/null and b/images/logo/medium/illustration_look/topisto.png differ diff --git a/images/logo/medium/illustration_look/topisto_rouge.png b/images/logo/medium/illustration_look/topisto_rouge.png new file mode 100644 index 0000000..d3cf060 Binary files /dev/null and b/images/logo/medium/illustration_look/topisto_rouge.png differ diff --git a/images/logo/medium/illustration_look/topisto_vert.png b/images/logo/medium/illustration_look/topisto_vert.png new file mode 100644 index 0000000..f6bb088 Binary files /dev/null and b/images/logo/medium/illustration_look/topisto_vert.png differ diff --git a/images/logo/medium/imagegrid_16/topisto_rouge.png b/images/logo/medium/imagegrid_16/topisto_rouge.png new file mode 100644 index 0000000..424b42e Binary files /dev/null and b/images/logo/medium/imagegrid_16/topisto_rouge.png differ diff --git a/images/logo/medium/imagegrid_16/topisto_vert.png b/images/logo/medium/imagegrid_16/topisto_vert.png new file mode 100644 index 0000000..b3d8063 Binary files /dev/null and b/images/logo/medium/imagegrid_16/topisto_vert.png differ diff --git a/images/logo/medium/imagegrid_32/topisto_rouge.png b/images/logo/medium/imagegrid_32/topisto_rouge.png new file mode 100644 index 0000000..e2834c3 Binary files /dev/null and b/images/logo/medium/imagegrid_32/topisto_rouge.png differ diff --git a/images/logo/medium/imagegrid_32/topisto_vert.png b/images/logo/medium/imagegrid_32/topisto_vert.png new file mode 100644 index 0000000..090288f Binary files /dev/null and b/images/logo/medium/imagegrid_32/topisto_vert.png differ diff --git a/images/logo/medium/imagegrid_4/topisto_rouge.png b/images/logo/medium/imagegrid_4/topisto_rouge.png new file mode 100644 index 0000000..b9f9b4e Binary files /dev/null and b/images/logo/medium/imagegrid_4/topisto_rouge.png differ diff --git a/images/logo/medium/imagegrid_4/topisto_vert.png b/images/logo/medium/imagegrid_4/topisto_vert.png new file mode 100644 index 0000000..779f7ac Binary files /dev/null and b/images/logo/medium/imagegrid_4/topisto_vert.png differ diff --git a/images/logo/medium/imagegrid_64/topisto_rouge.png b/images/logo/medium/imagegrid_64/topisto_rouge.png new file mode 100644 index 0000000..8a223b2 Binary files /dev/null and b/images/logo/medium/imagegrid_64/topisto_rouge.png differ diff --git a/images/logo/medium/imagegrid_64/topisto_vert.png b/images/logo/medium/imagegrid_64/topisto_vert.png new file mode 100644 index 0000000..edb7c1f Binary files /dev/null and b/images/logo/medium/imagegrid_64/topisto_vert.png differ diff --git a/images/logo/medium/imagegrid_8/topisto_rouge.png b/images/logo/medium/imagegrid_8/topisto_rouge.png new file mode 100644 index 0000000..78bc26e Binary files /dev/null and b/images/logo/medium/imagegrid_8/topisto_rouge.png differ diff --git a/images/logo/medium/imagegrid_8/topisto_vert.png b/images/logo/medium/imagegrid_8/topisto_vert.png new file mode 100644 index 0000000..b694a9a Binary files /dev/null and b/images/logo/medium/imagegrid_8/topisto_vert.png differ diff --git a/images/logo/medium/lava/topisto_rouge.png b/images/logo/medium/lava/topisto_rouge.png new file mode 100644 index 0000000..b8f076d Binary files /dev/null and b/images/logo/medium/lava/topisto_rouge.png differ diff --git a/images/logo/medium/lava/topisto_vert.png b/images/logo/medium/lava/topisto_vert.png new file mode 100644 index 0000000..16582f5 Binary files /dev/null and b/images/logo/medium/lava/topisto_vert.png differ diff --git a/images/logo/medium/linify/topisto_rouge.png b/images/logo/medium/linify/topisto_rouge.png new file mode 100644 index 0000000..1b812ef Binary files /dev/null and b/images/logo/medium/linify/topisto_rouge.png differ diff --git a/images/logo/medium/linify/topisto_vert.png b/images/logo/medium/linify/topisto_vert.png new file mode 100644 index 0000000..bc7ee6b Binary files /dev/null and b/images/logo/medium/linify/topisto_vert.png differ diff --git a/images/logo/medium/maze/topisto_1.png b/images/logo/medium/maze/topisto_1.png new file mode 100644 index 0000000..7289384 Binary files /dev/null and b/images/logo/medium/maze/topisto_1.png differ diff --git a/images/logo/medium/mineral_mozaic/topisto_rouge.png b/images/logo/medium/mineral_mozaic/topisto_rouge.png new file mode 100644 index 0000000..0efd933 Binary files /dev/null and b/images/logo/medium/mineral_mozaic/topisto_rouge.png differ diff --git a/images/logo/medium/mineral_mozaic/topisto_vert.png b/images/logo/medium/mineral_mozaic/topisto_vert.png new file mode 100644 index 0000000..8899ff3 Binary files /dev/null and b/images/logo/medium/mineral_mozaic/topisto_vert.png differ diff --git a/images/logo/medium/novel_fx/topisto_rouge.png b/images/logo/medium/novel_fx/topisto_rouge.png new file mode 100644 index 0000000..6543512 Binary files /dev/null and b/images/logo/medium/novel_fx/topisto_rouge.png differ diff --git a/images/logo/medium/novel_fx/topisto_vert.png b/images/logo/medium/novel_fx/topisto_vert.png new file mode 100644 index 0000000..87fe5c4 Binary files /dev/null and b/images/logo/medium/novel_fx/topisto_vert.png differ diff --git a/images/logo/medium/op_art/topisto_rouge.png b/images/logo/medium/op_art/topisto_rouge.png new file mode 100644 index 0000000..1ecd743 Binary files /dev/null and b/images/logo/medium/op_art/topisto_rouge.png differ diff --git a/images/logo/medium/op_art/topisto_vert.png b/images/logo/medium/op_art/topisto_vert.png new file mode 100644 index 0000000..a7481c3 Binary files /dev/null and b/images/logo/medium/op_art/topisto_vert.png differ diff --git a/images/logo/medium/poster_hope/topisto_rouge.png b/images/logo/medium/poster_hope/topisto_rouge.png new file mode 100644 index 0000000..c09a1f1 Binary files /dev/null and b/images/logo/medium/poster_hope/topisto_rouge.png differ diff --git a/images/logo/medium/poster_hope/topisto_vert.png b/images/logo/medium/poster_hope/topisto_vert.png new file mode 100644 index 0000000..f24d2c5 Binary files /dev/null and b/images/logo/medium/poster_hope/topisto_vert.png differ diff --git a/images/logo/medium/quadtree_variation/topisto_rouge.png b/images/logo/medium/quadtree_variation/topisto_rouge.png new file mode 100644 index 0000000..e213e0b Binary files /dev/null and b/images/logo/medium/quadtree_variation/topisto_rouge.png differ diff --git a/images/logo/medium/quadtree_variation/topisto_vert.png b/images/logo/medium/quadtree_variation/topisto_vert.png new file mode 100644 index 0000000..9fe7413 Binary files /dev/null and b/images/logo/medium/quadtree_variation/topisto_vert.png differ diff --git a/images/logo/medium/rodillus1/topisto_rouge.png b/images/logo/medium/rodillus1/topisto_rouge.png new file mode 100644 index 0000000..8b55934 Binary files /dev/null and b/images/logo/medium/rodillus1/topisto_rouge.png differ diff --git a/images/logo/medium/rodillus1/topisto_vert.png b/images/logo/medium/rodillus1/topisto_vert.png new file mode 100644 index 0000000..fa39263 Binary files /dev/null and b/images/logo/medium/rodillus1/topisto_vert.png differ diff --git a/images/logo/medium/rodillus2/topisto_rouge.png b/images/logo/medium/rodillus2/topisto_rouge.png new file mode 100644 index 0000000..b04b130 Binary files /dev/null and b/images/logo/medium/rodillus2/topisto_rouge.png differ diff --git a/images/logo/medium/rodillus2/topisto_vert.png b/images/logo/medium/rodillus2/topisto_vert.png new file mode 100644 index 0000000..e8190d8 Binary files /dev/null and b/images/logo/medium/rodillus2/topisto_vert.png differ diff --git a/images/logo/medium/scale/topisto_rouge.png b/images/logo/medium/scale/topisto_rouge.png new file mode 100644 index 0000000..3fae363 Binary files /dev/null and b/images/logo/medium/scale/topisto_rouge.png differ diff --git a/images/logo/medium/scale/topisto_vert.png b/images/logo/medium/scale/topisto_vert.png new file mode 100644 index 0000000..49b94bd Binary files /dev/null and b/images/logo/medium/scale/topisto_vert.png differ diff --git a/images/logo/medium/shapeism/topisto_rouge.png b/images/logo/medium/shapeism/topisto_rouge.png new file mode 100644 index 0000000..893a2a9 Binary files /dev/null and b/images/logo/medium/shapeism/topisto_rouge.png differ diff --git a/images/logo/medium/shapeism/topisto_vert.png b/images/logo/medium/shapeism/topisto_vert.png new file mode 100644 index 0000000..c8491ed Binary files /dev/null and b/images/logo/medium/shapeism/topisto_vert.png differ diff --git a/images/logo/medium/showkwaves/topisto_rouge.png b/images/logo/medium/showkwaves/topisto_rouge.png new file mode 100644 index 0000000..9e71a91 Binary files /dev/null and b/images/logo/medium/showkwaves/topisto_rouge.png differ diff --git a/images/logo/medium/showkwaves/topisto_vert.png b/images/logo/medium/showkwaves/topisto_vert.png new file mode 100644 index 0000000..e6550a6 Binary files /dev/null and b/images/logo/medium/showkwaves/topisto_vert.png differ diff --git a/images/logo/medium/stainglass/topisto_rouge.png b/images/logo/medium/stainglass/topisto_rouge.png new file mode 100644 index 0000000..33b9c2c Binary files /dev/null and b/images/logo/medium/stainglass/topisto_rouge.png differ diff --git a/images/logo/medium/stainglass/topisto_vert.png b/images/logo/medium/stainglass/topisto_vert.png new file mode 100644 index 0000000..41229d7 Binary files /dev/null and b/images/logo/medium/stainglass/topisto_vert.png differ diff --git a/images/logo/medium/topisto.png b/images/logo/medium/topisto.png new file mode 100644 index 0000000..9f22b10 Binary files /dev/null and b/images/logo/medium/topisto.png differ diff --git a/images/logo/medium/topisto_rouge.png b/images/logo/medium/topisto_rouge.png new file mode 100644 index 0000000..9f22b10 Binary files /dev/null and b/images/logo/medium/topisto_rouge.png differ diff --git a/images/logo/medium/topisto_vert.png b/images/logo/medium/topisto_vert.png new file mode 100644 index 0000000..358766d Binary files /dev/null and b/images/logo/medium/topisto_vert.png differ diff --git a/images/public_key_qrcode.png b/images/public_key_qrcode.png new file mode 100644 index 0000000..aa5dd78 Binary files /dev/null and b/images/public_key_qrcode.png differ diff --git a/images/topisto.png b/images/topisto.png new file mode 100644 index 0000000..9f22b10 Binary files /dev/null and b/images/topisto.png differ diff --git a/images/topisto_rouge.png b/images/topisto_rouge.png new file mode 100644 index 0000000..9f22b10 Binary files /dev/null and b/images/topisto_rouge.png differ diff --git a/images/topisto_vert.png b/images/topisto_vert.png new file mode 100644 index 0000000..358766d Binary files /dev/null and b/images/topisto_vert.png differ diff --git a/images/tx2block.gif b/images/tx2block.gif new file mode 100644 index 0000000..c96ee95 Binary files /dev/null and b/images/tx2block.gif differ diff --git a/images/wallet.png b/images/wallet.png new file mode 100644 index 0000000..46987fd Binary files /dev/null and b/images/wallet.png differ diff --git a/index.php b/index.php new file mode 100644 index 0000000..5cce129 --- /dev/null +++ b/index.php @@ -0,0 +1,222 @@ + + + + TOPISTO + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+


+

This site is a hobby.
It's also a testing place where i can do things that are impossible at work.

+

I like surfing and sailing on the ocean. But during longs winter nights, computing is fun. I'm interesting into cryptocurrencies, computationnal art, datavisualisation. I know that 42 is the answer to the Life, Universe and Everything Else. As Eto Demerzel said to me, the Seldon's Plan will save the Galaxy. My favorites books are the House of leaves, the Necronomicron and the DarkHold. I also know that great power comes with great responsibility. I'm still in the search of Captain Zero, because the Truth is out there.

+
+
+ avatar dragon gargoyle +
+
+
+ +
+
+
 
+
+
+
+
+
+

A kind of Blockchain Explorer +
+ +
+ +
+ +
+
+ +
+ +
+
+
 
+
+ Blog +
+
+
+ +'; + echo '$(document).ready(function(){'; + echo ' $("#'.$article.'_button").on("click", function(event) {'; + echo ' window.location="page.php?id='.$article.'"'; + echo ' })'; + echo '})'; + echo ''; + $header = str_replace('####BUTTON####','',$header); + } + + $liste = $header.PHP_EOL.$liste; +} +echo $liste; +?> + +
+

CONTACT

+
+
+

Contact me

+

Shambala

+

Employer : Mutiny

+

+00 666 666 666

+

+ + + + +

+
+ +
+ PGP :
+ +
+
+
+ + + + diff --git a/js/blockexplorer.js b/js/blockexplorer.js new file mode 100644 index 0000000..139d3dc --- /dev/null +++ b/js/blockexplorer.js @@ -0,0 +1,227 @@ + // Init array + var liste_blocks = { + '21E8' : '00000000000000000021e800c1e8df51b22c1588e5a624bea17e9faa34b2dc4a', + 'HURRICANE_1' : '0000000000000000000fe6d521a187a5523d5cef6f6c178923ff82ffe5a0f372', + 'SEGWIT' : '000000000000000000cbeff0b533f8e1189cf09dfbebf57a8ebe349362811b80', + 'SEGWIT_LOCK' : '0000000000000000012e6060980c6475a9a8e62a1bf44b76c5d51f707d54522c', + 'BCC' : '00000000000000000019f112ec0a9982926f1258cdcc558dd7c3b7e5dc7fa148', + 'BIP_91_LOCK' : '0000000000000000015411ca4b35f7b48ecab015b14de5627b647e262ba0ec40', + 'HALVING_2' : '000000000000000002cce816c0ab2c5c269cb081896b7dcb34b8422d6b74ffa1', + 'HALVING_1' : '000000000000048b95347e83192f69cf0366076336c639f9b7228e9ba171342e', + 'PIZZA' : '00000000006de085dadb3ec413ef074022fe781121b467e98960280dd246bb00', + 'TOPISTO' : '000000000a73e64735a2b75c97ea674950a9018da1420d01328a918c9ff9852c', + 'LEET' : '000000008bf44a528a09d203203a6a97c165cf53a92ecc27aed0b49b86a19564', + 'LUCIFER' : '00000000fc5b3c76f27f810ee775e480ae7fd604fd196b2d8da4257fcd39f4f9', + 'THE_ANSWER' : '00000000314e90489514c787d615cea50003af2023796ccdd085b6bcc1fa28f5', + 'GENESIS' : '000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f', + }; + + var flag_nav = true; + var classes = ['bg-grey-even','bg-grey-odd']; + var cur_class = 0; + var cur_height = []; + + function precisionRound(number) { + var precision = 4; + var factor = Math.pow(10, precision); + return Math.round((number/100000000) * factor) / factor; + } + + function addInfoForBlock(block) + { + var height = '300px'; + var contenu = ''; + var downloadingImage = new Image(); + + cur_height.push(block.height); + + cur_class = 1 - cur_class; + + contenu += '

block '+block.height+'

'; + contenu += ' '; + //contenu += ' '; + //contenu += ' '; + contenu += ' '; + contenu += ' '; + contenu += ' '; + contenu += ' '; + contenu += ' '; + contenu += ' '; + contenu += ' '; + contenu += '
hash'+block.hash+'
index'+block.block_index+'
timestamp'+block.time+'
nonce'+block.nonce+'
nb tx'+block.n_tx+'
outputs'+precisionRound(block.topisto_outputs).toFixed(4)+'
inputs'+precisionRound(block.topisto_inputs).toFixed(4)+'
fees'+precisionRound(block.topisto_fees).toFixed(4)+'
reward'+precisionRound(block.topisto_reward).toFixed(4)+'
'; + + $('#info_'+block.height).html(contenu); + + downloadingImage.onload = function(){ + $('#img_'+block.height).attr('src', this.src); + //$('#img_'+block.height).attr('height', height); + //$('#img_'+block.height).attr('width','auto'); + flag_nav = true; + }; + downloadingImage.src = 'images/block_image.php?methode=hasard&hash='+block.hash; + + return true; + } + + function addDivForBlock(block_height) + { + var contenu = ''; + + contenu += '
'; + contenu += '
'; + contenu += '
'; + contenu += '

COMPUTING ...

'; + contenu += '
'; + contenu += '
'; + contenu += '
'; + contenu += ' '; + contenu += '
'; + contenu += '
'; + contenu += '
'; + contenu += '
'; + + $('#blockchain').append(contenu); + + return true; + } + + function addDivForVoid() + { + var contenu = ''; + + contenu += '
'; + contenu += '
'; + contenu += '
'; + contenu += '

the VOID ...

'; + contenu += '
'; + contenu += '
'; + contenu += '
'; + contenu += ' '; + contenu += '
'; + contenu += '
'; + contenu += '
'; + contenu += '
'; + + $('#blockchain').append(contenu); + + return true; + } + + function toggleForwardBtn() + { + if (cur_height.length < 3) + { + $('#fast_forward_btn').removeClass('btn-success'); + $('#fast_forward_btn').addClass('btn-secondary'); + $('#btn-forward').removeClass('btn-info'); + $('#btn-forward').addClass('btn-secondary'); + } else { + $('#fast_forward_btn').removeClass('btn-secondary'); + $('#fast_forward_btn').addClass('btn-success'); + $('#btn-forward').removeClass('btn-secondary'); + $('#btn-forward').addClass('btn-info'); + } + return true; + } + + + function gotoBlock(block_name) + { + $(document).scrollTop( $("#explorer").offset().top ); + + // Bloquer la navigation pendant le calcul + if (!flag_nav) + { + window.alert('A block image is currently computed, please wait ...'); + return false; + } + flag_nav = false; + + if (block_name == 'NEXT') + { + flag_nav = true; + + // Supprimer un block + if (cur_height.length < 3) + { + window.alert('No Next Block, you are at '+$('#blockSelector').val()+' block !'); + return false; + } + + liste_blocks['PREVIOUS'] = liste_blocks['BLOCK_'+cur_height[cur_height.length-1]]; + cur_height.pop(); + + toggleForwardBtn(); + + $('#block_'+cur_height[cur_height.length-2]).slideDown(400, function() { + // Animation complete. + $('#blockchain').children('div:last').remove(); + cur_class = 1 - cur_class; + }); + + } else { + // Ajouter un block + addDivForBlock(cur_height[cur_height.length-1] - 1); + + // Décaler d'un block vers le haut + if (cur_height.length > 1) $('#block_'+cur_height[cur_height.length-2]).slideUp(); + + block_hash = ''; + if (block_name != 'LAST') block_hash = '?block_hash='+liste_blocks[block_name]; + $.getJSON('data/getBlockInfo.php'+block_hash, function( data ) { + liste_blocks['PREVIOUS'] = data.prev; + liste_blocks['BLOCK_'+data.height] = data.hash; + addInfoForBlock(data); + + toggleForwardBtn(); + }); + } + + return true; + } + + function initBlockchain(block_name) + { + $(document).scrollTop( $("#explorer").offset().top ); + + $('#blockchain').html(''); + cur_height = []; + cur_class = 0; + flag_nav = true; + + block_hash = ''; + if (block_name != 'LAST') block_hash = '?block_hash='+liste_blocks[block_name]; + $.getJSON('data/getBlockInfo.php'+block_hash, function( data ) { + addDivForBlock(data.height); + addInfoForBlock(data); + + liste_blocks['PREVIOUS'] = data.prev; + if (data.prev != '0000000000000000000000000000000000000000000000000000000000000000') + gotoBlock('PREVIOUS'); + else + addDivForVoid(); + }); + return true; + } + + function blockSelectorChange() + { + initBlockchain($('#blockSelector').val()); + $('#fast_forward_btn').attr('data-original-title', $('#blockSelector').val()); + } + + function initBlockExplorer(leblock) + { + // Init the selector + var select = $('#blockSelector'); + $.each(liste_blocks, function (key, text) { + select.append(new Option(key, key)); + }); + + if (cur_height.length == 0) initBlockchain('LAST'); + } + + $(document).ready(function(){ + last_block_hooks.push(initBlockExplorer); + }); + diff --git a/js/lastblock.js b/js/lastblock.js new file mode 100644 index 0000000..7445691 --- /dev/null +++ b/js/lastblock.js @@ -0,0 +1,34 @@ +function logBlockHash(leblock) +{ + console.log('Last Block detected : '+leblock.hash); +} + +var last_block = null; +var last_block_hooks = [logBlockHash]; +var last_block_refresh_flag = true; + +function getLastBlockInfo() +{ + $.get( "data/getBlockInfo.php", function( data ) { + if ((last_block == null)||(last_block.hash != data.hash)) + { + if (last_block_refresh_flag && (last_block != null)) + last_block_refresh_flag = window.confirm('New block detected, apply change ?'); + + last_block = data; + + if (last_block_refresh_flag) + { + last_block_hooks.forEach(function(element) { + element(last_block); + }); + } + } + }, "json" ); + + setTimeout(getLastBlockInfo, 30000); +} + +$(document).ready(function() { + getLastBlockInfo(); +}); diff --git a/page.php b/page.php new file mode 100644 index 0000000..12f32da --- /dev/null +++ b/page.php @@ -0,0 +1,80 @@ + + + + TOPISTO + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/simple.php b/simple.php new file mode 100644 index 0000000..5c79938 --- /dev/null +++ b/simple.php @@ -0,0 +1,386 @@ + + + + TOPISTO + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ avatar dragon gargoyle +
+
+


+

This site is a hobby.
It's also a testing place where i can do things that are impossible at work.

+

I like surfing and sailing on the ocean. But during longs winter nights, computing is fun. I'm interesting into cryptocurrencies, computationnal art, datavisualisation. I know that 42 is the answer to the Life, Universe and Everything Else. As Eto Demerzel said to me, the Seldon's Plan will save the Galaxy. My favorites books are the House of leaves, the Necronomicron and the DarkHold. I also know that great power comes with great responsibility. I'm still in the search of Captain Zero, because the truth is out there.

+
+
+
+ + +
+
+
+

A kind of Blockchain Explorer +
+ +
+ +
+ +
+
+ +
+ +
+

CONTACT

+
+
+

Contact me

+

Shambala

+

Employer : Mutiny

+

+00 666 666 666

+

+ + + + +

+
+ +
+ PGP :
+ +
+
+
+ + + +