diff --git a/txscript/standard.go b/txscript/standard.go index b81d1def..64b9f6a8 100644 --- a/txscript/standard.go +++ b/txscript/standard.go @@ -944,70 +944,38 @@ func GenerateSStxAddrPush(addr dcrutil.Address, amount dcrutil.Amount, limits ui return nil, scriptError(ErrUnsupportedAddress, str) } - // Prefix - dataPushes := []byte{ - 0x6a, // OP_RETURN - 0x1e, // OP_DATA_30 - } - - hash := addr.ScriptAddress() - - amountBuffer := make([]byte, 8) - binary.LittleEndian.PutUint64(amountBuffer, uint64(amount)) + // Concatenate the prefix, pubkeyhash, and amount. + adBytes := make([]byte, 20+8+2) + copy(adBytes[0:20], addr.ScriptAddress()) + binary.LittleEndian.PutUint64(adBytes[20:28], uint64(amount)) + binary.LittleEndian.PutUint16(adBytes[28:30], limits) // Set the bit flag indicating pay to script hash. if scriptType == ScriptHashTy { - amountBuffer[7] |= 1 << 7 + adBytes[27] |= 1 << 7 } - limitsBuffer := make([]byte, 2) - binary.LittleEndian.PutUint16(limitsBuffer, limits) - - // Concatenate the prefix, pubkeyhash, and amount. - addrOut := append(dataPushes, hash...) - addrOut = append(addrOut, amountBuffer...) - addrOut = append(addrOut, limitsBuffer...) - - return addrOut, nil + return NewScriptBuilder().AddOp(OP_RETURN).AddData(adBytes).Script() } // GenerateSSGenBlockRef generates an OP_RETURN push for the block header hash and // height which the block votes on. func GenerateSSGenBlockRef(blockHash chainhash.Hash, height uint32) ([]byte, error) { - // Prefix - dataPushes := []byte{ - 0x6a, // OP_RETURN - 0x24, // OP_DATA_36 - } - // Serialize the block hash and height - blockHeightBytes := make([]byte, 4) - binary.LittleEndian.PutUint32(blockHeightBytes, height) + brBytes := make([]byte, 32+4) + copy(brBytes[0:32], blockHash[:]) + binary.LittleEndian.PutUint32(brBytes[32:36], height) - blockData := append(blockHash[:], blockHeightBytes...) - - // Concatenate the prefix and block data - blockDataOut := append(dataPushes, blockData...) - - return blockDataOut, nil + return NewScriptBuilder().AddOp(OP_RETURN).AddData(brBytes).Script() } // GenerateSSGenVotes generates an OP_RETURN push for the vote bits in an SSGen tx. func GenerateSSGenVotes(votebits uint16) ([]byte, error) { - // Prefix - dataPushes := []byte{ - 0x6a, // OP_RETURN - 0x02, // OP_DATA_2 - } - // Serialize the votebits - voteBitsBytes := make([]byte, 2) - binary.LittleEndian.PutUint16(voteBitsBytes, votebits) + vbBytes := make([]byte, 2) + binary.LittleEndian.PutUint16(vbBytes, votebits) - // Concatenate the prefix and vote bits - voteBitsOut := append(dataPushes, voteBitsBytes...) - - return voteBitsOut, nil + return NewScriptBuilder().AddOp(OP_RETURN).AddData(vbBytes).Script() } // GenerateProvablyPruneableOut creates a provably-prunable script containing diff --git a/txscript/standard_test.go b/txscript/standard_test.go index 45e5045d..b5f7eed8 100644 --- a/txscript/standard_test.go +++ b/txscript/standard_test.go @@ -11,6 +11,7 @@ import ( "testing" "github.com/decred/dcrd/chaincfg" + "github.com/decred/dcrd/chaincfg/chainhash" "github.com/decred/dcrd/dcrec" "github.com/decred/dcrd/dcrec/secp256k1" "github.com/decred/dcrd/dcrutil" @@ -1115,3 +1116,106 @@ func TestGenerateProvablyPruneableOut(t *testing.T) { } } } + +// TestGenerateSStxAddrPush ensures an expected OP_RETURN push is generated. +func TestGenerateSStxAddrPush(t *testing.T) { + var tests = []struct { + addrStr string + amount dcrutil.Amount + limits uint16 + expected []byte + }{ + { + "Dcur2mcGjmENx4DhNqDctW5wJCVyT3Qeqkx", + 1000, + 10, + hexToBytes("6a1ef5916158e3e2c4551c1796708db8367207ed1" + + "3bbe8030000000000800a00"), + }, + { + "TscB7V5RuR1oXpA364DFEsNDuAs8Rk6BHJE", + 543543, + 256, + hexToBytes("6a1e7a5c4cca76f2e0b36db4763daacbd6cbb6ee6" + + "e7b374b0800000000000001"), + }, + } + for _, test := range tests { + addr, err := dcrutil.DecodeAddress(test.addrStr) + if err != nil { + t.Errorf("DecodeAddress failed: %v", err) + continue + } + s, err := GenerateSStxAddrPush(addr, test.amount, test.limits) + if err != nil { + t.Errorf("GenerateSStxAddrPush failed: %v", err) + continue + } + if !bytes.Equal(s, test.expected) { + t.Errorf("GenerateSStxAddrPush: unexpected script:\n "+ + "got %x\nwant %x", s, test.expected) + } + } +} + +// TestGenerateSSGenBlockRef ensures an expected OP_RETURN push is generated. +func TestGenerateSSGenBlockRef(t *testing.T) { + var tests = []struct { + blockHash string + height uint32 + expected []byte + }{ + { + "0000000000004740ad140c86753f9295e09f9cc81b1bb75d7f5552aeeedb7012", + 1000, + hexToBytes("6a241270dbeeae52557f5db71b1bc89c9fe095923" + + "f75860c14ad4047000000000000e8030000"), + }, + { + "000000000000000033eafc268a67c8d1f02343d7a96cf3fe2a4915ef779b52f9", + 290000, + hexToBytes("6a24f9529b77ef15492afef36ca9d74323f0d1c86" + + "78a26fcea330000000000000000d06c0400"), + }, + } + for _, test := range tests { + h, err := chainhash.NewHashFromStr(test.blockHash) + if err != nil { + t.Errorf("NewHashFromStr failed: %v", err) + continue + } + s, err := GenerateSSGenBlockRef(*h, test.height) + if err != nil { + t.Errorf("GenerateSSGenBlockRef failed: %v", err) + continue + } + if !bytes.Equal(s, test.expected) { + t.Errorf("GenerateSSGenBlockRef: unexpected script:\n"+ + " got %x\nwant %x", s, test.expected) + } + } +} + +// TestGenerateSSGenVotes ensures an expected OP_RETURN push is generated. +func TestGenerateSSGenVotes(t *testing.T) { + var tests = []struct { + votebits uint16 + expected []byte + }{ + {65535, hexToBytes("6a02ffff")}, + {256, hexToBytes("6a020001")}, + {127, hexToBytes("6a027f00")}, + {0, hexToBytes("6a020000")}, + } + for _, test := range tests { + s, err := GenerateSSGenVotes(test.votebits) + if err != nil { + t.Errorf("GenerateSSGenVotes failed: %v", err) + continue + } + if !bytes.Equal(s, test.expected) { + t.Errorf("GenerateSSGenVotes: unexpected script:\n "+ + "got %x\nwant %x", s, test.expected) + } + } +}