Skip to content

Commit

Permalink
feat(MPC-1756): update for TX Lifecycle API v2
Browse files Browse the repository at this point in the history
  • Loading branch information
MnrGreg committed Aug 29, 2024
1 parent 1ca52f2 commit 596c32d
Show file tree
Hide file tree
Showing 8 changed files with 92 additions and 58 deletions.
9 changes: 8 additions & 1 deletion 1-create-wallet/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"crypto/x509"
"encoding/base64"
"encoding/hex"
"encoding/pem"
"fmt"

Expand Down Expand Up @@ -110,8 +111,14 @@ func main() {
}
}

publicKeyBytes, err := tsmutils.PKIXPublicKeyToCompressedPoint(pkixPublicKeys[0])
if err != nil {
panic(err)
}
fmt.Println("Ethereum chain path m/44/60/0/0 derived wallet compressed public key:", hex.EncodeToString(publicKeyBytes))

// Convert the public key into an Ethereum address
publicKeyBytes, err := tsmutils.PKIXPublicKeyToUncompressedPoint(pkixPublicKeys[0])
publicKeyBytes, err = tsmutils.PKIXPublicKeyToUncompressedPoint(pkixPublicKeys[0])
if err != nil {
panic(err)
}
Expand Down
37 changes: 15 additions & 22 deletions 2-craft-transaction/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,10 @@ import (

var protocol = "ethereum"
var network = "sepolia"
var chainID = big.NewInt(11155111)
var url = "https://svc.blockdaemon.com/universal/v1/" + protocol + "/" + network + "/"
var url = "https://svc.blockdaemon.com"
var address = "..." // ! Set the wallet address created in step 1
var destinationAddress = "0x6Cc9397c3B38739daCbfaA68EaD5F5D77Ba5F455" // Optional - override destination address which defaults back to faucet
var amount = "0.003" // Set the amount to send in ETH
var destinationAddress = "0x52b09e2c73849B25F9b0328e2d4b444e9bd1EF30" // Optional - override destination address which defaults back to faucet
var amount = "0.001" // Set the amount to send in ETH

func main() {

Expand All @@ -30,7 +29,7 @@ func main() {
}

// * Get account balance https://docs.blockdaemon.com/reference/getlistofbalancesbyaddress
res, err := http.Get(url + "account/" + address + "?apiKey=" + apiKey)
res, err := http.Get(url + "/universal/v1/" + protocol + "/" + network + "/account/" + address + "?apiKey=" + apiKey)
if err != nil {
panic(err)
}
Expand All @@ -54,21 +53,15 @@ func main() {
balanceFloat.Mul(balanceFloat, big.NewFloat(math.Pow10(-account[0].Currency.Decimals)))
fmt.Printf("Balance at account %s: %s %s\n", address, balanceFloat.Text('f', 18), account[0].Currency.Symbol)

// * Transaction request - MaxFeePerGas and MaxPriorityFeePerGas are estimated automatically https://docs.blockdaemon.com/reference/txcreate
// * Transaction request - MaxFeePerGas and MaxPriorityFeePerGas are estimated automatically https://docs.blockdaemon.com/reference/txcreate-txapi
txRequest := struct {
From string `json:"from"`
To []struct {
Destination string `json:"destination"`
Amount string `json:"amount"`
} `json:"to"`
From string `json:"from"`
To string `json:"to"`
Value string `json:"value"`
}{
From: address,
To: []struct {
Destination string `json:"destination"`
Amount string `json:"amount"`
}{
{Destination: destinationAddress, Amount: amount},
},
From: address,
To: destinationAddress,
Value: amount,
}

reqBody, err := json.Marshal(txRequest)
Expand All @@ -77,7 +70,7 @@ func main() {
}

// Post unsigned transaction request
res, err = http.Post(url+"tx/create?apiKey="+apiKey, "application/json", bytes.NewReader(reqBody))
res, err = http.Post(url+"/tx/v1/"+protocol+"-"+network+"/create?apiKey="+apiKey, "application/json", bytes.NewReader(reqBody))
if err != nil {
panic(err)
}
Expand All @@ -104,8 +97,8 @@ func main() {
panic(err)
}

// * create a NewLondonSigner for EIP 1559 transactions
signer := types.NewCancunSigner(chainID)
fmt.Printf("Raw unsigned NewCancunSigner tx hash: %s\n", hex.EncodeToString(signer.Hash(unsignedTx).Bytes()))
// * create a NewLondonSigner EIP1559 transaction type hash
signer := types.NewLondonSigner(unsignedTx.ChainId())
fmt.Printf("Raw unsigned NewLondonSigner tx hash: %s\n", hex.EncodeToString(signer.Hash(unsignedTx).Bytes()))

}
23 changes: 21 additions & 2 deletions 3-sign-transaction/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ package main
import (
"context"
"crypto/x509"
"encoding/asn1"
"encoding/base64"
"encoding/hex"
"encoding/pem"
"fmt"
"math/big"
"sync"

"golang.org/x/sync/errgroup"
Expand All @@ -25,7 +27,7 @@ func main() {
// ! Specify the Builder Vailt TSM master key, created in step 1, to sign via the derived wallet chain path m/44/60/0/0
masterKeyID := "..."

// ! Specify the NewCancunSigner unsigned transaction hash created in step 2
// ! Specify the NewLondonSigner unsigned transaction hash created in step 2
unsignedTxHash := "..."

// Decode server public keys to bytes for use in TLS client authentication
Expand Down Expand Up @@ -113,5 +115,22 @@ func main() {
copy(sigBytes[0:32], signature.R())
copy(sigBytes[32:64], signature.S())
sigBytes[64] = byte(signature.RecoveryID())
fmt.Println("tx hash signature:", hex.EncodeToString(sigBytes))
fmt.Println("Tx hash signature:", hex.EncodeToString(sigBytes))

// Construct signature in ASN.1 DER format
derSignature := struct {
R, S *big.Int
V int
}{
R: new(big.Int).SetBytes(signature.R()),
S: new(big.Int).SetBytes(signature.S()),
V: signature.RecoveryID(),
}

// Marshal the structure to ASN.1 DER
der, err := asn1.Marshal(derSignature)
if err != nil {
panic(err)
}
fmt.Println("Tx hash signature (DER encoded):", hex.EncodeToString(der))
}
59 changes: 30 additions & 29 deletions 4-broadcast-signed-transaction/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"encoding/json"
"fmt"
"io"
"math/big"
"net/http"
"os"

Expand All @@ -15,16 +14,18 @@ import (

var protocol = "ethereum"
var network = "sepolia"
var chainID = big.NewInt(11155111)
var url = "https://svc.blockdaemon.com/universal/v1/" + protocol + "/" + network + "/"
var url = "https://svc.blockdaemon.com"

func main() {
// ! Set the transactionHashSignature created in step 4
transactionHashSignature := "..."
// ! Set the wallet public keu created in step 1
walletPublicKey := "..."

// ! Set the rawUnsignedTransaction created in step 3
// ! Set the rawUnsignedTransaction created in step 2
rawUnsignedTransaction := "..."

// ! Set the DER transactionHashSignature created in step 3
transactionHashSignature := "..."

// Access token is required
apiKey := os.Getenv("ACCESS_TOKEN")
if apiKey == "" {
Expand All @@ -39,41 +40,41 @@ func main() {
panic(err)
}

// Deserialize the transactionHashSignature
transactionHashSignatureBytes, _ := hex.DecodeString(transactionHashSignature)
// * Compile the unsigned tx with the signatureand broadcast to the blockchain https://docs.blockdaemon.com/reference/txcompileandsend-txapi
txRequest := struct {
Unsigned_tx string `json:"unsigned_tx"`
Signature string `json:"signature"`
Public_key string `json:"public_key"`
}{
Unsigned_tx: rawUnsignedTransaction,
Signature: transactionHashSignature,
Public_key: walletPublicKey,
}

// * Combine the unsigned transaction and the signature to create a signed transaction
signedTx, err := unsignedTx.WithSignature(types.NewLondonSigner(chainID), transactionHashSignatureBytes)
reqBody, err := json.Marshal(txRequest)
if err != nil {
panic(err)
}

// * Serialize the signed transaction for broadcast
raw, err := signedTx.MarshalBinary()
res, err := http.Post(url+"/tx/v1/"+protocol+"-"+network+"/compile_and_send?apiKey="+apiKey, "application/json", bytes.NewReader(reqBody))
resbodyBytes, _ := io.ReadAll(res.Body)
if err != nil {
panic(err)
}
fmt.Printf("\nRaw signed tx (RLP encoded): %x\n", raw)
defer res.Body.Close()

// * Broadcast the signed transaction to the blockchain https://docs.blockdaemon.com/reference/txsend
sendRequest := struct {
TX string `json:"tx"`
}{
TX: hex.EncodeToString(raw),
// Check HTTP status code
if res.StatusCode != http.StatusOK {
panic(fmt.Errorf("invalid status code:%d %s %s", res.StatusCode, http.StatusText(res.StatusCode), resbodyBytes))
}

reqBody, err := json.Marshal(sendRequest)
if err != nil {
panic(err)
// Parse broadcasted transaction response
var response struct {
ID string `json:"id"`
}

res, _ := http.Post(url+"tx/send?apiKey="+apiKey, "application/json", bytes.NewReader(reqBody))
resbodyBytes, _ := io.ReadAll(res.Body)
defer res.Body.Close()
if res.StatusCode != 200 {
panic(fmt.Errorf("HTTP request %d error: %sn/ %s", res.StatusCode, http.StatusText(res.StatusCode), resbodyBytes))
} else {
fmt.Printf("\nBroadcasted transaction hash: https://"+network+".etherscan.io/tx/%s\n", signedTx.Hash().String())
if err := json.NewDecoder(res.Body).Decode(&response); err != nil {
panic(err)
}
fmt.Printf("Broadcasted transaction hash: https://"+network+".etherscan.io/tx/%s\n", response.ID)

}
8 changes: 8 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Evaluation License Notice:

* Copyright © 2024 Blockdaemon Inc. - All Rights Reserved
* Unauthorized copying of this file, via any medium is strictly prohibited
* Proprietary and confidential, use only granted under applicable License

Permission is hereby granted only under a license to use for testing and evaluating purposes only, to any authorized person obtaining access to this software, associated documentation files, and services (the "Product") to use the Product to support personal or professional operations subject to the License's restrictions. No user may rent, loan, sub-license, license, distribute, copy, decompile, disassemble, merge, modify, adapt, translate, reverse-engineer, make alterations to or derivative works based upon or derive source code from, nor attempt to grant any other rights to or in any whole or part of the Product any whole or part of the Product.
This above copyright notice and this permission notice shall be included in all copies or substantial portions of the software.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ sequenceDiagram
client ->> TSM3: request signature (unsigned tx hash)
TSM3 -->> client: return partial signature
client ->> client: combine partial signatures
client ->> Blockchain: broadcast signed tx<br>(signed tx)
client ->> Blockchain: compile unsigned tx with signature and broadcast<br>(unsigned tx, signature, public key)
```

### Prerequisites
Expand All @@ -42,6 +42,7 @@ sequenceDiagram
```shell
go run 1-create-wallet/main.go
```
- note the new Ethereum Wallet public key (to be used for future signing)
- note the new Ethereum Wallet address and fund it with Sepolia ETH https://sepolia-faucet.pk910.de
- note the Builder Vault Master Key ID (to be used for future signing)

Expand All @@ -68,8 +69,9 @@ go run 3-sign-transaction/main.go


### Step 4. Broadcast signed raw transaction
- set the transaction signature hash (created in step 3)
- set the wallet compressed public key (created in step 1)
- set the raw unsigned transaction (created in step 2)
- set the transaction signature hash (created in step 3)
```shell
go run 4-broadcast-signed-transaction/main.go
```
Expand Down
6 changes: 4 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
module buildervault-wallettransact

go 1.21.0
go 1.22

toolchain go1.22.5

require (
github.com/ethereum/go-ethereum v1.14.0
Expand All @@ -11,7 +13,7 @@ require (
require (
filippo.io/edwards25519 v1.1.0 // indirect
github.com/bits-and-blooms/bitset v1.13.0 // indirect
github.com/btcsuite/btcd v0.22.0-beta // indirect
github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd // indirect
github.com/btcsuite/btcd/btcec/v2 v2.3.3 // indirect
github.com/consensys/bavard v0.1.13 // indirect
github.com/consensys/gnark-crypto v0.12.1 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
github.com/btcsuite/btcd v0.22.0-beta h1:LTDpDKUM5EeOFBPM8IXpinEcmZ6FWfNZbE3lfrfdnWo=
github.com/btcsuite/btcd v0.22.0-beta/go.mod h1:9n5ntfhhHQBIhUvlhDvD3Qg6fRUj4jkN0VB8L8svzOA=
github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd h1:js1gPwhcFflTZ7Nzl7WHaOTlTr5hIrR4n1NM4v9n4Kw=
github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A=
github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k=
github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU=
github.com/btcsuite/btcd/btcec/v2 v2.3.3 h1:6+iXlDKE8RMtKsvK0gshlXIuPbyWM/h84Ensb7o3sC0=
Expand Down

0 comments on commit 596c32d

Please sign in to comment.