mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-10-14 00:59:33 +00:00
[NOD-223] Removed Nulldata as standard tx type (#329)
* [NOD-223] Removed Nulldata as standard tx type * [NOD-223] Removed redundant space
This commit is contained in:
parent
ffd886498a
commit
0c5f3d72bd
@ -162,11 +162,6 @@ func TestScriptCompression(t *testing.T) {
|
||||
uncompressed: hexToBytes("3302aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac"),
|
||||
compressed: hexToBytes("293302aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac"),
|
||||
},
|
||||
{
|
||||
name: "null data",
|
||||
uncompressed: hexToBytes("6a200102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"),
|
||||
compressed: hexToBytes("286a200102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"),
|
||||
},
|
||||
{
|
||||
name: "requires 2 size bytes - data push 200 bytes",
|
||||
uncompressed: append(hexToBytes("4cc8"), bytes.Repeat([]byte{0x00}, 200)...),
|
||||
@ -265,7 +260,7 @@ func TestAmountCompression(t *testing.T) {
|
||||
compressed uint64
|
||||
}{
|
||||
{
|
||||
name: "0 BTC (sometimes used in nulldata)",
|
||||
name: "0 BTC",
|
||||
uncompressed: 0,
|
||||
compressed: 0,
|
||||
},
|
||||
@ -343,12 +338,6 @@ func TestCompressedTxOut(t *testing.T) {
|
||||
pkScript []byte
|
||||
compressed []byte
|
||||
}{
|
||||
{
|
||||
name: "nulldata with 0 BTC",
|
||||
amount: 0,
|
||||
pkScript: hexToBytes("6a200102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"),
|
||||
compressed: hexToBytes("00286a200102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"),
|
||||
},
|
||||
{
|
||||
name: "pay-to-pubkey-hash dust",
|
||||
amount: 546,
|
||||
|
@ -303,8 +303,7 @@ func checkTransactionStandard(tx *util.Tx, blueScore uint64,
|
||||
}
|
||||
|
||||
// None of the output public key scripts can be a non-standard script or
|
||||
// be "dust" (except when the script is a null data script).
|
||||
numNullDataOutputs := 0
|
||||
// be "dust".
|
||||
for i, txOut := range msgTx.TxOut {
|
||||
scriptClass := txscript.GetScriptClass(txOut.PkScript)
|
||||
err := checkPkScriptStandard(txOut.PkScript, scriptClass)
|
||||
@ -320,24 +319,12 @@ func checkTransactionStandard(tx *util.Tx, blueScore uint64,
|
||||
return txRuleError(rejectCode, str)
|
||||
}
|
||||
|
||||
// Accumulate the number of outputs which only carry data. For
|
||||
// all other script types, ensure the output value is not
|
||||
// "dust".
|
||||
if scriptClass == txscript.NullDataTy {
|
||||
numNullDataOutputs++
|
||||
} else if isDust(txOut, policy.MinRelayTxFee) {
|
||||
if isDust(txOut, policy.MinRelayTxFee) {
|
||||
str := fmt.Sprintf("transaction output %d: payment "+
|
||||
"of %d is dust", i, txOut.Value)
|
||||
return txRuleError(wire.RejectDust, str)
|
||||
}
|
||||
}
|
||||
|
||||
// A standard transaction must not have more than one output script that
|
||||
// only carries data.
|
||||
if numNullDataOutputs > 1 {
|
||||
str := "more than one transaction output in a nulldata script"
|
||||
return txRuleError(wire.RejectNonstandard, str)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -380,19 +380,6 @@ func TestCheckTransactionStandard(t *testing.T) {
|
||||
isStandard: false,
|
||||
code: wire.RejectNonstandard,
|
||||
},
|
||||
{
|
||||
name: "More than one nulldata output",
|
||||
tx: wire.NewNativeMsgTx(1, []*wire.TxIn{&dummyTxIn}, []*wire.TxOut{{
|
||||
Value: 0,
|
||||
PkScript: []byte{txscript.OpReturn},
|
||||
}, {
|
||||
Value: 0,
|
||||
PkScript: []byte{txscript.OpReturn},
|
||||
}}),
|
||||
height: 300000,
|
||||
isStandard: false,
|
||||
code: wire.RejectNonstandard,
|
||||
},
|
||||
{
|
||||
name: "Dust output",
|
||||
tx: wire.NewNativeMsgTx(1, []*wire.TxIn{&dummyTxIn}, []*wire.TxOut{{
|
||||
@ -404,13 +391,14 @@ func TestCheckTransactionStandard(t *testing.T) {
|
||||
code: wire.RejectDust,
|
||||
},
|
||||
{
|
||||
name: "One nulldata output with 0 amount (standard)",
|
||||
name: "Nulldata transaction",
|
||||
tx: wire.NewNativeMsgTx(1, []*wire.TxIn{&dummyTxIn}, []*wire.TxOut{{
|
||||
Value: 0,
|
||||
PkScript: []byte{txscript.OpReturn},
|
||||
}}),
|
||||
height: 300000,
|
||||
isStandard: true,
|
||||
isStandard: false,
|
||||
code: wire.RejectNonstandard,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -43,10 +43,6 @@ const (
|
||||
// provided public keys.
|
||||
ErrTooManyRequiredSigs
|
||||
|
||||
// ErrTooMuchNullData is returned from NullDataScript when the length of
|
||||
// the provided data exceeds MaxDataCarrierSize.
|
||||
ErrTooMuchNullData
|
||||
|
||||
// ------------------------------------------
|
||||
// Failures related to final execution state.
|
||||
// ------------------------------------------
|
||||
@ -239,7 +235,6 @@ var errorCodeStrings = map[ErrorCode]string{
|
||||
ErrUnsupportedAddress: "ErrUnsupportedAddress",
|
||||
ErrNotMultisigScript: "ErrNotMultisigScript",
|
||||
ErrTooManyRequiredSigs: "ErrTooManyRequiredSigs",
|
||||
ErrTooMuchNullData: "ErrTooMuchNullData",
|
||||
ErrEarlyReturn: "ErrEarlyReturn",
|
||||
ErrEmptyStack: "ErrEmptyStack",
|
||||
ErrEvalFalse: "ErrEvalFalse",
|
||||
|
@ -21,7 +21,6 @@ func TestErrorCodeStringer(t *testing.T) {
|
||||
{ErrInvalidIndex, "ErrInvalidIndex"},
|
||||
{ErrUnsupportedAddress, "ErrUnsupportedAddress"},
|
||||
{ErrTooManyRequiredSigs, "ErrTooManyRequiredSigs"},
|
||||
{ErrTooMuchNullData, "ErrTooMuchNullData"},
|
||||
{ErrNotMultisigScript, "ErrNotMultisigScript"},
|
||||
{ErrEarlyReturn, "ErrEarlyReturn"},
|
||||
{ErrEmptyStack, "ErrEmptyStack"},
|
||||
|
@ -146,9 +146,6 @@ func sign(chainParams *dagconfig.Params, tx *wire.MsgTx, idx int,
|
||||
signedScript, _ := signMultiSig(tx, idx, script, hashType,
|
||||
addresses, nrequired, kdb)
|
||||
return signedScript, class, addresses, nrequired, nil
|
||||
case NullDataTy:
|
||||
return nil, class, nil, 0,
|
||||
errors.New("can't sign NULLDATA transactions")
|
||||
default:
|
||||
return nil, class, nil, 0,
|
||||
errors.New("can't sign unknown transactions")
|
||||
|
@ -12,10 +12,6 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
// MaxDataCarrierSize is the maximum number of bytes allowed in pushed
|
||||
// data to be considered a nulldata transaction
|
||||
MaxDataCarrierSize = 80
|
||||
|
||||
// StandardVerifyFlags are the script flags which are used when
|
||||
// executing transaction scripts to enforce additional checks which
|
||||
// are required for the script to be considered standard. These checks
|
||||
@ -39,7 +35,6 @@ const (
|
||||
PubKeyHashTy // Pay pubkey hash.
|
||||
ScriptHashTy // Pay to script hash.
|
||||
MultiSigTy // Multi signature.
|
||||
NullDataTy // Empty data-only (provably prunable).
|
||||
)
|
||||
|
||||
// scriptClassToName houses the human-readable strings which describe each
|
||||
@ -50,7 +45,6 @@ var scriptClassToName = []string{
|
||||
PubKeyHashTy: "pubkeyhash",
|
||||
ScriptHashTy: "scripthash",
|
||||
MultiSigTy: "multisig",
|
||||
NullDataTy: "nulldata",
|
||||
}
|
||||
|
||||
// String implements the Stringer interface by returning the name of
|
||||
@ -118,24 +112,6 @@ func isMultiSig(pops []parsedOpcode) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// isNullData returns true if the passed script is a null data transaction,
|
||||
// false otherwise.
|
||||
func isNullData(pops []parsedOpcode) bool {
|
||||
// A nulldata transaction is either a single OP_RETURN or an
|
||||
// OP_RETURN SMALLDATA (where SMALLDATA is a data push up to
|
||||
// MaxDataCarrierSize bytes).
|
||||
l := len(pops)
|
||||
if l == 1 && pops[0].opcode.value == OpReturn {
|
||||
return true
|
||||
}
|
||||
|
||||
return l == 2 &&
|
||||
pops[0].opcode.value == OpReturn &&
|
||||
(isSmallInt(pops[1].opcode) || pops[1].opcode.value <=
|
||||
OpPushData4) &&
|
||||
len(pops[1].data) <= MaxDataCarrierSize
|
||||
}
|
||||
|
||||
// scriptType returns the type of the script being inspected from the known
|
||||
// standard types.
|
||||
func typeOfScript(pops []parsedOpcode) ScriptClass {
|
||||
@ -147,8 +123,6 @@ func typeOfScript(pops []parsedOpcode) ScriptClass {
|
||||
return ScriptHashTy
|
||||
} else if isMultiSig(pops) {
|
||||
return MultiSigTy
|
||||
} else if isNullData(pops) {
|
||||
return NullDataTy
|
||||
}
|
||||
return NonStandardTy
|
||||
}
|
||||
@ -191,8 +165,6 @@ func expectedInputs(pops []parsedOpcode, class ScriptClass) int {
|
||||
// for the extra push that is required to compensate.
|
||||
return asSmallInt(pops[0].opcode) + 1
|
||||
|
||||
case NullDataTy:
|
||||
fallthrough
|
||||
default:
|
||||
return -1
|
||||
}
|
||||
@ -376,19 +348,6 @@ func PayToScriptHashSignatureScript(redeemScript []byte, signature []byte) ([]by
|
||||
return signatureScript, nil
|
||||
}
|
||||
|
||||
// NullDataScript creates a provably-prunable script containing OP_RETURN
|
||||
// followed by the passed data. An Error with the error code ErrTooMuchNullData
|
||||
// will be returned if the length of the passed data exceeds MaxDataCarrierSize.
|
||||
func NullDataScript(data []byte) ([]byte, error) {
|
||||
if len(data) > MaxDataCarrierSize {
|
||||
str := fmt.Sprintf("data size %d is larger than max "+
|
||||
"allowed size %d", len(data), MaxDataCarrierSize)
|
||||
return nil, scriptError(ErrTooMuchNullData, str)
|
||||
}
|
||||
|
||||
return NewScriptBuilder().AddOp(OpReturn).AddData(data).Script()
|
||||
}
|
||||
|
||||
// MultiSigScript returns a valid script for a multisignature redemption where
|
||||
// nrequired of the keys in pubkeys are required to have signed the transaction
|
||||
// for success. An Error with the error code ErrTooManyRequiredSigs will be
|
||||
@ -500,10 +459,6 @@ func ExtractPkScriptAddrs(pkScript []byte, chainParams *dagconfig.Params) (Scrip
|
||||
}
|
||||
}
|
||||
|
||||
case NullDataTy:
|
||||
// Null data transactions have no addresses or required
|
||||
// signatures.
|
||||
|
||||
case NonStandardTy:
|
||||
// Don't attempt to extract addresses or required signatures for
|
||||
// nonstandard transactions.
|
||||
|
@ -817,75 +817,9 @@ var scriptClassTests = []struct {
|
||||
},
|
||||
|
||||
{
|
||||
// Nulldata with no data at all.
|
||||
name: "nulldata no data",
|
||||
script: "RETURN",
|
||||
class: NullDataTy,
|
||||
},
|
||||
{
|
||||
// Nulldata with single zero push.
|
||||
name: "nulldata zero",
|
||||
// Nulldata. It is standard in BTC but not in DAGCoin
|
||||
name: "nulldata",
|
||||
script: "RETURN 0",
|
||||
class: NullDataTy,
|
||||
},
|
||||
{
|
||||
// Nulldata with small integer push.
|
||||
name: "nulldata small int",
|
||||
script: "RETURN 1",
|
||||
class: NullDataTy,
|
||||
},
|
||||
{
|
||||
// Nulldata with max small integer push.
|
||||
name: "nulldata max small int",
|
||||
script: "RETURN 16",
|
||||
class: NullDataTy,
|
||||
},
|
||||
{
|
||||
// Nulldata with small data push.
|
||||
name: "nulldata small data",
|
||||
script: "RETURN DATA_8 0x046708afdb0fe554",
|
||||
class: NullDataTy,
|
||||
},
|
||||
{
|
||||
// Canonical nulldata with 60-byte data push.
|
||||
name: "canonical nulldata 60-byte push",
|
||||
script: "RETURN 0x3c 0x046708afdb0fe5548271967f1a67130b7105cd" +
|
||||
"6a828e03909a67962e0ea1f61deb649f6bc3f4cef3046708afdb" +
|
||||
"0fe5548271967f1a67130b7105cd6a",
|
||||
class: NullDataTy,
|
||||
},
|
||||
{
|
||||
// Non-canonical nulldata with 60-byte data push.
|
||||
name: "non-canonical nulldata 60-byte push",
|
||||
script: "RETURN PUSHDATA1 0x3c 0x046708afdb0fe5548271967f1a67" +
|
||||
"130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3" +
|
||||
"046708afdb0fe5548271967f1a67130b7105cd6a",
|
||||
class: NullDataTy,
|
||||
},
|
||||
{
|
||||
// Nulldata with max allowed data to be considered standard.
|
||||
name: "nulldata max standard push",
|
||||
script: "RETURN PUSHDATA1 0x50 0x046708afdb0fe5548271967f1a67" +
|
||||
"130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3" +
|
||||
"046708afdb0fe5548271967f1a67130b7105cd6a828e03909a67" +
|
||||
"962e0ea1f61deb649f6bc3f4cef3",
|
||||
class: NullDataTy,
|
||||
},
|
||||
{
|
||||
// Nulldata with more than max allowed data to be considered
|
||||
// standard (so therefore nonstandard)
|
||||
name: "nulldata exceed max standard push",
|
||||
script: "RETURN PUSHDATA1 0x51 0x046708afdb0fe5548271967f1a67" +
|
||||
"130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3" +
|
||||
"046708afdb0fe5548271967f1a67130b7105cd6a828e03909a67" +
|
||||
"962e0ea1f61deb649f6bc3f4cef308",
|
||||
class: NonStandardTy,
|
||||
},
|
||||
{
|
||||
// Almost nulldata, but add an additional opcode after the data
|
||||
// to make it nonstandard.
|
||||
name: "almost nulldata",
|
||||
script: "RETURN 4 TRUE",
|
||||
class: NonStandardTy,
|
||||
},
|
||||
|
||||
@ -996,11 +930,6 @@ func TestStringifyClass(t *testing.T) {
|
||||
class: MultiSigTy,
|
||||
stringed: "multisig",
|
||||
},
|
||||
{
|
||||
name: "nulldataty",
|
||||
class: NullDataTy,
|
||||
stringed: "nulldata",
|
||||
},
|
||||
{
|
||||
name: "broken",
|
||||
class: ScriptClass(255),
|
||||
@ -1016,89 +945,3 @@ func TestStringifyClass(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestNullDataScript tests whether NullDataScript returns a valid script.
|
||||
func TestNullDataScript(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
data []byte
|
||||
expected []byte
|
||||
err error
|
||||
class ScriptClass
|
||||
}{
|
||||
{
|
||||
name: "small int",
|
||||
data: hexToBytes("01"),
|
||||
expected: mustParseShortForm("RETURN 1"),
|
||||
err: nil,
|
||||
class: NullDataTy,
|
||||
},
|
||||
{
|
||||
name: "max small int",
|
||||
data: hexToBytes("10"),
|
||||
expected: mustParseShortForm("RETURN 16"),
|
||||
err: nil,
|
||||
class: NullDataTy,
|
||||
},
|
||||
{
|
||||
name: "data of size before OP_PUSHDATA1 is needed",
|
||||
data: hexToBytes("0102030405060708090a0b0c0d0e0f10111" +
|
||||
"2131415161718"),
|
||||
expected: mustParseShortForm("RETURN 0x18 0x01020304" +
|
||||
"05060708090a0b0c0d0e0f101112131415161718"),
|
||||
err: nil,
|
||||
class: NullDataTy,
|
||||
},
|
||||
{
|
||||
name: "just right",
|
||||
data: hexToBytes("000102030405060708090a0b0c0d0e0f101" +
|
||||
"112131415161718191a1b1c1d1e1f202122232425262" +
|
||||
"728292a2b2c2d2e2f303132333435363738393a3b3c3" +
|
||||
"d3e3f404142434445464748494a4b4c4d4e4f"),
|
||||
expected: mustParseShortForm("RETURN PUSHDATA1 0x50 " +
|
||||
"0x000102030405060708090a0b0c0d0e0f101112131" +
|
||||
"415161718191a1b1c1d1e1f20212223242526272829" +
|
||||
"2a2b2c2d2e2f303132333435363738393a3b3c3d3e3" +
|
||||
"f404142434445464748494a4b4c4d4e4f"),
|
||||
err: nil,
|
||||
class: NullDataTy,
|
||||
},
|
||||
{
|
||||
name: "too big",
|
||||
data: hexToBytes("000102030405060708090a0b0c0d0e0f101" +
|
||||
"112131415161718191a1b1c1d1e1f202122232425262" +
|
||||
"728292a2b2c2d2e2f303132333435363738393a3b3c3" +
|
||||
"d3e3f404142434445464748494a4b4c4d4e4f50"),
|
||||
expected: nil,
|
||||
err: scriptError(ErrTooMuchNullData, ""),
|
||||
class: NonStandardTy,
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
script, err := NullDataScript(test.data)
|
||||
if e := checkScriptError(err, test.err); e != nil {
|
||||
t.Errorf("NullDataScript: #%d (%s): %v", i, test.name,
|
||||
e)
|
||||
continue
|
||||
|
||||
}
|
||||
|
||||
// Check that the expected result was returned.
|
||||
if !bytes.Equal(script, test.expected) {
|
||||
t.Errorf("NullDataScript: #%d (%s) wrong result\n"+
|
||||
"got: %x\nwant: %x", i, test.name, script,
|
||||
test.expected)
|
||||
continue
|
||||
}
|
||||
|
||||
// Check that the script has the correct type.
|
||||
scriptType := GetScriptClass(script)
|
||||
if scriptType != test.class {
|
||||
t.Errorf("GetScriptClass: #%d (%s) wrong result -- "+
|
||||
"got: %v, want: %v", i, test.name, scriptType,
|
||||
test.class)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user