Skip to content

Commit

Permalink
Support BIP133 feefilter messages.
Browse files Browse the repository at this point in the history
 Conflicts:
	examples/src/main/java/org/bitcoinj/examples/PeerMonitor.java
  • Loading branch information
Andreas Schildbach authored and ripcurlx committed Aug 23, 2021
1 parent 80a259f commit 60dc0dc
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 10 deletions.
3 changes: 3 additions & 0 deletions core/src/main/java/org/bitcoinj/core/BitcoinSerializer.java
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ public class BitcoinSerializer extends MessageSerializer {
names.put(GetUTXOsMessage.class, "getutxos");
names.put(UTXOsMessage.class, "utxos");
names.put(SendHeadersMessage.class, "sendheaders");
names.put(FeeFilterMessage.class, "feefilter");
}

/**
Expand Down Expand Up @@ -261,6 +262,8 @@ private Message makeMessage(String command, int length, byte[] payloadBytes, byt
return new GetUTXOsMessage(params, payloadBytes);
} else if (command.equals("sendheaders")) {
return new SendHeadersMessage(params, payloadBytes);
} else if (command.equals("feefilter")) {
return new FeeFilterMessage(params, payloadBytes, this, length);
} else {
return new UnknownMessage(params, command, payloadBytes);
}
Expand Down
10 changes: 10 additions & 0 deletions core/src/main/java/org/bitcoinj/core/Coin.java
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,16 @@ public static Coin valueOf(final int coins, final int cents) {
return coin;
}

/**
* Create a {@code Coin} from a long integer number of satoshis.
*
* @param satoshis number of satoshis
* @return {@code Coin} object containing value in satoshis
*/
public static Coin ofSat(long satoshis) {
return Coin.valueOf(satoshis);
}

/**
* <p>Parses an amount expressed in the way humans are used to.</p>
* <p>This takes string in a format understood by {@link BigDecimal#BigDecimal(String)}, for example "0", "1", "0.10",
Expand Down
57 changes: 57 additions & 0 deletions core/src/main/java/org/bitcoinj/core/FeeFilterMessage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright by the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.bitcoinj.core;

import java.io.IOException;
import java.io.OutputStream;
import java.math.BigInteger;

/**
* <p>Represents an "feefilter" message on the P2P network, which instructs a peer to filter transaction invs for
* transactions that fall below the feerate provided.</p>
*
* <p>See <a href="https://github.com/bitcoin/bips/blob/master/bip-0133.mediawiki">BIP133</a> for details.</p>
*
* <p>Instances of this class are not safe for use by multiple threads.</p>
*/
public class FeeFilterMessage extends Message {
private Coin feeRate;

public FeeFilterMessage(NetworkParameters params, byte[] payloadBytes, BitcoinSerializer serializer, int length) {
super(params, payloadBytes, 0, serializer, length);
}

@Override
protected void bitcoinSerializeToStream(OutputStream stream) throws IOException {
super.bitcoinSerializeToStream(stream);
Utils.uint64ToByteStreamLE(BigInteger.valueOf(feeRate.value), stream);
}

@Override
protected void parse() throws ProtocolException {
feeRate = Coin.ofSat(readUint64().longValue());
}

public Coin getFeeRate() {
return feeRate;
}

@Override
public String toString() {
return "feefilter: " + feeRate.toFriendlyString() + "/kB";
}
}
3 changes: 2 additions & 1 deletion core/src/main/java/org/bitcoinj/core/NetworkParameters.java
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,8 @@ public static enum ProtocolVersion {
BLOOM_FILTER(70000), // BIP37
BLOOM_FILTER_BIP111(70011), // BIP111
WITNESS_VERSION(70012),
CURRENT(70012);
FEEFILTER(70013), // BIP133
CURRENT(70013);

private final int bitcoinProtocol;

Expand Down
13 changes: 13 additions & 0 deletions core/src/main/java/org/bitcoinj/core/Peer.java
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ public GetDataRequest(Sha256Hash hash, SettableFuture future) {
private static final int PING_MOVING_AVERAGE_WINDOW = 20;

private volatile VersionMessage vPeerVersionMessage;
private volatile Coin vFeeFilter;

// A settable future which completes (with this) when the connection is open
private final SettableFuture<Peer> connectionOpenFuture = SettableFuture.create();
Expand Down Expand Up @@ -495,6 +496,8 @@ protected void processMessage(Message m) throws Exception {
log.error("{} {}: Received {}", this, getPeerVersionMessage().subVer, m);
} else if (m instanceof SendHeadersMessage) {
// We ignore this message, because we don't announce new blocks.
} else if (m instanceof FeeFilterMessage) {
processFeeFilter((FeeFilterMessage) m);
} else {
log.warn("{}: Received unhandled message: {}", this, m);
}
Expand Down Expand Up @@ -1606,6 +1609,11 @@ protected void processPong(Pong m) {
}
}

private void processFeeFilter(FeeFilterMessage m) {
log.info("{}: Announced fee filter: {}/kB", this, m.getFeeRate().toFriendlyString());
vFeeFilter = m.getFeeRate();
}

/**
* Returns the difference between our best chain height and the peers, which can either be positive if we are
* behind the peer, or negative if the peer is ahead of us.
Expand Down Expand Up @@ -1647,6 +1655,11 @@ public VersionMessage getPeerVersionMessage() {
return vPeerVersionMessage;
}

/** Returns the fee filter announced by the remote peer, interpreted as satoshis per kB. */
public Coin getFeeFilter() {
return vFeeFilter;
}

/** Returns version data we announce to our remote peers. */
public VersionMessage getVersionMessage() {
return versionMessage;
Expand Down
38 changes: 29 additions & 9 deletions examples/src/main/java/org/bitcoinj/examples/PeerMonitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,20 @@

package org.bitcoinj.examples;

import com.google.common.collect.Lists;
import org.bitcoinj.core.*;
import org.bitcoinj.core.listeners.PeerConnectedEventListener;
import org.bitcoinj.core.listeners.PeerDisconnectedEventListener;
import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.core.Peer;
import org.bitcoinj.core.PeerGroup;
import org.bitcoinj.net.discovery.DnsDiscovery;
import org.bitcoinj.params.MainNetParams;
import org.bitcoinj.utils.BriefLogFormatter;
import com.google.common.collect.Lists;

import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumnModel;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
Expand All @@ -49,7 +48,8 @@ public class PeerMonitor {
private PeerTableModel peerTableModel;
private PeerTableRenderer peerTableRenderer;

private final HashMap<Peer, String> reverseDnsLookups = new HashMap<Peer, String>();
private final HashMap<Peer, String> reverseDnsLookups = new HashMap<>();
private final HashMap<Peer, AddressMessage> addressMessages = new HashMap<>();

public static void main(String[] args) throws Exception {
BriefLogFormatter.init();
Expand Down Expand Up @@ -144,7 +144,11 @@ public void stateChanged(ChangeEvent changeEvent) {
peerTable.setDefaultRenderer(String.class, peerTableRenderer);
peerTable.setDefaultRenderer(Integer.class, peerTableRenderer);
peerTable.setDefaultRenderer(Long.class, peerTableRenderer);
peerTable.getColumnModel().getColumn(0).setPreferredWidth(300);
TableColumnModel columnModel = peerTable.getColumnModel();
columnModel.getColumn(PeerTableModel.IP_ADDRESS).setPreferredWidth(300);
columnModel.getColumn(PeerTableModel.USER_AGENT).setPreferredWidth(150);
columnModel.getColumn(PeerTableModel.FEE_FILTER).setPreferredWidth(150);
columnModel.getColumn(PeerTableModel.ADDRESSES).setPreferredWidth(400);

JScrollPane scrollPane = new JScrollPane(peerTable);
window.getContentPane().add(scrollPane, BorderLayout.CENTER);
Expand All @@ -166,8 +170,10 @@ private class PeerTableModel extends AbstractTableModel {
public static final int PROTOCOL_VERSION = 1;
public static final int USER_AGENT = 2;
public static final int CHAIN_HEIGHT = 3;
public static final int PING_TIME = 4;
public static final int LAST_PING_TIME = 5;
public static final int FEE_FILTER = 4;
public static final int PING_TIME = 5;
public static final int LAST_PING_TIME = 6;
public static final int ADDRESSES = 7;

public List<Peer> connectedPeers = Lists.newArrayList();
public List<Peer> pendingPeers = Lists.newArrayList();
Expand All @@ -190,15 +196,17 @@ public String getColumnName(int i) {
case PROTOCOL_VERSION: return "Protocol version";
case USER_AGENT: return "User Agent";
case CHAIN_HEIGHT: return "Chain height";
case FEE_FILTER: return "Fee filter (per kB)";
case PING_TIME: return "Average ping";
case LAST_PING_TIME: return "Last ping";
case ADDRESSES: return "Peer addresses";
default: throw new RuntimeException();
}
}

@Override
public int getColumnCount() {
return 6;
return 8;
}

@Override
Expand Down Expand Up @@ -243,10 +251,15 @@ public Object getValueAt(int row, int col) {
return peer.getPeerVersionMessage().subVer;
case CHAIN_HEIGHT:
return peer.getBestHeight();
case FEE_FILTER:
Coin feeFilter = peer.getFeeFilter();
return feeFilter != null ? feeFilter.toFriendlyString() : "";
case PING_TIME:
return peer.getPingTime();
case LAST_PING_TIME:
return peer.getLastPingTime();
case ADDRESSES:
return getAddressesForPeer(peer);

default: throw new RuntimeException();
}
Expand All @@ -262,6 +275,13 @@ private Object getAddressForPeer(Peer peer) {
else
return peer.getAddress().getAddr().getHostAddress();
}

private String getAddressesForPeer(Peer peer) {
synchronized (addressMessages) {
AddressMessage addressMessage = addressMessages.get(peer);
return addressMessage != null ? addressMessage.toString() : "";
}
}
}

private class PeerTableRenderer extends JLabel implements TableCellRenderer {
Expand Down

0 comments on commit 60dc0dc

Please sign in to comment.