/* A simple bitcoin network simulator */ /* Last edited on 2015-10-11 02:58:36 by stolfilocal */ #define _GNU_SOURCE #include #include #include #include #include #include typedef enum { FALSE, TRUE } bool_t; /* Input simulation options, e.g. from command line? */ typedef struct options_t { /* Simulation parameters: */ double dt; /* Simulation time step. */ double Tsim; /* Total simulation time (sec). */ /* Protocol parameters: */ double Tbb; /* Mean interblock time (sec), e.g. 600. */ double maxbsize; /* Maximum block size (bytes). */ double reward; /* Block mining reward (BTC). */ /* Mining industry parameters: */ int32_t nm; /* Number of miners. */ /* Parameters of incoming traffic: */ double trate; /* Incoming transactions per second. */ double tsize; /* Mean transaction size (bytes). */ double tfpb; /* Mean transaction fee rate (BTC per byte). */ } options_t; /* We can start simulating all transaction with same size and fee rate, to simplify analysis of results. Other may be added later, such as variance of size and fee, fractal and periodic variations of transaction rate, etc. */ /* The global state of the simulation: */ typedef struct global_state_t { double time; /* Current simulation clock reading (sec). */ int32_t nmi; /* Number of miners. */ struct miner_t **mi; /* The miners are {mi[0..nmi-1]}. */ /* ??? The blocks and transactions known to the backbone ??? */ } global_state_t; /* The global state of the simulation. For simplicity, it is assumed that all miners are connected to a fictitious broadcast "backbone" network that is infinitely fast, although the miners have different finite connection speeds to the backbone. So, when a block is mined, first the hash, then the full block are sent to the backbone by the miner. The backbone decides which branch of the blockchain is the valid one. Once the backbone has that information, the other miners will start downloading it. Transactions too are sent by clients to the backbone, and from there go to each miner. Transmission to and from the backbone has delays that depend on the miner and on the size of the block or transaction. */ /* Statistics: */ typedef struct stats_t { int32_t nt_issued; /* Number of transactions issued. */ int32_t nb_mined; /* Total number of blocks mined, including orphans. */ int32_t nt_mined; /* Number of transactions confirmed. */ } stats_t; /* To be expanded later. */ /* Transaction data: */ typedef struct trans_t { double size; /* Transaction size in bytes. */ double fee; /* Total transaction fee. */ } trans_t; /* Basic transaction data relevant to the simulator. Other parameters, (such as total output value, time of issue, and time needed to validate), may be addded later. */ /* Block data: */ typedef struct block_t { int32_t height; /* Block height, starting with 0 (genesis blockof simulation). */ struct block_t *parent; /* The parent block, or {NULL} if genesis block. */ struct miner_t *miner; /* The miner that mined this block. */ double time; /* Simulation clock reading then block was mined. */ double size; /* Total block size, including header. */ double fee; /* Total transaction fees. */ int32_t ntr; /* Number of transactions in block. */ struct trans_t **tr; /* Transactions in this block are {tr[0..ntr-1]}. */ } block_t; /* Representation of a block, mined or candidate. Perhaps add the work done on it? */ /* Miner parameters and state: */ typedef struct miner_t { double h; /* Fraction of total hashrate owned by this miner. */ /* Communication delay parameters: */ double Dh; /* Delay for receiving/sending a block hash (sec). */ double Ds; /* Fixed delay to start receiving/sending a full block (sec). */ double Db; /* Extra delay proportional to block size (sec/byte). */ /* Mining policy: */ bool_t mfull; /* True iff the miner tries to download and validate full blocks. */ /* Internal miner state: */ struct block_t *blast; /* The most recent block whose hash is known to this miner. */ double blast_sz; /* Bytes of {blast} already downloaded. */ struct block_t *bcand; /* The candidate block being mined, or {NULL}. */ int ntr; /* Number of unconfirmed tx in the miner's queue. */ struct trans_t **trq; /* Queue of unconfirmed transactions known to this miner. */ struct block_t *bpost; /* A solved block being uploaded to the network, or {NULL}. */ double bpost_sz; /* Bytes of {bpost} already uploaded. */ struct trans_t *tdown; /* A transaction request being received, or {NULL}. */ double tdown_sz; /* Bytes of {tdown} already downloaded. */ } miner_t; /* The parameters and state of a miner. The miner thinks that the valid branch of the blockchain ends with block {blast}; but he has only {blast_sz} bytes of that block, possibly only the hash (in which case {blast_sz} is zero). If {blast_sz} is less than {blast->size}, and {mful} is true, the miner is trying to download the rest of that block to validate it. If {bcand} is not null, the miner is trying to solve the candidate block {bcand}. If {bpost} is not null, the miner is trying to upload the block {bpost}, that he just solved, to the rest of the network. He has already sent {bpost_sz} bytes of it. If {tdown} is not null, the miner is receiving the transaction request {*tdown} from a client, through the relay network. He has received already {tdown_sz} bytes of it. For simplicity, it is assumed that all miners are connected to a fictitious broadcast "backbone" network that is infinitely fast, so that the delay to send a full block of {b} bytes from any miner {m1} to any other miner {m2} is the sum of the delay to send the block to the backbone, {m1.Ds + b*m1.Db}, plus the delay of downloading the block from the backbone.