policy_test.go raw

   1  package mempool
   2  
   3  import (
   4  	"bytes"
   5  	"github.com/p9c/p9/pkg/amt"
   6  	"github.com/p9c/p9/pkg/btcaddr"
   7  	"github.com/p9c/p9/pkg/chaincfg"
   8  	"github.com/p9c/p9/pkg/constant"
   9  	"testing"
  10  	"time"
  11  	
  12  	"github.com/p9c/p9/pkg/chainhash"
  13  	ec "github.com/p9c/p9/pkg/ecc"
  14  	"github.com/p9c/p9/pkg/txscript"
  15  	"github.com/p9c/p9/pkg/util"
  16  	"github.com/p9c/p9/pkg/wire"
  17  )
  18  
  19  // TestCalcMinRequiredTxRelayFee tests the calcMinRequiredTxRelayFee API.
  20  func TestCalcMinRequiredTxRelayFee(t *testing.T) {
  21  	
  22  	tests := []struct {
  23  		name     string     // test description.
  24  		size     int64      // Transaction size in bytes.
  25  		relayFee amt.Amount // minimum relay transaction fee.
  26  		want     int64      // Expected fee.
  27  	}{
  28  		{
  29  			// Ensure combination of size and fee that are less than 1000 produce a non-zero fee.
  30  			"250 bytes with relay fee of 3",
  31  			250,
  32  			3,
  33  			3,
  34  		},
  35  		{
  36  			"100 bytes with default minimum relay fee",
  37  			100,
  38  			constant.DefaultMinRelayTxFee,
  39  			100,
  40  		},
  41  		{
  42  			"max standard tx size with default minimum relay fee",
  43  			maxStandardTxWeight / 4,
  44  			constant.DefaultMinRelayTxFee,
  45  			100000,
  46  		},
  47  		{
  48  			"max standard tx size with max satoshi relay fee",
  49  			maxStandardTxWeight / 4,
  50  			amt.MaxSatoshi,
  51  			amt.MaxSatoshi.Int64(),
  52  		},
  53  		{
  54  			"1500 bytes with 5000 relay fee",
  55  			1500,
  56  			5000,
  57  			7500,
  58  		},
  59  		{
  60  			"1500 bytes with 3000 relay fee",
  61  			1500,
  62  			3000,
  63  			4500,
  64  		},
  65  		{
  66  			"782 bytes with 5000 relay fee",
  67  			782,
  68  			5000,
  69  			3910,
  70  		},
  71  		{
  72  			"782 bytes with 3000 relay fee",
  73  			782,
  74  			3000,
  75  			2346,
  76  		},
  77  		{
  78  			"782 bytes with 2550 relay fee",
  79  			782,
  80  			2550,
  81  			1994,
  82  		},
  83  	}
  84  	for _, test := range tests {
  85  		got := calcMinRequiredTxRelayFee(test.size, test.relayFee)
  86  		if got != test.want {
  87  			t.Errorf(
  88  				"TestCalcMinRequiredTxRelayFee test '%s' "+
  89  					"failed: got %v want %v", test.name, got,
  90  				test.want,
  91  			)
  92  			continue
  93  		}
  94  	}
  95  }
  96  
  97  // TestCheckPkScriptStandard tests the checkPkScriptStandard API.
  98  func TestCheckPkScriptStandard(t *testing.T) {
  99  	var pubKeys [][]byte
 100  	for i := 0; i < 4; i++ {
 101  		pk, e := ec.NewPrivateKey(ec.S256())
 102  		if e != nil {
 103  			E.Ln(e)
 104  			t.Fatalf("TestCheckPkScriptStandard NewPrivateKey failed: %v", e)
 105  			return
 106  		}
 107  		pubKeys = append(pubKeys, pk.PubKey().SerializeCompressed())
 108  	}
 109  	tests := []struct {
 110  		name       string // test description.
 111  		script     *txscript.ScriptBuilder
 112  		isStandard bool
 113  	}{
 114  		{
 115  			"key1 and key2",
 116  			txscript.NewScriptBuilder().AddOp(txscript.OP_2).
 117  				AddData(pubKeys[0]).AddData(pubKeys[1]).
 118  				AddOp(txscript.OP_2).AddOp(txscript.OP_CHECKMULTISIG),
 119  			true,
 120  		},
 121  		{
 122  			"key1 or key2",
 123  			txscript.NewScriptBuilder().AddOp(txscript.OP_1).
 124  				AddData(pubKeys[0]).AddData(pubKeys[1]).
 125  				AddOp(txscript.OP_2).AddOp(txscript.OP_CHECKMULTISIG),
 126  			true,
 127  		},
 128  		{
 129  			"escrow",
 130  			txscript.NewScriptBuilder().AddOp(txscript.OP_2).
 131  				AddData(pubKeys[0]).AddData(pubKeys[1]).
 132  				AddData(pubKeys[2]).
 133  				AddOp(txscript.OP_3).AddOp(txscript.OP_CHECKMULTISIG),
 134  			true,
 135  		},
 136  		{
 137  			"one of four",
 138  			txscript.NewScriptBuilder().AddOp(txscript.OP_1).
 139  				AddData(pubKeys[0]).AddData(pubKeys[1]).
 140  				AddData(pubKeys[2]).AddData(pubKeys[3]).
 141  				AddOp(txscript.OP_4).AddOp(txscript.OP_CHECKMULTISIG),
 142  			false,
 143  		},
 144  		{
 145  			"malformed1",
 146  			txscript.NewScriptBuilder().AddOp(txscript.OP_3).
 147  				AddData(pubKeys[0]).AddData(pubKeys[1]).
 148  				AddOp(txscript.OP_2).AddOp(txscript.OP_CHECKMULTISIG),
 149  			false,
 150  		},
 151  		{
 152  			"malformed2",
 153  			txscript.NewScriptBuilder().AddOp(txscript.OP_2).
 154  				AddData(pubKeys[0]).AddData(pubKeys[1]).
 155  				AddOp(txscript.OP_3).AddOp(txscript.OP_CHECKMULTISIG),
 156  			false,
 157  		},
 158  		{
 159  			"malformed3",
 160  			txscript.NewScriptBuilder().AddOp(txscript.OP_0).
 161  				AddData(pubKeys[0]).AddData(pubKeys[1]).
 162  				AddOp(txscript.OP_2).AddOp(txscript.OP_CHECKMULTISIG),
 163  			false,
 164  		},
 165  		{
 166  			"malformed4",
 167  			txscript.NewScriptBuilder().AddOp(txscript.OP_1).
 168  				AddData(pubKeys[0]).AddData(pubKeys[1]).
 169  				AddOp(txscript.OP_0).AddOp(txscript.OP_CHECKMULTISIG),
 170  			false,
 171  		},
 172  		{
 173  			"malformed5",
 174  			txscript.NewScriptBuilder().AddOp(txscript.OP_1).
 175  				AddData(pubKeys[0]).AddData(pubKeys[1]).
 176  				AddOp(txscript.OP_CHECKMULTISIG),
 177  			false,
 178  		},
 179  		{
 180  			"malformed6",
 181  			txscript.NewScriptBuilder().AddOp(txscript.OP_1).
 182  				AddData(pubKeys[0]).AddData(pubKeys[1]),
 183  			false,
 184  		},
 185  	}
 186  	
 187  	for _, test := range tests {
 188  		script, err := test.script.Script()
 189  		
 190  		if err != nil {
 191  			
 192  			t.Fatalf(
 193  				"TestCheckPkScriptStandard test '%s' "+
 194  					"failed: %v", test.name, err,
 195  			)
 196  			// continue
 197  		}
 198  		scriptClass := txscript.GetScriptClass(script)
 199  		got := checkPkScriptStandard(script, scriptClass)
 200  		
 201  		if (test.isStandard && got != nil) ||
 202  			(!test.isStandard && got == nil) {
 203  			
 204  			t.Fatalf(
 205  				"TestCheckPkScriptStandard test '%s' failed",
 206  				test.name,
 207  			)
 208  			return
 209  		}
 210  	}
 211  }
 212  
 213  // TestDust tests the isDust API.
 214  func TestDust(t *testing.T) {
 215  	
 216  	pkScript := []byte{
 217  		0x76, 0xa9, 0x21, 0x03, 0x2f, 0x7e, 0x43,
 218  		0x0a, 0xa4, 0xc9, 0xd1, 0x59, 0x43, 0x7e, 0x84, 0xb9,
 219  		0x75, 0xdc, 0x76, 0xd9, 0x00, 0x3b, 0xf0, 0x92, 0x2c,
 220  		0xf3, 0xaa, 0x45, 0x28, 0x46, 0x4b, 0xab, 0x78, 0x0d,
 221  		0xba, 0x5e, 0x88, 0xac,
 222  	}
 223  	tests := []struct {
 224  		name     string // test description
 225  		txOut    wire.TxOut
 226  		relayFee amt.Amount // minimum relay transaction fee.
 227  		isDust   bool
 228  	}{
 229  		{
 230  			// Any value is allowed with a zero relay fee.
 231  			"zero value with zero relay fee",
 232  			wire.TxOut{Value: 0, PkScript: pkScript},
 233  			0,
 234  			false,
 235  		},
 236  		{
 237  			// Zero value is dust with any relay fee"
 238  			"zero value with very small tx fee",
 239  			wire.TxOut{Value: 0, PkScript: pkScript},
 240  			1,
 241  			true,
 242  		},
 243  		{
 244  			"38 byte public key script with value 584",
 245  			wire.TxOut{Value: 584, PkScript: pkScript},
 246  			1000,
 247  			true,
 248  		},
 249  		{
 250  			"38 byte public key script with value 585",
 251  			wire.TxOut{Value: 585, PkScript: pkScript},
 252  			1000,
 253  			false,
 254  		},
 255  		{
 256  			// Maximum allowed value is never dust.
 257  			"max satoshi amount is never dust",
 258  			wire.TxOut{Value: amt.MaxSatoshi.Int64(), PkScript: pkScript},
 259  			amt.MaxSatoshi,
 260  			false,
 261  		},
 262  		{
 263  			// Maximum int64 value causes overflow.
 264  			"maximum int64 value",
 265  			wire.TxOut{Value: 1<<63 - 1, PkScript: pkScript},
 266  			1<<63 - 1,
 267  			true,
 268  		},
 269  		{
 270  			// Unspendable pkScript due to an invalid public key script.
 271  			"unspendable pkScript",
 272  			wire.TxOut{Value: 5000, PkScript: []byte{0x01}},
 273  			0, // no relay fee
 274  			true,
 275  		},
 276  	}
 277  	
 278  	for _, test := range tests {
 279  		res := isDust(&test.txOut, test.relayFee)
 280  		
 281  		if res != test.isDust {
 282  			
 283  			t.Fatalf(
 284  				"Dust test '%s' failed: want %v got %v",
 285  				test.name, test.isDust, res,
 286  			)
 287  			// continue
 288  		}
 289  	}
 290  }
 291  
 292  // TestCheckTransactionStandard tests the checkTransactionStandard API.
 293  func TestCheckTransactionStandard(t *testing.T) {
 294  	
 295  	// Create some dummy, but otherwise standard, data for transactions.
 296  	prevOutHash, err := chainhash.NewHashFromStr("01")
 297  	
 298  	if err != nil {
 299  		t.Fatalf("NewShaHashFromStr: unexpected error: %v", err)
 300  	}
 301  	dummyPrevOut := wire.OutPoint{Hash: *prevOutHash, Index: 1}
 302  	dummySigScript := bytes.Repeat([]byte{0x00}, 65)
 303  	dummyTxIn := wire.TxIn{
 304  		PreviousOutPoint: dummyPrevOut,
 305  		SignatureScript:  dummySigScript,
 306  		Sequence:         wire.MaxTxInSequenceNum,
 307  	}
 308  	addrHash := [20]byte{0x01}
 309  	addr, err := btcaddr.NewPubKeyHash(
 310  		addrHash[:],
 311  		&chaincfg.TestNet3Params,
 312  	)
 313  	
 314  	if err != nil {
 315  		t.Fatalf("NewPubKeyHash: unexpected error: %v", err)
 316  	}
 317  	dummyPkScript, err := txscript.PayToAddrScript(addr)
 318  	
 319  	if err != nil {
 320  		t.Fatalf("PayToAddrScript: unexpected error: %v", err)
 321  	}
 322  	dummyTxOut := wire.TxOut{
 323  		Value:    100000000, // 1 DUO
 324  		PkScript: dummyPkScript,
 325  	}
 326  	tests := []struct {
 327  		name       string
 328  		tx         wire.MsgTx
 329  		height     int32
 330  		isStandard bool
 331  		code       wire.RejectCode
 332  	}{
 333  		{
 334  			name: "Typical pay-to-pubkey-hash transaction",
 335  			tx: wire.MsgTx{
 336  				Version:  1,
 337  				TxIn:     []*wire.TxIn{&dummyTxIn},
 338  				TxOut:    []*wire.TxOut{&dummyTxOut},
 339  				LockTime: 0,
 340  			},
 341  			height:     300000,
 342  			isStandard: true,
 343  		},
 344  		{
 345  			name: "Transaction version too high",
 346  			tx: wire.MsgTx{
 347  				Version:  wire.TxVersion + 1,
 348  				TxIn:     []*wire.TxIn{&dummyTxIn},
 349  				TxOut:    []*wire.TxOut{&dummyTxOut},
 350  				LockTime: 0,
 351  			},
 352  			height:     300000,
 353  			isStandard: false,
 354  			code:       wire.RejectNonstandard,
 355  		},
 356  		{
 357  			name: "Transaction is not finalized",
 358  			tx: wire.MsgTx{
 359  				Version: 1,
 360  				TxIn: []*wire.TxIn{
 361  					{
 362  						PreviousOutPoint: dummyPrevOut,
 363  						SignatureScript:  dummySigScript,
 364  						Sequence:         0,
 365  					},
 366  				},
 367  				TxOut:    []*wire.TxOut{&dummyTxOut},
 368  				LockTime: 300001,
 369  			},
 370  			height:     300000,
 371  			isStandard: false,
 372  			code:       wire.RejectNonstandard,
 373  		},
 374  		{
 375  			name: "Transaction size is too large",
 376  			tx: wire.MsgTx{
 377  				Version: 1,
 378  				TxIn:    []*wire.TxIn{&dummyTxIn},
 379  				TxOut: []*wire.TxOut{
 380  					{
 381  						Value: 0,
 382  						PkScript: bytes.Repeat(
 383  							[]byte{0x00},
 384  							(maxStandardTxWeight/4)+1,
 385  						),
 386  					},
 387  				},
 388  				LockTime: 0,
 389  			},
 390  			height:     300000,
 391  			isStandard: false,
 392  			code:       wire.RejectNonstandard,
 393  		},
 394  		{
 395  			name: "Signature script size is too large",
 396  			tx: wire.MsgTx{
 397  				Version: 1,
 398  				TxIn: []*wire.TxIn{
 399  					{
 400  						PreviousOutPoint: dummyPrevOut,
 401  						SignatureScript: bytes.Repeat(
 402  							[]byte{0x00},
 403  							maxStandardSigScriptSize+1,
 404  						),
 405  						Sequence: wire.MaxTxInSequenceNum,
 406  					},
 407  				},
 408  				TxOut:    []*wire.TxOut{&dummyTxOut},
 409  				LockTime: 0,
 410  			},
 411  			height:     300000,
 412  			isStandard: false,
 413  			code:       wire.RejectNonstandard,
 414  		},
 415  		{
 416  			name: "Signature script that does more than push data",
 417  			tx: wire.MsgTx{
 418  				Version: 1,
 419  				TxIn: []*wire.TxIn{
 420  					{
 421  						PreviousOutPoint: dummyPrevOut,
 422  						SignatureScript: []byte{
 423  							txscript.OP_CHECKSIGVERIFY,
 424  						},
 425  						Sequence: wire.MaxTxInSequenceNum,
 426  					},
 427  				},
 428  				TxOut:    []*wire.TxOut{&dummyTxOut},
 429  				LockTime: 0,
 430  			},
 431  			height:     300000,
 432  			isStandard: false,
 433  			code:       wire.RejectNonstandard,
 434  		},
 435  		{
 436  			name: "Valid but non standard public key script",
 437  			tx: wire.MsgTx{
 438  				Version: 1,
 439  				TxIn:    []*wire.TxIn{&dummyTxIn},
 440  				TxOut: []*wire.TxOut{
 441  					{
 442  						Value:    100000000,
 443  						PkScript: []byte{txscript.OP_TRUE},
 444  					},
 445  				},
 446  				LockTime: 0,
 447  			},
 448  			height:     300000,
 449  			isStandard: false,
 450  			code:       wire.RejectNonstandard,
 451  		},
 452  		{
 453  			name: "More than one nulldata output",
 454  			tx: wire.MsgTx{
 455  				Version: 1,
 456  				TxIn:    []*wire.TxIn{&dummyTxIn},
 457  				TxOut: []*wire.TxOut{
 458  					{
 459  						Value:    0,
 460  						PkScript: []byte{txscript.OP_RETURN},
 461  					}, {
 462  						Value:    0,
 463  						PkScript: []byte{txscript.OP_RETURN},
 464  					},
 465  				},
 466  				LockTime: 0,
 467  			},
 468  			height:     300000,
 469  			isStandard: false,
 470  			code:       wire.RejectNonstandard,
 471  		},
 472  		{
 473  			name: "Dust output",
 474  			tx: wire.MsgTx{
 475  				Version: 1,
 476  				TxIn:    []*wire.TxIn{&dummyTxIn},
 477  				TxOut: []*wire.TxOut{
 478  					{
 479  						Value:    0,
 480  						PkScript: dummyPkScript,
 481  					},
 482  				},
 483  				LockTime: 0,
 484  			},
 485  			height:     300000,
 486  			isStandard: false,
 487  			code:       wire.RejectDust,
 488  		},
 489  		{
 490  			name: "One nulldata output with 0 amount (standard)",
 491  			tx: wire.MsgTx{
 492  				Version: 1,
 493  				TxIn:    []*wire.TxIn{&dummyTxIn},
 494  				TxOut: []*wire.TxOut{
 495  					{
 496  						Value:    0,
 497  						PkScript: []byte{txscript.OP_RETURN},
 498  					},
 499  				},
 500  				LockTime: 0,
 501  			},
 502  			height:     300000,
 503  			isStandard: true,
 504  		},
 505  	}
 506  	pastMedianTime := time.Now()
 507  	
 508  	for _, test := range tests {
 509  		// Ensure standardness is as expected.
 510  		err := checkTransactionStandard(
 511  			util.NewTx(&test.tx),
 512  			test.height, pastMedianTime, constant.DefaultMinRelayTxFee, 1,
 513  		)
 514  		if err == nil && test.isStandard {
 515  			// Test passes since function returned standard for a transaction
 516  			// which is intended to be standard.
 517  			continue
 518  		}
 519  		if err == nil && !test.isStandard {
 520  			t.Errorf(
 521  				"checkTransactionStandard (%s): standard when "+
 522  					"it should not be", test.name,
 523  			)
 524  			continue
 525  		}
 526  		if err != nil && test.isStandard {
 527  			t.Errorf(
 528  				"checkTransactionStandard (%s): nonstandard "+
 529  					"when it should not be: %v", test.name, err,
 530  			)
 531  			continue
 532  		}
 533  		// Ensure error type is a TxRuleError inside of a RuleError.
 534  		rerr, ok := err.(RuleError)
 535  		if !ok {
 536  			t.Errorf(
 537  				"checkTransactionStandard (%s): unexpected "+
 538  					"error type - got %T", test.name, err,
 539  			)
 540  			continue
 541  		}
 542  		txrerr, ok := rerr.Err.(TxRuleError)
 543  		if !ok {
 544  			t.Errorf(
 545  				"checkTransactionStandard (%s): unexpected "+
 546  					"error type - got %T", test.name, rerr.Err,
 547  			)
 548  			continue
 549  		}
 550  		// Ensure the reject code is the expected one.
 551  		if txrerr.RejectCode != test.code {
 552  			t.Errorf(
 553  				"checkTransactionStandard (%s): unexpected "+
 554  					"error code - got %v, want %v", test.name,
 555  				txrerr.RejectCode, test.code,
 556  			)
 557  			continue
 558  		}
 559  	}
 560  }
 561