From ba2e53ef194a568c41cf5e9e101525f1e5360f2a Mon Sep 17 00:00:00 2001 From: Olivia Thet Date: Thu, 7 Nov 2024 14:58:11 +0700 Subject: [PATCH 1/2] add simple demo of how to derive deposit address using public key --- .gitignore | 1 + config-example.yaml | 2 + demo/README.md | 83 +++++++++++++++++++++ demo/address.go | 19 +++++ demo/cmd/derive/example.json | 14 ++++ demo/cmd/derive/main.go | 98 +++++++++++++++++++++++++ demo/utils.go | 136 +++++++++++++++++++++++++++++++++++ go.mod | 25 ++++++- go.sum | 67 ++++++++++++++++- 9 files changed, 440 insertions(+), 5 deletions(-) create mode 100644 config-example.yaml create mode 100644 demo/README.md create mode 100644 demo/address.go create mode 100644 demo/cmd/derive/example.json create mode 100644 demo/cmd/derive/main.go create mode 100644 demo/utils.go diff --git a/.gitignore b/.gitignore index bc40948..5881fae 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ 3dparty .vscode .env +config.yaml \ No newline at end of file diff --git a/config-example.yaml b/config-example.yaml new file mode 100644 index 0000000..970996a --- /dev/null +++ b/config-example.yaml @@ -0,0 +1,2 @@ +demo: + public-key: "0x..." \ No newline at end of file diff --git a/demo/README.md b/demo/README.md new file mode 100644 index 0000000..e51ccdd --- /dev/null +++ b/demo/README.md @@ -0,0 +1,83 @@ +# Demo + +This demo shows how to generate and verify proof of ownership for both Lombard hot wallet addresses and deposit addresses. The hot wallet addresses are Taproot addresses (tb1p..., bc1p...) and the deposit addresses are P2WPKH Segwit addresses(tb1q..., bc1q...). For each address, there is 2 steps: + +1. Generate the proof. The proof is a transaction signed by your key with an arbitrarily chosen "challenge" string in the `OP_RETURN` script. The transaction transfers zero value from a set of invalid UTXOs and pays a zero fee, so it cannot be mined. +2. Verify the proof. Since you have the key's address, the proof (the signature), and the challenge string, you can pass these parameters to verify that the signature was made by that key and contains that challenge in the `OP_RETURN` script. + +## Generate the proof + +If you do not have keys or a CubeSigner account, follow the [Keys Setup Guide](./generate-keys-setup.md). Now that you have your keys, you can generate proofs: + +### 1. Configure + +Set the following values in your `golang/config.yaml` + +| Config | Description | Example | +|-----------------|-----------------------------------------|--------------------| +| demo.address | Bitcoin address to generate proof for | `tb1...`, `bc1...` | +| demo.challenge | Challenge message to sign | `TEST` | +| cubist.session | Base64 encoded CubeSigner session token | | +| cubist.key-id | Identifier for the signing key | `Key#Btc...` | +| cubist.role-id | Identifier for the authorization role | `Role#...` | + + +If `demo.address` is a deposit address, it will need additional fields, which you can get via the `deposit_metadata` for `demo.address` at https://mainnet.prod.lombard.finance/api/v1/address. + +| Config | Description | Example | +|-----------------------------|-------------------------------------------|--------------------| +| demo.deposit.to-address | Deposit address' destination EVM address | `0x6c5e839bde85b6381f41e8e374797457c68e630b` | +| demo.deposit.to-blockchain | Deposit address' destination chain | `DESTINATION_BLOCKCHAIN_ETHEREUM` | +| demo.deposit.referral | Deposit address' referral | `lombard` | +| demo.deposit.nonce | Deposit address' nonce | `0` | + + +### 2. Sign the proof +```bash +cd golang +go run demo/cmd/prove/main.go +``` + + +This prints the signature of the proof. You will need this signature for proof verification. + + +## Verify the proof + +To verify proof of ownership, you will need the address and signature from the proof generation. You will also need the challenge; if you didn't specifically pass a challenge string, the default challenge string is "LOMBARD PROOF OF OWNERSHIP". + + +### 1. Configure +Set the following values in `golang/config.yaml` + +| Config | Description | Example | +|-----------------|-----------------------------------------|--------------------| +| demo.address | Bitcoin address to verify proof for | `tb1...`, `bc1...` | +| demo.challenge | Challenge message to verify for | `TEST` | +| demo.signature | Bitcoin signature to verify proof for | | + + +### 2. Verify the proof signature +``` +cd golang +go run demo/cmd/verify/main.go +``` + +If the proof is successfully verified, the output will look like +``` +LOMBARD PROOF OF OWNERSHIP +Successfully verified proof +``` + +## How verification works + +As you can see in the demo.go code, we first convert the `signature` (the raw deserialized signature) string to a `wire.MsgTx` struct. Then we set the `address`, `signature`, and `challenge` in the `ProofData` struct. We call `Verify()` on this struct which does the following: + +1. Determine the address type. +2. Parses the signature and public key. +3. Converts the public key to a tweaked Taproot/Segwit address. +4. Checks that address matches the expected address. +5. Verifies the signature. +6. Checks that the challenge in the `OP_RETURN` script matches the expected challenge. +7. Returns true and no error if all these checks pass. + diff --git a/demo/address.go b/demo/address.go new file mode 100644 index 0000000..39f91ba --- /dev/null +++ b/demo/address.go @@ -0,0 +1,19 @@ +package demo + +type Address struct { + BTCAddress string `json:"btc_address"` + Type string `json:"type"` + DepositMetadata DepositMetadata `json:"deposit_metadata"` + CreatedAt string `json:"created_at"` +} + +type DepositMetadata struct { + ToAddress string `json:"to_address"` + ToBlockchain string `json:"to_blockchain"` + Referral string `json:"referral"` + Nonce uint32 `json:"nonce"` +} + +type AddressList struct { + Addresses []Address `json:"addresses"` +} diff --git a/demo/cmd/derive/example.json b/demo/cmd/derive/example.json new file mode 100644 index 0000000..3d4d102 --- /dev/null +++ b/demo/cmd/derive/example.json @@ -0,0 +1,14 @@ +{ + "addresses": [ + { + "btc_address": "bc1q29nrqh3cj5q5r0n7yjea6hezkrxhf6nyfv3afz", + "type": "ADDRESS_TYPE_DEPOSIT", + "deposit_metadata": { + "to_address": "0x57f9672ba603251c9c03b36cabdbbca7ca8cfcf4", + "to_blockchain": "DESTINATION_BLOCKCHAIN_ETHEREUM", + "referral": "lombard" + }, + "created_at": "1729794078" + } + ] +} \ No newline at end of file diff --git a/demo/cmd/derive/main.go b/demo/cmd/derive/main.go new file mode 100644 index 0000000..69ca611 --- /dev/null +++ b/demo/cmd/derive/main.go @@ -0,0 +1,98 @@ +package main + +import ( + "encoding/hex" + "encoding/json" + "fmt" + "os" + "strings" + + "github.com/btcsuite/btcd/chaincfg" + "github.com/decred/dcrd/dcrec/secp256k1/v4" + "github.com/ethereum/go-ethereum/common" + deposit_address "github.com/lombard-finance/deposit-address" + utils "github.com/lombard-finance/deposit-address/demo" + "github.com/spf13/viper" +) + +// In top level project folder, run `go run demo/cmd/derive/main.go`. +func main() { + // Initialize configuration + utils.InitConfig() + + // Read value from config.yaml file + // This is the public key for the base deposit key on Cubist + basePubKey := viper.GetString("demo.public-key") + if basePubKey == "" { + fmt.Println("Missing `demo.public-key`") + return + } + + // Get this from the API instead of this example file, e.g. + // https://mainnet.prod.lombard.finance/api/v1/address?to_address=0x57F9672bA603251C9C03B36cabdBBcA7Ca8Cfcf4&to_blockchain=DESTINATION_BLOCKCHAIN_ETHEREUM&limit=1&offset=0&asc=false&referralId=lombard + data, err := os.ReadFile("./demo/cmd/derive/example.json") + if err != nil { + fmt.Println("Error reading file:", err) + return + } + + var addressList utils.AddressList + err = json.Unmarshal(data, &addressList) + if err != nil { + fmt.Println("Error parsing JSON:", err) + return + } + + for _, addr := range addressList.Addresses { + // get this from config + params := &chaincfg.MainNetParams + + // wallet address on destination chain + walletAddr := common.HexToAddress(addr.DepositMetadata.ToAddress) + + auxData, err := deposit_address.ComputeAuxDataV0(addr.DepositMetadata.Nonce, []byte(addr.DepositMetadata.Referral)) + if err != nil { + fmt.Println("Error computing aux data:", err) + return + } + + // parse public key of base deposit key + pkBytes, err := hex.DecodeString(strings.TrimPrefix(basePubKey, "0x")) + if err != nil { + fmt.Println("Decoding public key hex:", err) + return + } + + pk, err := secp256k1.ParsePubKey(pkBytes) + if err != nil { + fmt.Println("Error decoding SEC1 encoded pubkey:", err) + return + } + + if addr.DepositMetadata.ToBlockchain == "DESTINATION_BLOCKCHAIN_ETHEREUM" { + // for ethereum mainnet, LBTC contract address is 0x8236a87084f8B84306f72007F36F2618A5634494. get this from config. + lbtcContractAddrEthMainnet := [20]byte{0x82, 0x36, 0xa8, 0x70, 0x84, 0xf8, 0xB8, 0x43, 0x06, 0xf7, 0x20, 0x07, 0xF3, 0x6F, 0x26, 0x18, 0xA5, 0x63, 0x44, 0x94} + lbtcContractAddr := common.BytesToAddress(lbtcContractAddrEthMainnet[:20]) + + // for ethereum mainnet, chain id is 1. get this from config. + chainId := [32]byte{} + chainId[31] = 1 + + // derive the address + derivedAddr, err := deposit_address.EvmDepositSegwitAddr(pk, lbtcContractAddr, walletAddr, chainId[:], auxData[:], params) + if err != nil { + fmt.Println("Error tweaking address:", err) + return + } + + fmt.Println("Address:", derivedAddr) + + if addr.BTCAddress != derivedAddr { + fmt.Printf("Derived address (%s) doesn't match expected (%s)", derivedAddr, addr.BTCAddress) + } else { + fmt.Println("Addresses match") + } + } + } + +} diff --git a/demo/utils.go b/demo/utils.go new file mode 100644 index 0000000..613cade --- /dev/null +++ b/demo/utils.go @@ -0,0 +1,136 @@ +package demo + +import ( + "fmt" + "log" + "path/filepath" + "runtime" + "strings" + + "github.com/btcsuite/btcd/chaincfg" + "github.com/pkg/errors" + "github.com/spf13/viper" +) + +type AddressType string + +const ( + Unknown AddressType = "Unknown" + Taproot AddressType = "Taproot" + SegWit AddressType = "Segwit" +) + +func InitConfig() { + // Get the directory of the current file + _, filename, _, ok := runtime.Caller(0) + if !ok { + log.Fatal("No caller information") + } + + // Go up two directories from the current file location + currentDir := filepath.Dir(filename) + projectRoot := filepath.Join(currentDir, "..") + configPath := filepath.Join(projectRoot, "config.yaml") + + // For debugging + fmt.Printf("Looking for config at: %s\n", configPath) + + viper.SetConfigFile(configPath) + + // Set default values before reading the config file + viper.SetDefault("demo.challenge", "LOMBARD PROOF OF OWNERSHIP") + + if err := viper.ReadInConfig(); err != nil { + log.Fatalf("Error reading config file: %v", err) + } + + // Print which config file was used (for debugging) + fmt.Printf("Using config file: %s\n", viper.ConfigFileUsed()) +} + +func CheckAddressTypes(addresses []string) (AddressType, error) { + if len(addresses) == 0 { + return Unknown, fmt.Errorf("empty address list") + } + + // Get type of first address as reference + expectedType := GetAddressType(addresses[0]) + if expectedType == Unknown { + return Unknown, fmt.Errorf("invalid address format: %s", addresses[0]) + } + + // Check all other addresses match the same type + for i, addr := range addresses[1:] { + currentType := GetAddressType(addr) + if currentType == Unknown { + return Unknown, fmt.Errorf("invalid address format at index %d: %s", i+1, addr) + } + if currentType != expectedType { + return Unknown, fmt.Errorf("address type mismatch: expected %s, got %s at index %d: %s", + expectedType, currentType, i+1, addr) + } + } + + return expectedType, nil +} + +func GetAddressType(address string) AddressType { + if isTaproot(address) { + return Taproot + } + + if isSegWit(address) { + return SegWit + } + + return Unknown +} + +func isTaproot(address string) bool { + return strings.HasPrefix(address, "bc1p") || strings.HasPrefix(address, "tb1p") +} + +func isSegWit(address string) bool { + return (strings.HasPrefix(address, "bc1") || strings.HasPrefix(address, "tb1")) && + !strings.HasPrefix(address, "bc1p") && !strings.HasPrefix(address, "tb1p") +} + +func CheckNetworkParams(addresses []string) (*chaincfg.Params, error) { + if len(addresses) == 0 { + return nil, fmt.Errorf("empty address list") + } + + // Get network of first address as reference + expectedNet, err := GetNetworkParams(addresses[0]) + if err != nil || expectedNet == nil { + return nil, fmt.Errorf("invalid address network: %s", addresses[0]) + } + + // Check all other addresses match the same network + for i, addr := range addresses[1:] { + currentNet, err := GetNetworkParams(addr) + if err == nil || currentNet == nil { + return nil, fmt.Errorf("invalid address network at index %d: %s", i+1, addr) + } + if currentNet != expectedNet { + return nil, fmt.Errorf("address network mismatch: expected %v, got %v at index %d: %s", + expectedNet, currentNet, i+1, addr) + } + } + + return expectedNet, nil +} + +func GetNetworkParams(address string) (*chaincfg.Params, error) { + // Check for mainnet prefix (bc1) + if strings.HasPrefix(address, "bc1") { + return &chaincfg.MainNetParams, nil + } + + // Check for testnet/signet prefix (tb1) + if strings.HasPrefix(address, "tb1") { + return &chaincfg.SigNetParams, nil + } + + return nil, errors.Errorf("unsupported address format: %s", address) +} diff --git a/go.mod b/go.mod index 34159dd..cf72f19 100644 --- a/go.mod +++ b/go.mod @@ -5,19 +5,40 @@ go 1.22 require ( github.com/btcsuite/btcd v0.24.0 github.com/btcsuite/btcd/btcutil v1.1.5 + github.com/btcsuite/btcd/btcutil/psbt v1.1.9 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 github.com/ethereum/go-ethereum v1.14.5 github.com/pkg/errors v0.9.1 + github.com/spf13/viper v1.19.0 github.com/stretchr/testify v1.9.0 ) require ( github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect + github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/decred/dcrd/crypto/blake256 v1.0.1 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect github.com/holiman/uint256 v1.2.4 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/cast v1.6.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + go.uber.org/atomic v1.9.0 // indirect + go.uber.org/multierr v1.9.0 // indirect golang.org/x/crypto v0.22.0 // indirect + golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect golang.org/x/sys v0.20.0 // indirect + golang.org/x/text v0.14.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 4115256..67cee68 100644 --- a/go.sum +++ b/go.sum @@ -12,10 +12,13 @@ github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9Ur github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE= github.com/btcsuite/btcd/btcutil v1.1.5 h1:+wER79R5670vs/ZusMTF1yTcRYE5GUsFbdjdisflzM8= github.com/btcsuite/btcd/btcutil v1.1.5/go.mod h1:PSZZ4UitpLBWzxGd5VGOrLnmOjtPP/a6HaFo12zMs00= +github.com/btcsuite/btcd/btcutil/psbt v1.1.9 h1:UmfOIiWMZcVMOLaN+lxbbLSuoINGS1WmK1TZNI0b4yk= +github.com/btcsuite/btcd/btcutil/psbt v1.1.9/go.mod h1:ehBEvU91lxSlXtA+zZz3iFYx7Yq9eqnKx4/kSrnsvMY= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6sjybR934QNHSJZPTQ= github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= @@ -27,17 +30,24 @@ github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtE github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= +github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= +github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= github.com/ethereum/go-ethereum v1.14.5 h1:szuFzO1MhJmweXjoM5nSAeDvjNUH3vIQoMzzQnfvjpw= github.com/ethereum/go-ethereum v1.14.5/go.mod h1:VEDGGhSxY7IEjn98hJRFXl/uFvpRgbIIf2PpXiyGGgc= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= @@ -49,7 +59,11 @@ github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEW github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU= github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= @@ -57,6 +71,14 @@ github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -66,20 +88,54 @@ github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5 github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= +github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= +github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= +go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -100,6 +156,8 @@ golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -109,9 +167,12 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= From a29b16d6d5adb666f087aeff44ec9371c1f722f6 Mon Sep 17 00:00:00 2001 From: Olivia Thet Date: Thu, 7 Nov 2024 15:06:10 +0700 Subject: [PATCH 2/2] update README --- demo/README.md | 85 ++++++++++---------------------------------------- 1 file changed, 16 insertions(+), 69 deletions(-) diff --git a/demo/README.md b/demo/README.md index e51ccdd..4eb7451 100644 --- a/demo/README.md +++ b/demo/README.md @@ -1,83 +1,30 @@ # Demo -This demo shows how to generate and verify proof of ownership for both Lombard hot wallet addresses and deposit addresses. The hot wallet addresses are Taproot addresses (tb1p..., bc1p...) and the deposit addresses are P2WPKH Segwit addresses(tb1q..., bc1q...). For each address, there is 2 steps: +This demo shows how to derive a deposit addresses from an address' deposit metadata and the base deposit public key. -1. Generate the proof. The proof is a transaction signed by your key with an arbitrarily chosen "challenge" string in the `OP_RETURN` script. The transaction transfers zero value from a set of invalid UTXOs and pays a zero fee, so it cannot be mined. -2. Verify the proof. Since you have the key's address, the proof (the signature), and the challenge string, you can pass these parameters to verify that the signature was made by that key and contains that challenge in the `OP_RETURN` script. +## Derive the deposit address -## Generate the proof - -If you do not have keys or a CubeSigner account, follow the [Keys Setup Guide](./generate-keys-setup.md). Now that you have your keys, you can generate proofs: +You will need the following to derive the deposit addresses: +- The deposit address metadata from the Lombard public API. For mainnet, all the paginated deposit addresses are availalbe at https://mainnet.prod.lombard.finance/api/v1/address. To get a specific address, like that in our example, you can go to https://mainnet.prod.lombard.finance/api/v1/address?to_address=0x57F9672bA603251C9C03B36cabdBBcA7Ca8Cfcf4&to_blockchain=DESTINATION_BLOCKCHAIN_ETHEREUM&limit=1&offset=0&asc=false&referralId=lombard. +- The Lombard base deposit public key. Lombard's base deposit key is stored on Cubist on and for each environment, we can provide you its public key. You must configure this in `config.yaml` by following the instructions below. ### 1. Configure -Set the following values in your `golang/config.yaml` - -| Config | Description | Example | -|-----------------|-----------------------------------------|--------------------| -| demo.address | Bitcoin address to generate proof for | `tb1...`, `bc1...` | -| demo.challenge | Challenge message to sign | `TEST` | -| cubist.session | Base64 encoded CubeSigner session token | | -| cubist.key-id | Identifier for the signing key | `Key#Btc...` | -| cubist.role-id | Identifier for the authorization role | `Role#...` | - +Set the following values in your `config.yaml` -If `demo.address` is a deposit address, it will need additional fields, which you can get via the `deposit_metadata` for `demo.address` at https://mainnet.prod.lombard.finance/api/v1/address. +| Config | Description | Example | +|-----------------|------------------------------------------------------------|--------------------| +| demo.public-key | The public key of Lombard's base deposit key on Cubist. | `0x...` | -| Config | Description | Example | -|-----------------------------|-------------------------------------------|--------------------| -| demo.deposit.to-address | Deposit address' destination EVM address | `0x6c5e839bde85b6381f41e8e374797457c68e630b` | -| demo.deposit.to-blockchain | Deposit address' destination chain | `DESTINATION_BLOCKCHAIN_ETHEREUM` | -| demo.deposit.referral | Deposit address' referral | `lombard` | -| demo.deposit.nonce | Deposit address' nonce | `0` | - -### 2. Sign the proof +### 2. Derive the address ```bash -cd golang -go run demo/cmd/prove/main.go -``` - - -This prints the signature of the proof. You will need this signature for proof verification. - - -## Verify the proof - -To verify proof of ownership, you will need the address and signature from the proof generation. You will also need the challenge; if you didn't specifically pass a challenge string, the default challenge string is "LOMBARD PROOF OF OWNERSHIP". - - -### 1. Configure -Set the following values in `golang/config.yaml` - -| Config | Description | Example | -|-----------------|-----------------------------------------|--------------------| -| demo.address | Bitcoin address to verify proof for | `tb1...`, `bc1...` | -| demo.challenge | Challenge message to verify for | `TEST` | -| demo.signature | Bitcoin signature to verify proof for | | - - -### 2. Verify the proof signature -``` -cd golang -go run demo/cmd/verify/main.go +go run demo/cmd/derive/main.go ``` -If the proof is successfully verified, the output will look like -``` -LOMBARD PROOF OF OWNERSHIP -Successfully verified proof -``` - -## How verification works - -As you can see in the demo.go code, we first convert the `signature` (the raw deserialized signature) string to a `wire.MsgTx` struct. Then we set the `address`, `signature`, and `challenge` in the `ProofData` struct. We call `Verify()` on this struct which does the following: - -1. Determine the address type. -2. Parses the signature and public key. -3. Converts the public key to a tweaked Taproot/Segwit address. -4. Checks that address matches the expected address. -5. Verifies the signature. -6. Checks that the challenge in the `OP_RETURN` script matches the expected challenge. -7. Returns true and no error if all these checks pass. +If the derivation is successful, the outpu will look like +```shell +Address: bc1q29nrqh3cj5q5r0n7yjea6hezkrxhf6nyfv3afz +Addresses match +``` \ No newline at end of file