Coverage Summary for Class: GasPriceTracker (org.ethereum.listener)
Class |
Class, %
|
Method, %
|
Line, %
|
GasPriceTracker |
100%
(1/1)
|
50%
(3/6)
|
29.4%
(10/34)
|
1 /*
2 * This file is part of RskJ
3 * Copyright (C) 2017 RSK Labs Ltd.
4 * (derived from ethereumJ library, Copyright (c) 2016 <ether.camp>)
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 package org.ethereum.listener;
21
22 import co.rsk.core.Coin;
23 import co.rsk.remasc.RemascTransaction;
24 import org.ethereum.core.Block;
25 import org.ethereum.core.Transaction;
26 import org.ethereum.core.TransactionReceipt;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29
30 import java.math.BigInteger;
31 import java.util.Arrays;
32 import java.util.List;
33 import java.util.concurrent.atomic.AtomicReference;
34
35 /**
36 * Calculates a 'reasonable' Gas price based on statistics of the latest transaction's Gas prices
37 *
38 * Normally the price returned should be sufficient to execute a transaction since ~25% of the latest
39 * transactions were executed at this or lower price.
40 *
41 * Created by Anton Nashatyrev on 22.09.2015.
42 */
43 public class GasPriceTracker extends EthereumListenerAdapter {
44
45 private static final Logger logger = LoggerFactory.getLogger("gaspricetracker");
46
47 private static final int WINDOW_SIZE = 512;
48
49 private static final BigInteger BI_10 = BigInteger.valueOf(10);
50 private static final BigInteger BI_11 = BigInteger.valueOf(11);
51
52 private final Coin[] window = new Coin[WINDOW_SIZE];
53 private final AtomicReference<Coin> bestBlockPriceRef = new AtomicReference<>();
54
55 private Coin defaultPrice = Coin.valueOf(20_000_000_000L);
56 private int idx = WINDOW_SIZE - 1;
57
58 private Coin lastVal;
59
60 @Override
61 public void onBestBlock(Block block, List<TransactionReceipt> receipts) {
62 bestBlockPriceRef.set(block.getMinimumGasPrice());
63 }
64
65 @Override
66 public synchronized void onBlock(Block block, List<TransactionReceipt> receipts) {
67 logger.trace("Start onBlock");
68
69 defaultPrice = block.getMinimumGasPrice();
70
71 for (Transaction tx : block.getTransactionsList()) {
72 onTransaction(tx);
73 }
74
75 logger.trace("End onBlock");
76 }
77
78 private void onTransaction(Transaction tx) {
79 if (tx instanceof RemascTransaction) {
80 return;
81 }
82
83 if (idx == -1) {
84 idx = WINDOW_SIZE - 1;
85 lastVal = null; // recalculate only 'sometimes'
86 }
87
88 window[idx--] = tx.getGasPrice();
89 }
90
91 public synchronized Coin getGasPrice() {
92 if (window[0] == null) { // not filled yet
93 return defaultPrice;
94 } else {
95 if (lastVal == null) {
96 Coin[] values = Arrays.copyOf(window, WINDOW_SIZE);
97 Arrays.sort(values);
98 lastVal = values[values.length / 4]; // 25% percentile
99 }
100
101 Coin bestBlockPrice = bestBlockPriceRef.get();
102 if (bestBlockPrice == null) {
103 return lastVal;
104 } else {
105 return Coin.max(lastVal, bestBlockPrice.multiply(BI_11).divide(BI_10));
106 }
107 }
108 }
109 }