-
Notifications
You must be signed in to change notification settings - Fork 3
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
Support multi-ecosystem derivation and sui addresses #5
base: main
Are you sure you want to change the base?
Changes from all commits
e1fe639
9a46df3
0e8ac00
2fbf0b8
e9f39c9
ec6c545
f894e53
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -5,24 +5,16 @@ import ( | |||||
"hash" | ||||||
|
||||||
"github.com/btcsuite/btcd/chaincfg" | ||||||
eth "github.com/ethereum/go-ethereum/common" | ||||||
"github.com/lombard-finance/ledger-utils/address" | ||||||
"github.com/lombard-finance/ledger-utils/chainid" | ||||||
"github.com/pkg/errors" | ||||||
) | ||||||
|
||||||
type Address = eth.Address | ||||||
type Sha256 = hash.Hash | ||||||
|
||||||
// Chain type tags | ||||||
// | ||||||
// These tags are used to distinguish deposit addresses for different chain types | ||||||
const ( | ||||||
ChainIdSize int = 32 | ||||||
EvmTag uint8 = 0 | ||||||
// TODO define more chain-type identifiers | ||||||
) | ||||||
|
||||||
const ( | ||||||
DepositAddrTag = "LombardDepositAddr" | ||||||
DepositAddrTag = "LombardDepositAddr" | ||||||
DeprecatedChainTag = byte(0) | ||||||
) | ||||||
|
||||||
// Create a tagged hasher used to compute Lombard deposit addresses | ||||||
|
@@ -44,66 +36,63 @@ func depositHasher() Sha256 { | |||||
return h | ||||||
} | ||||||
|
||||||
// EvmDepositTweak Compute the tweak bytes for an EVM deposit address. | ||||||
// DepositTweak Compute the tweak bytes for a deposit address. | ||||||
// | ||||||
// This is defined as | ||||||
// This is generally defined as | ||||||
// | ||||||
// taggedHash( AuxData || EvmTag || ChainId || LBTCAddress || WalletAddress ) | ||||||
// taggedHash( AuxData || ChainId || LBTCAddress || WalletAddress ) | ||||||
// | ||||||
// where 'taggedHash' is a sha256 instance as returned by 'depositHasher()', | ||||||
// 'EvmTag' is defined above, 'ChainId' is serialized as 32 big-endian bytes, | ||||||
// LBTCAddress and WalletAddress are 20-byte EVM addresses, and AuxData is a | ||||||
// 32-byte value encoding chain-agnostic auxiliary data. | ||||||
func EvmDepositTweak(lbtcContract, wallet Address, chainId, auxData []byte) ([]byte, error) { | ||||||
// where 'taggedHash' is a sha256 instance as returned by 'depositHasher()', 'ChainId' is a 32 bytes | ||||||
// big-endian identifier of the chain, LBTCAddress and WalletAddress are byte arrays representing | ||||||
// the respective addresses on the selected chain, and AuxData is a 32-byte value encoding | ||||||
// chain-agnostic auxiliary data. | ||||||
func DepositTweak(lbtcContract, wallet address.Address, chainId chainid.LChainId, auxData []byte) ([]byte, error) { | ||||||
if len(auxData) != AuxDataSize { | ||||||
return nil, errors.Errorf("wrong size for auxData (got %v, want %v)", len(auxData), AuxDataSize) | ||||||
} | ||||||
if len(chainId) != ChainIdSize { | ||||||
return nil, errors.Errorf("wrong size for chainId (got %v, want %v)", len(chainId), ChainIdSize) | ||||||
} | ||||||
|
||||||
h := depositHasher() | ||||||
|
||||||
// aux data (32 bytes) | ||||||
h.Write(auxData[:]) | ||||||
|
||||||
// EVM tag (1 byte) | ||||||
h.Write([]byte{EvmTag}) | ||||||
// 1 byte tag previously used to select chain, now deprecated and constant | ||||||
// for backward compatibility | ||||||
h.Write([]byte{DeprecatedChainTag}) | ||||||
|
||||||
// EVM chain-id (32 bytes) | ||||||
// we zero-pad if `chainId` is less than 32 bytes and error if it is more. | ||||||
h.Write(chainId[:]) | ||||||
// chain-id (32 bytes) as defined by Lombard documentation | ||||||
h.Write(chainId.Bytes()) | ||||||
|
||||||
// LBTC contract address (20 bytes) | ||||||
// LBTC contract address | ||||||
h.Write(lbtcContract.Bytes()) | ||||||
|
||||||
// Destination wallet address (20 bytes) | ||||||
// Destination wallet address | ||||||
h.Write(wallet.Bytes()) | ||||||
|
||||||
return h.Sum(nil), nil | ||||||
} | ||||||
|
||||||
// EvmDepositSegwitPubkey Compute the segwit public key to be used for an EVM deposit. | ||||||
// DepositSegwitPubkey Compute the segwit public key to be used for a deposit. | ||||||
// | ||||||
// - 'pk' is the base (untweaked) public key to tweak | ||||||
// - 'lbtcContract' is the EVM address of the destination LBTC bridge contract | ||||||
// - 'wallet' is the EVM address that will claim this deposit | ||||||
// - 'chainId' is the chain id for the target EVM chain | ||||||
func EvmDepositSegwitPubkey(pk *PublicKey, lbtcContract, wallet Address, chainId, auxData []byte) (*PublicKey, error) { | ||||||
// - 'lbtcContract' is the address of the LBTC contract or object on the destination chain | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
// - 'wallet' is the address that will claim the deposit on the destination chain | ||||||
// - 'chainId' is the chain id for the target chain as defined in the Lombard documentation | ||||||
func DepositSegwitPubkey(pk *PublicKey, lbtcContract, wallet address.Address, chainId chainid.LChainId, auxData []byte) (*PublicKey, error) { | ||||||
// compute tweak bytes | ||||||
tweakBytes, err := EvmDepositTweak(lbtcContract, wallet, chainId, auxData) | ||||||
tweakBytes, err := DepositTweak(lbtcContract, wallet, chainId, auxData) | ||||||
if err != nil { | ||||||
return nil, err | ||||||
} | ||||||
|
||||||
return TweakPublicKey(pk, tweakBytes) | ||||||
} | ||||||
|
||||||
// EvmDepositSegwitAddr Compute the segwit deposit address to be used for an EVM deposit. | ||||||
// See EvmDepositSegwitPubkey doc for argument descriptions. | ||||||
func EvmDepositSegwitAddr(pk *PublicKey, bridge, wallet Address, chainId, auxData []byte, net *chaincfg.Params) (string, error) { | ||||||
// DepositSegwitAddr Compute the segwit deposit address to be used for a deposit on the specified chain. | ||||||
// See depositSegwitPubkey doc for argument descriptions. | ||||||
func DepositSegwitAddr(pk *PublicKey, bridge, wallet address.Address, chainId chainid.LChainId, auxData []byte, net *chaincfg.Params) (string, error) { | ||||||
// compute the pubkey | ||||||
tpk, err := EvmDepositSegwitPubkey(pk, bridge, wallet, chainId, auxData) | ||||||
tpk, err := DepositSegwitPubkey(pk, bridge, wallet, chainId, auxData) | ||||||
if err != nil { | ||||||
return "", err | ||||||
} | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,11 +5,14 @@ import ( | |
"encoding/binary" | ||
"encoding/hex" | ||
"fmt" | ||
"github.com/decred/dcrd/dcrec/secp256k1/v4" | ||
"testing" | ||
|
||
"github.com/decred/dcrd/dcrec/secp256k1/v4" | ||
"github.com/lombard-finance/ledger-utils/address" | ||
"github.com/lombard-finance/ledger-utils/chainid" | ||
"github.com/stretchr/testify/require" | ||
|
||
"github.com/btcsuite/btcd/chaincfg" | ||
eth "github.com/ethereum/go-ethereum/common" | ||
) | ||
|
||
type TestVal struct { | ||
|
@@ -194,29 +197,32 @@ func TestEthTweakValueRustKat(t *testing.T) { | |
v4 := sha256.Sum256(v3[:]) | ||
hashVal = v4 | ||
|
||
lbtcContractAddr := eth.BytesToAddress(v1[:20]) | ||
walletAddr := eth.BytesToAddress(v2[:20]) | ||
lbtcContract, err := address.NewEvmAddress(v1[:20]) | ||
require.NoError(t, err, "error in test configuration for lbtc contract") | ||
wallet, err := address.NewEvmAddress(v2[:20]) | ||
require.NoError(t, err, "error in test configuration for wallet") | ||
chainIdU64 := binary.BigEndian.Uint64(v3[:8]) | ||
var chainId [32]byte | ||
binary.BigEndian.PutUint64(chainId[24:], chainIdU64) | ||
var chainIdBytes [32]byte | ||
binary.BigEndian.PutUint64(chainIdBytes[24:], chainIdU64) | ||
chainId, _ := chainid.NewLChainId(chainIdBytes[:]) | ||
auxData := v4 | ||
|
||
// check tweak result | ||
tweak, err := EvmDepositTweak(lbtcContractAddr, walletAddr, chainId[:], auxData[:]) | ||
tweak, err := DepositTweak(lbtcContract, wallet, chainId, auxData[:]) | ||
if err != nil { | ||
panic(fmt.Sprintf("error computing deposit tweak: %v", err)) | ||
} | ||
tweakString := hex.EncodeToString(tweak) | ||
|
||
// check deposit pubkey result | ||
tpk, err := EvmDepositSegwitPubkey(pk, lbtcContractAddr, walletAddr, chainId[:], auxData[:]) | ||
tpk, err := DepositSegwitPubkey(pk, lbtcContract, wallet, chainId, auxData[:]) | ||
if err != nil { | ||
panic(fmt.Sprintf("error tweaking pubkey: %v", err)) | ||
} | ||
tpkString := hex.EncodeToString(tpk.SerializeCompressed()) | ||
|
||
// check segwit address | ||
segwitAddr, err := EvmDepositSegwitAddr(pk, lbtcContractAddr, walletAddr, chainId[:], auxData[:], params) | ||
segwitAddr, err := DepositSegwitAddr(pk, lbtcContract, wallet, chainId, auxData[:], params) | ||
if err != nil { | ||
panic(fmt.Sprintf("error tweaking addr: %v", err)) | ||
} | ||
|
@@ -237,3 +243,99 @@ func TestEthTweakValueRustKat(t *testing.T) { | |
} | ||
} | ||
} | ||
|
||
var referenceValues = []struct { | ||
hashxtree marked this conversation as resolved.
Show resolved
Hide resolved
|
||
testLabel string | ||
rootDepositKey string | ||
auxData string | ||
lbtcContract string | ||
wallet string | ||
chainId string | ||
expectedTweak string | ||
expectedPubkey string | ||
expectedSegwitAddr string | ||
btcParams chaincfg.Params | ||
}{ | ||
{ | ||
testLabel: "Sui Testnet - 1", | ||
rootDepositKey: "0x043dcf7a68429b23a0396ca61c1ab243ccbbcc629ff04c59394458d6db5dd2bb159e0b7a71ef07247b59a0a21b1f1eaee61a40064ade423e926f38550065a43587", | ||
auxData: "2137aefeb756a435f07fceff39a061bd2a062b617bd8857e9c32b44ef2596bc8", // ComputeAuxDataV0(0, [32]byte{0...}) | ||
lbtcContract: "54945cd3d15c0012d35a92ed6f1f373157216fc6bdc5bd79b03ee86da3ca455b", | ||
wallet: "0d3c73069aef96e8a1d209e2c96ddefc4b911d025932e414db201be70f0ae15e", | ||
chainId: "010000000000000000000000000000000000000000000000000000004c78adac", | ||
expectedTweak: "c5f14fe401d015c4ea34632e6d775b751e1925fe718e6b339d2a74689b3a0609", | ||
expectedPubkey: "02a2188a8c2449e16c3f50aab677c8be90916a7c7e6ab25a88222fc846257c28fb", | ||
expectedSegwitAddr: "tb1q9sjdz0vpnsshhule5kkxvfggvq8lznpck4tay0", | ||
btcParams: chaincfg.SigNetParams, | ||
}, | ||
{ | ||
testLabel: "Sui Testnet - 2", | ||
rootDepositKey: "0x043dcf7a68429b23a0396ca61c1ab243ccbbcc629ff04c59394458d6db5dd2bb159e0b7a71ef07247b59a0a21b1f1eaee61a40064ade423e926f38550065a43587", | ||
auxData: "2137aefeb756a435f07fceff39a061bd2a062b617bd8857e9c32b44ef2596bc8", // ComputeAuxDataV0(0, [32]byte{0...}) | ||
lbtcContract: "54945cd3d15c0012d35a92ed6f1f373157216fc6bdc5bd79b03ee86da3ca455b", | ||
wallet: "5e9ae2ae1c76cb14be16cd2d521f8200c95cc94ab30947c61ade11a0a6439d28", | ||
chainId: "010000000000000000000000000000000000000000000000000000004c78adac", | ||
expectedTweak: "a6d4eb9bcfa5c683513b06fd531184792767f5355402ce23c6dab417696f6392", | ||
expectedPubkey: "0393e2e2e1acc9a702d8b62e990ed9eff4e36589c6cd44e48101a2d3c3c58d5abb", | ||
expectedSegwitAddr: "tb1q8uwc6au5765r9jttj949dg6f2qzcqt58svy8c0", | ||
btcParams: chaincfg.SigNetParams, | ||
}, | ||
{ | ||
testLabel: "Sui Mainnet - 1", | ||
rootDepositKey: "0x043dcf7a68429b23a0396ca61c1ab243ccbbcc629ff04c59394458d6db5dd2bb159e0b7a71ef07247b59a0a21b1f1eaee61a40064ade423e926f38550065a43587", | ||
auxData: "2137aefeb756a435f07fceff39a061bd2a062b617bd8857e9c32b44ef2596bc8", // ComputeAuxDataV0(0, [32]byte{0...}) | ||
lbtcContract: "54945cd3d15c0012d35a92ed6f1f373157216fc6bdc5bd79b03ee86da3ca455b", | ||
wallet: "0d3c73069aef96e8a1d209e2c96ddefc4b911d025932e414db201be70f0ae15e", | ||
chainId: "0100000000000000000000000000000000000000000000000000000035834a8a", | ||
expectedTweak: "35b7205e7d5f1b077091f3164e5daed121f4bb27799c57d9acd976a4044a18bd", | ||
expectedPubkey: "02744518bc0dafc22c494f7dc9ec780fa6d9ae53d3be720ee003f672edfba1063b", | ||
expectedSegwitAddr: "bc1qk5xk9gfr5r8l57ma2euyyvc8h7kc9etlfz2w98", | ||
btcParams: chaincfg.MainNetParams, | ||
}, | ||
{ | ||
testLabel: "Sui Mainnet - 2", | ||
rootDepositKey: "0x043dcf7a68429b23a0396ca61c1ab243ccbbcc629ff04c59394458d6db5dd2bb159e0b7a71ef07247b59a0a21b1f1eaee61a40064ade423e926f38550065a43587", | ||
auxData: "2137aefeb756a435f07fceff39a061bd2a062b617bd8857e9c32b44ef2596bc8", // ComputeAuxDataV0(0, [32]byte{0...}) | ||
lbtcContract: "54945cd3d15c0012d35a92ed6f1f373157216fc6bdc5bd79b03ee86da3ca455b", | ||
wallet: "5e9ae2ae1c76cb14be16cd2d521f8200c95cc94ab30947c61ade11a0a6439d28", | ||
chainId: "0100000000000000000000000000000000000000000000000000000035834a8a", | ||
expectedTweak: "e454f631af7c2f235c372be61a63c58a1b94832e0240cf1a06b9e657df5d9c13", | ||
expectedPubkey: "034a915f6ac8d6c6920a754392338aa2f41a4070d30564d6af3749d80a9b58eb81", | ||
expectedSegwitAddr: "bc1qagvmd7y5x5hkkn6avva5mv0thhlnn6ktuh5ppd", | ||
btcParams: chaincfg.MainNetParams, | ||
}, | ||
} | ||
|
||
func TestWithReferenceValues(t *testing.T) { | ||
// just an initial seed to generate constant data for all tests | ||
hashVal := sha256.Sum256([]byte("segwit_lombard_tweak_test_rs")) | ||
pk := secp256k1.PrivKeyFromBytes(hashVal[:]).PubKey() | ||
Comment on lines
+310
to
+312
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. shouldn't you read this public key from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @russanto @hashxtree any thoughts here? Thanks so much! |
||
|
||
for _, rf := range referenceValues { | ||
t.Run(rf.testLabel, func(t *testing.T) { | ||
lbtcContract, err := address.NewSuiAddressFromHex(rf.lbtcContract) | ||
require.NoError(t, err) | ||
wallet, err := address.NewSuiAddressFromHex(rf.wallet) | ||
require.NoError(t, err) | ||
chainId, err := chainid.NewLChainIdFromHex(rf.chainId) | ||
require.NoError(t, err) | ||
auxDataBytes, err := hex.DecodeString(rf.auxData) | ||
require.NoError(t, err) | ||
|
||
// check tweak result | ||
tweak, err := DepositTweak(lbtcContract, wallet, chainId, auxDataBytes) | ||
require.NoError(t, err, "error on deposit tweak calculation") | ||
require.Equal(t, rf.expectedTweak, hex.EncodeToString(tweak)) | ||
|
||
// check deposit pubkey result | ||
tpk, err := DepositSegwitPubkey(pk, lbtcContract, wallet, chainId, auxDataBytes) | ||
require.NoError(t, err, "error tweaking the public key") | ||
require.Equal(t, rf.expectedPubkey, hex.EncodeToString(tpk.SerializeCompressed())) | ||
|
||
// check segwit address | ||
segwitAddr, err := DepositSegwitAddr(pk, lbtcContract, wallet, chainId, auxDataBytes, &rf.btcParams) | ||
require.NoError(t, err, "error deriving address") | ||
require.Equal(t, rf.expectedSegwitAddr, segwitAddr) | ||
}) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,38 +1,32 @@ | ||
package deposit_address | ||
|
||
import ( | ||
eth "github.com/ethereum/go-ethereum/common" | ||
"github.com/lombard-finance/ledger-utils/address" | ||
"github.com/lombard-finance/ledger-utils/chainid" | ||
"github.com/pkg/errors" | ||
) | ||
|
||
type BlockchainType string | ||
|
||
const ( | ||
BlockchainTypeEvm BlockchainType = "evm" | ||
) | ||
|
||
// CalcTweakBytes Compute the tweakBytes for a given request, dispatching on `blockchainType` | ||
func CalcTweakBytes( | ||
blockchainType BlockchainType, | ||
chainId [32]byte, | ||
toAddress, lbtcAddress, auxData []byte, | ||
chainId chainid.LChainId, | ||
toAddress, lbtcAddress address.Address, | ||
auxData []byte, | ||
) ([]byte, error) { | ||
|
||
switch blockchainType { | ||
case BlockchainTypeEvm: | ||
// evm chain uses 20-byte address | ||
if len(lbtcAddress) != 20 { | ||
return nil, errors.Errorf("bad LbtcAddress (got %d bytes, expected 20)", len(lbtcAddress)) | ||
} | ||
|
||
lbtcAddr := eth.BytesToAddress(lbtcAddress) | ||
if len(toAddress) != 20 { | ||
return nil, errors.Errorf("bad ToAddress (got %d bytes, expected 20)", len(toAddress)) | ||
} | ||
|
||
depositAddr := eth.BytesToAddress(toAddress) | ||
return EvmDepositTweak(lbtcAddr, depositAddr, chainId[:], auxData) | ||
default: | ||
return nil, errors.Errorf("unsupported blockchain type: %s", blockchainType) | ||
if chainId.Ecosystem() != toAddress.Ecosystem() { | ||
return nil, errors.Errorf( | ||
"ecosystem mismatch between chain (%s) and to address (%s:%s)", | ||
chainId.Ecosystem().String(), | ||
toAddress.Ecosystem().String(), | ||
toAddress.String(), | ||
) | ||
} | ||
if chainId.Ecosystem() != lbtcAddress.Ecosystem() { | ||
return nil, errors.Errorf( | ||
"ecosystem mismatch between chain (%s) and LBTC address (%s:%s)", | ||
chainId.Ecosystem().String(), | ||
lbtcAddress.Ecosystem().String(), | ||
lbtcAddress.String(), | ||
) | ||
} | ||
return DepositTweak(lbtcAddress, toAddress, chainId, auxData) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LChainId
is better to show that it's something custom