github.com/mit-dci/lit@v0.0.0-20221102210550-8c3d3b49f2ce/portxo/portxo.go (about) 1 package portxo 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "fmt" 7 8 "github.com/mit-dci/lit/wire" 9 ) 10 11 type TxoMode uint8 12 13 /* PorTxo specify a utxo, and all the information needed to spend it. 14 The first 3 fields (Op, Amt, Mode) are required in all cases. 15 If KeyGen.Depth != 0, that means no key path is supplied, and PrivKey 16 is probably empty / ignored. Having both a KeyGen and a PrivKey is redunant. 17 Having neither KeyGen nor PrivKey means there's no private key, and no 18 indication of how to get it; in that case get the private key from somewhere else. 19 20 If BOTH KeyGen AND PrivKey are filled in, add em up! Add the two private keys, 21 modulo the curve order. 22 23 PkScript can also be left empty depending on the mode. Basically only script-hash 24 modes need it, as the previous pkscript can be generated 25 26 PreSigStack are data pushes that happen before the signature is pushed. 27 28 I was thinking of putting PostSigStack as well but I think it makes more sense to 29 always have the data before the sig, since there's pretty much only CHECKSIGVERIFY 30 now and you might as well put the sig check last. 31 32 Also makes sense that with a MAST or pay-to-script-merkle-root type of structure, 33 you'd want a bunch stuff before the sig. 34 35 */ 36 type PorTxo struct { 37 // got rid of NetID. If you want to specify different networks / coins, 38 // use KeyGen.Step[1], that's what it's for. 39 // Heck, set KeyGen.Depth to 0 and still use Step[1] as the network / coin... 40 // NetID byte // indicates what network / coin utxo is in 41 Op wire.OutPoint `json:"outpoint"` // unique outpoint 42 Value int64 `json:"val"` // higher is better 43 Height int32 `json:"height"` // block height of utxo (not needed? nice to know?) 44 Seq uint32 `json:"seq"` // used for relative timelock 45 Mode TxoMode `json:"mode"` // what kind of output 46 47 KeyGen `json:"keygen"` 48 49 PkScript []byte `json:"privkeyscript"` // if empty, try to generate based on mode and priv key 50 51 PreSigStack [][]byte `json:"stack"` // items to push before the sig 52 } 53 54 // Constants defining txo modes 55 const ( 56 // Flags which combined can turn into full utxo modes 57 FlagTxoPubKeyHash TxoMode = 0x01 58 FlagTxoScript TxoMode = 0x02 59 FlagTxoWitness TxoMode = 0x04 60 FlagTxoCompressed TxoMode = 0x08 61 FlagTxoUncompressed TxoMode = 0x10 62 63 // fully specified tx output modes 64 // raw pubkey outputs (old school) 65 TxoP2PKUncomp = FlagTxoUncompressed 66 TxoP2PKComp = FlagTxoCompressed 67 68 // pub key hash outputs, standard p2pkh (common) 69 TxoP2PKHUncomp = FlagTxoPubKeyHash | FlagTxoUncompressed 70 TxoP2PKHComp = FlagTxoCompressed | FlagTxoPubKeyHash 71 72 // script hash 73 TxoP2SHUncomp = FlagTxoScript | FlagTxoUncompressed 74 TxoP2SHComp = FlagTxoScript | FlagTxoCompressed 75 76 // witness p2wpkh modes 77 TxoP2WPKHUncomp = FlagTxoWitness | FlagTxoPubKeyHash | FlagTxoUncompressed 78 TxoP2WPKHComp = FlagTxoWitness | FlagTxoPubKeyHash | FlagTxoCompressed 79 80 // witness script hash 81 TxoP2WSHUncomp = FlagTxoWitness | FlagTxoScript | FlagTxoUncompressed 82 TxoP2WSHComp = FlagTxoWitness | FlagTxoScript | FlagTxoCompressed 83 84 // unknown 85 TxoUnknownMode = 0x80 86 ) 87 88 var modeStrings = map[TxoMode]string{ 89 TxoP2PKUncomp: "raw pubkey uncompressed", 90 TxoP2PKComp: "raw pubkey compressed", 91 92 TxoP2PKHUncomp: "pubkey hash uncompressed", 93 TxoP2PKHComp: "pubkey hash compressed", 94 95 TxoP2SHUncomp: "script hash uncompressed", 96 TxoP2SHComp: "script hash compressed", 97 98 TxoP2WPKHUncomp: "witness pubkey hash uncompressed", 99 TxoP2WPKHComp: "witness pubkey hash compressed", 100 101 TxoP2WSHUncomp: "witness script hash uncompressed", 102 TxoP2WSHComp: "witness script hash compressed", 103 } 104 105 // String returns the InvType in human-readable form. 106 func (m TxoMode) String() string { 107 s, ok := modeStrings[m] 108 if ok { 109 return s 110 } 111 return fmt.Sprintf("unknown TxoMode %x", uint8(m)) 112 } 113 114 // Compare deep-compares two portable utxos, returning true if they're the same 115 func (u *PorTxo) Equal(z *PorTxo) bool { 116 if u == nil || z == nil { 117 return false 118 } 119 120 if !u.Op.Hash.IsEqual(&z.Op.Hash) { 121 return false 122 } 123 if u.Op.Index != z.Op.Index { 124 return false 125 } 126 if u.Value != z.Value || u.Seq != z.Seq || u.Mode != z.Mode || u.Height != z.Height { 127 return false 128 } 129 if u.KeyGen.PrivKey != z.KeyGen.PrivKey { 130 return false 131 } 132 if !bytes.Equal(u.KeyGen.Bytes(), z.KeyGen.Bytes()) { 133 return false 134 } 135 if !bytes.Equal(u.PkScript, z.PkScript) { 136 return false 137 } 138 139 // compare pre sig stack lengths 140 if len(u.PreSigStack) != len(z.PreSigStack) { 141 return false 142 } 143 144 // if we're here, lengths for both are the same. Iterate and compare stacks 145 for i, _ := range u.PreSigStack { 146 if !bytes.Equal(u.PreSigStack[i], z.PreSigStack[i]) { 147 return false 148 } 149 } 150 151 return true 152 } 153 154 func (u *PorTxo) String() string { 155 var s string 156 var empty [32]byte 157 if u == nil { 158 return "nil utxo" 159 } 160 s = u.Op.String() 161 s += fmt.Sprintf("\n\ta:%d h:%d seq:%d %s\n", 162 u.Value, u.Height, u.Seq, u.Mode.String()) 163 164 if u.KeyGen.PrivKey == empty { 165 s += fmt.Sprintf("\tprivate key not available (zero)\n") 166 } else { 167 s += fmt.Sprintf("\tprivate key available (non-zero)\n") 168 } 169 if u.KeyGen.Depth == 0 || u.KeyGen.Depth > 5 { 170 s += fmt.Sprintf("\tno key derivation path\n") 171 } else { 172 s += fmt.Sprintf("%s\n", u.KeyGen.String()) 173 } 174 s += fmt.Sprintf("\tPkScript (len %d): %x\n", len(u.PkScript), u.PkScript) 175 176 // list pre-sig elements 177 s += fmt.Sprintf("\t%d pre-sig elements:", len(u.PreSigStack)) 178 for _, e := range u.PreSigStack { 179 s += fmt.Sprintf(" [%x]", e) 180 } 181 s += fmt.Sprintf("\n") 182 183 return s 184 } 185 186 /* serialized (im/ex)Portable Utxos are 106 up to 357 bytes. 187 Op 36 188 Amt 8 189 Height 4 190 Seq 4 191 Mode 1 192 193 Keygen 53 194 195 PreStackNum (1 byte) 196 PreStackItemLen (1 byte) 197 PreStackItem (max 255 bytes each) 198 PostStackNum (1 byte) 199 PostStackItemLen (1 byte) 200 PostStackItem (max 255 bytes each) 201 PkScriptLen (1 byte) 202 PkScript (max 255 bytes) 203 204 205 */ 206 207 func PorTxoFromBytes(b []byte) (*PorTxo, error) { 208 // should be max 1KiB for now 209 if len(b) < 106 || len(b) > 1024 { 210 return nil, fmt.Errorf("%d bytes, need 106-1024", len(b)) 211 } 212 213 buf := bytes.NewBuffer(b) 214 215 var u PorTxo 216 var err error 217 218 err = u.Op.Hash.SetBytes(buf.Next(32)) 219 if err != nil { 220 return nil, err 221 } 222 err = binary.Read(buf, binary.BigEndian, &u.Op.Index) 223 if err != nil { 224 return nil, err 225 } 226 err = binary.Read(buf, binary.BigEndian, &u.Value) 227 if err != nil { 228 return nil, err 229 } 230 err = binary.Read(buf, binary.BigEndian, &u.Height) 231 if err != nil { 232 return nil, err 233 } 234 err = binary.Read(buf, binary.BigEndian, &u.Seq) 235 if err != nil { 236 return nil, err 237 } 238 err = binary.Read(buf, binary.BigEndian, &u.Mode) 239 if err != nil { 240 return nil, err 241 } 242 243 var kgenarr [53]byte 244 copy(kgenarr[:], buf.Next(53)) 245 u.KeyGen = KeyGenFromBytes(kgenarr) 246 247 // get PkScript length byte 248 PkScriptLen, err := buf.ReadByte() 249 if err != nil { 250 return nil, err 251 } 252 // make PkScript the right size to write into 253 u.PkScript = make([]byte, PkScriptLen) 254 255 // write from buffer into PkScript 256 _, err = buf.Read(u.PkScript) 257 if err != nil { 258 return nil, err 259 } 260 261 // get number of pre-sig stack items 262 PreSigStackNumItems, err := buf.ReadByte() 263 if err != nil { 264 return nil, err 265 } 266 267 u.PreSigStack = make([][]byte, PreSigStackNumItems) 268 // iterate through reading each presigStack item 269 for i, _ := range u.PreSigStack { 270 // read length of this element 271 elementLength, err := buf.ReadByte() 272 if err != nil { 273 return nil, err 274 } 275 // size element slice 276 u.PreSigStack[i] = make([]byte, elementLength) 277 // copy element data 278 _, err = buf.Read(u.PreSigStack[i]) 279 if err != nil { 280 return nil, err 281 } 282 } 283 284 return &u, nil 285 } 286 287 // Spendable takes in the current block height, and returns whether 288 // this utxo is spendable now or not. It checks height (unconfirmed 289 func (u *PorTxo) Mature(curHeight int32) bool { 290 // Nil portxo should be an error but just return false 291 if u == nil { 292 return false 293 } 294 //if sequence is nonzero, it's CSV locked, so check if it's either 295 // unconfirmed or confirmed at a height that's too shallow 296 if u.Seq > 1 && 297 (u.Height < 100 || u.Height+int32(u.Seq) > curHeight) { 298 return false 299 } 300 return true 301 } 302 303 // EstSize returns an estimated vsize in bytes for using this portxo as 304 // an input. Might not be perfectly accurate. Also maybe should switch 305 // from vsize to weight..? everything is vsize right now though. 306 func (u *PorTxo) EstSize() int64 { 307 // Txins by mode: 308 // P2 PKH is op,seq (40) + pub(33) + sig(71) = 144 309 // P2 WPKH is op,seq(40) + [(33+71 / 4) = 26] = 66 310 // P2 WSH is op,seq(40) + [75(script) + 71]/4 (36) = 76 311 switch u.Mode { 312 case TxoP2PKHComp: // non witness is about 150 bytes 313 return 144 314 case TxoP2WPKHComp: // witness mode is around 66 vsize 315 return 66 316 case TxoP2WSHComp: 317 return 76 318 } 319 return 150 // guess that unknown is 150 bytes 320 } 321 322 func (u *PorTxo) Bytes() ([]byte, error) { 323 if u == nil { 324 return nil, fmt.Errorf("Can't serialize nil Utxo") 325 } 326 327 var buf bytes.Buffer 328 329 // _, err := buf.Write(u.Op.Hash.CloneBytes()) 330 _, err := buf.Write(u.Op.Hash.CloneBytes()) 331 if err != nil { 332 return nil, err 333 } 334 335 err = binary.Write(&buf, binary.BigEndian, u.Op.Index) 336 if err != nil { 337 return nil, err 338 } 339 340 err = binary.Write(&buf, binary.BigEndian, u.Value) 341 if err != nil { 342 return nil, err 343 } 344 err = binary.Write(&buf, binary.BigEndian, u.Height) 345 if err != nil { 346 return nil, err 347 } 348 err = binary.Write(&buf, binary.BigEndian, u.Seq) 349 if err != nil { 350 return nil, err 351 } 352 err = binary.Write(&buf, binary.BigEndian, u.Mode) // mode 353 if err != nil { 354 return nil, err 355 } 356 _, err = buf.Write(u.KeyGen.Bytes()) // keypath 357 if err != nil { 358 return nil, err 359 } 360 361 // check pkScript length 362 if len(u.PkScript) > 255 { 363 return nil, fmt.Errorf("PkScript too long (255 byte max)") 364 } 365 // write length of pkScript 366 err = buf.WriteByte(uint8(len(u.PkScript))) 367 if err != nil { 368 return nil, err 369 } 370 // write PkScript 371 _, err = buf.Write(u.PkScript) 372 if err != nil { 373 return nil, err 374 } 375 376 // check Pre-sig stack number of elements 377 if len(u.PreSigStack) > 255 { 378 return nil, fmt.Errorf("Too many PreSigStack items (255 items max)") 379 } 380 // write number of PreSigStack items 381 err = buf.WriteByte(uint8(len(u.PreSigStack))) 382 if err != nil { 383 return nil, err 384 } 385 // iterate through PreSigStack items and write each 386 for i, element := range u.PreSigStack { 387 // check element length 388 if len(element) > 255 { 389 return nil, fmt.Errorf("PreSigStack item %d %d bytes (255 max)", 390 i, len(element)) 391 } 392 // write length of element 393 err = buf.WriteByte(uint8(len(element))) 394 if err != nil { 395 return nil, err 396 } 397 // write element itself 398 _, err = buf.Write(element) 399 if err != nil { 400 return nil, err 401 } 402 } 403 404 return buf.Bytes(), nil 405 }