scriptbuilder_test.go raw

   1  package txscript
   2  
   3  import (
   4  	"bytes"
   5  	"testing"
   6  )
   7  
   8  // TestScriptBuilderAddOp tests that pushing opcodes to a script via the ScriptBuilder API works as expected.
   9  func TestScriptBuilderAddOp(t *testing.T) {
  10  	t.Parallel()
  11  	tests := []struct {
  12  		name     string
  13  		opcodes  []byte
  14  		expected []byte
  15  	}{
  16  		{
  17  			name:     "push OP_0",
  18  			opcodes:  []byte{OP_0},
  19  			expected: []byte{OP_0},
  20  		},
  21  		{
  22  			name:     "push OP_1 OP_2",
  23  			opcodes:  []byte{OP_1, OP_2},
  24  			expected: []byte{OP_1, OP_2},
  25  		},
  26  		{
  27  			name:     "push OP_HASH160 OP_EQUAL",
  28  			opcodes:  []byte{OP_HASH160, OP_EQUAL},
  29  			expected: []byte{OP_HASH160, OP_EQUAL},
  30  		},
  31  	}
  32  	// Run tests and individually add each op via AddOp.
  33  	builder := NewScriptBuilder()
  34  	t.Logf("Running %d tests", len(tests))
  35  	for i, test := range tests {
  36  		builder.Reset()
  37  		for _, opcode := range test.opcodes {
  38  			builder.AddOp(opcode)
  39  		}
  40  		result, e := builder.Script()
  41  		if e != nil {
  42  			t.Errorf("ScriptBuilder.AddOp #%d (%s) unexpected "+
  43  				"error: %v", i, test.name, e,
  44  			)
  45  			continue
  46  		}
  47  		if !bytes.Equal(result, test.expected) {
  48  			t.Errorf("ScriptBuilder.AddOp #%d (%s) wrong result\n"+
  49  				"got: %x\nwant: %x", i, test.name, result,
  50  				test.expected,
  51  			)
  52  			continue
  53  		}
  54  	}
  55  	// Run tests and bulk add ops via AddOps.
  56  	t.Logf("Running %d tests", len(tests))
  57  	for i, test := range tests {
  58  		builder.Reset()
  59  		result, e := builder.AddOps(test.opcodes).Script()
  60  		if e != nil {
  61  			t.Errorf("ScriptBuilder.AddOps #%d (%s) unexpected "+
  62  				"error: %v", i, test.name, e,
  63  			)
  64  			continue
  65  		}
  66  		if !bytes.Equal(result, test.expected) {
  67  			t.Errorf("ScriptBuilder.AddOps #%d (%s) wrong result\n"+
  68  				"got: %x\nwant: %x", i, test.name, result,
  69  				test.expected,
  70  			)
  71  			continue
  72  		}
  73  	}
  74  }
  75  
  76  // TestScriptBuilderAddInt64 tests that pushing signed integers to a script via the ScriptBuilder API works as expected.
  77  func TestScriptBuilderAddInt64(t *testing.T) {
  78  	t.Parallel()
  79  	tests := []struct {
  80  		name     string
  81  		val      int64
  82  		expected []byte
  83  	}{
  84  		{name: "push -1", val: -1, expected: []byte{OP_1NEGATE}},
  85  		{name: "push small int 0", val: 0, expected: []byte{OP_0}},
  86  		{name: "push small int 1", val: 1, expected: []byte{OP_1}},
  87  		{name: "push small int 2", val: 2, expected: []byte{OP_2}},
  88  		{name: "push small int 3", val: 3, expected: []byte{OP_3}},
  89  		{name: "push small int 4", val: 4, expected: []byte{OP_4}},
  90  		{name: "push small int 5", val: 5, expected: []byte{OP_5}},
  91  		{name: "push small int 6", val: 6, expected: []byte{OP_6}},
  92  		{name: "push small int 7", val: 7, expected: []byte{OP_7}},
  93  		{name: "push small int 8", val: 8, expected: []byte{OP_8}},
  94  		{name: "push small int 9", val: 9, expected: []byte{OP_9}},
  95  		{name: "push small int 10", val: 10, expected: []byte{OP_10}},
  96  		{name: "push small int 11", val: 11, expected: []byte{OP_11}},
  97  		{name: "push small int 12", val: 12, expected: []byte{OP_12}},
  98  		{name: "push small int 13", val: 13, expected: []byte{OP_13}},
  99  		{name: "push small int 14", val: 14, expected: []byte{OP_14}},
 100  		{name: "push small int 15", val: 15, expected: []byte{OP_15}},
 101  		{name: "push small int 16", val: 16, expected: []byte{OP_16}},
 102  		{name: "push 17", val: 17, expected: []byte{OP_DATA_1, 0x11}},
 103  		{name: "push 65", val: 65, expected: []byte{OP_DATA_1, 0x41}},
 104  		{name: "push 127", val: 127, expected: []byte{OP_DATA_1, 0x7f}},
 105  		{name: "push 128", val: 128, expected: []byte{OP_DATA_2, 0x80, 0}},
 106  		{name: "push 255", val: 255, expected: []byte{OP_DATA_2, 0xff, 0}},
 107  		{name: "push 256", val: 256, expected: []byte{OP_DATA_2, 0, 0x01}},
 108  		{name: "push 32767", val: 32767, expected: []byte{OP_DATA_2, 0xff, 0x7f}},
 109  		{name: "push 32768", val: 32768, expected: []byte{OP_DATA_3, 0, 0x80, 0}},
 110  		{name: "push -2", val: -2, expected: []byte{OP_DATA_1, 0x82}},
 111  		{name: "push -3", val: -3, expected: []byte{OP_DATA_1, 0x83}},
 112  		{name: "push -4", val: -4, expected: []byte{OP_DATA_1, 0x84}},
 113  		{name: "push -5", val: -5, expected: []byte{OP_DATA_1, 0x85}},
 114  		{name: "push -17", val: -17, expected: []byte{OP_DATA_1, 0x91}},
 115  		{name: "push -65", val: -65, expected: []byte{OP_DATA_1, 0xc1}},
 116  		{name: "push -127", val: -127, expected: []byte{OP_DATA_1, 0xff}},
 117  		{name: "push -128", val: -128, expected: []byte{OP_DATA_2, 0x80, 0x80}},
 118  		{name: "push -255", val: -255, expected: []byte{OP_DATA_2, 0xff, 0x80}},
 119  		{name: "push -256", val: -256, expected: []byte{OP_DATA_2, 0x00, 0x81}},
 120  		{name: "push -32767", val: -32767, expected: []byte{OP_DATA_2, 0xff, 0xff}},
 121  		{name: "push -32768", val: -32768, expected: []byte{OP_DATA_3, 0x00, 0x80, 0x80}},
 122  	}
 123  	builder := NewScriptBuilder()
 124  	t.Logf("Running %d tests", len(tests))
 125  	for i, test := range tests {
 126  		builder.Reset().AddInt64(test.val)
 127  		result, e := builder.Script()
 128  		if e != nil {
 129  			t.Errorf("ScriptBuilder.AddInt64 #%d (%s) unexpected "+
 130  				"error: %v", i, test.name, e,
 131  			)
 132  			continue
 133  		}
 134  		if !bytes.Equal(result, test.expected) {
 135  			t.Errorf("ScriptBuilder.AddInt64 #%d (%s) wrong result\n"+
 136  				"got: %x\nwant: %x", i, test.name, result,
 137  				test.expected,
 138  			)
 139  			continue
 140  		}
 141  	}
 142  }
 143  
 144  // TestScriptBuilderAddData tests that pushing data to a script via the ScriptBuilder API works as expected and conforms
 145  // to BIP0062.
 146  func TestScriptBuilderAddData(t *testing.T) {
 147  	t.Parallel()
 148  	tests := []struct {
 149  		name     string
 150  		data     []byte
 151  		expected []byte
 152  		useFull  bool // use AddFullData instead of AddData.
 153  	}{
 154  		// BIP0062: Pushing an empty byte sequence must use OP_0.
 155  		{name: "push empty byte sequence", data: nil, expected: []byte{OP_0}},
 156  		{name: "push 1 byte 0x00", data: []byte{0x00}, expected: []byte{OP_0}},
 157  		// BIP0062: Pushing a 1-byte sequence of byte 0x01 through 0x10 must use OP_n.
 158  		{name: "push 1 byte 0x01", data: []byte{0x01}, expected: []byte{OP_1}},
 159  		{name: "push 1 byte 0x02", data: []byte{0x02}, expected: []byte{OP_2}},
 160  		{name: "push 1 byte 0x03", data: []byte{0x03}, expected: []byte{OP_3}},
 161  		{name: "push 1 byte 0x04", data: []byte{0x04}, expected: []byte{OP_4}},
 162  		{name: "push 1 byte 0x05", data: []byte{0x05}, expected: []byte{OP_5}},
 163  		{name: "push 1 byte 0x06", data: []byte{0x06}, expected: []byte{OP_6}},
 164  		{name: "push 1 byte 0x07", data: []byte{0x07}, expected: []byte{OP_7}},
 165  		{name: "push 1 byte 0x08", data: []byte{0x08}, expected: []byte{OP_8}},
 166  		{name: "push 1 byte 0x09", data: []byte{0x09}, expected: []byte{OP_9}},
 167  		{name: "push 1 byte 0x0a", data: []byte{0x0a}, expected: []byte{OP_10}},
 168  		{name: "push 1 byte 0x0b", data: []byte{0x0b}, expected: []byte{OP_11}},
 169  		{name: "push 1 byte 0x0c", data: []byte{0x0c}, expected: []byte{OP_12}},
 170  		{name: "push 1 byte 0x0d", data: []byte{0x0d}, expected: []byte{OP_13}},
 171  		{name: "push 1 byte 0x0e", data: []byte{0x0e}, expected: []byte{OP_14}},
 172  		{name: "push 1 byte 0x0f", data: []byte{0x0f}, expected: []byte{OP_15}},
 173  		{name: "push 1 byte 0x10", data: []byte{0x10}, expected: []byte{OP_16}},
 174  		// BIP0062: Pushing the byte 0x81 must use OP_1NEGATE.
 175  		{name: "push 1 byte 0x81", data: []byte{0x81}, expected: []byte{OP_1NEGATE}},
 176  		// BIP0062: Pushing any other byte sequence up to 75 bytes must use the normal data push (opcode byte n, with n
 177  		// the number of bytes, followed n bytes of data being pushed).
 178  		{name: "push 1 byte 0x11", data: []byte{0x11}, expected: []byte{OP_DATA_1, 0x11}},
 179  		{name: "push 1 byte 0x80", data: []byte{0x80}, expected: []byte{OP_DATA_1, 0x80}},
 180  		{name: "push 1 byte 0x82", data: []byte{0x82}, expected: []byte{OP_DATA_1, 0x82}},
 181  		{name: "push 1 byte 0xff", data: []byte{0xff}, expected: []byte{OP_DATA_1, 0xff}},
 182  		{
 183  			name:     "push data len 17",
 184  			data:     bytes.Repeat([]byte{0x49}, 17),
 185  			expected: append([]byte{OP_DATA_17}, bytes.Repeat([]byte{0x49}, 17)...),
 186  		},
 187  		{
 188  			name:     "push data len 75",
 189  			data:     bytes.Repeat([]byte{0x49}, 75),
 190  			expected: append([]byte{OP_DATA_75}, bytes.Repeat([]byte{0x49}, 75)...),
 191  		},
 192  		// BIP0062: Pushing 76 to 255 bytes must use OP_PUSHDATA1.
 193  		{
 194  			name:     "push data len 76",
 195  			data:     bytes.Repeat([]byte{0x49}, 76),
 196  			expected: append([]byte{OP_PUSHDATA1, 76}, bytes.Repeat([]byte{0x49}, 76)...),
 197  		},
 198  		{
 199  			name:     "push data len 255",
 200  			data:     bytes.Repeat([]byte{0x49}, 255),
 201  			expected: append([]byte{OP_PUSHDATA1, 255}, bytes.Repeat([]byte{0x49}, 255)...),
 202  		},
 203  		// BIP0062: Pushing 256 to 520 bytes must use OP_PUSHDATA2.
 204  		{
 205  			name:     "push data len 256",
 206  			data:     bytes.Repeat([]byte{0x49}, 256),
 207  			expected: append([]byte{OP_PUSHDATA2, 0, 1}, bytes.Repeat([]byte{0x49}, 256)...),
 208  		},
 209  		{
 210  			name:     "push data len 520",
 211  			data:     bytes.Repeat([]byte{0x49}, 520),
 212  			expected: append([]byte{OP_PUSHDATA2, 0x08, 0x02}, bytes.Repeat([]byte{0x49}, 520)...),
 213  		},
 214  		// BIP0062: OP_PUSHDATA4 can never be used, as pushes over 520 bytes are not allowed, and those below can be
 215  		// done using other operators.
 216  		{
 217  			name:     "push data len 521",
 218  			data:     bytes.Repeat([]byte{0x49}, 521),
 219  			expected: nil,
 220  		},
 221  		{
 222  			name:     "push data len 32767 (canonical)",
 223  			data:     bytes.Repeat([]byte{0x49}, 32767),
 224  			expected: nil,
 225  		},
 226  		{
 227  			name:     "push data len 65536 (canonical)",
 228  			data:     bytes.Repeat([]byte{0x49}, 65536),
 229  			expected: nil,
 230  		},
 231  		// Additional tests for the PushFullData function that intentionally allows data pushes to exceed the limit for
 232  		// regression testing purposes. 3-byte data push via OP_PUSHDATA_2.
 233  		{
 234  			name:     "push data len 32767 (non-canonical)",
 235  			data:     bytes.Repeat([]byte{0x49}, 32767),
 236  			expected: append([]byte{OP_PUSHDATA2, 255, 127}, bytes.Repeat([]byte{0x49}, 32767)...),
 237  			useFull:  true,
 238  		},
 239  		// 5-byte data push via OP_PUSHDATA_4.
 240  		{
 241  			name:     "push data len 65536 (non-canonical)",
 242  			data:     bytes.Repeat([]byte{0x49}, 65536),
 243  			expected: append([]byte{OP_PUSHDATA4, 0, 0, 1, 0}, bytes.Repeat([]byte{0x49}, 65536)...),
 244  			useFull:  true,
 245  		},
 246  	}
 247  	builder := NewScriptBuilder()
 248  	t.Logf("Running %d tests", len(tests))
 249  	for i, test := range tests {
 250  		if !test.useFull {
 251  			builder.Reset().AddData(test.data)
 252  		} else {
 253  			builder.Reset().AddFullData(test.data)
 254  		}
 255  		result, _ := builder.Script()
 256  		if !bytes.Equal(result, test.expected) {
 257  			t.Errorf("ScriptBuilder.AddData #%d (%s) wrong result\n"+
 258  				"got: %x\nwant: %x", i, test.name, result,
 259  				test.expected,
 260  			)
 261  			continue
 262  		}
 263  	}
 264  }
 265  
 266  // TestExceedMaxScriptSize ensures that all of the functions that can be used to add data to a script don't allow the
 267  // script to exceed the max allowed size.
 268  func TestExceedMaxScriptSize(t *testing.T) {
 269  	t.Parallel()
 270  	// Start off by constructing a max size script.
 271  	builder := NewScriptBuilder()
 272  	builder.Reset().AddFullData(make([]byte, MaxScriptSize-3))
 273  	origScript, e := builder.Script()
 274  	if e != nil {
 275  		t.Fatalf("Unexpected error for max size script: %v", e)
 276  	}
 277  	// Ensure adding data that would exceed the maximum size of the script does not add the data.
 278  	script, e := builder.AddData([]byte{0x00}).Script()
 279  	if _, ok := e.(ErrScriptNotCanonical); !ok || e == nil {
 280  		t.Fatalf("ScriptBuilder.AddData allowed exceeding max script "+
 281  			"size: %v", len(script),
 282  		)
 283  	}
 284  	if !bytes.Equal(script, origScript) {
 285  		t.Fatalf("ScriptBuilder.AddData unexpected modified script - "+
 286  			"got len %d, want len %d", len(script), len(origScript),
 287  		)
 288  	}
 289  	// Ensure adding an opcode that would exceed the maximum size of the script does not add the data.
 290  	builder.Reset().AddFullData(make([]byte, MaxScriptSize-3))
 291  	script, e = builder.AddOp(OP_0).Script()
 292  	if _, ok := e.(ErrScriptNotCanonical); !ok || e == nil {
 293  		t.Fatalf("ScriptBuilder.AddOp unexpected modified script - "+
 294  			"got len %d, want len %d", len(script), len(origScript),
 295  		)
 296  	}
 297  	if !bytes.Equal(script, origScript) {
 298  		t.Fatalf("ScriptBuilder.AddOp unexpected modified script - "+
 299  			"got len %d, want len %d", len(script), len(origScript),
 300  		)
 301  	}
 302  	// Ensure adding an integer that would exceed the maximum size of the script does not add the data.
 303  	builder.Reset().AddFullData(make([]byte, MaxScriptSize-3))
 304  	script, e = builder.AddInt64(0).Script()
 305  	if _, ok := e.(ErrScriptNotCanonical); !ok || e == nil {
 306  		t.Fatalf("ScriptBuilder.AddInt64 unexpected modified script - "+
 307  			"got len %d, want len %d", len(script), len(origScript),
 308  		)
 309  	}
 310  	if !bytes.Equal(script, origScript) {
 311  		t.Fatalf("ScriptBuilder.AddInt64 unexpected modified script - "+
 312  			"got len %d, want len %d", len(script), len(origScript),
 313  		)
 314  	}
 315  }
 316  
 317  // TestErroredScript ensures that all of the functions that can be used to add data to a script don't modify the script
 318  // once an error has happened.
 319  func TestErroredScript(t *testing.T) {
 320  	t.Parallel()
 321  	// Start off by constructing a near max size script that has enough space left to add each data type without an
 322  	// error and force an initial error condition.
 323  	builder := NewScriptBuilder()
 324  	builder.Reset().AddFullData(make([]byte, MaxScriptSize-8))
 325  	origScript, e := builder.Script()
 326  	if e != nil {
 327  		t.Fatalf("ScriptBuilder.AddFullData unexpected error: %v", e)
 328  	}
 329  	script, e := builder.AddData([]byte{0x00, 0x00, 0x00, 0x00, 0x00}).Script()
 330  	if _, ok := e.(ErrScriptNotCanonical); !ok || e == nil {
 331  		t.Fatalf("ScriptBuilder.AddData allowed exceeding max script "+
 332  			"size: %v", len(script),
 333  		)
 334  	}
 335  	if !bytes.Equal(script, origScript) {
 336  		t.Fatalf("ScriptBuilder.AddData unexpected modified script - "+
 337  			"got len %d, want len %d", len(script), len(origScript),
 338  		)
 339  	}
 340  	// Ensure adding data, even using the non-canonical path, to a script that has errored doesn't succeed.
 341  	script, e = builder.AddFullData([]byte{0x00}).Script()
 342  	if _, ok := e.(ErrScriptNotCanonical); !ok || e == nil {
 343  		t.Fatal("ScriptBuilder.AddFullData succeeded on errored script")
 344  	}
 345  	if !bytes.Equal(script, origScript) {
 346  		t.Fatalf("ScriptBuilder.AddFullData unexpected modified "+
 347  			"script - got len %d, want len %d", len(script),
 348  			len(origScript),
 349  		)
 350  	}
 351  	// Ensure adding data to a script that has errored doesn't succeed.
 352  	script, e = builder.AddData([]byte{0x00}).Script()
 353  	if _, ok := e.(ErrScriptNotCanonical); !ok || e == nil {
 354  		t.Fatal("ScriptBuilder.AddData succeeded on errored script")
 355  	}
 356  	if !bytes.Equal(script, origScript) {
 357  		t.Fatalf("ScriptBuilder.AddData unexpected modified "+
 358  			"script - got len %d, want len %d", len(script),
 359  			len(origScript),
 360  		)
 361  	}
 362  	// Ensure adding an opcode to a script that has errored doesn't succeed.
 363  	script, e = builder.AddOp(OP_0).Script()
 364  	if _, ok := e.(ErrScriptNotCanonical); !ok || e == nil {
 365  		t.Fatal("ScriptBuilder.AddOp succeeded on errored script")
 366  	}
 367  	if !bytes.Equal(script, origScript) {
 368  		t.Fatalf("ScriptBuilder.AddOp unexpected modified script - "+
 369  			"got len %d, want len %d", len(script), len(origScript),
 370  		)
 371  	}
 372  	// Ensure adding an integer to a script that has errored doesn't succeed.
 373  	script, e = builder.AddInt64(0).Script()
 374  	if _, ok := e.(ErrScriptNotCanonical); !ok || e == nil {
 375  		t.Fatal("ScriptBuilder.AddInt64 succeeded on errored script")
 376  	}
 377  	if !bytes.Equal(script, origScript) {
 378  		t.Fatalf("ScriptBuilder.AddInt64 unexpected modified script - "+
 379  			"got len %d, want len %d", len(script), len(origScript),
 380  		)
 381  	}
 382  	// Ensure the error has a message set.
 383  	if e.Error() == "" {
 384  		t.Fatal("ErrScriptNotCanonical.ScriptError does not have any text")
 385  	}
 386  }
 387