mirror of
https://github.com/bitcoinj/bitcoinj.git
synced 2024-11-20 18:22:12 +01:00
Set length field to fix a unit test.
This commit is contained in:
parent
3aafe80d99
commit
464b528175
246
src/com/google/bitcoin/core/AlertMessage.java
Normal file
246
src/com/google/bitcoin/core/AlertMessage.java
Normal file
@ -0,0 +1,246 @@
|
||||
/*
|
||||
* Copyright 2011 Google Inc.
|
||||
*
|
||||
* 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 com.google.bitcoin.core;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Alerts are signed messages that are broadcast on the peer-to-peer network if they match a hard-coded signing key.
|
||||
* The private keys are held by a small group of core Bitcoin developers, and alerts may be broadcast in the event of
|
||||
* an available upgrade or a serious network problem. Alerts have an expiration time, data that specifies what
|
||||
* set of software versions it matches and the ability to cancel them by broadcasting another type of alert.<p>
|
||||
*
|
||||
* The right course of action on receiving an alert is usually to either ensure a human will see it (display on screen,
|
||||
* log, email), or if you decide to use alerts for notifications that are specific to your app in some way, to parse it.
|
||||
* For example, you could treat it as an upgrade notification specific to your app. Satoshi designed alerts to ensure
|
||||
* that software upgrades could be distributed independently of a hard-coded website, in order to allow everything to
|
||||
* be purely peer-to-peer. You don't have to use this of course, and indeed it often makes more sense not to.<p>
|
||||
*
|
||||
* Before doing anything with an alert, you should check {@link AlertMessage#isSignatureValid()}.
|
||||
*/
|
||||
public class AlertMessage extends Message {
|
||||
private byte[] content;
|
||||
private byte[] signature;
|
||||
|
||||
// See the getters for documentation of what each field means.
|
||||
private long version = 1;
|
||||
private Date relayUntil;
|
||||
private Date expiration;
|
||||
private long id;
|
||||
private long cancel;
|
||||
private Set<Long> cancelSet;
|
||||
private long minVer, maxVer;
|
||||
private Set<String> matchingSubVers;
|
||||
private long priority;
|
||||
private String comment, statusBar, reserved;
|
||||
|
||||
// Chosen arbitrarily to avoid memory blowups.
|
||||
private static final long MAX_SET_SIZE = 100;
|
||||
|
||||
public AlertMessage(NetworkParameters params, byte[] payloadBytes) throws ProtocolException {
|
||||
super(params, payloadBytes, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
void parse() throws ProtocolException {
|
||||
// Alerts are formatted in two levels. The top level contains two byte arrays: a signature, and a serialized
|
||||
// data structure containing the actual alert data.
|
||||
int startPos = cursor;
|
||||
content = readByteArray();
|
||||
signature = readByteArray();
|
||||
// Now we need to parse out the contents of the embedded structure. Rewind back to the start of the message.
|
||||
cursor = startPos;
|
||||
readVarInt(); // Skip the length field on the content array.
|
||||
// We're inside the embedded structure.
|
||||
version = readUint32();
|
||||
// Read the timestamps. Bitcoin uses seconds since the epoch.
|
||||
relayUntil = new Date(readUint64().longValue() * 1000);
|
||||
expiration = new Date(readUint64().longValue() * 1000);
|
||||
id = readUint32();
|
||||
cancel = readUint32();
|
||||
// Sets are serialized as <len><item><item><item>....
|
||||
long cancelSetSize = readVarInt();
|
||||
if (cancelSetSize < 0 || cancelSetSize > MAX_SET_SIZE) {
|
||||
throw new ProtocolException("Bad cancel set size: " + cancelSetSize);
|
||||
}
|
||||
// Using a hashset here is very inefficient given that this will normally be only one item. But Java doesn't
|
||||
// make it easy to do better. What we really want is just an array-backed set.
|
||||
cancelSet = new HashSet<Long>((int)cancelSetSize);
|
||||
for (long i = 0; i < cancelSetSize; i++) {
|
||||
cancelSet.add(readUint32());
|
||||
}
|
||||
minVer = readUint32();
|
||||
maxVer = readUint32();
|
||||
// Read the subver matching set.
|
||||
long subverSetSize = readVarInt();
|
||||
if (subverSetSize < 0 || subverSetSize > MAX_SET_SIZE) {
|
||||
throw new ProtocolException("Bad subver set size: " + subverSetSize);
|
||||
}
|
||||
matchingSubVers = new HashSet<String>((int)subverSetSize);
|
||||
for (long i = 0; i < subverSetSize; i++) {
|
||||
matchingSubVers.add(readStr());
|
||||
}
|
||||
priority = readUint32();
|
||||
comment = readStr();
|
||||
statusBar = readStr();
|
||||
reserved = readStr();
|
||||
|
||||
length = cursor - offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the digital signature attached to the message verifies. Don't do anything with the alert if it
|
||||
* doesn't verify, because that would allow arbitrary attackers to spam your users.
|
||||
*/
|
||||
public boolean isSignatureValid() {
|
||||
return ECKey.verify(Utils.doubleDigest(content), signature, params.alertSigningKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void parseLite() throws ProtocolException {
|
||||
// Do nothing, lazy parsing isn't useful for alerts.
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Field accessors.
|
||||
|
||||
/**
|
||||
* The time at which the alert should stop being broadcast across the network. Note that you can still receive
|
||||
* the alert after this time from other nodes if the alert still applies to them or to you.
|
||||
*/
|
||||
public Date getRelayUntil() {
|
||||
return relayUntil;
|
||||
}
|
||||
|
||||
public void setRelayUntil(Date relayUntil) {
|
||||
this.relayUntil = relayUntil;
|
||||
}
|
||||
|
||||
/**
|
||||
* The time at which the alert ceases to be relevant. It should not be presented to the user or app administrator
|
||||
* after this time.
|
||||
*/
|
||||
public Date getExpiration() {
|
||||
return expiration;
|
||||
}
|
||||
|
||||
public void setExpiration(Date expiration) {
|
||||
this.expiration = expiration;
|
||||
}
|
||||
|
||||
/**
|
||||
* The numeric identifier of this alert. Each alert should have a unique ID, but the signer can choose any number.
|
||||
* If an alert is broadcast with a cancel field higher than this ID, this alert is considered cancelled.
|
||||
* @return uint32
|
||||
*/
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* A marker that results in any alerts with an ID lower than this value to be considered cancelled.
|
||||
* @return uint32
|
||||
*/
|
||||
public long getCancel() {
|
||||
return cancel;
|
||||
}
|
||||
|
||||
public void setCancel(long cancel) {
|
||||
this.cancel = cancel;
|
||||
}
|
||||
|
||||
/**
|
||||
* The inclusive lower bound on software versions that are considered for the purposes of this alert. The Satoshi
|
||||
* client compares this against a protocol version field, but as long as the subVer field is used to restrict it your
|
||||
* alerts could use any version numbers.
|
||||
* @return uint32
|
||||
*/
|
||||
public long getMinVer() {
|
||||
return minVer;
|
||||
}
|
||||
|
||||
public void setMinVer(long minVer) {
|
||||
this.minVer = minVer;
|
||||
}
|
||||
|
||||
/**
|
||||
* The inclusive upper bound on software versions considered for the purposes of this alert. The Satoshi
|
||||
* client compares this against a protocol version field, but as long as the subVer field is used to restrict it your
|
||||
* alerts could use any version numbers.
|
||||
* @return
|
||||
*/
|
||||
public long getMaxVer() {
|
||||
return maxVer;
|
||||
}
|
||||
|
||||
public void setMaxVer(long maxVer) {
|
||||
this.maxVer = maxVer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides an integer ordering amongst simultaneously active alerts.
|
||||
* @return uint32
|
||||
*/
|
||||
public long getPriority() {
|
||||
return priority;
|
||||
}
|
||||
|
||||
public void setPriority(long priority) {
|
||||
this.priority = priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* This field is unused. It is presumably intended for the author of the alert to provide a justification for it
|
||||
* visible to protocol developers but not users.
|
||||
*/
|
||||
public String getComment() {
|
||||
return comment;
|
||||
}
|
||||
|
||||
public void setComment(String comment) {
|
||||
this.comment = comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* A string that is intended to display in the status bar of the official GUI client. It contains the user-visible
|
||||
* message. English only.
|
||||
*/
|
||||
public String getStatusBar() {
|
||||
return statusBar;
|
||||
}
|
||||
|
||||
public void setStatusBar(String statusBar) {
|
||||
this.statusBar = statusBar;
|
||||
}
|
||||
|
||||
/**
|
||||
* This field is never used.
|
||||
*/
|
||||
public String getReserved() {
|
||||
return reserved;
|
||||
}
|
||||
|
||||
public void setReserved(String reserved) {
|
||||
this.reserved = reserved;
|
||||
}
|
||||
}
|
@ -334,6 +334,9 @@ public class BitcoinSerializer {
|
||||
return new VersionAck(params, payloadBytes);
|
||||
} else if (command.equals("headers")) {
|
||||
return new HeadersMessage(params, payloadBytes);
|
||||
} else if (command.equals("alert")) {
|
||||
log.info("alert payload " + Utils.bytesToHexString(payloadBytes));
|
||||
return new AlertMessage(params, payloadBytes);
|
||||
} else {
|
||||
log.warn("No support for deserializing message with name {}", command);
|
||||
return new UnknownMessage(params, command, payloadBytes);
|
||||
|
@ -36,6 +36,11 @@ public class NetworkParameters implements Serializable {
|
||||
*/
|
||||
public static final int PROTOCOL_VERSION = 31800;
|
||||
|
||||
/**
|
||||
* The alert signing key originally owned by Satoshi, and now passed on to Gavin along with a few others.
|
||||
*/
|
||||
public static final byte[] SATOSHI_KEY = Hex.decode("04fc9702847840aaf195de8442ebecedf5b095cdbb9bc716bda9110971b28a49e0ead8564ff0db22209e0374782c093bb899692d524e9d6a6956e7c5ecbcd68284");
|
||||
|
||||
// TODO: Seed nodes and checkpoint values should be here as well.
|
||||
|
||||
/**
|
||||
@ -68,6 +73,11 @@ public class NetworkParameters implements Serializable {
|
||||
* test and production BitCoin networks use 2 weeks (1209600 seconds).
|
||||
*/
|
||||
public int targetTimespan;
|
||||
/**
|
||||
* The key used to sign {@link AlertMessage}s. You can use {@link ECKey#verify(byte[], byte[], byte[])} to verify
|
||||
* signatures using it.
|
||||
*/
|
||||
public byte[] alertSigningKey;
|
||||
|
||||
private static Block createGenesis(NetworkParameters n) {
|
||||
Block genesisBlock = new Block(n);
|
||||
@ -107,6 +117,7 @@ public class NetworkParameters implements Serializable {
|
||||
n.dumpedPrivateKeyHeader = 239;
|
||||
n.interval = INTERVAL;
|
||||
n.targetTimespan = TARGET_TIMESPAN;
|
||||
n.alertSigningKey = SATOSHI_KEY;
|
||||
n.genesisBlock = createGenesis(n);
|
||||
n.genesisBlock.setTime(1296688602L);
|
||||
n.genesisBlock.setDifficultyTarget(0x1d07fff8L);
|
||||
@ -132,6 +143,7 @@ public class NetworkParameters implements Serializable {
|
||||
n.dumpedPrivateKeyHeader = 128;
|
||||
n.interval = INTERVAL;
|
||||
n.targetTimespan = TARGET_TIMESPAN;
|
||||
n.alertSigningKey = SATOSHI_KEY;
|
||||
n.genesisBlock = createGenesis(n);
|
||||
n.genesisBlock.setDifficultyTarget(0x1d00ffffL);
|
||||
n.genesisBlock.setTime(1231006505L);
|
||||
|
55
tests/com/google/bitcoin/core/AlertMessageTest.java
Normal file
55
tests/com/google/bitcoin/core/AlertMessageTest.java
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2011 Google Inc.
|
||||
*
|
||||
* 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 com.google.bitcoin.core;
|
||||
|
||||
import org.bouncycastle.util.encoders.Hex;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
|
||||
public class AlertMessageTest {
|
||||
private static final byte[] TEST_KEY_PRIV = Hex.decode("6421e091445ade4b24658e96aa60959ce800d8ea9e7bd8613335aa65ba8d840b");
|
||||
private NetworkParameters params;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
ECKey key = new ECKey(new BigInteger(1, TEST_KEY_PRIV));
|
||||
params = NetworkParameters.unitTests();
|
||||
params.alertSigningKey = key.getPubKey();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserialize() throws Exception {
|
||||
// A CAlert taken from the reference implementation.
|
||||
// TODO: This does not check the subVer or set fields. Support proper version matching.
|
||||
final byte[] payload = Hex.decode("5c010000004544eb4e000000004192ec4e00000000eb030000e9030000000000000048ee00000088130000002f43416c6572742073797374656d20746573743a2020202020202020207665722e302e352e3120617661696c61626c6500473045022100ec799908c008b272d5e5cd5a824abaaac53d210cc1fa517d8e22a701ecdb9e7002206fa1e7e7c251d5ba0d7c1fe428fc1870662f2927531d1cad8d4581b45bc4f8a7");
|
||||
AlertMessage alert = new AlertMessage(params, payload);
|
||||
assertEquals(1324041285, alert.getRelayUntil().getTime() / 1000);
|
||||
assertEquals(1324126785, alert.getExpiration().getTime() / 1000);
|
||||
assertEquals(1003, alert.getId());
|
||||
assertEquals(1001, alert.getCancel());
|
||||
assertEquals(0, alert.getMinVer());
|
||||
assertEquals(61000, alert.getMaxVer());
|
||||
assertEquals(5000, alert.getPriority());
|
||||
assertEquals("CAlert system test: ver.0.5.1 available", alert.getStatusBar());
|
||||
assertTrue(alert.isSignatureValid());
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user