mirror of
https://github.com/kaspanet/kaspad.git
synced 2026-03-17 13:54:56 +00:00
txscript: Convert to new scriptnum type.
This commit implements a new type, named scriptNum, for handling all numeric values used in scripts and converts the code over to make use of it. This is being done for a few of reasons. First, the consensus rules for handling numeric values in the scripts require special handling with subtle semantics. By encapsulating those details into a type specifically dedicated to that purpose, it simplifies the code and generally helps prevent improper usage. Second, the new type is quite a bit more efficient than big.Ints which are designed to be arbitrarily large and thus involve a lot of heap allocations and additional multi-precision bookkeeping. Because this new type is based on an int64, it allows the numbers to be stack allocated thereby eliminating a lot of GC and also eliminates the extra multi-precision arithmetic bookkeeping. The use of an int64 is possible because the consensus rules dictate that when data is interpreted as a number, it is limited to an int32 even though results outside of this range are allowed so long as they are not interpreted as integers again themselves. Thus, the maximum possible result comes from multiplying a max int32 by itself which safely fits into an int64 and can then still appropriately provide the serialization of the larger number as required by consensus. Finally, it more closely resembles the implementation used by Bitcoin Core and thus makes is easier to compare the behavior between the two implementations. This commit also includes a full suite of tests with 100% coverage of the semantics of the new type.
This commit is contained in:
@@ -129,62 +129,6 @@ func TestScriptBuilderAddInt64(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestScriptBuilderAddUint64 tests that pushing unsigned integers to a script
|
||||
// via the ScriptBuilder API works as expected.
|
||||
func TestScriptBuilderAddUint64(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
val uint64
|
||||
expected []byte
|
||||
}{
|
||||
{name: "push small int 0", val: 0, expected: []byte{txscript.OP_0}},
|
||||
{name: "push small int 1", val: 1, expected: []byte{txscript.OP_1}},
|
||||
{name: "push small int 2", val: 2, expected: []byte{txscript.OP_2}},
|
||||
{name: "push small int 3", val: 3, expected: []byte{txscript.OP_3}},
|
||||
{name: "push small int 4", val: 4, expected: []byte{txscript.OP_4}},
|
||||
{name: "push small int 5", val: 5, expected: []byte{txscript.OP_5}},
|
||||
{name: "push small int 6", val: 6, expected: []byte{txscript.OP_6}},
|
||||
{name: "push small int 7", val: 7, expected: []byte{txscript.OP_7}},
|
||||
{name: "push small int 8", val: 8, expected: []byte{txscript.OP_8}},
|
||||
{name: "push small int 9", val: 9, expected: []byte{txscript.OP_9}},
|
||||
{name: "push small int 10", val: 10, expected: []byte{txscript.OP_10}},
|
||||
{name: "push small int 11", val: 11, expected: []byte{txscript.OP_11}},
|
||||
{name: "push small int 12", val: 12, expected: []byte{txscript.OP_12}},
|
||||
{name: "push small int 13", val: 13, expected: []byte{txscript.OP_13}},
|
||||
{name: "push small int 14", val: 14, expected: []byte{txscript.OP_14}},
|
||||
{name: "push small int 15", val: 15, expected: []byte{txscript.OP_15}},
|
||||
{name: "push small int 16", val: 16, expected: []byte{txscript.OP_16}},
|
||||
{name: "push 17", val: 17, expected: []byte{txscript.OP_DATA_1, 0x11}},
|
||||
{name: "push 65", val: 65, expected: []byte{txscript.OP_DATA_1, 0x41}},
|
||||
{name: "push 127", val: 127, expected: []byte{txscript.OP_DATA_1, 0x7f}},
|
||||
{name: "push 128", val: 128, expected: []byte{txscript.OP_DATA_2, 0x80, 0}},
|
||||
{name: "push 255", val: 255, expected: []byte{txscript.OP_DATA_2, 0xff, 0}},
|
||||
{name: "push 256", val: 256, expected: []byte{txscript.OP_DATA_2, 0, 0x01}},
|
||||
{name: "push 32767", val: 32767, expected: []byte{txscript.OP_DATA_2, 0xff, 0x7f}},
|
||||
{name: "push 32768", val: 32768, expected: []byte{txscript.OP_DATA_3, 0, 0x80, 0}},
|
||||
}
|
||||
|
||||
builder := txscript.NewScriptBuilder()
|
||||
t.Logf("Running %d tests", len(tests))
|
||||
for i, test := range tests {
|
||||
builder.Reset().AddUint64(test.val)
|
||||
result, err := builder.Script()
|
||||
if err != nil {
|
||||
t.Errorf("ScriptBuilder.AddUint64 #%d (%s) unexpected "+
|
||||
"error: %v", i, test.name, err)
|
||||
continue
|
||||
}
|
||||
if !bytes.Equal(result, test.expected) {
|
||||
t.Errorf("ScriptBuilder.AddUint64 #%d (%s) wrong result\n"+
|
||||
"got: %x\nwant: %x", i, test.name, result,
|
||||
test.expected)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestScriptBuilderAddData tests that pushing data to a script via the
|
||||
// ScriptBuilder API works as expected and conforms to BIP0062.
|
||||
func TestScriptBuilderAddData(t *testing.T) {
|
||||
@@ -373,19 +317,6 @@ func TestExceedMaxScriptSize(t *testing.T) {
|
||||
t.Fatalf("ScriptBuilder.AddInt64 unexpected modified script - "+
|
||||
"got len %d, want len %d", len(script), len(origScript))
|
||||
}
|
||||
|
||||
// Ensure adding an unsigned integer that would exceed the maximum size
|
||||
// of the script does not add the data.
|
||||
builder.Reset().AddFullData(make([]byte, maxScriptSize-3))
|
||||
script, err = builder.AddUint64(0).Script()
|
||||
if _, ok := err.(txscript.ErrScriptNotCanonical); !ok || err == nil {
|
||||
t.Fatalf("ScriptBuilder.AddUint64 unexpected modified script - "+
|
||||
"got len %d, want len %d", len(script), len(origScript))
|
||||
}
|
||||
if !bytes.Equal(script, origScript) {
|
||||
t.Fatalf("ScriptBuilder.AddUint64 unexpected modified script - "+
|
||||
"got len %d, want len %d", len(script), len(origScript))
|
||||
}
|
||||
}
|
||||
|
||||
// TestErroredScript ensures that all of the functions that can be used to add
|
||||
@@ -457,17 +388,6 @@ func TestErroredScript(t *testing.T) {
|
||||
"got len %d, want len %d", len(script), len(origScript))
|
||||
}
|
||||
|
||||
// Ensure adding an unsigned integer to a script that has errored
|
||||
// doesn't succeed.
|
||||
script, err = builder.AddUint64(0).Script()
|
||||
if _, ok := err.(txscript.ErrScriptNotCanonical); !ok || err == nil {
|
||||
t.Fatal("ScriptBuilder.AddUint64 succeeded on errored script")
|
||||
}
|
||||
if !bytes.Equal(script, origScript) {
|
||||
t.Fatalf("ScriptBuilder.AddUint64 unexpected modified script - "+
|
||||
"got len %d, want len %d", len(script), len(origScript))
|
||||
}
|
||||
|
||||
// Ensure the error has a message set.
|
||||
if err.Error() == "" {
|
||||
t.Fatal("ErrScriptNotCanonical.Error does not have any text")
|
||||
|
||||
Reference in New Issue
Block a user