diff --git a/deposit_addr.go b/deposit_addr.go index c62ecc4..738f03d 100644 --- a/deposit_addr.go +++ b/deposit_addr.go @@ -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,54 +36,51 @@ 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 +// - '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 } @@ -99,11 +88,11 @@ func EvmDepositSegwitPubkey(pk *PublicKey, lbtcContract, wallet Address, chainId 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 } diff --git a/deposit_addr_test.go b/deposit_addr_test.go index d9e1d6d..c2806c6 100644 --- a/deposit_addr_test.go +++ b/deposit_addr_test.go @@ -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 { + 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() + + 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) + }) + } +} diff --git a/go.mod b/go.mod index 34159dd..087e890 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/btcsuite/btcd v0.24.0 github.com/btcsuite/btcd/btcutil v1.1.5 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 - github.com/ethereum/go-ethereum v1.14.5 + github.com/lombard-finance/ledger-utils v0.2.0 github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.9.0 ) @@ -15,7 +15,6 @@ 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/holiman/uint256 v1.2.4 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect golang.org/x/crypto v0.22.0 // indirect golang.org/x/sys v0.20.0 // indirect diff --git a/go.sum b/go.sum index 4115256..30bfcb0 100644 --- a/go.sum +++ b/go.sum @@ -34,8 +34,6 @@ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeC 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/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/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -50,13 +48,13 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw 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/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -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= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= 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/lombard-finance/ledger-utils v0.2.0 h1:nrIg5g9tu3jH1ViAnvMXqpA00cIs/CjkqU2Rb7Ujqv8= +github.com/lombard-finance/ledger-utils v0.2.0/go.mod h1:a61awHF3EakbFArqyz8UaQt120DUJH3wEOaI671SThs= 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= diff --git a/segwit_tweak_test.go b/segwit_tweak_test.go index 9b04ae9..284395f 100644 --- a/segwit_tweak_test.go +++ b/segwit_tweak_test.go @@ -5,6 +5,8 @@ import ( "encoding/hex" "fmt" "testing" + + "github.com/decred/dcrd/dcrec/secp256k1/v4" ) // known-answer test values generated from reference rust impl diff --git a/tweak_bytes.go b/tweak_bytes.go index bbdb8c5..260593d 100644 --- a/tweak_bytes.go +++ b/tweak_bytes.go @@ -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) }