From ba91650106fb7bc98aceeed1ba35d7860561abe9 Mon Sep 17 00:00:00 2001 From: mollkeith Date: Thu, 23 Jan 2025 23:18:34 +0800 Subject: [PATCH 1/5] update deploy script for password --- README.MD | 26 ++++++++++++++---------- app/arbiter/arbiter/arbiter.go | 31 ++++++++--------------------- app/arbiter/crypto/keystore.go | 18 +++++++++++++++++ app/arbiter/main.go | 20 ++++++++++++++++--- docker/docker_run_arbiter_signer.sh | 4 +++- docker/dockerfile | 5 ++--- docs/deploy_loan_arbiter.md | 6 ++++-- docs/deploy_loan_arbiter.sh | 14 ++++++------- 8 files changed, 74 insertions(+), 50 deletions(-) create mode 100644 app/arbiter/crypto/keystore.go diff --git a/README.MD b/README.MD index bd53edc..32f92a3 100644 --- a/README.MD +++ b/README.MD @@ -72,23 +72,27 @@ $ make ### 3. prepare keystore to keyFilePath -1. create **btcKey.json** **escKey.json** and put it into **keyFilePath** +1. create **btcKey** **escKey** and put it into **keyFilePath** btcKey is used to sign request_arbitration_btc_tx escKey is used to submit arbitration signature to esc arbiter contract, gas fee(esc ELA) is needed 2. keysotre file need to set hex encoded private key - btcKey.json - ```json - { - "privKey": "[HEX_PRIV_KEY]" - } + use [encrypt.sh](https://github.com/BeL2Labs/Arbiter_Signer/blob/main/docs/encrypt.sh) to create btc keystore: + ```shell + ~ chmod a+x encrypt.sh + ~ ./encrypt.sh + ~ > YOUR_HEX_PRIV_KEY + ~ > YOUR_PASSWORD + ~ > btcKey ``` - escKey.json - ```json - { - "privKey": "[HEX_PRIV_KEY]" - } + use [encrypt.sh](https://github.com/BeL2Labs/Arbiter_Signer/blob/main/docs/encrypt.sh) to create esc keystore: + ```shell + ~ chmod a+x encrypt.sh + ~ ./encrypt.sh + ~ > YOUR_HEX_PRIV_KEY + ~ > YOUR_PASSWORD + ~ > escKey ``` ### 4. run arbiter diff --git a/app/arbiter/arbiter/arbiter.go b/app/arbiter/arbiter/arbiter.go index a33cb51..e060cfa 100644 --- a/app/arbiter/arbiter/arbiter.go +++ b/app/arbiter/arbiter/arbiter.go @@ -8,7 +8,6 @@ import ( "crypto/sha256" "encoding/gob" "encoding/hex" - "encoding/json" "log" "os" "path/filepath" @@ -30,6 +29,7 @@ import ( "github.com/BeL2Labs/Arbiter_Signer/app/arbiter/config" "github.com/BeL2Labs/Arbiter_Signer/app/arbiter/contract" "github.com/BeL2Labs/Arbiter_Signer/app/arbiter/contract/events" + "github.com/BeL2Labs/Arbiter_Signer/app/arbiter/crypto" ) const DELAY_BLOCK uint64 = 3 @@ -49,25 +49,21 @@ type Arbiter struct { logger *log.Logger } -func NewArbiter(ctx context.Context, config *config.Config) *Arbiter { - escData, err := os.ReadFile(config.EscKeyFilePath) +func NewArbiter(ctx context.Context, config *config.Config, password string) *Arbiter { + escPrivKey, err := crypto.GetKeyFromKeystore(config.EscKeyFilePath, password) if err != nil { g.Log().Fatal(ctx, "get esc keyfile error", err, " keystore path ", config.EscKeyFilePath) } - var escAccount account - err = json.Unmarshal(escData, &escAccount) - if err != nil { - g.Log().Fatal(ctx, "Unmarshal keyfile error", err, " content ", string(escData)) + escAccount := account{ + PrivateKey: escPrivKey, } - arbiterData, err := os.ReadFile(config.ArbiterKeyFilePath) + arbiterPrivKey, err := crypto.GetKeyFromKeystore(config.ArbiterKeyFilePath, password) if err != nil { g.Log().Fatal(ctx, "get arbiter keyfile error", err, " keystore path ", config.ArbiterKeyFilePath) } - var arbiterAccount account - err = json.Unmarshal(arbiterData, &arbiterAccount) - if err != nil { - g.Log().Fatal(ctx, "Unmarshal keyfile error", err, " content ", string(arbiterData)) + arbiterAccount := account{ + PrivateKey: arbiterPrivKey, } err = createDir(config) @@ -114,17 +110,6 @@ func (v *Arbiter) listenESCContract() { startHeight = v.config.ESCStartHeight } - keyfile := v.config.EscKeyFilePath - data, err := os.ReadFile(keyfile) - if err != nil { - g.Log().Fatal(v.ctx, "get keyfile error", err, " private key path ", keyfile) - } - var a account - err = json.Unmarshal(data, &a) - if err != nil { - g.Log().Fatal(v.ctx, "Unmarshal keyfile error", err, " content ", string(data)) - } - v.escNode.Start(startHeight) } diff --git a/app/arbiter/crypto/keystore.go b/app/arbiter/crypto/keystore.go new file mode 100644 index 0000000..82b8166 --- /dev/null +++ b/app/arbiter/crypto/keystore.go @@ -0,0 +1,18 @@ +// Copyright (c) 2025 The bel2 developers + +package crypto + +import ( + "fmt" + "os/exec" +) + +func GetKeyFromKeystore(encryptedFile string, password string) (string, error) { + cmd := exec.Command("openssl", "enc", "-d", "-aes-256-cbc", "-in", encryptedFile, "-pass", fmt.Sprintf("pass:%s", password)) + output, err := cmd.Output() + if err != nil { + return "", err + } + + return string(output), nil +} diff --git a/app/arbiter/main.go b/app/arbiter/main.go index c132a75..57c8052 100644 --- a/app/arbiter/main.go +++ b/app/arbiter/main.go @@ -4,6 +4,7 @@ package main import ( "context" + "flag" "fmt" "os" "path/filepath" @@ -20,6 +21,19 @@ import ( ) func main() { + passwordPtr := flag.String("p", "", "Specify the password") + flag.Parse() + var password string + if *passwordPtr == "" { + fmt.Print("please input key password: ") + _, err := fmt.Scanln(&password) + if err != nil { + fmt.Println("read password error:", err) + os.Exit(1) + } + } else { + password = *passwordPtr + } if len(os.Args) > 1 { operation := os.Args[1] @@ -41,7 +55,7 @@ func main() { wg.Add(1) // start arbiter g.Log().Info(ctx, "Starting arbiter...") - arb := arbiter.NewArbiter(ctx, initConfig(ctx)) + arb := arbiter.NewArbiter(ctx, initConfig(ctx), password) arb.Start() wg.Wait() } @@ -116,8 +130,8 @@ func initConfig(ctx context.Context) *config.Config { g.Log().Info(ctx, "keyFilePath:", keyFilePath) // if want to submit to ESC contract successfully, need to use esc ela as gas. - escKeyFilePath := gfile.Join(keyFilePath, "escKey.json") - arbiterKeyFilePath := gfile.Join(keyFilePath, "btcKey.json") + escKeyFilePath := gfile.Join(keyFilePath, "escKey") + arbiterKeyFilePath := gfile.Join(keyFilePath, "btcKey") logPath := gfile.Join(dataPath, "logs/") loanPath := gfile.Join(dataPath, "loan/") loanNeedSignReqPath := gfile.Join(loanPath, "request/") diff --git a/docker/docker_run_arbiter_signer.sh b/docker/docker_run_arbiter_signer.sh index 078927e..a8c922f 100644 --- a/docker/docker_run_arbiter_signer.sh +++ b/docker/docker_run_arbiter_signer.sh @@ -2,9 +2,11 @@ read -p "please input Arbiter address: " arbiter_address read -p "please input Arbiter btc private key: " arbiter_btc_private_key read -p "please input Arbiter esc private key: " arbiter_esc_private_key +read -p "please set keystore password: " keystore_password docker run -d \ -e ARBITER_BTC_PRIVATE_KEY="$arbiter_btc_private_key" \ -e ARBITER_ESC_PRIVATE_KEY="$arbiter_esc_private_key" \ -e ARBITER_ADDRESS="$arbiter_address" \ - mollkeith/arbiter-signer:v0.0.1 \ No newline at end of file + -e ARBITER_KEYPASS="$keystore_password" \ + mollkeith/arbiter-signer:v0.0.2 \ No newline at end of file diff --git a/docker/dockerfile b/docker/dockerfile index 6c23c66..316c86e 100644 --- a/docker/dockerfile +++ b/docker/dockerfile @@ -6,6 +6,5 @@ RUN cd ~ RUN wget https://download.bel2.org/loan-arbiter/deploy_loan_arbiter.sh RUN chmod a+x deploy_loan_arbiter.sh RUN echo $ARBITER_ADDRESS -RUN echo $ARBITER_BTC_PRIVATE_KEY -RUN echo $ARBITER_ESC_PRIVATE_KEY -ENTRYPOINT ["/bin/bash", "-c", "./deploy_loan_arbiter.sh \"$ARBITER_ADDRESS\" \"$ARBITER_BTC_PRIVATE_KEY\" \"$ARBITER_ESC_PRIVATE_KEY\"; tail -f /dev/null"] +RUN echo $ARBITER_KEYPASS +ENTRYPOINT ["/bin/bash", "-c", "./deploy_loan_arbiter.sh \"$ARBITER_ADDRESS\" \"$ARBITER_BTC_PRIVATE_KEY\" \"$ARBITER_ESC_PRIVATE_KEY\" \"$ARBITER_KEYPASS\"; tail -f /dev/null"] diff --git a/docs/deploy_loan_arbiter.md b/docs/deploy_loan_arbiter.md index 1acde31..62c81dc 100644 --- a/docs/deploy_loan_arbiter.md +++ b/docs/deploy_loan_arbiter.md @@ -20,7 +20,7 @@ 5. Execute deploy script ```shell - ./deploy_loan_arbiter.sh [your_arbiter_esc_address] [hex_encoded_btc_private_key] [hex_encoded_esc_private_key] + ./deploy_loan_arbiter.sh [your_arbiter_esc_address] [hex_encoded_btc_private_key] [hex_encoded_esc_private_key] [your_keystore_password] ``` replace ***[your_arbiter_esc_address]*** with your esc arbiter address, not operator address, with "0x" at the begining. @@ -28,9 +28,11 @@ replace ***[hex_encoded_esc_private_key]*** with your own esc operator private key, without "0x" at the begining. + replace ***[your_keystore_password]*** with your own keystore password. + For example: ```shell - ./deploy_loan_arbiter.sh 0x0262aB0ED65373cC855C34529fDdeAa0e686D913 0123456789abcdef015522dd7fee2104750cb5c0be9d06d42348cf9b65c253cb0 0123456789abcdef015522dd7fee2104750cb5c0be9d06d42348cf9b65c253cb0 + ./deploy_loan_arbiter.sh 0x0262aB0ED65373cC855C34529fDdeAa0e686D913 0123456789abcdef015522dd7fee2104750cb5c0be9d06d42348cf9b65c253cb0 0123456789abcdef015522dd7fee2104750cb5c0be9d06d42348cf9b65c253cb0 mypassword ``` esc private key used to submit arbiter signature to esc contract, need to have enough ESC ELA! diff --git a/docs/deploy_loan_arbiter.sh b/docs/deploy_loan_arbiter.sh index 9f2a575..694c2d7 100644 --- a/docs/deploy_loan_arbiter.sh +++ b/docs/deploy_loan_arbiter.sh @@ -81,10 +81,10 @@ deploy_arbiter() #mv conf/config.yaml . sed -i "s/0x0262aB0ED65373cC855C34529fDdeAa0e686D913/$1/g" config.yaml - #prepare key json - mv btcKey.json escKey.json keys/ - sed -i "s/hex_encoded_private_key/$2/g" keys/btcKey.json - sed -i "s/hex_encoded_private_key/$3/g" keys/escKey.json + #prepare keystore + openssl enc -aes-256-cbc -salt -in <(echo -n $2) -out "btcKey" -pass pass:$4 + openssl enc -aes-256-cbc -salt -in <(echo -n $3) -out "escKey" -pass pass:$4 + mv btcKey escKey keys/ #prepare arbiter if [ "$(uname -m)" == "armv6l" ] || [ "$(uname -m)" == "armv7l" ] || [ "$(uname -m)" == "aarch64" ]; then @@ -95,7 +95,7 @@ deploy_arbiter() echo_info "Replacing arbiter.." cp -v loan-arbiter-linux-arm64/arbiter ~/loan_arbiter/ echo_info "Starting arbtier..." - ./arbiter --gf.gcfg.file=config.yaml > $SCRIPT_PATH/data/logs/arbiter.log 2>&1 & + ./arbiter --gf.gcfg.file=config.yaml -p $4 > $SCRIPT_PATH/data/logs/arbiter.log 2>&1 & #rm -f loan-arbiter-linux-arm64.tgz conf.tgz else @@ -106,7 +106,7 @@ deploy_arbiter() echo_info "Replacing arbiter.." cp -v loan-arbiter-linux-x86_64/arbiter ~/loan_arbiter/ echo_info "Starting arbtier..." - ./arbiter --gf.gcfg.file=config.yaml > $SCRIPT_PATH/data/logs/arbiter.log 2>&1 & + ./arbiter --gf.gcfg.file=config.yaml -p $4 > $SCRIPT_PATH/data/logs/arbiter.log 2>&1 & #rm -f loan-arbiter-linux-x86_64.tgz conf.tgz fi @@ -116,4 +116,4 @@ deploy_arbiter() } SCRIPT_PATH=~/loan_arbiter -deploy_arbiter $1 $2 $3 +deploy_arbiter $1 $2 $3 $4 From d45eb3363e926026f65dfe12527a83aa266a8923 Mon Sep 17 00:00:00 2001 From: mollkeith Date: Sat, 25 Jan 2025 17:01:52 +0800 Subject: [PATCH 2/5] update keystore file type --- app/arbiter/arbiter/arbiter.go | 4 +- app/arbiter/crypto/keystore.go | 133 +++++++++++++++++++++++++++++++-- 2 files changed, 127 insertions(+), 10 deletions(-) diff --git a/app/arbiter/arbiter/arbiter.go b/app/arbiter/arbiter/arbiter.go index e060cfa..d6743b6 100644 --- a/app/arbiter/arbiter/arbiter.go +++ b/app/arbiter/arbiter/arbiter.go @@ -50,7 +50,7 @@ type Arbiter struct { } func NewArbiter(ctx context.Context, config *config.Config, password string) *Arbiter { - escPrivKey, err := crypto.GetKeyFromKeystore(config.EscKeyFilePath, password) + escPrivKey, err := crypto.GetEthKeyFromKeystore(config.EscKeyFilePath, password) if err != nil { g.Log().Fatal(ctx, "get esc keyfile error", err, " keystore path ", config.EscKeyFilePath) } @@ -58,7 +58,7 @@ func NewArbiter(ctx context.Context, config *config.Config, password string) *Ar PrivateKey: escPrivKey, } - arbiterPrivKey, err := crypto.GetKeyFromKeystore(config.ArbiterKeyFilePath, password) + arbiterPrivKey, err := crypto.GetBtcKeyFromKeystore(config.ArbiterKeyFilePath, password) if err != nil { g.Log().Fatal(ctx, "get arbiter keyfile error", err, " keystore path ", config.ArbiterKeyFilePath) } diff --git a/app/arbiter/crypto/keystore.go b/app/arbiter/crypto/keystore.go index 82b8166..a6f8f53 100644 --- a/app/arbiter/crypto/keystore.go +++ b/app/arbiter/crypto/keystore.go @@ -1,18 +1,135 @@ -// Copyright (c) 2025 The bel2 developers - package crypto import ( - "fmt" - "os/exec" + "crypto/aes" + "crypto/cipher" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "errors" + "os" + + "github.com/ethereum/go-ethereum/accounts/keystore" ) -func GetKeyFromKeystore(encryptedFile string, password string) (string, error) { - cmd := exec.Command("openssl", "enc", "-d", "-aes-256-cbc", "-in", encryptedFile, "-pass", fmt.Sprintf("pass:%s", password)) - output, err := cmd.Output() +// ReadBTCKeystore reads a BTC keystore file +func ReadBTCKeystore(path string) ([]byte, error) { + data, err := os.ReadFile(path) + if err != nil { + return nil, err + } + + // BTC keystore is binary format, return raw data + return data, nil +} + +// ReadETHKeystore reads an ETH keystore file +func ReadETHKeystore(path string) (*keystore.Key, error) { + data, err := os.ReadFile(path) + if err != nil { + return nil, err + } + + // Parse ETH keystore JSON + var key keystore.Key + if err := json.Unmarshal(data, &key); err != nil { + return nil, err + } + + return &key, nil +} + +// ReadKeystore automatically detects and reads either BTC or ETH keystore +func ReadKeystore(path string) (interface{}, error) { + data, err := os.ReadFile(path) + if err != nil { + return nil, err + } + + // Try to parse as ETH keystore first + var ethKey keystore.Key + if err := json.Unmarshal(data, ðKey); err == nil { + return ðKey, nil + } + + // If not ETH keystore, treat as BTC keystore + return data, nil +} + +// GetKeyType determines if a keystore is BTC or ETH format +func GetKeyType(path string) (string, error) { + data, err := os.ReadFile(path) if err != nil { return "", err } - return string(output), nil + // Check if data is valid JSON + var temp interface{} + if json.Unmarshal(data, &temp) == nil { + return "eth", nil + } + + // Check if data is valid hex (BTC WIF) + if _, err := hex.DecodeString(string(data)); err == nil { + return "btc", nil + } + + return "", errors.New("unknown keystore format") +} + +// GetEthKeyFromKeystore reads an ETH keystore file and returns the private key as hex string +func GetEthKeyFromKeystore(path, password string) (string, error) { + // Read ETH keystore file + data, err := os.ReadFile(path) + if err != nil { + return "", err + } + + // Decrypt ETH keystore + privateKey, err := keystore.DecryptKey(data, password) + if err != nil { + return "", err + } + return hex.EncodeToString(privateKey.PrivateKey.D.Bytes()), nil +} + +// GetBtcKeyFromKeystore reads a BTC keystore file and returns the private key as hex string +func GetBtcKeyFromKeystore(path, password string) (string, error) { + data, err := ReadBTCKeystore(path) + if err != nil { + return "", err + } + // Decrypt BTC keystore using password + decryptedData, err := decryptBTCKeystore(data, password) + if err != nil { + return "", err + } + return hex.EncodeToString(decryptedData), nil +} + +// decryptBTCKeystore decrypts BTC keystore data using password +func decryptBTCKeystore(data []byte, password string) ([]byte, error) { + key := sha256.Sum256([]byte(password)) + block, err := aes.NewCipher(key[:]) + if err != nil { + return nil, err + } + + gcm, err := cipher.NewGCM(block) + if err != nil { + return nil, err + } + + nonceSize := gcm.NonceSize() + if len(data) < nonceSize { + return nil, errors.New("ciphertext too short") + } + + nonce, ciphertext := data[:nonceSize], data[nonceSize:] + plaintext, err := gcm.Open(nil, nonce, ciphertext, nil) + if err != nil { + return nil, err + } + + return plaintext, nil } From 3cb2abff2fb44295e5bccbd0bd2f641eff5c06dc Mon Sep 17 00:00:00 2001 From: mollkeith Date: Sat, 25 Jan 2025 22:36:19 +0800 Subject: [PATCH 3/5] add keystore generator --- Makefile | 4 +- app/keystore-generator/btc/btc.go | 75 +++++++++++++ app/keystore-generator/btc/btc_test.go | 27 +++++ app/keystore-generator/eth/eth.go | 29 +++++ app/keystore-generator/eth/eth_test.go | 49 ++++++++ app/keystore-generator/main.go | 149 +++++++++++++++++++++++++ 6 files changed, 332 insertions(+), 1 deletion(-) create mode 100644 app/keystore-generator/btc/btc.go create mode 100644 app/keystore-generator/btc/btc_test.go create mode 100644 app/keystore-generator/eth/eth.go create mode 100644 app/keystore-generator/eth/eth_test.go create mode 100644 app/keystore-generator/main.go diff --git a/Makefile b/Makefile index d7aaacb..bd84c6c 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,7 @@ all: go build -o arbiter app/arbiter/main.go + go build -o keystore-generator app/keystore-generator/main.go linux: - GOARCH=amd64 GOOS=linux go build -o arbiter app/arbiter/main.go \ No newline at end of file + GOARCH=amd64 GOOS=linux go build -o arbiter app/arbiter/main.go + GOARCH=amd64 GOOS=linux go build -o keystore-generator app/keystore-generator/main.go \ No newline at end of file diff --git a/app/keystore-generator/btc/btc.go b/app/keystore-generator/btc/btc.go new file mode 100644 index 0000000..60be5ad --- /dev/null +++ b/app/keystore-generator/btc/btc.go @@ -0,0 +1,75 @@ +// Copyright (c) 2025 The bel2 developers +package btc + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "crypto/sha256" + "errors" + "fmt" + "io" + "os" +) + +func ParseKeystore(filePath string, password string) (string, error) { + data, err := os.ReadFile(filePath) + if err != nil { + return "", fmt.Errorf("failed to read keystore file: %v", err) + } + + // Decrypt the data + decrypted, err := decrypt(data, password) + if err != nil { + return "", fmt.Errorf("failed to decrypt keystore: %v", err) + } + + return string(decrypted), nil +} + +func Encrypt(data []byte, password string) ([]byte, error) { + key := sha256.Sum256([]byte(password)) + block, err := aes.NewCipher(key[:]) + if err != nil { + return nil, err + } + + gcm, err := cipher.NewGCM(block) + if err != nil { + return nil, err + } + + nonce := make([]byte, gcm.NonceSize()) + if _, err := io.ReadFull(rand.Reader, nonce); err != nil { + return nil, err + } + + ciphertext := gcm.Seal(nonce, nonce, data, nil) + return ciphertext, nil +} + +func decrypt(data []byte, password string) ([]byte, error) { + key := sha256.Sum256([]byte(password)) + block, err := aes.NewCipher(key[:]) + if err != nil { + return nil, err + } + + gcm, err := cipher.NewGCM(block) + if err != nil { + return nil, err + } + + nonceSize := gcm.NonceSize() + if len(data) < nonceSize { + return nil, errors.New("ciphertext too short") + } + + nonce, ciphertext := data[:nonceSize], data[nonceSize:] + plaintext, err := gcm.Open(nil, nonce, ciphertext, nil) + if err != nil { + return nil, err + } + + return plaintext, nil +} diff --git a/app/keystore-generator/btc/btc_test.go b/app/keystore-generator/btc/btc_test.go new file mode 100644 index 0000000..5a292cd --- /dev/null +++ b/app/keystore-generator/btc/btc_test.go @@ -0,0 +1,27 @@ +// Copyright (c) 2025 The bel2 developers +package btc + +import ( + "os" + "testing" +) + +func TestParseKeystore(t *testing.T) { + // Create test keystore file + testWIF := "cV1F7Nfk8ZNZ1PjYz3z7J7q1z7J7q1z7J7q1z7J7q1z7J7q1z7J7q1z7J7q" + err := os.WriteFile("test_keystore.txt", []byte(testWIF), 0600) + if err != nil { + t.Fatalf("Failed to create test keystore file: %v", err) + } + defer os.Remove("test_keystore.txt") + + // Test parsing + got, err := ParseKeystore("test_keystore.txt", "password") + if err != nil { + t.Errorf("ParseKeystore() error = %v", err) + return + } + if got != testWIF { + t.Errorf("ParseKeystore() = %v, want %v", got, testWIF) + } +} diff --git a/app/keystore-generator/eth/eth.go b/app/keystore-generator/eth/eth.go new file mode 100644 index 0000000..d24996c --- /dev/null +++ b/app/keystore-generator/eth/eth.go @@ -0,0 +1,29 @@ +// Copyright (c) 2025 The bel2 developers +package eth + +import ( + "fmt" + "os" + + "github.com/ethereum/go-ethereum/accounts/keystore" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" +) + +func ParseKeystore(filePath string, password string) (string, error) { + // Read keystore file + data, err := os.ReadFile(filePath) + if err != nil { + return "", fmt.Errorf("failed to read keystore file: %v", err) + } + + // Decrypt keystore + key, err := keystore.DecryptKey(data, password) + if err != nil { + return "", fmt.Errorf("failed to decrypt keystore: %v", err) + } + + // Get private key + privateKeyBytes := crypto.FromECDSA(key.PrivateKey) + return common.Bytes2Hex(privateKeyBytes), nil +} diff --git a/app/keystore-generator/eth/eth_test.go b/app/keystore-generator/eth/eth_test.go new file mode 100644 index 0000000..47526f7 --- /dev/null +++ b/app/keystore-generator/eth/eth_test.go @@ -0,0 +1,49 @@ +// Copyright (c) 2025 The bel2 developers +package eth + +import ( + "os" + "testing" +) + +func TestParseKeystore(t *testing.T) { + // Create test keystore file + testKeystore := `{ + "address": "3f5ce5fbfe3e9af3971dd833d26ba9b5c936f0be", + "crypto": { + "cipher": "aes-128-ctr", + "ciphertext": "5318b4d5bcd28de64ee5559e671353e16f075ecae9f99c7a79a38af5f869aa46", + "cipherparams": { + "iv": "6087dab2f9fdbbfaddc31a909735c1e6" + }, + "kdf": "scrypt", + "kdfparams": { + "dklen": 32, + "n": 262144, + "p": 1, + "r": 8, + "salt": "ae3cd4e7013836a3df6bd7241b12db061dbe2c6785853cce422d148a624ce0bd" + }, + "mac": "517ead924a9d0dc3124507e3393d175ce3ff7c1e96529c6c555ce9e51205e9b2" + }, + "id": "e13b209c-3b2f-4327-bab0-3bef2e51630d", + "version": 3 + }` + + err := os.WriteFile("test_keystore.json", []byte(testKeystore), 0600) + if err != nil { + t.Fatalf("Failed to create test keystore file: %v", err) + } + defer os.Remove("test_keystore.json") + + // Test parsing + expectedPrivateKey := "4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318" + got, err := ParseKeystore("test_keystore.json", "testpassword") + if err != nil { + t.Errorf("ParseKeystore() error = %v", err) + return + } + if got != expectedPrivateKey { + t.Errorf("ParseKeystore() = %v, want %v", got, expectedPrivateKey) + } +} diff --git a/app/keystore-generator/main.go b/app/keystore-generator/main.go new file mode 100644 index 0000000..e1ade72 --- /dev/null +++ b/app/keystore-generator/main.go @@ -0,0 +1,149 @@ +// Copyright (c) 2025 The bel2 developers +package main + +import ( + "bufio" + "encoding/hex" + "flag" + "fmt" + "os" + + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/chaincfg" + "github.com/ethereum/go-ethereum/accounts/keystore" + "github.com/ethereum/go-ethereum/crypto" + + "github.com/BeL2Labs/Arbiter_Signer/app/keystore-generator/btc" + "github.com/BeL2Labs/Arbiter_Signer/app/keystore-generator/eth" +) + +func main() { + chain := flag.String("c", "eth", "Chain type (eth or btc)") + privateKey := flag.String("s", "", "Private key (64 hex characters)") + password := flag.String("p", "", "Password for keystore") + outputFile := flag.String("o", "", "Output filename (optional)") + fileToParse := flag.String("f", "", "Keystore file to parse") + flag.Parse() + + if *fileToParse != "" { + privateKey, err := eth.ParseKeystore(*fileToParse, *password) + if err != nil { + fmt.Println("Error:", err) + os.Exit(1) + } + fmt.Println("Private key:", privateKey) + } else { + switch *chain { + case "eth": + GenerateETHKeystore(*privateKey, *password, *outputFile) + case "btc": + GenerateBTCKeystore(*privateKey, *password, *outputFile) + default: + fmt.Println("Error: Invalid chain type") + os.Exit(1) + } + } +} + +func GenerateETHKeystore(privateKeyHex string, password string, outputFile string) { + if privateKeyHex == "" { + fmt.Println("Error: Private key is required") + os.Exit(1) + } + + if len(privateKeyHex) != 64 { + fmt.Println("Error: Private key must be exactly 64 hex characters") + os.Exit(1) + } + + if password == "" { + fmt.Print("Enter password: ") + reader := bufio.NewReader(os.Stdin) + inputPassword, _ := reader.ReadString('\n') + password = inputPassword[:len(inputPassword)-1] + } + + privateKeyBytes, err := hex.DecodeString(privateKeyHex) + if err != nil { + fmt.Println("Error: Invalid private key format - must be 64 hex characters") + os.Exit(1) + } + + privateKey, err := crypto.ToECDSA(privateKeyBytes) + if err != nil { + fmt.Println("Error: Failed to create private key:", err) + os.Exit(1) + } + + ks := keystore.NewKeyStore(".", keystore.StandardScryptN, keystore.StandardScryptP) + + account, err := ks.ImportECDSA(privateKey, password) + if err != nil { + fmt.Println("Error: Failed to import private key:", err) + os.Exit(1) + } + + if outputFile != "" { + newPath := fmt.Sprintf("%s", outputFile) + err := os.Rename(account.URL.Path, newPath) + if err != nil { + fmt.Println("Error: Failed to rename keystore file:", err) + os.Exit(1) + } + fmt.Println("Ethereum keystore created successfully at:", newPath) + } else { + fmt.Println("Ethereum keystore created successfully at:", account.URL.Path) + } +} + +func GenerateBTCKeystore(privateKeyHex string, password string, outputFile string) { + if privateKeyHex == "" { + fmt.Println("Error: Private key is required") + os.Exit(1) + } + + if len(privateKeyHex) != 64 { + fmt.Println("Error: Private key must be exactly 64 hex characters") + os.Exit(1) + } + + if password == "" { + fmt.Print("Enter password: ") + reader := bufio.NewReader(os.Stdin) + inputPassword, _ := reader.ReadString('\n') + password = inputPassword[:len(inputPassword)-1] + } + + privateKeyBytes, err := hex.DecodeString(privateKeyHex) + if err != nil { + fmt.Println("Error: Invalid private key format - must be 64 hex characters") + os.Exit(1) + } + + privKey, _ := btcec.PrivKeyFromBytes(privateKeyBytes) + wif, err := btcutil.NewWIF(privKey, &chaincfg.MainNetParams, true) + if err != nil { + fmt.Println("Error: Failed to create Bitcoin WIF:", err) + os.Exit(1) + } + + filename := fmt.Sprintf("btc_keystore_%s.txt", wif.String()[:8]) + if outputFile != "" { + filename = outputFile + } + + // Encrypt WIF with password + encrypted, err := btc.Encrypt([]byte(wif.String()), password) + if err != nil { + fmt.Println("Error: Failed to encrypt keystore:", err) + os.Exit(1) + } + + if err := os.WriteFile(filename, encrypted, 0600); err != nil { + fmt.Println("Error: Failed to save Bitcoin keystore:", err) + os.Exit(1) + } + + fmt.Println("Bitcoin keystore created successfully at:", filename) +} From 38315f38701d4ea5de233f8e357b0f7505289689 Mon Sep 17 00:00:00 2001 From: mollkeith Date: Sat, 25 Jan 2025 22:37:22 +0800 Subject: [PATCH 4/5] update deploy script --- README.MD | 38 +++++++++++++++++-------------------- docs/deploy_loan_arbiter.sh | 8 +++++--- 2 files changed, 22 insertions(+), 24 deletions(-) diff --git a/README.MD b/README.MD index 32f92a3..824b8ab 100644 --- a/README.MD +++ b/README.MD @@ -68,32 +68,28 @@ $ make keyFilePath: "~/loan_arbiter/keys" ``` - - ### 3. prepare keystore to keyFilePath 1. create **btcKey** **escKey** and put it into **keyFilePath** btcKey is used to sign request_arbitration_btc_tx escKey is used to submit arbitration signature to esc arbiter contract, gas fee(esc ELA) is needed -2. keysotre file need to set hex encoded private key - - use [encrypt.sh](https://github.com/BeL2Labs/Arbiter_Signer/blob/main/docs/encrypt.sh) to create btc keystore: - ```shell - ~ chmod a+x encrypt.sh - ~ ./encrypt.sh - ~ > YOUR_HEX_PRIV_KEY - ~ > YOUR_PASSWORD - ~ > btcKey - ``` - use [encrypt.sh](https://github.com/BeL2Labs/Arbiter_Signer/blob/main/docs/encrypt.sh) to create esc keystore: - ```shell - ~ chmod a+x encrypt.sh - ~ ./encrypt.sh - ~ > YOUR_HEX_PRIV_KEY - ~ > YOUR_PASSWORD - ~ > escKey - ``` +2. Use keystore-generator tool to create keystore files: + + Install keystore-generator: + ```shell + go install github.com/BeL2Labs/keystore-generator@latest + ``` + + Generate BTC keystore: + ```shell + keystore-generator -c btc -s YOUR_PRIVATE_KEY -p YOUR_PASSWORD -o btcKey + ``` + + Generate ESC keystore: + ```shell + keystore-generator -c eth -s YOUR_PRIVATE_KEY -p YOUR_PASSWORD -o escKey + ``` ### 4. run arbiter @@ -117,4 +113,4 @@ The automated deployment process can be referenced in the documentation: [deploy_loan_arbiter.md](https://github.com/BeL2Labs/Arbiter_Signer/blob/main/docs/deploy_loan_arbiter.md) ## License -arbiter signer is licensed under the [copyfree](http://copyfree.org) MIT License. \ No newline at end of file +arbiter signer is licensed under the [copyfree](http://copyfree.org) MIT License. diff --git a/docs/deploy_loan_arbiter.sh b/docs/deploy_loan_arbiter.sh index 694c2d7..6aa85ec 100644 --- a/docs/deploy_loan_arbiter.sh +++ b/docs/deploy_loan_arbiter.sh @@ -82,9 +82,11 @@ deploy_arbiter() sed -i "s/0x0262aB0ED65373cC855C34529fDdeAa0e686D913/$1/g" config.yaml #prepare keystore - openssl enc -aes-256-cbc -salt -in <(echo -n $2) -out "btcKey" -pass pass:$4 - openssl enc -aes-256-cbc -salt -in <(echo -n $3) -out "escKey" -pass pass:$4 - mv btcKey escKey keys/ + # Generate BTC keystore + keystore-generator -c btc -s $2 -p $4 -o btcKey + # Generate ESC keystore + keystore-generator -c eth -s $3 -p $4 -o escKey + mv btcKey escKey keys/ #prepare arbiter if [ "$(uname -m)" == "armv6l" ] || [ "$(uname -m)" == "armv7l" ] || [ "$(uname -m)" == "aarch64" ]; then From 997cc560b343db54d307d18bac5883a479b3fdf8 Mon Sep 17 00:00:00 2001 From: mollkeith Date: Sat, 25 Jan 2025 22:49:57 +0800 Subject: [PATCH 5/5] remove useless tests --- app/keystore-generator/btc/btc_test.go | 27 -------------- app/keystore-generator/eth/eth_test.go | 49 -------------------------- 2 files changed, 76 deletions(-) delete mode 100644 app/keystore-generator/btc/btc_test.go delete mode 100644 app/keystore-generator/eth/eth_test.go diff --git a/app/keystore-generator/btc/btc_test.go b/app/keystore-generator/btc/btc_test.go deleted file mode 100644 index 5a292cd..0000000 --- a/app/keystore-generator/btc/btc_test.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) 2025 The bel2 developers -package btc - -import ( - "os" - "testing" -) - -func TestParseKeystore(t *testing.T) { - // Create test keystore file - testWIF := "cV1F7Nfk8ZNZ1PjYz3z7J7q1z7J7q1z7J7q1z7J7q1z7J7q1z7J7q1z7J7q" - err := os.WriteFile("test_keystore.txt", []byte(testWIF), 0600) - if err != nil { - t.Fatalf("Failed to create test keystore file: %v", err) - } - defer os.Remove("test_keystore.txt") - - // Test parsing - got, err := ParseKeystore("test_keystore.txt", "password") - if err != nil { - t.Errorf("ParseKeystore() error = %v", err) - return - } - if got != testWIF { - t.Errorf("ParseKeystore() = %v, want %v", got, testWIF) - } -} diff --git a/app/keystore-generator/eth/eth_test.go b/app/keystore-generator/eth/eth_test.go deleted file mode 100644 index 47526f7..0000000 --- a/app/keystore-generator/eth/eth_test.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) 2025 The bel2 developers -package eth - -import ( - "os" - "testing" -) - -func TestParseKeystore(t *testing.T) { - // Create test keystore file - testKeystore := `{ - "address": "3f5ce5fbfe3e9af3971dd833d26ba9b5c936f0be", - "crypto": { - "cipher": "aes-128-ctr", - "ciphertext": "5318b4d5bcd28de64ee5559e671353e16f075ecae9f99c7a79a38af5f869aa46", - "cipherparams": { - "iv": "6087dab2f9fdbbfaddc31a909735c1e6" - }, - "kdf": "scrypt", - "kdfparams": { - "dklen": 32, - "n": 262144, - "p": 1, - "r": 8, - "salt": "ae3cd4e7013836a3df6bd7241b12db061dbe2c6785853cce422d148a624ce0bd" - }, - "mac": "517ead924a9d0dc3124507e3393d175ce3ff7c1e96529c6c555ce9e51205e9b2" - }, - "id": "e13b209c-3b2f-4327-bab0-3bef2e51630d", - "version": 3 - }` - - err := os.WriteFile("test_keystore.json", []byte(testKeystore), 0600) - if err != nil { - t.Fatalf("Failed to create test keystore file: %v", err) - } - defer os.Remove("test_keystore.json") - - // Test parsing - expectedPrivateKey := "4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318" - got, err := ParseKeystore("test_keystore.json", "testpassword") - if err != nil { - t.Errorf("ParseKeystore() error = %v", err) - return - } - if got != expectedPrivateKey { - t.Errorf("ParseKeystore() = %v, want %v", got, expectedPrivateKey) - } -}