generated from bitcoin-sv/template
-
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathlockup_test.go
More file actions
258 lines (210 loc) · 7.95 KB
/
lockup_test.go
File metadata and controls
258 lines (210 loc) · 7.95 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
package lockup
import (
"bytes"
"encoding/hex"
"math/big"
"testing"
"time"
ec "github.com/bsv-blockchain/go-sdk/primitives/ec"
"github.com/bsv-blockchain/go-sdk/script"
"github.com/bsv-blockchain/go-sdk/script/interpreter"
"github.com/bsv-blockchain/go-sdk/transaction"
sighash "github.com/bsv-blockchain/go-sdk/transaction/sighash"
"github.com/stretchr/testify/require"
)
// TestLockPrefixSuffix verifies that the LockPrefix and LockSuffix constants are set
func TestLockPrefixSuffix(t *testing.T) {
require.NotNil(t, LockPrefix)
require.NotNil(t, LockSuffix)
require.NotEmpty(t, LockPrefix)
require.NotEmpty(t, LockSuffix)
// Log the lengths for diagnostic purposes
t.Logf("LockPrefix length: %d", len(LockPrefix))
t.Logf("LockSuffix length: %d", len(LockSuffix))
}
// TestLockCreate verifies the Lock creation functionality
func TestLockCreate(t *testing.T) {
// Create a test private key
privKey, err := ec.NewPrivateKey()
require.NoError(t, err)
// Get the public key hash
pubKey := privKey.PubKey()
pubKeyBytes := pubKey.Compressed()
// Create address from public key hash
address, err := script.NewAddressFromPublicKeyHash(pubKeyBytes[:20], true)
require.NoError(t, err)
// Create a lock for 1 hour in the future
lockTime := uint32(time.Now().Unix()) + 3600 //nolint:gosec // G115: safe test value
lock := &Lock{
Address: address,
Until: lockTime,
}
// Create the locking script
lockScript := lock.Lock()
require.NotNil(t, lockScript)
// Log script for diagnostic purposes
t.Logf("Lock script created: %x", *lockScript)
// Verify the script format - check prefix at the beginning and suffix at the end
scriptBytes := *lockScript
require.True(t, bytes.HasPrefix(scriptBytes, LockPrefix), "Script should start with LockPrefix")
require.True(t, bytes.HasSuffix(scriptBytes, LockSuffix), "Script should end with LockSuffix")
}
// TestLockDecode verifies the Decode functionality
func TestLockDecode(t *testing.T) {
// Create a test private key
privKey, err := ec.NewPrivateKey()
require.NoError(t, err)
// Get the public key hash
pubKey := privKey.PubKey()
pubKeyBytes := pubKey.Compressed()
// Create address from public key hash
address, err := script.NewAddressFromPublicKeyHash(pubKeyBytes[:20], true)
require.NoError(t, err)
// Create a lock for 1 hour in the future
lockTime := uint32(time.Now().Unix()) + 3600 //nolint:gosec // G115: safe test value
lock := &Lock{
Address: address,
Until: lockTime,
}
// Create the locking script
lockScript := lock.Lock()
require.NotNil(t, lockScript)
// Now decode the script
decodedLock := Decode(lockScript, true)
require.NotNil(t, decodedLock)
// Verify the decoded values match what we put in
require.Equal(t, address.AddressString, decodedLock.Address.AddressString)
require.Equal(t, lockTime, decodedLock.Until)
}
// TestLockUnlocker verifies the LockUnlocker functionality
func TestLockUnlocker(t *testing.T) {
// Create a test private key
privKey, err := ec.NewPrivateKey()
require.NoError(t, err)
// Get the public key hash
pubKey := privKey.PubKey()
pubKeyBytes := pubKey.Compressed()
// Create address from public key hash
address, err := script.NewAddressFromPublicKeyHash(pubKeyBytes[:20], true)
require.NoError(t, err)
// Create a lock for 1 hour in the future
lockTime := uint32(time.Now().Unix()) + 3600 //nolint:gosec // G115: safe test value
lock := &Lock{
Address: address,
Until: lockTime,
}
// Create the locking script
lockScript := lock.Lock()
require.NotNil(t, lockScript)
// Create a transaction
tx := transaction.NewTransaction()
tx.Version = 1
tx.LockTime = 0
// Convert locking script to hex for AddInputFrom
lockScriptHex := hex.EncodeToString(*lockScript)
// Add an input using the lockup script
err = tx.AddInputFrom(
"1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", // Dummy TXID
0, // Output index
lockScriptHex, // Locking script hex
100000000, // 1 BSV in satoshis
nil, // No unlocking template
)
require.NoError(t, err)
// Add an output
p2pkhBytes := make([]byte, 0, 25)
p2pkhBytes = append(p2pkhBytes, script.OpDUP, script.OpHASH160, script.OpDATA20)
p2pkhBytes = append(p2pkhBytes, address.PublicKeyHash...)
p2pkhBytes = append(p2pkhBytes, script.OpEQUALVERIFY, script.OpCHECKSIG)
p2pkhScript := script.Script(p2pkhBytes)
tx.AddOutput(&transaction.TransactionOutput{
Satoshis: 99999000, // Minus fees
LockingScript: &p2pkhScript,
})
// Create a lockup unlocker
shf := sighash.AllForkID
unlocker := LockUnlocker{
PrivateKey: privKey,
SigHashFlag: &shf,
}
// Estimate the unlocking script length
estimatedLength := unlocker.EstimateLength(tx, 0)
require.Positive(t, estimatedLength)
t.Logf("Estimated unlocking script length: %d", estimatedLength)
// Sign the transaction
unlockingScript, err := unlocker.Sign(tx, 0)
require.NoError(t, err)
require.NotNil(t, unlockingScript)
// Set the unlocking script
tx.Inputs[0].UnlockingScript = unlockingScript
// Log information for diagnostic purposes
t.Logf("Transaction signed: %s", tx.String())
t.Logf("Unlocking script length: %d", len(*unlockingScript))
}
// TestLockScriptFormat verifies the format of a lock script
func TestLockScriptFormat(t *testing.T) {
// Generate a test address
privKey, err := ec.NewPrivateKey()
require.NoError(t, err)
address, err := script.NewAddressFromPublicKey(privKey.PubKey(), true)
require.NoError(t, err)
// Create a lock for 1 hour in the future
lockTime := uint32(time.Now().Unix()) + 3600 //nolint:gosec // G115: safe test value
lock := &Lock{
Address: address,
Until: lockTime,
}
// Create the locking script
lockScript := lock.Lock()
require.NotNil(t, lockScript)
// Verify the script is properly formed
chunks, err := lockScript.Chunks()
require.NoError(t, err)
require.NotEmpty(t, chunks)
// Verify the script format - check prefix at the beginning and suffix at the end
scriptBytes := *lockScript
require.True(t, bytes.HasPrefix(scriptBytes, LockPrefix), "Script should start with LockPrefix")
require.True(t, bytes.HasSuffix(scriptBytes, LockSuffix), "Script should end with LockSuffix")
// Extract the public key hash from the script
// The PKH should be in the first PUSHDATA after the prefix
pos := len(LockPrefix)
op, err := lockScript.ReadOp(&pos)
require.NoError(t, err)
// Verify the extracted PKH matches the address PKH
require.Equal(t, []byte(address.PublicKeyHash), op.Data, "Address public key hash mismatch in script")
// Verify the locktime is encoded in the script
untilBytes := (&interpreter.ScriptNumber{
Val: big.NewInt(int64(lockTime)),
AfterGenesis: true,
}).Bytes()
// Check if the script contains the locktime bytes
op, err = lockScript.ReadOp(&pos)
require.NoError(t, err)
require.Equal(t, untilBytes, op.Data, "Locktime bytes mismatch in script")
}
// TestLockDecodeInvalid verifies that Decode properly handles invalid scripts
func TestLockDecodeInvalid(t *testing.T) {
// Create an invalid script
invalidScript := script.NewFromBytes([]byte{})
_ = invalidScript.AppendOpcodes(script.OpRETURN)
// Try to decode
decodedLock := Decode(invalidScript, true)
require.Nil(t, decodedLock)
// Create a script with valid prefix but invalid content
invalidWithPrefix := script.NewFromBytes(LockPrefix)
_ = invalidWithPrefix.AppendOpcodes(script.OpRETURN)
// Try to decode
decodedLock = Decode(invalidWithPrefix, true)
require.Nil(t, decodedLock)
}
// TestLockDecodeWithInvalidPKH verifies that Decode properly handles invalid public key hash
func TestLockDecodeWithInvalidPKH(t *testing.T) {
// Create a script with valid prefix but invalid PKH length
invalidPKHScript := script.NewFromBytes(LockPrefix)
_ = invalidPKHScript.AppendPushData([]byte("invalidpkh")) // Not 20 bytes
_ = invalidPKHScript.AppendPushData([]byte{0, 0, 0, 0}) // Valid locktime bytes
invalidPKHScript = script.NewFromBytes(append(*invalidPKHScript, LockSuffix...))
// Try to decode
decodedLock := Decode(invalidPKHScript, true)
require.Nil(t, decodedLock)
}