diff --git a/sighash143/sighash143.go b/sighash143/sighash143.go deleted file mode 100644 index 419bf54e..00000000 --- a/sighash143/sighash143.go +++ /dev/null @@ -1,317 +0,0 @@ -package main - -import ( - "bytes" - "encoding/binary" - "encoding/hex" - "fmt" - "io" - "io/ioutil" - "log" - "math" - "strings" - - "github.com/btcsuite/btcd/btcec" - "github.com/btcsuite/btcd/txscript" - "github.com/btcsuite/btcd/wire" - "github.com/lightningnetwork/lnd/uspv" -) - -const ( - inspk0 = "2103c9f4836b9a4f77fc0d81f7bcb01b7f1b35916864b9476c241ce9fc198bd25432ac" - inamt0 = int64(625000000) - - inspk1 = "00141d0f172a0ecb48aee1be1f2687d2963ae33f71a1" - inamt1 = int64(600000000) - - xpecthash = "c37af31116d1b27caf68aae9e3ac82f1477929014d5b917657d0eb49478cb670" -) - -// calcWitnessSignatureHash is the witnessified version of calcSignatureHash -// put your pkscripts in the sigscript slot before handing the tx to this -// function. Also you're clearly supposed to cache the 3 sub-hashes generated -// here, because they apply to the tx, not a txin. But this doesn't yet. -func calcWitnessSignatureHash(sigscript []byte, - hashType txscript.SigHashType, tx *wire.MsgTx, idx int, amt int64) []byte { - // in the script.go calcSignatureHash(), idx is assumed safe, so I guess - // that's OK here too...? Nah I'm gonna check - if idx > len(tx.TxIn)-1 { - fmt.Printf("calcWitnessSignatureHash error: idx %d but %d txins", - idx, len(tx.TxIn)) - return nil - } - - // first get hashPrevOuts, hashSequence, and hashOutputs - hashPrevOuts := calcHashPrevOuts(tx, hashType) - hashSequence := calcHashSequence(tx, hashType) - hashOutputs := calcHashOutputs(tx, idx, hashType) - - var buf4 [4]byte // buffer for 4-byte stuff - var buf8 [8]byte // buffer for 8-byte stuff - var pre []byte // the pre-image we're generating - - binary.LittleEndian.PutUint32(buf4[:], uint32(tx.Version)) - pre = append(pre, buf4[:]...) - - pre = append(pre, hashPrevOuts.Bytes()...) - pre = append(pre, hashSequence.Bytes()...) - - // outpoint being spent - pre = append(pre, tx.TxIn[idx].PreviousOutPoint.Hash.Bytes()...) - binary.LittleEndian.PutUint32(buf4[:], tx.TxIn[idx].PreviousOutPoint.Index) - pre = append(pre, buf4[:]...) - - // scriptCode which is some new thing - - // detect wpkh mode - fmt.Printf("txin %d sscript len %d\n", idx, len(tx.TxIn[idx].SignatureScript)) - if len(sigscript) == 22 && sigscript[0] == 0x00 && sigscript[1] == 0x14 { - // wpkh mode .... recreate op_dup codes here - sCode := []byte{0x19, 0x76, 0xa9, 0x14} - sCode = append(sCode, sigscript[2:22]...) - sCode = append(sCode, []byte{0x88, 0xac}...) - pre = append(pre, sCode...) - } else if len(sigscript) == 34 && - sigscript[0] == 0x00 && sigscript[1] == 0x20 { - // whs mode- need to remove codeseparators. this doesn't yet. - var buf bytes.Buffer - writeVarBytes(&buf, 0, sigscript) - pre = append(pre, buf.Bytes()...) - } else { - // ?? this is not witness tx! fail - fmt.Printf("Non witness error ") - return nil - } - - // amount being signed off - binary.LittleEndian.PutUint64(buf8[:], uint64(amt)) - pre = append(pre, buf8[:]...) - - // nsequence of input - binary.LittleEndian.PutUint32(buf4[:], tx.TxIn[idx].Sequence) - pre = append(pre, buf4[:]...) - - pre = append(pre, hashOutputs.Bytes()...) - - // locktime - binary.LittleEndian.PutUint32(buf4[:], tx.LockTime) - pre = append(pre, buf4[:]...) - - // hashType... in 4 bytes, instead of 1, because reasons. - binary.LittleEndian.PutUint32(buf4[:], uint32(hashType)) - pre = append(pre, buf4[:]...) - - fmt.Printf("calcWitnessSignatureHash pre: %x\n", pre) - hsh := wire.DoubleSha256SH(pre) - return hsh.Bytes() -} - -// calcHashPrevOuts makes a single hash of all previous outputs in the tx -func calcHashPrevOuts(tx *wire.MsgTx, hType txscript.SigHashType) wire.ShaHash { - // skip this (0x00) for anyonecanpay - if hType == txscript.SigHashAnyOneCanPay { - var empty [32]byte - return empty - } - - var pre []byte - for _, in := range tx.TxIn { - // first append 32 byte hash - pre = append(pre, in.PreviousOutPoint.Hash.Bytes()...) - // then make a buffer, put 4 byte index in lil' endian and append that - var buf [4]byte - binary.LittleEndian.PutUint32(buf[:], in.PreviousOutPoint.Index) - pre = append(pre, buf[:]...) - } - fmt.Printf("calcHashPrevOuts pre: %x\n", pre) - return wire.DoubleSha256SH(pre) -} - -// calcHashSequence is hash of txins' seq numbers, lil' endian, stuck together -func calcHashSequence(tx *wire.MsgTx, hType txscript.SigHashType) wire.ShaHash { - // skip (0x00) for single, none, anyonecanpay - if hType == txscript.SigHashSingle || hType == txscript.SigHashNone || - hType == txscript.SigHashAnyOneCanPay { - var empty [32]byte - return empty - } - - var pre []byte - for _, in := range tx.TxIn { - var buf [4]byte - binary.LittleEndian.PutUint32(buf[:], in.Sequence) - pre = append(pre, buf[:]...) - } - fmt.Printf("calcHashSequence pre: %x\n", pre) - return wire.DoubleSha256SH(pre) -} - -// calcHashOutputs also wants a input index, which it only uses for -// sighash single. If it's not sighash single, just put a 0 or whatever. -func calcHashOutputs( - tx *wire.MsgTx, inIndex int, hType txscript.SigHashType) wire.ShaHash { - if hType == txscript.SigHashNone || - (hType == txscript.SigHashSingle && inIndex <= len(tx.TxOut)) { - var empty [32]byte - return empty - } - if hType == txscript.SigHashSingle { - var buf bytes.Buffer - writeTxOut(&buf, 0, 0, tx.TxOut[inIndex]) - return wire.DoubleSha256SH(buf.Bytes()) - } - - var pre []byte - for _, out := range tx.TxOut { - var buf bytes.Buffer - writeTxOut(&buf, 0, 0, out) - pre = append(pre, buf.Bytes()...) - } - fmt.Printf("calcHashOutputs pre: %x\n", pre) - return wire.DoubleSha256SH(pre) -} - -func main() { - fmt.Printf("sighash 143\n") - - // get previous pkscripts for inputs 0 and 1 from hex - in0spk, err := hex.DecodeString(string(inspk0)) - if err != nil { - log.Fatal(err) - } - in1spk, err := hex.DecodeString(string(inspk1)) - if err != nil { - log.Fatal(err) - } - xpkt, err := hex.DecodeString(string(xpecthash)) - if err != nil { - log.Fatal(err) - } - - // load tx skeleton from local file - fmt.Printf("loading tx from file tx.hex\n") - txhex, err := ioutil.ReadFile("tx.hex") - if err != nil { - log.Fatal(err) - } - - txhex = []byte(strings.TrimSpace(string(txhex))) - txbytes, err := hex.DecodeString(string(txhex)) - if err != nil { - log.Fatal(err) - } - - fmt.Printf("loaded %d byte tx %x\n", len(txbytes), txbytes) - - // make tx - ttx := wire.NewMsgTx() - // deserialize into tx - buf := bytes.NewBuffer(txbytes) - ttx.Deserialize(buf) - - fmt.Printf("=====tx locktime: %x\n", ttx.LockTime) - ttx.TxIn[0].SignatureScript = in0spk - // ttx.TxIn[1].SignatureScript = in1spk - - fmt.Printf(uspv.TxToString(ttx)) - - priv, err := btcec.NewPrivateKey(btcec.S256()) - if err != nil { - log.Fatal(err) - } - // assert flag before writing witness - ttx.Flags = 0x01 - ttx.TxIn[1].Witness, err = txscript.WitnessScript(ttx, 1, inamt1, in1spk, - txscript.SigHashAll, priv, true) - // ttx.TxIn[1].SignatureScript = nil - - hxh := calcWitnessSignatureHash(in1spk, txscript.SigHashAll, ttx, 1, inamt1) - - fmt.Printf("got sigHash %x NON LIB\n", hxh) - fmt.Printf("expect hash %x\n ", xpkt) - fmt.Printf("\n%s\n", uspv.TxToString(ttx)) - - fmt.Printf("loading tx from file xtx.hex\n") - txhex, err = ioutil.ReadFile("xtx.hex") - if err != nil { - log.Fatal(err) - } - txhex = []byte(strings.TrimSpace(string(txhex))) - txbytes, err = hex.DecodeString(string(txhex)) - if err != nil { - log.Fatal(err) - } - // make tx - xtx := wire.NewMsgTx() - // deserialize into tx - buf = bytes.NewBuffer(txbytes) - xtx.Deserialize(buf) - - // ttx.Flags = 0x01 - fmt.Printf("\n%s\n", uspv.TxToString(xtx)) -} - -// pver can be 0, doesn't do anything in these. Same for msg.Version -// writeVarInt serializes val to w using a variable number of bytes depending -// on its value. -func writeVarInt(w io.Writer, pver uint32, val uint64) error { - if val < 0xfd { - _, err := w.Write([]byte{uint8(val)}) - return err - } - - if val <= math.MaxUint16 { - var buf [3]byte - buf[0] = 0xfd - binary.LittleEndian.PutUint16(buf[1:], uint16(val)) - _, err := w.Write(buf[:]) - return err - } - - if val <= math.MaxUint32 { - var buf [5]byte - buf[0] = 0xfe - binary.LittleEndian.PutUint32(buf[1:], uint32(val)) - _, err := w.Write(buf[:]) - return err - } - - var buf [9]byte - buf[0] = 0xff - binary.LittleEndian.PutUint64(buf[1:], val) - _, err := w.Write(buf[:]) - return err -} - -// writeVarBytes serializes a variable length byte array to w as a varInt -// containing the number of bytes, followed by the bytes themselves. -func writeVarBytes(w io.Writer, pver uint32, bytes []byte) error { - slen := uint64(len(bytes)) - err := writeVarInt(w, pver, slen) - if err != nil { - return err - } - - _, err = w.Write(bytes) - if err != nil { - return err - } - return nil -} - -// writeTxOut encodes to into the bitcoin protocol encoding for a transaction -// output (TxOut) to w. -func writeTxOut(w io.Writer, pver uint32, version int32, to *wire.TxOut) error { - var buf [8]byte - binary.LittleEndian.PutUint64(buf[:], uint64(to.Value)) - _, err := w.Write(buf[:]) - if err != nil { - return err - } - - err = writeVarBytes(w, pver, to.PkScript) - if err != nil { - return err - } - return nil -}