Skip to content

Commit d39fbbb

Browse files
julia-zackmarcos-iov
authored andcommitted
Add test cases. Better error handling when data is null or larger than 4 bytes. Refactor
1 parent 1d34d64 commit d39fbbb

File tree

4 files changed

+65
-16
lines changed

4 files changed

+65
-16
lines changed

src/main/java/co/rsk/bitcoinj/script/RedeemScriptValidator.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ protected static boolean hasStandardRedeemScriptStructure(List<ScriptChunk> chun
5252
}
5353

5454
int numKeys = secondToLastChunk.decodePositiveN();
55-
// and there should be as many data chunks as keys
56-
if (numKeys < 1 || chunksSize != numKeys + 3) { // numKeys + M + N + OP_CHECKMULTISIG
55+
// and there should be numKeys+3 total chunks (keys + OP_M + OP_N + OP_CHECKMULTISIG)
56+
if (chunksSize != numKeys + 3) {
5757
return false;
5858
}
5959

@@ -65,7 +65,7 @@ protected static boolean hasStandardRedeemScriptStructure(List<ScriptChunk> chun
6565

6666
return true;
6767
} catch (IllegalStateException e) {
68-
return false; // Not a number
68+
return false; // Not a number
6969
}
7070
}
7171

src/main/java/co/rsk/bitcoinj/script/ScriptChunk.java

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import static com.google.common.base.Preconditions.checkState;
2222
import static java.util.Objects.isNull;
2323

24-
import co.rsk.bitcoinj.core.ScriptException;
2524
import co.rsk.bitcoinj.core.Utils;
2625
import com.google.common.base.Objects;
2726
import java.io.IOException;
@@ -151,7 +150,7 @@ public boolean isPositiveN() {
151150
try {
152151
decodePositiveN();
153152
return true;
154-
} catch(IllegalArgumentException | ScriptException e) {
153+
} catch (IllegalArgumentException e) {
155154
return false;
156155
}
157156
}
@@ -191,14 +190,11 @@ private int decodePositiveNConsideringEncoding() {
191190
throw new IllegalArgumentException("Number from chunk is not positive.");
192191
}
193192

194-
return castToBigInteger(data).intValue(); // values up to Integer.MAX_VALUE can be cast as ints
195-
}
196-
197-
// copied implementation from Script.castToBigInteger to avoid circular dependency
198-
private static BigInteger castToBigInteger(byte[] chunk) throws ScriptException {
199-
if (chunk.length > 4)
200-
throw new ScriptException("Script attempted to use an integer larger than 4 bytes");
201-
return Utils.decodeMPI(Utils.reverseBytes(chunk), false);
193+
if (dataLength > 4) {
194+
throw new IllegalArgumentException("Number from chunk has more than 4 bytes.");
195+
}
196+
BigInteger bigInteger = Utils.decodeMPI(Utils.reverseBytes(data), false);
197+
return bigInteger.intValue(); // values up to Integer.MAX_VALUE can be cast as ints
202198
}
203199

204200
@Override

src/test/java/co/rsk/bitcoinj/script/RedeemScriptValidatorTest.java

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ public class RedeemScriptValidatorTest {
2323
private final BtcECKey ecKey1 = BtcECKey.fromPrivate(BigInteger.valueOf(110));
2424
private final BtcECKey ecKey2 = BtcECKey.fromPrivate(BigInteger.valueOf(220));
2525
private final BtcECKey ecKey3 = BtcECKey.fromPrivate(BigInteger.valueOf(330));
26+
private final BtcECKey ecKey4 = BtcECKey.fromPrivate(BigInteger.valueOf(440));
2627

2728
private Script standardRedeemScript;
2829
private Script flyoverStandardRedeemScript;
@@ -145,6 +146,46 @@ public void hasStandardRedeemScriptStructure_withLessKeysPushedThanExpected_shou
145146
Assert.assertFalse(RedeemScriptValidator.hasStandardRedeemScriptStructure(redeemScriptChunks));
146147
}
147148

149+
@Test
150+
public void hasStandardRedeemScriptStructure_withNegativeThreshold_shouldBeFalse() {
151+
for (int n=0; n<20; n++) {
152+
int i = -(1 << n); // i = -(2^n)
153+
ScriptBuilder builder = new ScriptBuilder();
154+
Script redeemScript = builder
155+
.number(i)
156+
.data(ecKey1.getPubKey())
157+
.data(ecKey2.getPubKey())
158+
.data(ecKey3.getPubKey())
159+
.data(ecKey4.getPubKey())
160+
.op(ScriptOpCodes.OP_4)
161+
.op(ScriptOpCodes.OP_CHECKMULTISIG)
162+
.build();
163+
164+
List<ScriptChunk> redeemScriptChunks = redeemScript.getChunks();
165+
Assert.assertFalse(RedeemScriptValidator.hasStandardRedeemScriptStructure(redeemScriptChunks));
166+
}
167+
}
168+
169+
@Test
170+
public void hasStandardRedeemScriptStructure_withNegativeTotalKeys_shouldBeFalse() {
171+
for (int n=0; n<20; n++) {
172+
int i = -(1 << n); // i = -(2^n)
173+
ScriptBuilder builder = new ScriptBuilder();
174+
Script redeemScript = builder
175+
.op(ScriptOpCodes.OP_4)
176+
.data(ecKey1.getPubKey())
177+
.data(ecKey2.getPubKey())
178+
.data(ecKey3.getPubKey())
179+
.data(ecKey4.getPubKey())
180+
.number(i)
181+
.op(ScriptOpCodes.OP_CHECKMULTISIG)
182+
.build();
183+
184+
List<ScriptChunk> redeemScriptChunks = redeemScript.getChunks();
185+
Assert.assertFalse(RedeemScriptValidator.hasStandardRedeemScriptStructure(redeemScriptChunks));
186+
}
187+
}
188+
148189
@Test
149190
public void hasStandardRedeemScriptStructure_withARedeemLikeScript_withoutOpCheckMultisigAsLastOpcode_shouldBeFalse() {
150191
ScriptBuilder builder = new ScriptBuilder();

src/test/java/co/rsk/bitcoinj/script/ScriptChunkTest.java

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -164,9 +164,9 @@ public void decodePositiveN_withPositiveNumber_returnsExpectedNumber() {
164164
}
165165

166166
@Test
167-
public void decodePositiveN_forZero_throwsNPE() {
168-
int zero = 0;
167+
public void decodePositiveN_forZero_throwsIAE() {
169168
ScriptBuilder builder = new ScriptBuilder();
169+
int zero = 0;
170170
builder.number(zero);
171171
Script script = builder.build();
172172

@@ -190,7 +190,19 @@ public void decodePositiveN_withNegativeNumber_throwsIAE() {
190190
}
191191

192192
@Test
193-
public void decodePositiveN_withNullPushData_throwsNPE() {
193+
public void decodePositiveN_withNumberLargerThan4Bytes_throwsIAE() {
194+
long i = 1L << 32;
195+
ScriptBuilder builder = new ScriptBuilder();
196+
builder.number(i);
197+
Script script = builder.build();
198+
199+
ScriptChunk chunk = script.chunks.get(0);
200+
assertFalse(chunk.isPositiveN());
201+
assertThrows(IllegalArgumentException.class, chunk::decodePositiveN);
202+
}
203+
204+
@Test
205+
public void decodePositiveN_withNullPushData_throwsIAE() {
194206
ScriptChunk chunk = new ScriptChunk(OP_PUSHDATA1, null);
195207
assertFalse(chunk.isPositiveN());
196208
assertThrows(IllegalArgumentException.class, chunk::decodePositiveN);

0 commit comments

Comments
 (0)