@@ -19,6 +19,7 @@ import (
19
19
"time"
20
20
21
21
"github.com/dustin/go-humanize"
22
+ "github.com/filecoin-project/curio/market/mk20"
22
23
"github.com/google/uuid"
23
24
"github.com/ipfs/go-cid"
24
25
"github.com/ipni/go-libipni/maurl"
@@ -42,7 +43,6 @@ import (
42
43
"github.com/filecoin-project/curio/lib/keystore"
43
44
mk12_libp2p "github.com/filecoin-project/curio/market/libp2p"
44
45
"github.com/filecoin-project/curio/market/mk12"
45
-
46
46
"github.com/filecoin-project/lotus/api"
47
47
chain_types "github.com/filecoin-project/lotus/chain/types"
48
48
"github.com/filecoin-project/lotus/chain/wallet"
@@ -1561,3 +1561,254 @@ var dealStatusCmd = &cli.Command{
1561
1561
return nil
1562
1562
},
1563
1563
}
1564
+
1565
+ var mk20Clientcmd = & cli.Command {
1566
+ Name : "mk20-client" ,
1567
+ Usage : "mk20 client for Curio" ,
1568
+ Flags : []cli.Flag {
1569
+ mk12_client_repo ,
1570
+ },
1571
+ Subcommands : []* cli.Command {
1572
+ initCmd ,
1573
+ mk20DealCmd ,
1574
+ },
1575
+ }
1576
+
1577
+ var mk20DealCmd = & cli.Command {
1578
+ Name : "deal" ,
1579
+ Usage : "Make a mk20 deal with Curio" ,
1580
+ Flags : []cli.Flag {
1581
+ & cli.StringFlag {
1582
+ Name : "http-url" ,
1583
+ Usage : "http url to CAR file" ,
1584
+ Required : true ,
1585
+ },
1586
+ & cli.StringSliceFlag {
1587
+ Name : "http-headers" ,
1588
+ Usage : "http headers to be passed with the request (e.g key=value)" ,
1589
+ },
1590
+ & cli.Uint64Flag {
1591
+ Name : "car-size" ,
1592
+ Usage : "size of the CAR file: required for online deals" ,
1593
+ Required : true ,
1594
+ },
1595
+ & cli.StringFlag {
1596
+ Name : "provider" ,
1597
+ Usage : "storage provider on-chain address" ,
1598
+ Required : true ,
1599
+ },
1600
+ & cli.StringFlag {
1601
+ Name : "commp" ,
1602
+ Usage : "commp of the CAR file" ,
1603
+ Required : true ,
1604
+ },
1605
+ & cli.Uint64Flag {
1606
+ Name : "piece-size" ,
1607
+ Usage : "size of the CAR file as a padded piece" ,
1608
+ Required : true ,
1609
+ },
1610
+ & cli.IntFlag {
1611
+ Name : "duration" ,
1612
+ Usage : "duration of the deal in epochs" ,
1613
+ Value : 518400 , // default is 2880 * 180 == 180 days
1614
+ },
1615
+ & cli.BoolFlag {
1616
+ Name : "verified" ,
1617
+ Usage : "whether the deal funds should come from verified client data-cap" ,
1618
+ Value : false ,
1619
+ },
1620
+ & cli.BoolFlag {
1621
+ Name : "indexing" ,
1622
+ Usage : "indicates that an deal should be indexed" ,
1623
+ Value : true ,
1624
+ },
1625
+ & cli.StringFlag {
1626
+ Name : "wallet" ,
1627
+ Usage : "wallet address to be used to initiate the deal" ,
1628
+ },
1629
+ & cli.BoolFlag {
1630
+ Name : "announce" ,
1631
+ Usage : "indicates that deal should be announced to the IPNI(Network Indexer)" ,
1632
+ Value : true ,
1633
+ },
1634
+ },
1635
+ Action : func (cctx * cli.Context ) error {
1636
+ ctx := cctx .Context
1637
+ n , err := Setup (cctx .String (mk12_client_repo .Name ))
1638
+ if err != nil {
1639
+ return err
1640
+ }
1641
+
1642
+ api , closer , err := lcli .GetGatewayAPIV1 (cctx )
1643
+ if err != nil {
1644
+ return fmt .Errorf ("cant setup gateway connection: %w" , err )
1645
+ }
1646
+ defer closer ()
1647
+ if err != nil {
1648
+ return xerrors .Errorf ("cant setup gateway connection: %w" , err )
1649
+ }
1650
+ defer closer ()
1651
+
1652
+ walletAddr , err := n .GetProvidedOrDefaultWallet (ctx , cctx .String ("wallet" ))
1653
+ if err != nil {
1654
+ return err
1655
+ }
1656
+
1657
+ log .Debugw ("selected wallet" , "wallet" , walletAddr )
1658
+
1659
+ maddr , err := address .NewFromString (cctx .String ("provider" ))
1660
+ if err != nil {
1661
+ return err
1662
+ }
1663
+
1664
+ minfo , err := api .StateMinerInfo (ctx , maddr , chain_types .EmptyTSK )
1665
+ if err != nil {
1666
+ return err
1667
+ }
1668
+ if minfo .PeerId == nil {
1669
+ return xerrors .Errorf ("storage provider %s has no peer ID set on-chain" , maddr )
1670
+ }
1671
+
1672
+ var maddrs []multiaddr.Multiaddr
1673
+ for _ , mma := range minfo .Multiaddrs {
1674
+ ma , err := multiaddr .NewMultiaddrBytes (mma )
1675
+ if err != nil {
1676
+ return xerrors .Errorf ("storage provider %s had invalid multiaddrs in their info: %w" , maddr , err )
1677
+ }
1678
+ maddrs = append (maddrs , ma )
1679
+ }
1680
+ if len (maddrs ) == 0 {
1681
+ return xerrors .Errorf ("storage provider %s has no multiaddrs set on-chain" , maddr )
1682
+ }
1683
+
1684
+ addrInfo := & peer.AddrInfo {
1685
+ ID : * minfo .PeerId ,
1686
+ Addrs : maddrs ,
1687
+ }
1688
+
1689
+ log .Debugw ("found storage provider" , "id" , addrInfo .ID , "multiaddrs" , addrInfo .Addrs , "addr" , maddr )
1690
+
1691
+ var hurls []* url.URL
1692
+
1693
+ for _ , ma := range addrInfo .Addrs {
1694
+ hurl , err := maurl .ToURL (ma )
1695
+ if err != nil {
1696
+ return xerrors .Errorf ("failed to convert multiaddr %s to URL: %w" , ma , err )
1697
+ }
1698
+ if hurl .Scheme == "ws" {
1699
+ hurl .Scheme = "http"
1700
+ }
1701
+ if hurl .Scheme == "wss" {
1702
+ hurl .Scheme = "https"
1703
+ }
1704
+ log .Debugw ("converted multiaddr to URL" , "url" , hurl , "multiaddr" , ma .String ())
1705
+ hurls = append (hurls , hurl )
1706
+ }
1707
+
1708
+ commp := cctx .String ("commp" )
1709
+ pieceCid , err := cid .Parse (commp )
1710
+ if err != nil {
1711
+ return xerrors .Errorf ("parsing commp '%s': %w" , commp , err )
1712
+ }
1713
+
1714
+ pieceSize := cctx .Uint64 ("piece-size" )
1715
+ if pieceSize == 0 {
1716
+ return xerrors .Errorf ("must provide piece-size parameter for CAR url" )
1717
+ }
1718
+
1719
+ carFileSize := cctx .Uint64 ("car-size" )
1720
+ if carFileSize == 0 {
1721
+ return xerrors .Errorf ("size of car file cannot be 0" )
1722
+ }
1723
+
1724
+ url , err := url .Parse (cctx .String ("http-url" ))
1725
+ if err != nil {
1726
+ return xerrors .Errorf ("parsing http url: %w" , err )
1727
+ }
1728
+
1729
+ var headers http.Header
1730
+
1731
+ for _ , header := range cctx .StringSlice ("http-headers" ) {
1732
+ sp := strings .Split (header , "=" )
1733
+ if len (sp ) != 2 {
1734
+ return xerrors .Errorf ("malformed http header: %s" , header )
1735
+ }
1736
+ headers .Add (sp [0 ], sp [1 ])
1737
+ }
1738
+
1739
+ d := mk20.DataSource {
1740
+ PieceCID : pieceCid ,
1741
+ Size : abi .PaddedPieceSize (pieceSize ),
1742
+ Format : mk20.PieceDataFormat {
1743
+ Car : & mk20.FormatCar {},
1744
+ },
1745
+ SourceHTTP : & mk20.DataSourceHTTP {
1746
+ RawSize : carFileSize ,
1747
+ URLs : []mk20.HttpUrl {
1748
+ {
1749
+ URL : url .String (),
1750
+ Headers : headers ,
1751
+ Priority : 0 ,
1752
+ Fallback : true ,
1753
+ },
1754
+ },
1755
+ },
1756
+ }
1757
+
1758
+ p := mk20.Products {
1759
+ DDOV1 : & mk20.DDOV1 {
1760
+ Provider : maddr ,
1761
+ Client : walletAddr ,
1762
+ PieceManager : walletAddr ,
1763
+ Duration : abi .ChainEpoch (cctx .Int64 ("duration" )),
1764
+ ContractAddress : cctx .String ("contract-address" ),
1765
+ ContractVerifyMethod : cctx .String ("contract-verify-method" ),
1766
+ ContractVerifyMethodParams : []byte ("test bytes" ),
1767
+ Indexing : cctx .Bool ("indexing" ),
1768
+ AnnounceToIPNI : cctx .Bool ("announce" ),
1769
+ },
1770
+ }
1771
+
1772
+ id , err := mk20 .NewULID ()
1773
+ if err != nil {
1774
+ return err
1775
+ }
1776
+ log .Debugw ("generated deal id" , "id" , id )
1777
+
1778
+ deal := mk20.Deal {
1779
+ Identifier : id ,
1780
+ Data : d ,
1781
+ Products : p ,
1782
+ }
1783
+
1784
+ log .Debugw ("deal" , "deal" , deal )
1785
+
1786
+ body , err := json .Marshal (deal )
1787
+ if err != nil {
1788
+ return err
1789
+ }
1790
+
1791
+ // Try to request all URLs one by one and exit after first success
1792
+ for _ , u := range hurls {
1793
+ s := u .String () + "/market/mk20/store"
1794
+ log .Debugw ("trying to send request to" , "url" , u .String ())
1795
+ req , err := http .NewRequest ("POST" , s , bytes .NewReader (body ))
1796
+ if err != nil {
1797
+ return xerrors .Errorf ("failed to create request: %w" , err )
1798
+ }
1799
+ req .Header .Set ("Content-Type" , "application/cbor" )
1800
+ resp , err := http .DefaultClient .Do (req )
1801
+ if err != nil {
1802
+ log .Warnw ("failed to send request" , "url" , s , "error" , err )
1803
+ continue
1804
+ }
1805
+ defer resp .Body .Close ()
1806
+ if resp .StatusCode != http .StatusOK {
1807
+ log .Warnw ("failed to send request" , "url" , s , "status" , resp .StatusCode , "body" , resp .Body )
1808
+ continue
1809
+ }
1810
+ return nil
1811
+ }
1812
+ return xerrors .Errorf ("failed to send request to any of the URLs" )
1813
+ },
1814
+ }
0 commit comments