Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PoR demo #2

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
3dparty
.vscode
.env
config.yaml
2 changes: 2 additions & 0 deletions config-example.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
demo:
public-key: "0x..."
30 changes: 30 additions & 0 deletions demo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Demo

This demo shows how to derive a deposit addresses from an address' deposit metadata and the base deposit public key.

## Derive the deposit address

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 `config.yaml`

| Config | Description | Example |
|-----------------|------------------------------------------------------------|--------------------|
| demo.public-key | The public key of Lombard's base deposit key on Cubist. | `0x...` |


### 2. Derive the address
```bash
go run demo/cmd/derive/main.go
```


If the derivation is successful, the outpu will look like
```shell
Address: bc1q29nrqh3cj5q5r0n7yjea6hezkrxhf6nyfv3afz
Addresses match
```
19 changes: 19 additions & 0 deletions demo/address.go
Original file line number Diff line number Diff line change
@@ -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"`
}
14 changes: 14 additions & 0 deletions demo/cmd/derive/example.json
Original file line number Diff line number Diff line change
@@ -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"
}
]
}
98 changes: 98 additions & 0 deletions demo/cmd/derive/main.go
Original file line number Diff line number Diff line change
@@ -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)
Copy link

@koonopek koonopek Nov 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

crucial

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")
}
}
}

}
136 changes: 136 additions & 0 deletions demo/utils.go
Original file line number Diff line number Diff line change
@@ -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)
}
25 changes: 23 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
Loading