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:
parent
7b24a72e45
commit
ccc3dbd339
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user