Advertisement
Guest User

Untitled

a guest
Jun 18th, 2018
224
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Go 33.79 KB | None | 0 0
  1. // Copyright (c) 2017 The Decred developers
  2. // Use of this source code is governed by an ISC
  3. // license that can be found in the LICENSE file.
  4.  
  5. package main
  6.  
  7. import (
  8.     "bufio"
  9.     "bytes"
  10.     "crypto/rand"
  11.     "crypto/sha256"
  12.     "encoding/hex"
  13.     "encoding/json"
  14.     "errors"
  15.     "flag"
  16.     "fmt"
  17.     "net"
  18.     "os"
  19.     "strconv"
  20.     "strings"
  21.     "time"
  22.  
  23.     "github.com/btcsuite/btcd/chaincfg"
  24.     "github.com/btcsuite/btcd/chaincfg/chainhash"
  25.     rpc "github.com/btcsuite/btcd/rpcclient"
  26.     "github.com/btcsuite/btcd/txscript"
  27.     "github.com/btcsuite/btcd/wire"
  28.     "github.com/btcsuite/btcutil"
  29.     "github.com/btcsuite/btcwallet/wallet/txrules"
  30.     "golang.org/x/crypto/ripemd160"
  31. )
  32.  
  33. const verify = true
  34.  
  35. const secretSize = 32
  36.  
  37. const txVersion = 2
  38.  
  39. var (
  40.     chainParams = &chaincfg.MainNetParams
  41. )
  42.  
  43. var (
  44.     flagset     = flag.NewFlagSet("", flag.ExitOnError)
  45.     connectFlag = flagset.String("s", "localhost", "host[:port] of Bitcoin Core wallet RPC server")
  46.     rpcuserFlag = flagset.String("rpcuser", "", "username for wallet RPC authentication")
  47.     rpcpassFlag = flagset.String("rpcpass", "", "password for wallet RPC authentication")
  48.     testnetFlag = flagset.Bool("testnet", false, "use testnet network")
  49.     regtestFlag = flagset.Bool("regtest", false, "use regtest network")
  50. )
  51.  
  52. // There are two directions that the atomic swap can be performed, as the
  53. // initiator can be on either chain.  This tool only deals with creating the
  54. // Bitcoin transactions for these swaps.  A second tool should be used for the
  55. // transaction on the other chain.  Any chain can be used so long as it supports
  56. // OP_SHA256 and OP_CHECKLOCKTIMEVERIFY.
  57. //
  58. // Example scenerios using bitcoin as the second chain:
  59. //
  60. // Scenerio 1:
  61. //   cp1 initiates (dcr)
  62. //   cp2 participates with cp1 H(S) (btc)
  63. //   cp1 redeems btc revealing S
  64. //     - must verify H(S) in contract is hash of known secret
  65. //   cp2 redeems dcr with S
  66. //
  67. // Scenerio 2:
  68. //   cp1 initiates (btc)
  69. //   cp2 participates with cp1 H(S) (dcr)
  70. //   cp1 redeems dcr revealing S
  71. //     - must verify H(S) in contract is hash of known secret
  72. //   cp2 redeems btc with S
  73.  
  74. func init() {
  75.     flagset.Usage = func() {
  76.         fmt.Println("Usage: btcatomicswap [flags] cmd [cmd args]")
  77.         fmt.Println()
  78.         fmt.Println("Commands:")
  79.         fmt.Println("  initiate <participant address> <amount>")
  80.         fmt.Println("  participate <initiator address> <amount> <secret hash>")
  81.         fmt.Println("  redeem <contract> <contract transaction> <secret>")
  82.         fmt.Println("  refund <contract> <contract transaction>")
  83.         fmt.Println("  extractsecret <redemption transaction> <secret hash>")
  84.         fmt.Println("  auditcontract <contract> <contract transaction>")
  85.         fmt.Println()
  86.         fmt.Println("Flags:")
  87.         flagset.PrintDefaults()
  88.     }
  89. }
  90.  
  91. type command interface {
  92.     runCommand(*rpc.Client) error
  93. }
  94.  
  95. // offline commands don't require wallet RPC.
  96. type offlineCommand interface {
  97.     command
  98.     runOfflineCommand() error
  99. }
  100.  
  101. type initiateCmd struct {
  102.     cp2Addr *btcutil.AddressPubKeyHash
  103.     amount  btcutil.Amount
  104. }
  105.  
  106. type participateCmd struct {
  107.     cp1Addr    *btcutil.AddressPubKeyHash
  108.     amount     btcutil.Amount
  109.     secretHash []byte
  110. }
  111.  
  112. type redeemCmd struct {
  113.     contract   []byte
  114.     contractTx *wire.MsgTx
  115.     secret     []byte
  116. }
  117.  
  118. type refundCmd struct {
  119.     contract   []byte
  120.     contractTx *wire.MsgTx
  121. }
  122.  
  123. type extractSecretCmd struct {
  124.     redemptionTx *wire.MsgTx
  125.     secretHash   []byte
  126. }
  127.  
  128. type auditContractCmd struct {
  129.     contract   []byte
  130.     contractTx *wire.MsgTx
  131. }
  132.  
  133. func main() {
  134.     err, showUsage := run()
  135.     if err != nil {
  136.         fmt.Fprintln(os.Stderr, err)
  137.     }
  138.     if showUsage {
  139.         flagset.Usage()
  140.     }
  141.     if err != nil || showUsage {
  142.         os.Exit(1)
  143.     }
  144. }
  145.  
  146. func checkCmdArgLength(args []string, required int) (nArgs int) {
  147.     if len(args) < required {
  148.         return 0
  149.     }
  150.     for i, arg := range args[:required] {
  151.         if len(arg) != 1 && strings.HasPrefix(arg, "-") {
  152.             return i
  153.         }
  154.     }
  155.     return required
  156. }
  157.  
  158. func run() (err error, showUsage bool) {
  159.     flagset.Parse(os.Args[1:])
  160.     args := flagset.Args()
  161.     if len(args) == 0 {
  162.         return nil, true
  163.     }
  164.     cmdArgs := 0
  165.     switch args[0] {
  166.     case "initiate":
  167.         cmdArgs = 2
  168.     case "participate":
  169.         cmdArgs = 3
  170.     case "redeem":
  171.         cmdArgs = 3
  172.     case "refund":
  173.         cmdArgs = 2
  174.     case "extractsecret":
  175.         cmdArgs = 2
  176.     case "auditcontract":
  177.         cmdArgs = 2
  178.     default:
  179.         return fmt.Errorf("unknown command %v", args[0]), true
  180.     }
  181.     nArgs := checkCmdArgLength(args[1:], cmdArgs)
  182.     flagset.Parse(args[1+nArgs:])
  183.     if nArgs < cmdArgs {
  184.         return fmt.Errorf("%s: too few arguments", args[0]), true
  185.     }
  186.     if flagset.NArg() != 0 {
  187.         return fmt.Errorf("unexpected argument: %s", flagset.Arg(0)), true
  188.     }
  189.  
  190.     if *testnetFlag {
  191.         chainParams = &chaincfg.TestNet3Params
  192.     }
  193.  
  194.     if *regtestFlag {
  195.         chainParams = &chaincfg.RegressionNetParams
  196.     }
  197.  
  198.     var cmd command
  199.     switch args[0] {
  200.     case "initiate":
  201.         cp2Addr, err := btcutil.DecodeAddress(args[1], chainParams)
  202.         if err != nil {
  203.             return fmt.Errorf("failed to decode participant address: %v", err), true
  204.         }
  205.         if !cp2Addr.IsForNet(chainParams) {
  206.             return fmt.Errorf("participant address is not "+
  207.                 "intended for use on %v", chainParams.Name), true
  208.         }
  209.         cp2AddrP2PKH, ok := cp2Addr.(*btcutil.AddressPubKeyHash)
  210.         if !ok {
  211.             return errors.New("participant address is not P2PKH"), true
  212.         }
  213.  
  214.         amountF64, err := strconv.ParseFloat(args[2], 64)
  215.         if err != nil {
  216.             return fmt.Errorf("failed to decode amount: %v", err), true
  217.         }
  218.         amount, err := btcutil.NewAmount(amountF64)
  219.         if err != nil {
  220.             return err, true
  221.         }
  222.  
  223.         cmd = &initiateCmd{cp2Addr: cp2AddrP2PKH, amount: amount}
  224.  
  225.     case "participate":
  226.         cp1Addr, err := btcutil.DecodeAddress(args[1], chainParams)
  227.         if err != nil {
  228.             return fmt.Errorf("failed to decode initiator address: %v", err), true
  229.         }
  230.         if !cp1Addr.IsForNet(chainParams) {
  231.             return fmt.Errorf("initiator address is not "+
  232.                 "intended for use on %v", chainParams.Name), true
  233.         }
  234.         cp1AddrP2PKH, ok := cp1Addr.(*btcutil.AddressPubKeyHash)
  235.         if !ok {
  236.             return errors.New("initiator address is not P2PKH"), true
  237.         }
  238.  
  239.         amountF64, err := strconv.ParseFloat(args[2], 64)
  240.         if err != nil {
  241.             return fmt.Errorf("failed to decode amount: %v", err), true
  242.         }
  243.         amount, err := btcutil.NewAmount(amountF64)
  244.         if err != nil {
  245.             return err, true
  246.         }
  247.  
  248.         secretHash, err := hex.DecodeString(args[3])
  249.         if err != nil {
  250.             return errors.New("secret hash must be hex encoded"), true
  251.         }
  252.         if len(secretHash) != sha256.Size {
  253.             return errors.New("secret hash has wrong size"), true
  254.         }
  255.  
  256.         cmd = &participateCmd{cp1Addr: cp1AddrP2PKH, amount: amount, secretHash: secretHash}
  257.  
  258.     case "redeem":
  259.         contract, err := hex.DecodeString(args[1])
  260.         if err != nil {
  261.             return fmt.Errorf("failed to decode contract: %v", err), true
  262.         }
  263.  
  264.         contractTxBytes, err := hex.DecodeString(args[2])
  265.         if err != nil {
  266.             return fmt.Errorf("failed to decode contract transaction: %v", err), true
  267.         }
  268.         var contractTx wire.MsgTx
  269.         err = contractTx.Deserialize(bytes.NewReader(contractTxBytes))
  270.         if err != nil {
  271.             return fmt.Errorf("failed to decode contract transaction: %v", err), true
  272.         }
  273.  
  274.         secret, err := hex.DecodeString(args[3])
  275.         if err != nil {
  276.             return fmt.Errorf("failed to decode secret: %v", err), true
  277.         }
  278.  
  279.         cmd = &redeemCmd{contract: contract, contractTx: &contractTx, secret: secret}
  280.  
  281.     case "refund":
  282.         contract, err := hex.DecodeString(args[1])
  283.         if err != nil {
  284.             return fmt.Errorf("failed to decode contract: %v", err), true
  285.         }
  286.  
  287.         contractTxBytes, err := hex.DecodeString(args[2])
  288.         if err != nil {
  289.             return fmt.Errorf("failed to decode contract transaction: %v", err), true
  290.         }
  291.         var contractTx wire.MsgTx
  292.         err = contractTx.Deserialize(bytes.NewReader(contractTxBytes))
  293.         if err != nil {
  294.             return fmt.Errorf("failed to decode contract transaction: %v", err), true
  295.         }
  296.  
  297.         cmd = &refundCmd{contract: contract, contractTx: &contractTx}
  298.  
  299.     case "extractsecret":
  300.         redemptionTxBytes, err := hex.DecodeString(args[1])
  301.         if err != nil {
  302.             return fmt.Errorf("failed to decode redemption transaction: %v", err), true
  303.         }
  304.         var redemptionTx wire.MsgTx
  305.         err = redemptionTx.Deserialize(bytes.NewReader(redemptionTxBytes))
  306.         if err != nil {
  307.             return fmt.Errorf("failed to decode redemption transaction: %v", err), true
  308.         }
  309.  
  310.         secretHash, err := hex.DecodeString(args[2])
  311.         if err != nil {
  312.             return errors.New("secret hash must be hex encoded"), true
  313.         }
  314.         if len(secretHash) != sha256.Size {
  315.             return errors.New("secret hash has wrong size"), true
  316.         }
  317.  
  318.         cmd = &extractSecretCmd{redemptionTx: &redemptionTx, secretHash: secretHash}
  319.  
  320.     case "auditcontract":
  321.         contract, err := hex.DecodeString(args[1])
  322.         if err != nil {
  323.             return fmt.Errorf("failed to decode contract: %v", err), true
  324.         }
  325.  
  326.         contractTxBytes, err := hex.DecodeString(args[2])
  327.         if err != nil {
  328.             return fmt.Errorf("failed to decode contract transaction: %v", err), true
  329.         }
  330.         var contractTx wire.MsgTx
  331.         err = contractTx.Deserialize(bytes.NewReader(contractTxBytes))
  332.         if err != nil {
  333.             return fmt.Errorf("failed to decode contract transaction: %v", err), true
  334.         }
  335.  
  336.         cmd = &auditContractCmd{contract: contract, contractTx: &contractTx}
  337.     }
  338.  
  339.     // Offline commands don't need to talk to the wallet.
  340.     if cmd, ok := cmd.(offlineCommand); ok {
  341.         return cmd.runOfflineCommand(), false
  342.     }
  343.  
  344.     connect, err := normalizeAddress(*connectFlag, walletPort(chainParams))
  345.     if err != nil {
  346.         return fmt.Errorf("wallet server address: %v", err), true
  347.     }
  348.  
  349.     connConfig := &rpc.ConnConfig{
  350.         Host:         connect,
  351.         User:         *rpcuserFlag,
  352.         Pass:         *rpcpassFlag,
  353.         DisableTLS:   true,
  354.         HTTPPostMode: true,
  355.     }
  356.     client, err := rpc.New(connConfig, nil)
  357.     if err != nil {
  358.         return fmt.Errorf("rpc connect: %v", err), false
  359.     }
  360.     defer func() {
  361.         client.Shutdown()
  362.         client.WaitForShutdown()
  363.     }()
  364.  
  365.     err = cmd.runCommand(client)
  366.     return err, false
  367. }
  368.  
  369. func normalizeAddress(addr string, defaultPort string) (hostport string, err error) {
  370.     host, port, origErr := net.SplitHostPort(addr)
  371.     if origErr == nil {
  372.         return net.JoinHostPort(host, port), nil
  373.     }
  374.     addr = net.JoinHostPort(addr, defaultPort)
  375.     _, _, err = net.SplitHostPort(addr)
  376.     if err != nil {
  377.         return "", origErr
  378.     }
  379.     return addr, nil
  380. }
  381.  
  382. func walletPort(params *chaincfg.Params) string {
  383.     switch params {
  384.     case &chaincfg.MainNetParams:
  385.         return "8332"
  386.     case &chaincfg.TestNet3Params:
  387.         return "18332"
  388.     case &chaincfg.RegressionNetParams:
  389.         return "18443"
  390.     default:
  391.         return ""
  392.     }
  393. }
  394.  
  395. // createSig creates and returns the serialized raw signature and compressed
  396. // pubkey for a transaction input signature.  Due to limitations of the Bitcoin
  397. // Core RPC API, this requires dumping a private key and signing in the client,
  398. // rather than letting the wallet sign.
  399. func createSig(tx *wire.MsgTx, idx int, pkScript []byte, addr btcutil.Address,
  400.     c *rpc.Client) (sig, pubkey []byte, err error) {
  401.  
  402.     wif, err := c.DumpPrivKey(addr)
  403.     if err != nil {
  404.         return nil, nil, err
  405.     }
  406.     sig, err = txscript.RawTxInSignature(tx, idx, pkScript, txscript.SigHashAll, wif.PrivKey)
  407.     if err != nil {
  408.         return nil, nil, err
  409.     }
  410.     return sig, wif.PrivKey.PubKey().SerializeCompressed(), nil
  411. }
  412.  
  413. // fundRawTransaction calls the fundrawtransaction JSON-RPC method.  It is
  414. // implemented manually as client support is currently missing from the
  415. // btcd/rpcclient package.
  416. func fundRawTransaction(c *rpc.Client, tx *wire.MsgTx, feePerKb btcutil.Amount) (fundedTx *wire.MsgTx, fee btcutil.Amount, err error) {
  417.     var buf bytes.Buffer
  418.     buf.Grow(tx.SerializeSize())
  419.     tx.Serialize(&buf)
  420.     param0, err := json.Marshal(hex.EncodeToString(buf.Bytes()))
  421.     if err != nil {
  422.         return nil, 0, err
  423.     }
  424.     param1, err := json.Marshal(struct {
  425.         FeeRate float64 `json:"feeRate"`
  426.     }{
  427.         FeeRate: feePerKb.ToBTC(),
  428.     })
  429.     if err != nil {
  430.         return nil, 0, err
  431.     }
  432.     params := []json.RawMessage{param0, param1}
  433.     rawResp, err := c.RawRequest("fundrawtransaction", params)
  434.     if err != nil {
  435.         return nil, 0, err
  436.     }
  437.     var resp struct {
  438.         Hex       string  `json:"hex"`
  439.         Fee       float64 `json:"fee"`
  440.         ChangePos float64 `json:"changepos"`
  441.     }
  442.     err = json.Unmarshal(rawResp, &resp)
  443.     if err != nil {
  444.         return nil, 0, err
  445.     }
  446.     fundedTxBytes, err := hex.DecodeString(resp.Hex)
  447.     if err != nil {
  448.         return nil, 0, err
  449.     }
  450.     fundedTx = &wire.MsgTx{}
  451.     err = fundedTx.Deserialize(bytes.NewReader(fundedTxBytes))
  452.     if err != nil {
  453.         return nil, 0, err
  454.     }
  455.     feeAmount, err := btcutil.NewAmount(resp.Fee)
  456.     if err != nil {
  457.         return nil, 0, err
  458.     }
  459.     return fundedTx, feeAmount, nil
  460. }
  461.  
  462. // getFeePerKb queries the wallet for the transaction relay fee/kB to use and
  463. // the minimum mempool relay fee.  It first tries to get the user-set fee in the
  464. // wallet.  If unset, it attempts to find an estimate using estimatefee 6.  If
  465. // both of these fail, it falls back to mempool relay fee policy.
  466. func getFeePerKb(c *rpc.Client) (useFee, relayFee btcutil.Amount, err error) {
  467.     var netInfoResp struct {
  468.         RelayFee float64 `json:"relayfee"`
  469.     }
  470.     var walletInfoResp struct {
  471.         PayTxFee float64 `json:"paytxfee"`
  472.     }
  473.     var estimateResp struct {
  474.         FeeRate float64 `json:"feerate"`
  475.     }
  476.  
  477.     netInfoRawResp, err := c.RawRequest("getnetworkinfo", nil)
  478.     if err == nil {
  479.         err = json.Unmarshal(netInfoRawResp, &netInfoResp)
  480.         if err != nil {
  481.             return 0, 0, err
  482.         }
  483.     }
  484.     walletInfoRawResp, err := c.RawRequest("getwalletinfo", nil)
  485.     if err == nil {
  486.         err = json.Unmarshal(walletInfoRawResp, &walletInfoResp)
  487.         if err != nil {
  488.             return 0, 0, err
  489.         }
  490.     }
  491.  
  492.     relayFee, err = btcutil.NewAmount(netInfoResp.RelayFee)
  493.     if err != nil {
  494.         return 0, 0, err
  495.     }
  496.     payTxFee, err := btcutil.NewAmount(walletInfoResp.PayTxFee)
  497.     if err != nil {
  498.         return 0, 0, err
  499.     }
  500.  
  501.     // Use user-set wallet fee when set and not lower than the network relay
  502.     // fee.
  503.     if payTxFee != 0 {
  504.         maxFee := payTxFee
  505.         if relayFee > maxFee {
  506.             maxFee = relayFee
  507.         }
  508.         return maxFee, relayFee, nil
  509.     }
  510.  
  511.     params := []json.RawMessage{[]byte("6")}
  512.     estimateRawResp, err := c.RawRequest("estimatesmartfee", params)
  513.     if err != nil {
  514.         return 0, 0, err
  515.     }
  516.  
  517.     err = json.Unmarshal(estimateRawResp, &estimateResp)
  518.     if err == nil && estimateResp.FeeRate > 0 {
  519.         useFee, err = btcutil.NewAmount(estimateResp.FeeRate)
  520.         if relayFee > useFee {
  521.             useFee = relayFee
  522.         }
  523.         return useFee, relayFee, err
  524.     }
  525.  
  526.     fmt.Println("warning: falling back to mempool relay fee policy")
  527.     return relayFee, relayFee, nil
  528. }
  529.  
  530. // getRawChangeAddress calls the getrawchangeaddress JSON-RPC method.  It is
  531. // implemented manually as the rpcclient implementation always passes the
  532. // account parameter which was removed in Bitcoin Core 0.15.
  533. func getRawChangeAddress(c *rpc.Client) (btcutil.Address, error) {
  534.     params := []json.RawMessage{[]byte(`"legacy"`)}
  535.     rawResp, err := c.RawRequest("getrawchangeaddress", params)
  536.     if err != nil {
  537.         return nil, err
  538.     }
  539.     var addrStr string
  540.     err = json.Unmarshal(rawResp, &addrStr)
  541.     if err != nil {
  542.         return nil, err
  543.     }
  544.     addr, err := btcutil.DecodeAddress(addrStr, chainParams)
  545.     if err != nil {
  546.         return nil, err
  547.     }
  548.     if !addr.IsForNet(chainParams) {
  549.         return nil, fmt.Errorf("address %v is not intended for use on %v",
  550.             addrStr, chainParams.Name)
  551.     }
  552.     if _, ok := addr.(*btcutil.AddressPubKeyHash); !ok {
  553.         return nil, fmt.Errorf("getrawchangeaddress: address %v is not P2PKH",
  554.             addr)
  555.     }
  556.     return addr, nil
  557. }
  558.  
  559. func promptPublishTx(c *rpc.Client, tx *wire.MsgTx, name string) error {
  560.     reader := bufio.NewReader(os.Stdin)
  561.     for {
  562.         fmt.Printf("Publish %s transaction? [y/N] ", name)
  563.         answer, err := reader.ReadString('\n')
  564.         if err != nil {
  565.             return err
  566.         }
  567.         answer = strings.TrimSpace(strings.ToLower(answer))
  568.  
  569.         switch answer {
  570.         case "y", "yes":
  571.         case "n", "no", "":
  572.             return nil
  573.         default:
  574.             fmt.Println("please answer y or n")
  575.             continue
  576.         }
  577.  
  578.         txHash, err := c.SendRawTransaction(tx, false)
  579.         if err != nil {
  580.             return fmt.Errorf("sendrawtransaction: %v", err)
  581.         }
  582.         fmt.Printf("Published %s transaction (%v)\n", name, txHash)
  583.         return nil
  584.     }
  585. }
  586.  
  587. // contractArgs specifies the common parameters used to create the initiator's
  588. // and participant's contract.
  589. type contractArgs struct {
  590.     them       *btcutil.AddressPubKeyHash
  591.     amount     btcutil.Amount
  592.     locktime   int64
  593.     secretHash []byte
  594. }
  595.  
  596. // builtContract houses the details regarding a contract and the contract
  597. // payment transaction, as well as the transaction to perform a refund.
  598. type builtContract struct {
  599.     contract       []byte
  600.     contractP2SH   btcutil.Address
  601.     contractTxHash *chainhash.Hash
  602.     contractTx     *wire.MsgTx
  603.     contractFee    btcutil.Amount
  604.     refundTx       *wire.MsgTx
  605.     refundFee      btcutil.Amount
  606. }
  607.  
  608. // buildContract creates a contract for the parameters specified in args, using
  609. // wallet RPC to generate an internal address to redeem the refund and to sign
  610. // the payment to the contract transaction.
  611. func buildContract(c *rpc.Client, args *contractArgs) (*builtContract, error) {
  612.     refundAddr, err := getRawChangeAddress(c)
  613.     if err != nil {
  614.         return nil, fmt.Errorf("getrawchangeaddress: %v", err)
  615.     }
  616.     refundAddrH, ok := refundAddr.(interface {
  617.         Hash160() *[ripemd160.Size]byte
  618.     })
  619.     if !ok {
  620.         return nil, errors.New("unable to create hash160 from change address")
  621.     }
  622.  
  623.     contract, err := atomicSwapContract(refundAddrH.Hash160(), args.them.Hash160(),
  624.         args.locktime, args.secretHash)
  625.     if err != nil {
  626.         return nil, err
  627.     }
  628.     contractP2SH, err := btcutil.NewAddressScriptHash(contract, chainParams)
  629.     if err != nil {
  630.         return nil, err
  631.     }
  632.     contractP2SHPkScript, err := txscript.PayToAddrScript(contractP2SH)
  633.     if err != nil {
  634.         return nil, err
  635.     }
  636.  
  637.     feePerKb, minFeePerKb, err := getFeePerKb(c)
  638.     if err != nil {
  639.         return nil, err
  640.     }
  641.  
  642.     unsignedContract := wire.NewMsgTx(txVersion)
  643.     unsignedContract.AddTxOut(wire.NewTxOut(int64(args.amount), contractP2SHPkScript))
  644.     unsignedContract, contractFee, err := fundRawTransaction(c, unsignedContract, feePerKb)
  645.     if err != nil {
  646.         return nil, fmt.Errorf("fundrawtransaction: %v", err)
  647.     }
  648.     contractTx, complete, err := c.SignRawTransaction(unsignedContract)
  649.     if err != nil {
  650.         return nil, fmt.Errorf("signrawtransaction: %v", err)
  651.     }
  652.     if !complete {
  653.         return nil, errors.New("signrawtransaction: failed to completely sign contract transaction")
  654.     }
  655.  
  656.     contractTxHash := contractTx.TxHash()
  657.  
  658.     refundTx, refundFee, err := buildRefund(c, contract, contractTx, feePerKb, minFeePerKb)
  659.     if err != nil {
  660.         return nil, err
  661.     }
  662.  
  663.     return &builtContract{
  664.         contract,
  665.         contractP2SH,
  666.         &contractTxHash,
  667.         contractTx,
  668.         contractFee,
  669.         refundTx,
  670.         refundFee,
  671.     }, nil
  672. }
  673.  
  674. func buildRefund(c *rpc.Client, contract []byte, contractTx *wire.MsgTx, feePerKb, minFeePerKb btcutil.Amount) (
  675.     refundTx *wire.MsgTx, refundFee btcutil.Amount, err error) {
  676.  
  677.     contractP2SH, err := btcutil.NewAddressScriptHash(contract, chainParams)
  678.     if err != nil {
  679.         return nil, 0, err
  680.     }
  681.     contractP2SHPkScript, err := txscript.PayToAddrScript(contractP2SH)
  682.     if err != nil {
  683.         return nil, 0, err
  684.     }
  685.  
  686.     contractTxHash := contractTx.TxHash()
  687.     contractOutPoint := wire.OutPoint{Hash: contractTxHash, Index: ^uint32(0)}
  688.     for i, o := range contractTx.TxOut {
  689.         if bytes.Equal(o.PkScript, contractP2SHPkScript) {
  690.             contractOutPoint.Index = uint32(i)
  691.             break
  692.         }
  693.     }
  694.     if contractOutPoint.Index == ^uint32(0) {
  695.         return nil, 0, errors.New("contract tx does not contain a P2SH contract payment")
  696.     }
  697.  
  698.     refundAddress, err := getRawChangeAddress(c)
  699.     if err != nil {
  700.         return nil, 0, fmt.Errorf("getrawchangeaddress: %v", err)
  701.     }
  702.     refundOutScript, err := txscript.PayToAddrScript(refundAddress)
  703.     if err != nil {
  704.         return nil, 0, err
  705.     }
  706.  
  707.     pushes, err := txscript.ExtractAtomicSwapDataPushes(0, contract)
  708.     if err != nil {
  709.         // expected to only be called with good input
  710.         panic(err)
  711.     }
  712.  
  713.     refundAddr, err := btcutil.NewAddressPubKeyHash(pushes.RefundHash160[:], chainParams)
  714.     if err != nil {
  715.         return nil, 0, err
  716.     }
  717.  
  718.     refundTx = wire.NewMsgTx(txVersion)
  719.     refundTx.LockTime = uint32(pushes.LockTime)
  720.     refundTx.AddTxOut(wire.NewTxOut(0, refundOutScript)) // amount set below
  721.     refundSize := estimateRefundSerializeSize(contract, refundTx.TxOut)
  722.     refundFee = txrules.FeeForSerializeSize(feePerKb, refundSize)
  723.     refundTx.TxOut[0].Value = contractTx.TxOut[contractOutPoint.Index].Value - int64(refundFee)
  724.     if txrules.IsDustOutput(refundTx.TxOut[0], minFeePerKb) {
  725.         return nil, 0, fmt.Errorf("refund output value of %v is dust", btcutil.Amount(refundTx.TxOut[0].Value))
  726.     }
  727.  
  728.     txIn := wire.NewTxIn(&contractOutPoint, nil, nil)
  729.     txIn.Sequence = 0
  730.     refundTx.AddTxIn(txIn)
  731.  
  732.     refundSig, refundPubKey, err := createSig(refundTx, 0, contract, refundAddr, c)
  733.     if err != nil {
  734.         return nil, 0, err
  735.     }
  736.     refundSigScript, err := refundP2SHContract(contract, refundSig, refundPubKey)
  737.     if err != nil {
  738.         return nil, 0, err
  739.     }
  740.     refundTx.TxIn[0].SignatureScript = refundSigScript
  741.  
  742.     if verify {
  743.         e, err := txscript.NewEngine(contractTx.TxOut[contractOutPoint.Index].PkScript,
  744.             refundTx, 0, txscript.StandardVerifyFlags, txscript.NewSigCache(10),
  745.             txscript.NewTxSigHashes(refundTx), contractTx.TxOut[contractOutPoint.Index].Value)
  746.         if err != nil {
  747.             panic(err)
  748.         }
  749.         err = e.Execute()
  750.         if err != nil {
  751.             panic(err)
  752.         }
  753.     }
  754.  
  755.     return refundTx, refundFee, nil
  756. }
  757.  
  758. func sha256Hash(x []byte) []byte {
  759.     h := sha256.Sum256(x)
  760.     return h[:]
  761. }
  762.  
  763. func calcFeePerKb(absoluteFee btcutil.Amount, serializeSize int) float64 {
  764.     return float64(absoluteFee) / float64(serializeSize) / 1e5
  765. }
  766.  
  767. func (cmd *initiateCmd) runCommand(c *rpc.Client) error {
  768.     var secret [secretSize]byte
  769.     _, err := rand.Read(secret[:])
  770.     if err != nil {
  771.         return err
  772.     }
  773.     secretHash := sha256Hash(secret[:])
  774.  
  775.     // locktime after 500,000,000 (Tue Nov  5 00:53:20 1985 UTC) is interpreted
  776.     // as a unix time rather than a block height.
  777.     locktime := time.Now().Add(48 * time.Hour).Unix()
  778.  
  779.     b, err := buildContract(c, &contractArgs{
  780.         them:       cmd.cp2Addr,
  781.         amount:     cmd.amount,
  782.         locktime:   locktime,
  783.         secretHash: secretHash,
  784.     })
  785.     if err != nil {
  786.         return err
  787.     }
  788.  
  789.     refundTxHash := b.refundTx.TxHash()
  790.     contractFeePerKb := calcFeePerKb(b.contractFee, b.contractTx.SerializeSize())
  791.     refundFeePerKb := calcFeePerKb(b.refundFee, b.refundTx.SerializeSize())
  792.  
  793.     fmt.Printf("Secret:      %x\n", secret)
  794.     fmt.Printf("Secret hash: %x\n\n", secretHash)
  795.     fmt.Printf("Contract fee: %v (%0.8f BTC/kB)\n", b.contractFee, contractFeePerKb)
  796.     fmt.Printf("Refund fee:   %v (%0.8f BTC/kB)\n\n", b.refundFee, refundFeePerKb)
  797.     fmt.Printf("Contract (%v):\n", b.contractP2SH)
  798.     fmt.Printf("%x\n\n", b.contract)
  799.     var contractBuf bytes.Buffer
  800.     contractBuf.Grow(b.contractTx.SerializeSize())
  801.     b.contractTx.Serialize(&contractBuf)
  802.     fmt.Printf("Contract transaction (%v):\n", b.contractTxHash)
  803.     fmt.Printf("%x\n\n", contractBuf.Bytes())
  804.     var refundBuf bytes.Buffer
  805.     refundBuf.Grow(b.refundTx.SerializeSize())
  806.     b.refundTx.Serialize(&refundBuf)
  807.     fmt.Printf("Refund transaction (%v):\n", &refundTxHash)
  808.     fmt.Printf("%x\n\n", refundBuf.Bytes())
  809.  
  810.     return promptPublishTx(c, b.contractTx, "contract")
  811. }
  812.  
  813. func (cmd *participateCmd) runCommand(c *rpc.Client) error {
  814.     // locktime after 500,000,000 (Tue Nov  5 00:53:20 1985 UTC) is interpreted
  815.     // as a unix time rather than a block height.
  816.     locktime := time.Now().Add(24 * time.Hour).Unix()
  817.  
  818.     b, err := buildContract(c, &contractArgs{
  819.         them:       cmd.cp1Addr,
  820.         amount:     cmd.amount,
  821.         locktime:   locktime,
  822.         secretHash: cmd.secretHash,
  823.     })
  824.     if err != nil {
  825.         return err
  826.     }
  827.  
  828.     refundTxHash := b.refundTx.TxHash()
  829.     contractFeePerKb := calcFeePerKb(b.contractFee, b.contractTx.SerializeSize())
  830.     refundFeePerKb := calcFeePerKb(b.refundFee, b.refundTx.SerializeSize())
  831.  
  832.     fmt.Printf("Contract fee: %v (%0.8f BTC/kB)\n", b.contractFee, contractFeePerKb)
  833.     fmt.Printf("Refund fee:   %v (%0.8f BTC/kB)\n\n", b.refundFee, refundFeePerKb)
  834.     fmt.Printf("Contract (%v):\n", b.contractP2SH)
  835.     fmt.Printf("%x\n\n", b.contract)
  836.     var contractBuf bytes.Buffer
  837.     contractBuf.Grow(b.contractTx.SerializeSize())
  838.     b.contractTx.Serialize(&contractBuf)
  839.     fmt.Printf("Contract transaction (%v):\n", b.contractTxHash)
  840.     fmt.Printf("%x\n\n", contractBuf.Bytes())
  841.     var refundBuf bytes.Buffer
  842.     refundBuf.Grow(b.refundTx.SerializeSize())
  843.     b.refundTx.Serialize(&refundBuf)
  844.     fmt.Printf("Refund transaction (%v):\n", &refundTxHash)
  845.     fmt.Printf("%x\n\n", refundBuf.Bytes())
  846.  
  847.     return promptPublishTx(c, b.contractTx, "contract")
  848. }
  849.  
  850. func (cmd *redeemCmd) runCommand(c *rpc.Client) error {
  851.     pushes, err := txscript.ExtractAtomicSwapDataPushes(0, cmd.contract)
  852.     if err != nil {
  853.         return err
  854.     }
  855.     if pushes == nil {
  856.         return errors.New("contract is not an atomic swap script recognized by this tool")
  857.     }
  858.     recipientAddr, err := btcutil.NewAddressPubKeyHash(pushes.RecipientHash160[:],
  859.         chainParams)
  860.     if err != nil {
  861.         return err
  862.     }
  863.     contractHash := btcutil.Hash160(cmd.contract)
  864.     contractOut := -1
  865.     for i, out := range cmd.contractTx.TxOut {
  866.         sc, addrs, _, _ := txscript.ExtractPkScriptAddrs(out.PkScript, chainParams)
  867.         if sc == txscript.ScriptHashTy &&
  868.             bytes.Equal(addrs[0].(*btcutil.AddressScriptHash).Hash160()[:], contractHash) {
  869.             contractOut = i
  870.             break
  871.         }
  872.     }
  873.     if contractOut == -1 {
  874.         return errors.New("transaction does not contain a contract output")
  875.     }
  876.  
  877.     addr, err := getRawChangeAddress(c)
  878.     if err != nil {
  879.         return fmt.Errorf("getrawchangeaddres: %v", err)
  880.     }
  881.     outScript, err := txscript.PayToAddrScript(addr)
  882.     if err != nil {
  883.         return err
  884.     }
  885.  
  886.     contractTxHash := cmd.contractTx.TxHash()
  887.     contractOutPoint := wire.OutPoint{
  888.         Hash:  contractTxHash,
  889.         Index: uint32(contractOut),
  890.     }
  891.  
  892.     feePerKb, minFeePerKb, err := getFeePerKb(c)
  893.     if err != nil {
  894.         return err
  895.     }
  896.  
  897.     redeemTx := wire.NewMsgTx(txVersion)
  898.     redeemTx.LockTime = uint32(pushes.LockTime)
  899.     redeemTx.AddTxIn(wire.NewTxIn(&contractOutPoint, nil, nil))
  900.     redeemTx.AddTxOut(wire.NewTxOut(0, outScript)) // amount set below
  901.     redeemSize := estimateRedeemSerializeSize(cmd.contract, redeemTx.TxOut)
  902.     fee := txrules.FeeForSerializeSize(feePerKb, redeemSize)
  903.     redeemTx.TxOut[0].Value = cmd.contractTx.TxOut[contractOut].Value - int64(fee)
  904.     if txrules.IsDustOutput(redeemTx.TxOut[0], minFeePerKb) {
  905.         return fmt.Errorf("redeem output value of %v is dust", btcutil.Amount(redeemTx.TxOut[0].Value))
  906.     }
  907.  
  908.     redeemSig, redeemPubKey, err := createSig(redeemTx, 0, cmd.contract, recipientAddr, c)
  909.     if err != nil {
  910.         return err
  911.     }
  912.     redeemSigScript, err := redeemP2SHContract(cmd.contract, redeemSig, redeemPubKey, cmd.secret)
  913.     if err != nil {
  914.         return err
  915.     }
  916.     redeemTx.TxIn[0].SignatureScript = redeemSigScript
  917.  
  918.     redeemTxHash := redeemTx.TxHash()
  919.     redeemFeePerKb := calcFeePerKb(fee, redeemTx.SerializeSize())
  920.  
  921.     var buf bytes.Buffer
  922.     buf.Grow(redeemTx.SerializeSize())
  923.     redeemTx.Serialize(&buf)
  924.     fmt.Printf("Redeem fee: %v (%0.8f BTC/kB)\n\n", fee, redeemFeePerKb)
  925.     fmt.Printf("Redeem transaction (%v):\n", &redeemTxHash)
  926.     fmt.Printf("%x\n\n", buf.Bytes())
  927.  
  928.     if verify {
  929.         e, err := txscript.NewEngine(cmd.contractTx.TxOut[contractOutPoint.Index].PkScript,
  930.             redeemTx, 0, txscript.StandardVerifyFlags, txscript.NewSigCache(10),
  931.             txscript.NewTxSigHashes(redeemTx), cmd.contractTx.TxOut[contractOut].Value)
  932.         if err != nil {
  933.             panic(err)
  934.         }
  935.         err = e.Execute()
  936.         if err != nil {
  937.             panic(err)
  938.         }
  939.     }
  940.  
  941.     return promptPublishTx(c, redeemTx, "redeem")
  942. }
  943.  
  944. func (cmd *refundCmd) runCommand(c *rpc.Client) error {
  945.     pushes, err := txscript.ExtractAtomicSwapDataPushes(0, cmd.contract)
  946.     if err != nil {
  947.         return err
  948.     }
  949.     if pushes == nil {
  950.         return errors.New("contract is not an atomic swap script recognized by this tool")
  951.     }
  952.  
  953.     feePerKb, minFeePerKb, err := getFeePerKb(c)
  954.     if err != nil {
  955.         return err
  956.     }
  957.  
  958.     refundTx, refundFee, err := buildRefund(c, cmd.contract, cmd.contractTx, feePerKb, minFeePerKb)
  959.     if err != nil {
  960.         return err
  961.     }
  962.     refundTxHash := refundTx.TxHash()
  963.     var buf bytes.Buffer
  964.     buf.Grow(refundTx.SerializeSize())
  965.     refundTx.Serialize(&buf)
  966.  
  967.     refundFeePerKb := calcFeePerKb(refundFee, refundTx.SerializeSize())
  968.  
  969.     fmt.Printf("Refund fee: %v (%0.8f BTC/kB)\n\n", refundFee, refundFeePerKb)
  970.     fmt.Printf("Refund transaction (%v):\n", &refundTxHash)
  971.     fmt.Printf("%x\n\n", buf.Bytes())
  972.  
  973.     return promptPublishTx(c, refundTx, "refund")
  974. }
  975.  
  976. func (cmd *extractSecretCmd) runCommand(c *rpc.Client) error {
  977.     return cmd.runOfflineCommand()
  978. }
  979.  
  980. func (cmd *extractSecretCmd) runOfflineCommand() error {
  981.     // Loop over all pushed data from all inputs, searching for one that hashes
  982.     // to the expected hash.  By searching through all data pushes, we avoid any
  983.     // issues that could be caused by the initiator redeeming the participant's
  984.     // contract with some "nonstandard" or unrecognized transaction or script
  985.     // type.
  986.     for _, in := range cmd.redemptionTx.TxIn {
  987.         pushes, err := txscript.PushedData(in.SignatureScript)
  988.         if err != nil {
  989.             return err
  990.         }
  991.         for _, push := range pushes {
  992.             if bytes.Equal(sha256Hash(push), cmd.secretHash) {
  993.                 fmt.Printf("Secret: %x\n", push)
  994.                 return nil
  995.             }
  996.         }
  997.     }
  998.     return errors.New("transaction does not contain the secret")
  999. }
  1000.  
  1001. func (cmd *auditContractCmd) runCommand(c *rpc.Client) error {
  1002.     return cmd.runOfflineCommand()
  1003. }
  1004.  
  1005. func (cmd *auditContractCmd) runOfflineCommand() error {
  1006.     contractHash160 := btcutil.Hash160(cmd.contract)
  1007.     contractOut := -1
  1008.     for i, out := range cmd.contractTx.TxOut {
  1009.         sc, addrs, _, err := txscript.ExtractPkScriptAddrs(out.PkScript, chainParams)
  1010.         if err != nil || sc != txscript.ScriptHashTy {
  1011.             continue
  1012.         }
  1013.         if bytes.Equal(addrs[0].(*btcutil.AddressScriptHash).Hash160()[:], contractHash160) {
  1014.             contractOut = i
  1015.             break
  1016.         }
  1017.     }
  1018.     if contractOut == -1 {
  1019.         return errors.New("transaction does not contain the contract output")
  1020.     }
  1021.  
  1022.     pushes, err := txscript.ExtractAtomicSwapDataPushes(0, cmd.contract)
  1023.     if err != nil {
  1024.         return err
  1025.     }
  1026.     if pushes == nil {
  1027.         return errors.New("contract is not an atomic swap script recognized by this tool")
  1028.     }
  1029.     if pushes.SecretSize != secretSize {
  1030.         return fmt.Errorf("contract specifies strange secret size %v", pushes.SecretSize)
  1031.     }
  1032.  
  1033.     contractAddr, err := btcutil.NewAddressScriptHash(cmd.contract, chainParams)
  1034.     if err != nil {
  1035.         return err
  1036.     }
  1037.     recipientAddr, err := btcutil.NewAddressPubKeyHash(pushes.RecipientHash160[:],
  1038.         chainParams)
  1039.     if err != nil {
  1040.         return err
  1041.     }
  1042.     refundAddr, err := btcutil.NewAddressPubKeyHash(pushes.RefundHash160[:],
  1043.         chainParams)
  1044.     if err != nil {
  1045.         return err
  1046.     }
  1047.  
  1048.     fmt.Printf("Contract address:        %v\n", contractAddr)
  1049.     fmt.Printf("Contract value:          %v\n", btcutil.Amount(cmd.contractTx.TxOut[contractOut].Value))
  1050.     fmt.Printf("Recipient address:       %v\n", recipientAddr)
  1051.     fmt.Printf("Author's refund address: %v\n\n", refundAddr)
  1052.  
  1053.     fmt.Printf("Secret hash: %x\n\n", pushes.SecretHash[:])
  1054.  
  1055.     if pushes.LockTime >= int64(txscript.LockTimeThreshold) {
  1056.         t := time.Unix(pushes.LockTime, 0)
  1057.         fmt.Printf("Locktime: %v\n", t.UTC())
  1058.         reachedAt := time.Until(t).Truncate(time.Second)
  1059.         if reachedAt > 0 {
  1060.             fmt.Printf("Locktime reached in %v\n", reachedAt)
  1061.         } else {
  1062.             fmt.Printf("Contract refund time lock has expired\n")
  1063.         }
  1064.     } else {
  1065.         fmt.Printf("Locktime: block %v\n", pushes.LockTime)
  1066.     }
  1067.  
  1068.     return nil
  1069. }
  1070.  
  1071. // atomicSwapContract returns an output script that may be redeemed by one of
  1072. // two signature scripts:
  1073. //
  1074. //   <their sig> <their pubkey> <initiator secret> 1
  1075. //
  1076. //   <my sig> <my pubkey> 0
  1077. //
  1078. // The first signature script is the normal redemption path done by the other
  1079. // party and requires the initiator's secret.  The second signature script is
  1080. // the refund path performed by us, but the refund can only be performed after
  1081. // locktime.
  1082. func atomicSwapContract(pkhMe, pkhThem *[ripemd160.Size]byte, locktime int64, secretHash []byte) ([]byte, error) {
  1083.     b := txscript.NewScriptBuilder()
  1084.  
  1085.     b.AddOp(txscript.OP_IF) // Normal redeem path
  1086.     {
  1087.         // Require initiator's secret to be a known length that the redeeming
  1088.         // party can audit.  This is used to prevent fraud attacks between two
  1089.         // currencies that have different maximum data sizes.
  1090.         b.AddOp(txscript.OP_SIZE)
  1091.         b.AddInt64(secretSize)
  1092.         b.AddOp(txscript.OP_EQUALVERIFY)
  1093.  
  1094.         // Require initiator's secret to be known to redeem the output.
  1095.         b.AddOp(txscript.OP_SHA256)
  1096.         b.AddData(secretHash)
  1097.         b.AddOp(txscript.OP_EQUALVERIFY)
  1098.  
  1099.         // Verify their signature is being used to redeem the output.  This
  1100.         // would normally end with OP_EQUALVERIFY OP_CHECKSIG but this has been
  1101.         // moved outside of the branch to save a couple bytes.
  1102.         b.AddOp(txscript.OP_DUP)
  1103.         b.AddOp(txscript.OP_HASH160)
  1104.         b.AddData(pkhThem[:])
  1105.     }
  1106.     b.AddOp(txscript.OP_ELSE) // Refund path
  1107.     {
  1108.         // Verify locktime and drop it off the stack (which is not done by
  1109.         // CLTV).
  1110.         b.AddInt64(locktime)
  1111.         b.AddOp(txscript.OP_CHECKLOCKTIMEVERIFY)
  1112.         b.AddOp(txscript.OP_DROP)
  1113.  
  1114.         // Verify our signature is being used to redeem the output.  This would
  1115.         // normally end with OP_EQUALVERIFY OP_CHECKSIG but this has been moved
  1116.         // outside of the branch to save a couple bytes.
  1117.         b.AddOp(txscript.OP_DUP)
  1118.         b.AddOp(txscript.OP_HASH160)
  1119.         b.AddData(pkhMe[:])
  1120.     }
  1121.     b.AddOp(txscript.OP_ENDIF)
  1122.  
  1123.     // Complete the signature check.
  1124.     b.AddOp(txscript.OP_EQUALVERIFY)
  1125.     b.AddOp(txscript.OP_CHECKSIG)
  1126.  
  1127.     return b.Script()
  1128. }
  1129.  
  1130. // redeemP2SHContract returns the signature script to redeem a contract output
  1131. // using the redeemer's signature and the initiator's secret.  This function
  1132. // assumes P2SH and appends the contract as the final data push.
  1133. func redeemP2SHContract(contract, sig, pubkey, secret []byte) ([]byte, error) {
  1134.     b := txscript.NewScriptBuilder()
  1135.     b.AddData(sig)
  1136.     b.AddData(pubkey)
  1137.     b.AddData(secret)
  1138.     b.AddInt64(1)
  1139.     b.AddData(contract)
  1140.     return b.Script()
  1141. }
  1142.  
  1143. // refundP2SHContract returns the signature script to refund a contract output
  1144. // using the contract author's signature after the locktime has been reached.
  1145. // This function assumes P2SH and appends the contract as the final data push.
  1146. func refundP2SHContract(contract, sig, pubkey []byte) ([]byte, error) {
  1147.     b := txscript.NewScriptBuilder()
  1148.     b.AddData(sig)
  1149.     b.AddData(pubkey)
  1150.     b.AddInt64(0)
  1151.     b.AddData(contract)
  1152.     return b.Script()
  1153. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement