Link

Copper Unlimited Server

Introduction

This doc has been written for Copper Unlimited (CU) Server usage. CU Server is an alternative to manually signing transactions process via CU Desktop UI. We have adapted the Copper Unlimited Desktop app by adding API endpoints for transaction signing. To do so, you can easily export your wallet’s shards from CU Desktop, add the shards to CU Server and sign transaction requests via its internal API. In the end, we dockerized the solution and published it for your usage. For further information, please check the sections below.

Software Requirements

The Copper Unlimited Server presented as a docker image derived from node:12.16.2-buster-slim. Thus, it’s based heavily on docker, docker-compose and internally runs the NodeJS daemon. Below some information regarding the image:

  • derived from node:12.16.2-buster-slim
  • publicly available at https://hub.docker.com/r/copperexchange/copper-unlimited
  • internally listening to port 3010
  • supports SSL/TLS configuration. To enable please see SSL/TLS section in use cases.

Launching Steps

  1.  In order to sign transactions from CU Server, you need to export your wallet’s shards from CU Desktop via wallet backup functionality. During exporting process the CU Desktop will ask a password to encrypt each wallet’s shard in order to prevent accessing the shard data in case if a potential attacker gains access to the filesystem. At the end of this step, you should have a folder with your encrypted shards.
  2.  Once you have the exported shards in the folder you can start the dockerized CU Server. Before running, please notice that you may need to specify the fields below:
    • a path to your folder with shards
    • a port that CU Server endpoints will listen to. To launch the server, use the command below (for any docker run command reference, see official guidelines):
       docker run --rm -ti \
       -v path_to_your_folder_with_shards:/app/keys/ \
       -p your_port_for_the_cu_server:3010 \
       copperexchange/copper-unlimited:latest
      

Use cases:

Part Sign Transaction

  1. Start Copper Unlimited server
     docker run --rm -ti \
       -v ${PWD}/key-backups/:/app/keys/ \
       -p 3010:3010 \
       copperexchange/copper-unlimited:latest
    

    ${PWD}/key-backups/ is the folder that contains MPC wallets backup from Copper Unlimited.

  2. Create withdraw order (example: move funds from Vault to some WG exchange)

     + POST https://api.copper.co/platform/orders
        
     + Request
       {
         "externalOrderId": "ckdr8xy1l00303g5neu5iunh4",
         "orderType": "withdraw",
         "baseCurrency": "ETH",
         "mainCurrency": "ETH",
         "amount": "0.01",
         "portfolioId": "edd7e0ea-9d00-4538-b187-c14813d7b0ba",  // from account
         "toPortfolioId": "28a26c72-4d72-4f97-9b36-6811ab708216", // to account
         "includeFeeInWithdraw": true,
         "feeLevel": "low"
       } 
          
     + Response 201
       {
         "orderId": "5824946",
         "orderType": "withdraw",
         "amount": "0.01",
         "baseCurrency": "ETH",
         "mainCurrency": "ETH",
         "externalOrderId": "ckdr8xy1l00303g5neu5iunh4",
         "extra": {
           "feeLevel": "low",
           "includeFeeInWithdraw": true,
           "toAddress": "0xf71b4a401734a3f1fe9794003a4956ada7753175",
           "toPortfolioId": "28a26c72-4d72-4f97-9b36-6811ab708216"
         },
         "portfolioId": "edd7e0ea-9d00-4538-b187-c14813d7b0ba",
         "portfolioType": "custody",
         "status": "awaiting-settlement"
       }
    
  3. Start transaction settlement flow

     + PATCH https://api.copper.co/platform/orders/{orderId}
       
     + Headers
       Content-Type: application/vnd.start-custody-settlement+json
        
     + Response 200
       {
         "amount": "0.01",
         "baseCurrency": "ETH",
         "externalOrderId": "ckdr8xy1l00303g5neu5iunh4",
         "extra": {
           "toPortfolioId": "ck8plg2if000f3j5qnnbw13op",
           "transactionRequest": {
             "publicKeyHash": "765188378c75dd61140d765242a0a3d09cd15680a862769e4000f75204fa86de",
             "currency": "ETH",
             "mainCurrency": "ETH",
             "amount": "10000000000000000",
             "toAddress": "0xf71b4a401734a3f1fe9794003a4956ada7753175",
             "includeFee": true,
             "transactionRequest": {
               "fee": "231000000000000",
               "outputs": [
                 {
                   "value": "10000000000000000",
                   "address": "0xf71b4a401734a3f1fe9794003a4956ada7753175"
                 }
               ],
               "gasLimit": "21000",
               "gasPrice": "11000000000",
               "sequence": "4",
               "includeFee": true,
               "nonceNumber": "15"
             }
           },
           "toAddress": "0xf71b4a401734a3f1fe9794003a4956ada7753175",
           "reportingCurrencyRate": "338.15",
           "feesPercent": "0",
           "feeLevel": "low",
           "includeFeeInWithdraw": true,
           "withdrawFee": "0.000231000000000000"
         },
         "mainCurrency": "ETH",
         "orderId": "5824946",
         "orderType": "withdraw",
         "portfolioId": "edd7e0ea-9d00-4538-b187-c14813d7b0ba",
         "portfolioType": "custody",
         "status": "working"
       }
    
  4. Sign transaction in offline copper-unlimited-server

    + POST http://127.0.0.1:3010/signatures
       
    + Headers
      Content-Type: application/json
    
    + Request
      {
        "type": "request",
        "keyPassword": "myTestPaswordFromKeyBackup.1234#",
        "keyFilename": "BTC_My Default Vault_wallet.copper",
          
         // get all this fields from order.extra.transactionRequest, described above
        "signRequest": {
          "publicKeyHash": "765188378c75dd61140d765242a0a3d09cd15680a862769e4000f75204fa86de",
          "currency": "ETH",
          "mainCurrency": "ETH",
          "amount": "10000000000000000",
          "toAddress": "0xf71b4a401734a3f1fe9794003a4956ada7753175",
          "includeFee": true,
          "transactionRequest": {
            "fee": "231000000000000",
            "outputs": [
              {
                "value": "10000000000000000",
                "address": "0xf71b4a401734a3f1fe9794003a4956ada7753175"
              }
            ],
            "gasLimit": "21000",
            "gasPrice": "11000000000",
            "sequence": "4",
            "includeFee": true,
            "nonceNumber": "15"
          }
        }
      }
       
    + Response 200 OK
      {
        "type": "part-signed-tx",
        "value": "AQRwL5FqGrbLaPO4w+NoEq9oZ...R5I/wjEQND/4YmIn+rbN9uh5U/0=",
      }
    
  5. Upload part-signed transaction to Copper

    “masterPassword” required only for Warm Vault tx signing.

     + PATCH https://api.copper.co/platform/orders/{orderId}
        
     + Headers
       Content-Type: application/vnd.upload-part-signed-tx+json
       
     + Request
       {
         "masterPassword": "97dee97560699a639f3cf55c464855eefe97ae97493b242fe01ecdbab39ea463",  // optional, sha256 hash from Withdrawal Password (hex string), required for Warm Vault
       } 
          
     + Request
       {
         "transaction": {
           "type": "part-signed-tx",
           "value": "AQRwL5FqGrbLaPO4w+NoEq9oZ...R5I/wjEQND/4YmIn+rbN9uh5U/0="
         }
       }
       
     + Response 200
       {
         "amount": "0.01",
         "baseCurrency": "ETH",
         "externalOrderId": "ckdr8xy1l00303g5neu5iunh4",
         "extra": {
           "transactionId": "0xc5344c295cffc4db0dfe869bd395ced34e0877b0f139b8939cf2c357ea1599e4",
           ...
         },
         "mainCurrency": "ETH",
         "orderId": "5824946",
         "orderType": "withdraw",
         "portfolioId": "edd7e0ea-9d00-4538-b187-c14813d7b0ba",
         "portfolioType": "custody",
         "status": "processing"
       }
    

Enabling SSL/TLS

To enable SSL/TLS add Volume with your key and certificate as follows:

1.1 Generate custom certs:
```
openssl genrsa -out privatekey.key
openssl req -new -key privatekey.key -out csr.pem
openssl x509 -req -days 9999 -in csr.pem -signkey privatekey.key -out certificate.cert
```   

1.2 Pass it to docker container: 
```js
docker run --rm -ti \
  -v ${PWD}/key-backups/:/app/keys/ \
  -v ${PWD}/ssl/certificate.cert:/app/ssl/certificate.cert \
  -v ${PWD}/ssl/privatekey.key:/app/ssl/privatekey.key \
  -p 3010:3010 \
  copperexchange/copper-unlimited:latest
```