3
0
mirror of https://github.com/Qortal/altcoinj.git synced 2025-02-12 18:25:51 +00:00

Implement standard checks for BIP62 shortest possible data push rules. Also fix ScriptBuilder so it doesn't build longer than necessary data pushes any more.

This commit is contained in:
Andreas Schildbach 2014-05-26 16:26:22 +02:00
parent 7b24a72e45
commit ccc3dbd339
4 changed files with 120 additions and 3 deletions

View File

@ -48,10 +48,19 @@ public class ScriptBuilder {
}
public ScriptBuilder data(byte[] data) {
// implements BIP62
byte[] copy = Arrays.copyOf(data, data.length);
int opcode;
if (data.length < OP_PUSHDATA1) {
opcode = data.length; // OP_0 in case of empty vector
if (data.length == 0) {
opcode = OP_0;
} else if (data.length == 1) {
byte b = data[0];
if (b >= 1 && b <= 16)
opcode = Script.encodeToOpN(b);
else
opcode = 1;
} else if (data.length < OP_PUSHDATA1) {
opcode = data.length;
} else if (data.length < 256) {
opcode = OP_PUSHDATA1;
} else if (data.length < 65536) {

View File

@ -26,6 +26,10 @@ import java.util.Arrays;
import javax.annotation.Nullable;
import static com.google.bitcoin.core.Utils.bytesToHexString;
import static com.google.bitcoin.script.ScriptOpCodes.OP_0;
import static com.google.bitcoin.script.ScriptOpCodes.OP_1;
import static com.google.bitcoin.script.ScriptOpCodes.OP_16;
import static com.google.bitcoin.script.ScriptOpCodes.OP_1NEGATE;
import static com.google.bitcoin.script.ScriptOpCodes.OP_PUSHDATA1;
import static com.google.bitcoin.script.ScriptOpCodes.OP_PUSHDATA2;
import static com.google.bitcoin.script.ScriptOpCodes.OP_PUSHDATA4;
@ -64,11 +68,43 @@ public class ScriptChunk {
return opcode > OP_PUSHDATA4;
}
/**
* Returns true if this chunk is pushdata content, including the single-byte pushdatas.
*/
public boolean isPushData() {
return opcode <= OP_16;
}
public int getStartLocationInProgram() {
checkState(startLocationInProgram >= 0);
return startLocationInProgram;
}
/**
* Called on a pushdata chunk, returns true if it uses the smallest possible way (according to BIP62) to push the data.
*/
public boolean isShortestPossiblePushData() {
checkState(isPushData());
if (data.length == 0)
return opcode == OP_0;
if (data.length == 1) {
byte b = data[0];
if (b >= 0x01 && b <= 0x10)
return opcode == OP_1 + b - 1;
if (b == 0x81)
return opcode == OP_1NEGATE;
}
if (data.length < OP_PUSHDATA1)
return opcode == data.length;
if (data.length < 256)
return opcode == OP_PUSHDATA1;
if (data.length < 65536)
return opcode == OP_PUSHDATA2;
// can never be used, but implemented for completeness
return opcode == OP_PUSHDATA4;
}
public void write(OutputStream stream) throws IOException {
if (isOpCode()) {
checkState(data == null);

View File

@ -20,8 +20,11 @@ package com.google.bitcoin.wallet;
import com.google.bitcoin.core.NetworkParameters;
import com.google.bitcoin.core.Transaction;
import com.google.bitcoin.core.TransactionConfidence;
import com.google.bitcoin.core.TransactionInput;
import com.google.bitcoin.core.TransactionOutput;
import com.google.bitcoin.core.Wallet;
import com.google.bitcoin.script.ScriptChunk;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -104,7 +107,8 @@ public class DefaultRiskAnalysis implements RiskAnalysis {
public enum RuleViolation {
NONE,
VERSION,
DUST
DUST,
SHORTEST_POSSIBLE_PUSHDATA
}
/**
@ -127,6 +131,25 @@ public class DefaultRiskAnalysis implements RiskAnalysis {
log.warn("TX considered non-standard due to output {} being dusty", i);
return RuleViolation.DUST;
}
for (ScriptChunk chunk : output.getScriptPubKey().getChunks()) {
if (chunk.isPushData() && !chunk.isShortestPossiblePushData()) {
log.warn("TX considered non-standard due to output {} having a longer than necessary data push: {}",
i, chunk);
return RuleViolation.SHORTEST_POSSIBLE_PUSHDATA;
}
}
}
final List<TransactionInput> inputs = tx.getInputs();
for (int i = 0; i < inputs.size(); i++) {
TransactionInput input = inputs.get(i);
for (ScriptChunk chunk : input.getScriptSig().getChunks()) {
if (chunk.data != null && chunk.isShortestPossiblePushData()) {
log.warn("TX considered non-standard due to input {} having a longer than necessary data push: {}",
i, chunk);
return RuleViolation.SHORTEST_POSSIBLE_PUSHDATA;
}
}
}
return RuleViolation.NONE;

View File

@ -0,0 +1,49 @@
/**
* Copyright 2014 Andreas Schildbach
*
* 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.script;
import static com.google.bitcoin.script.ScriptOpCodes.OP_PUSHDATA1;
import static com.google.bitcoin.script.ScriptOpCodes.OP_PUSHDATA2;
import static com.google.bitcoin.script.ScriptOpCodes.OP_PUSHDATA4;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
public class ScriptChunkTest {
@Test
public void testShortestPossibleDataPush() {
assertTrue("empty push", new ScriptBuilder().data(new byte[0]).build().getChunks().get(0)
.isShortestPossiblePushData());
for (byte i = -1; i < 127; i++)
assertTrue("push of single byte " + i, new ScriptBuilder().data(new byte[] { i }).build().getChunks()
.get(0).isShortestPossiblePushData());
for (int len = 2; len < Script.MAX_SCRIPT_ELEMENT_SIZE; len++)
assertTrue("push of " + len + " bytes", new ScriptBuilder().data(new byte[len]).build().getChunks().get(0)
.isShortestPossiblePushData());
// non-standard chunks
for (byte i = 1; i <= 16; i++)
assertFalse("push of smallnum " + i, new ScriptChunk(1, new byte[] { i }).isShortestPossiblePushData());
assertFalse("push of 75 bytes", new ScriptChunk(OP_PUSHDATA1, new byte[75]).isShortestPossiblePushData());
assertFalse("push of 255 bytes", new ScriptChunk(OP_PUSHDATA2, new byte[255]).isShortestPossiblePushData());
assertFalse("push of 65535 bytes", new ScriptChunk(OP_PUSHDATA4, new byte[65535]).isShortestPossiblePushData());
}
}