/// <inheritdoc/> public bool TryDeserialize(FastStreamReader stream, out string error) { if (!CompactInt.TryRead(stream, out CompactInt count, out error)) { return(false); } // A quick check to avoid data loss during cast below if (count > int.MaxValue) { error = "Item count is too big."; return(false); } Items = new PushDataOp[(int)count]; for (int i = 0; i < Items.Length; i++) { PushDataOp temp = new PushDataOp(); if (!temp.TryReadWitness(stream, out error)) { return(false); } Items[i] = temp; } error = null; return(true); }
/// <summary> /// Initializes a new instance of <see cref="SignatureScript"/> using the given block height /// (used for creating the signature script of the coinbase transactions). /// </summary> /// <exception cref="ArgumentOutOfRangeException"/> /// <param name="height">Block height</param> /// <param name="extraData">An extra data to push in this coinbase script</param> public SignatureScript(int height, byte[] extraData) { if (height < 0) { throw new ArgumentOutOfRangeException(nameof(height), "Block height can not be negative."); } PushDataOp hPush = new PushDataOp(height); if (extraData == null) { SetData(new IOperation[] { hPush }); if (Data.Length < Constants.MinCoinbaseScriptLength) { SetData(new IOperation[] { hPush, new PushDataOp(Encoding.UTF8.GetBytes("Bitcoin.Net")) }); } } else { // No need for a min length check since with any height >= 1 it will be at least 2 bytes if (extraData.Length > Constants.MaxCoinbaseScriptLength - 4) // 1 byte push + 3 byte height byte { throw new ArgumentOutOfRangeException(nameof(extraData.Length), $"Coinbase script can not be bigger than {Constants.MaxCoinbaseScriptLength}"); } SetData(new IOperation[] { hPush, new PushDataOp(extraData) }); } }
public static IEnumerable <object[]> GetMultiSigOutOfRangeExCases() { var rdm = new MockSerializableRedeemScript(RedeemScriptType.MultiSig, new byte[1], 0); var tx = new MockTxIdTx("") { TxInList = new TxIn[1] }; var zero = new PushDataOp(OP._0); var one = new PushDataOp(OP._1); var two = new PushDataOp(OP._2); var neg = new PushDataOp(OP.Negative1); var chsig = new CheckMultiSigOp(); yield return(new object[] { Helper.ShortSig1, rdm, tx, -1, "Invalid input index." }); yield return(new object[] { Helper.ShortSig1, rdm, tx, 1, "Invalid input index." }); yield return(new object[] { Helper.ShortSig1, new MockSerializableRedeemScript(RedeemScriptType.MultiSig, new byte[Constants.MaxScriptItemLength + 1], 0), tx, 0, "Redeem script is bigger than allowed length." }); yield return(new object[] { Helper.ShortSig1, new MockEvaluatableRedeemScript(RedeemScriptType.MultiSig, new IOperation[] { neg }, 0), tx, 0, "M can not be negative." }); yield return(new object[] { Helper.ShortSig1, new MockEvaluatableRedeemScript(RedeemScriptType.MultiSig, new IOperation[] { zero }, 0), tx, 0, "M value zero is not allowed" }); yield return(new object[] { Helper.ShortSig1, new MockEvaluatableRedeemScript(RedeemScriptType.MultiSig, new IOperation[] { one, neg, chsig }, 0), tx, 0, "N can not be negative." }); yield return(new object[] { Helper.ShortSig1, new MockEvaluatableRedeemScript(RedeemScriptType.MultiSig, new IOperation[] { one, zero, chsig }, 0), tx, 0, "N value zero is not allowed" }); yield return(new object[] { Helper.ShortSig1, new MockEvaluatableRedeemScript(RedeemScriptType.MultiSig, new IOperation[] { two, one, chsig }, 0), tx, 0, "M can not be bigger than N." }); }
public void Constructor_FromBytesTest() { byte[] data = { 1, 2, 3 }; PushDataOp op = new PushDataOp(data); // Make sure data is cloned data[0] = 255; Helper.ComparePrivateField(op, "data", new byte[] { 1, 2, 3 }); Assert.Equal(3, (byte)op.OpValue); }
public void Constructor_FromScriptTest() { byte[] data = { 1, 2, 3 }; MockSerializableScript scr = new MockSerializableScript(data, 255); PushDataOp op = new PushDataOp(scr); // Make sure data is cloned scr.Data[0] = 255; Helper.ComparePrivateField(op, "data", new byte[] { 1, 2, 3 }); Assert.Equal(3, (byte)op.OpValue); }
public void Constructor_FromBytesTest() { byte[][] data = new byte[][] { new byte[] { 1, 2, 3 }, new byte[] { 10, 20 }, new byte[] { 255, 255 } }; PushDataOp[] expected = new PushDataOp[] { new PushDataOp(new byte[] { 1, 2, 3 }), new PushDataOp(new byte[] { 10, 20 }), new PushDataOp(new byte[] { 255, 255 }) }; Witness wit = new Witness(data); Assert.Equal(expected, wit.Items); }
/// <summary> /// Initializes a new instance of <see cref="Witness"/> using the given data array. /// </summary> /// <exception cref="ArgumentNullException"/> /// <param name="dataItems">An array of byte arrays to use as witness items</param> public Witness(byte[][] dataItems) { if (dataItems == null) { throw new ArgumentNullException(nameof(dataItems), "Data items can not be null."); } Items = new PushDataOp[dataItems.Length]; for (int i = 0; i < Items.Length; i++) { Items[i] = new PushDataOp(dataItems[i]); } }
public void Constructor_DefaultTest() { PushDataOp op = new PushDataOp(); FastStream stream = new FastStream(); op.WriteToStream(stream); byte[] actual = stream.ToByteArray(); byte[] expected = new byte[1] { 0 }; Assert.Equal(expected, actual); }
/// <inheritdoc/> public bool VerifyCoinbase(int height, IConsensus consensus) { if (Data.Length < Constants.MinCoinbaseScriptLength || Data.Length > Constants.MaxCoinbaseScriptLength) { return(false); } if (consensus.IsBip34Enabled(height)) { PushDataOp op = new PushDataOp(); return(op.TryRead(new FastStreamReader(Data), out _) && op.TryGetNumber(out long h, out _, true, 5) && h == height); } else { return(true); } }
/// <inheritdoc/> public void SetToP2WSH_MultiSig(Signature[] sigs, RedeemScript redeem) { Items = new PushDataOp[sigs.Length + 2]; // OP_0 | Sig1 | sig2 | .... | sig(n) | redeemScript Items[0] = new PushDataOp(OP._0); for (int i = 1; i <= sigs.Length; i++) { Items[i] = new PushDataOp(sigs[i].ToByteArray()); } FastStream stream = new FastStream(); PushDataOp temp = new PushDataOp(redeem.Data); temp.WriteToStream(stream); Items[^ 1] = new PushDataOp(stream.ToByteArray());
private bool VerifyP2pkh(ITransaction tx, int index, PushDataOp sigPush, PushDataOp pubPush, ReadOnlySpan <byte> pubScrData, out string error) { var actualHash = hash160.ComputeHash(pubPush.data); if (!pubScrData.Slice(3, 20).SequenceEqual(actualHash)) { error = "Invalid hash."; return(false); } Signature sig; if (consensus.IsStrictDerSig(BlockHeight)) { if (!Signature.TryReadStrict(sigPush.data, out sig, out error)) { return(false); } } else { if (!Signature.TryReadLoose(sigPush.data, out sig, out error)) { return(false); } } if (!PublicKey.TryRead(pubPush.data, out PublicKey pubK)) { error = "Invalid public key"; return(false); } byte[] toSign = tx.SerializeForSigning(pubScrData.ToArray(), index, sig.SigHash); if (calc.Verify(toSign, sig, pubK, ForceLowS)) { error = null; return(true); } else { error = "Invalid signature"; return(false); } }
public void EqualsTest() { IOperation r1_1 = new ReturnOp(); IOperation r1_2 = new ReturnOp(); IOperation r2_1 = new ReturnOp(new byte[] { 1, 2 }, false); IOperation r2_2 = new ReturnOp(new byte[] { 1, 2 }, false); IOperation r2_3 = new ReturnOp(new byte[] { 1, 2 }, true); IOperation r2_4 = new PushDataOp(new byte[] { 1, 2 }); string diff = "ReturnOp"; Assert.True(r1_1.Equals(r1_1)); Assert.True(r1_1.Equals(r1_2)); Assert.False(r1_1.Equals(r2_1)); Assert.True(r2_1.Equals(r2_1)); Assert.True(r2_1.Equals(r2_2)); Assert.False(r2_1.Equals(r2_3)); Assert.False(r2_1.Equals(r2_4)); Assert.False(r2_1.Equals(diff)); }
private bool VerifyP2wpkh(ITransaction tx, int index, PushDataOp sigPush, PushDataOp pubPush, ReadOnlySpan <byte> pubScrData, ulong amount, out string error) { if (sigPush.data == null || pubPush.data == null) { error = "Invalid data pushes in P2WPKH witness."; return(false); } var actualHash = hash160.ComputeHash(pubPush.data); if (!pubScrData.Slice(2, 20).SequenceEqual(actualHash)) { error = "Invalid hash."; return(false); } if (!Signature.TryReadStrict(sigPush.data, out Signature sig, out error)) { error = $"Invalid signature ({error})"; return(false); } if (!PublicKey.TryRead(pubPush.data, out PublicKey pubK)) { error = "Invalid public key"; return(false); } byte[] toSign = tx.SerializeForSigningSegWit(scrSer.ConvertP2wpkh(actualHash), index, amount, sig.SigHash); if (calc.Verify(toSign, sig, pubK, ForceLowS)) { error = null; return(true); } else { error = "Invalid signature"; return(false); } }
public static IEnumerable <object[]> GetMultiSigArgExCases() { var tx = new MockTxIdTx("") { TxInList = new TxIn[1] }; PushDataOp badNum = new PushDataOp(); badNum.TryRead(new FastStreamReader(new byte[] { 1, 0 }), out _); var two = new PushDataOp(OP._2); var chsig = new CheckMultiSigOp(); yield return(new object[] { Helper.ShortSig1, new MockSerializableRedeemScript(RedeemScriptType.Empty, new byte[0], 0), tx, 0, "Invalid redeem script type." }); yield return(new object[] { Helper.ShortSig1, new MockEvaluatableRedeemScript(RedeemScriptType.MultiSig, null, 0), tx, 0, "Can not evaluate redeem script: Foo" }); yield return(new object[] { Helper.ShortSig1, new MockEvaluatableRedeemScript(RedeemScriptType.MultiSig, new IOperation[] { badNum }, 0), tx, 0, "Invalid m" }); yield return(new object[] { Helper.ShortSig1, new MockEvaluatableRedeemScript(RedeemScriptType.MultiSig, new IOperation[] { two, badNum, chsig }, 0), tx, 0, "Invalid n" }); }
private bool TryRead(byte[] data, List <IOperation> opList, ref int offset, out string error) { if (IsPushOp(data[offset])) { PushDataOp op = new PushDataOp(); if (!op.TryRead(data, ref offset, out error, IsWitness)) { return(false); } opList.Add(op); } else if (data[offset] == (byte)OP.RETURN) { ReturnOp op = new ReturnOp(); if (!op.TryRead(data, ref offset, out error, len)) { return(false); } opList.Add(op); } else { switch ((OP)data[offset]) { // Invalid OPs: case OP.VerIf: case OP.VerNotIf: error = $"Invalid OP was found: {GetOpName((OP)data[offset])}"; return(false); // Disabled OPs: case OP.CAT: case OP.SubStr: case OP.LEFT: case OP.RIGHT: case OP.INVERT: case OP.AND: case OP.OR: case OP.XOR: case OP.MUL2: case OP.DIV2: case OP.MUL: case OP.DIV: case OP.MOD: case OP.LSHIFT: case OP.RSHIFT: error = $"Disabled OP was found: {GetOpName((OP)data[offset])}"; return(false); // Special case of IFs: case OP.IF: IFOp ifop = new IFOp(); List <IOperation> ifOps = new List <IOperation>(); offset++; while (data[offset] != (byte)OP.EndIf && data[offset] != (byte)OP.ELSE && offset < endIndex) { if (!TryRead(data, ifOps, ref offset, out error)) { return(false); } if (offset > endIndex) { error = "Bad format."; return(false); } if (ifOps.Count == 0) { error = "Empty OP_IF"; return(false); } } if (data[offset] == (byte)OP.ELSE) { List <IOperation> elseOps = new List <IOperation>(); offset++; while (data[offset] != (byte)OP.EndIf && offset < endIndex) { if (!TryRead(data, elseOps, ref offset, out error)) { return(false); } } if (offset > endIndex) { error = "Bad format."; return(false); } if (elseOps.Count == 0) { error = "Empty OP_ELSE"; return(false); } ifop.elseOps = elseOps.ToArray(); } if (data[offset] != (byte)OP.EndIf) { error = "No OP_ENDIF was found."; //this may never happen! return(false); } ifop.mainOps = ifOps.ToArray(); opList.Add(ifop); break; case OP.NotIf: NotIfOp notifOp = new NotIfOp(); List <IOperation> ifOps2 = new List <IOperation>(); offset++; while (data[offset] != (byte)OP.EndIf && data[offset] != (byte)OP.ELSE && offset < endIndex) { if (!TryRead(data, ifOps2, ref offset, out error)) { return(false); } if (offset > endIndex) { error = "Bad format."; return(false); } if (ifOps2.Count == 0) { error = "Empty OP_IF"; return(false); } } if (data[offset] == (byte)OP.ELSE) { List <IOperation> elseOps2 = new List <IOperation>(); offset++; while (data[offset] != (byte)OP.EndIf && offset < endIndex) { if (!TryRead(data, elseOps2, ref offset, out error)) { return(false); } } if (offset > endIndex) { error = "Bad format."; return(false); } if (elseOps2.Count == 0) { error = "Empty OP_ELSE"; return(false); } notifOp.elseOps = elseOps2.ToArray(); } if (data[offset] != (byte)OP.EndIf) { error = "No OP_ENDIF was found."; //this may never happen! return(false); } notifOp.mainOps = ifOps2.ToArray(); opList.Add(notifOp); break; case OP.ELSE: error = "OP_ELSE found without prior OP_IF or OP_NOTIF."; return(false); case OP.EndIf: error = "OP_EndIf found without prior OP_IF or OP_NOTIF."; return(false); // From OP_0 to OP_16 except OP_Reserved is already handled. case OP.Reserved: opList.Add(new ReservedOp()); break; case OP.NOP: opList.Add(new NOPOp()); break; case OP.VER: opList.Add(new VEROp()); break; // OP.IF and OP.NotIf moved to top // OP.VerIf and OP.VerNotIf moved to top (Invalid tx) // OP.ELSE and OP.EndIf moved to top case OP.VERIFY: opList.Add(new VerifyOp()); break; // OP.RETURN is already handled case OP.ToAltStack: opList.Add(new ToAltStackOp()); break; case OP.FromAltStack: opList.Add(new FromAltStackOp()); break; case OP.DROP2: opList.Add(new DROP2Op()); break; case OP.DUP2: opList.Add(new DUP2Op()); break; case OP.DUP3: opList.Add(new DUP3Op()); break; case OP.OVER2: opList.Add(new OVER2Op()); break; case OP.ROT2: opList.Add(new ROT2Op()); break; case OP.SWAP2: opList.Add(new SWAP2Op()); break; case OP.IfDup: opList.Add(new IfDupOp()); break; case OP.DEPTH: opList.Add(new DEPTHOp()); break; case OP.DROP: opList.Add(new DROPOp()); break; case OP.DUP: opList.Add(new DUPOp()); break; case OP.NIP: opList.Add(new NIPOp()); break; case OP.OVER: opList.Add(new OVEROp()); break; case OP.PICK: opList.Add(new PICKOp()); break; case OP.ROLL: opList.Add(new ROLLOp()); break; case OP.ROT: opList.Add(new ROTOp()); break; case OP.SWAP: opList.Add(new SWAPOp()); break; case OP.TUCK: opList.Add(new TUCKOp()); break; // OP_ (CAT SubStr LEFT RIGHT INVERT AND OR XOR) are moved to top case OP.SIZE: opList.Add(new SizeOp()); break; case OP.EQUAL: opList.Add(new EqualOp()); break; case OP.EqualVerify: opList.Add(new EqualVerifyOp()); break; case OP.Reserved1: opList.Add(new Reserved1Op()); break; case OP.Reserved2: opList.Add(new Reserved2Op()); break; case OP.ADD1: opList.Add(new ADD1Op()); break; case OP.SUB1: opList.Add(new SUB1Op()); break; // OP.MUL2 and OP.DIV2 are moved to top (disabled op). case OP.NEGATE: opList.Add(new NEGATEOp()); break; case OP.ABS: opList.Add(new ABSOp()); break; case OP.NOT: opList.Add(new NOTOp()); break; case OP.NotEqual0: opList.Add(new NotEqual0Op()); break; case OP.ADD: opList.Add(new AddOp()); break; case OP.SUB: opList.Add(new SUBOp()); break; // OP_ (MUL DIV MOD LSHIFT RSHIFT) are moved to top (disabled op). case OP.BoolAnd: opList.Add(new BoolAndOp()); break; case OP.BoolOr: opList.Add(new BoolOrOp()); break; case OP.NumEqual: opList.Add(new NumEqualOp()); break; case OP.NumEqualVerify: opList.Add(new NumEqualVerifyOp()); break; case OP.NumNotEqual: opList.Add(new NumNotEqualOp()); break; case OP.LessThan: opList.Add(new LessThanOp()); break; case OP.GreaterThan: opList.Add(new GreaterThanOp()); break; case OP.LessThanOrEqual: opList.Add(new LessThanOrEqualOp()); break; case OP.GreaterThanOrEqual: opList.Add(new GreaterThanOrEqualOp()); break; case OP.MIN: opList.Add(new MINOp()); break; case OP.MAX: opList.Add(new MAXOp()); break; case OP.WITHIN: opList.Add(new WITHINOp()); break; case OP.RIPEMD160: opList.Add(new RipeMd160Op()); break; case OP.SHA1: opList.Add(new Sha1Op()); break; case OP.SHA256: opList.Add(new Sha256Op()); break; case OP.HASH160: opList.Add(new Hash160Op()); break; case OP.HASH256: opList.Add(new Hash256Op()); break; //case OP.CodeSeparator: // break; case OP.CheckSig: opList.Add(new CheckSigOp()); break; case OP.CheckSigVerify: opList.Add(new CheckSigVerifyOp()); break; case OP.CheckMultiSig: opList.Add(new CheckMultiSigOp()); break; case OP.CheckMultiSigVerify: opList.Add(new CheckMultiSigVerifyOp()); break; case OP.NOP1: opList.Add(new NOP1Op()); break; case OP.CheckLocktimeVerify: opList.Add(new CheckLocktimeVerifyOp()); break; //case OP.CheckSequenceVerify: // break; case OP.NOP4: opList.Add(new NOP4Op()); break; case OP.NOP5: opList.Add(new NOP5Op()); break; case OP.NOP6: opList.Add(new NOP6Op()); break; case OP.NOP7: opList.Add(new NOP7Op()); break; case OP.NOP8: opList.Add(new NOP8Op()); break; case OP.NOP9: opList.Add(new NOP9Op()); break; case OP.NOP10: opList.Add(new NOP10Op()); break; default: error = "Undefined OP code"; return(false); } offset++; } error = null; return(true); }
/// <summary> /// Deserializes the given byte array starting from the specified offset. The return value indicates success. /// </summary> /// <param name="data">Byte array containing a <see cref="Script"/>.</param> /// <param name="offset">The offset inside the <paramref name="data"/> to start from.</param> /// <param name="error">Error message (null if sucessful, otherwise will contain information about the failure).</param> /// <returns>True if deserialization was successful, false if otherwise.</returns> public virtual bool TryDeserialize(byte[] data, ref int offset, out string error) { if (offset < 0) { error = "Offset can not be negative."; return(false); } if (data == null || data.Length - offset < 0) { error = "Data length is not valid."; return(false); } if (!CompactInt.TryReadFromBytes(data, ref offset, out CompactInt lengthOrCount, out error)) { return(false); } if (lengthOrCount.Value > int.MaxValue) { error = (IsWitness ? "Item count" : "Script length") + "is too big."; return(false); } if (IsWitness) { // cast is ok since the check happend in previous line. int count = (int)lengthOrCount; if (count > maxLenOrCount) { error = "Invalid witness item count."; return(false); } OperationList = new IOperation[count]; for (int i = 0; i < count; i++) { // TODO: the assumption here is that witness doesn't have anything but PushDataOp, this may be wrong. PushDataOp op = new PushDataOp(); if (!op.TryRead(data, ref offset, out error, true)) { return(false); } OperationList[i] = op; } } else { // cast is ok same as above len = (int)lengthOrCount; // Empty script (offset doesn't change, it already changed when reading CompactInt) if (len == 0) { OperationList = new IOperation[0]; error = null; return(true); } List <IOperation> opList = new List <IOperation>(); endIndex = offset + len; while (offset < endIndex) { if (!TryRead(data, opList, ref offset, out error)) { return(false); } } if (offset != endIndex) { error = "Invalid stack format."; return(false); } OperationList = opList.ToArray(); } error = null; return(true); }
public void Constructor_FromOpNumTest(OP val) { PushDataOp op = new PushDataOp(val); Assert.Equal(val, op.OpValue); }
public void Constructor_FromInt_HasOpNum_Test(int i, OP expected) { PushDataOp op = new PushDataOp(i); Assert.Equal(expected, op.OpValue); }