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

feat: add subscribe RegisterOracle Tx and execute ApproveOracleRegistration Tx #21

Merged
merged 12 commits into from
Dec 13, 2022
52 changes: 48 additions & 4 deletions cmd/oracled/cmd/register_oracle.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import (
"time"

"github.com/cosmos/cosmos-sdk/client/input"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/edgelesssys/ego/enclave"
oracletypes "github.com/medibloc/panacea-core/v2/x/oracle/types"
"github.com/medibloc/panacea-oracle/client/flags"
"github.com/medibloc/panacea-oracle/crypto"
oracleevent "github.com/medibloc/panacea-oracle/event/oracle"
Expand Down Expand Up @@ -64,22 +66,64 @@ func registerOracleCmd() *cobra.Command {
defer queryClient.Close()

// get oracle account from mnemonic.
_, err = panacea.NewOracleAccount(conf.OracleMnemonic, conf.OracleAccNum, conf.OracleAccIndex)
oracleAccount, err := panacea.NewOracleAccount(conf.OracleMnemonic, conf.OracleAccNum, conf.OracleAccIndex)
if err != nil {
return fmt.Errorf("failed to get oracle account from mnemonic: %w", err)
}

// generate node key and its remote report
_, nodePubKeyRemoteReport, err := generateSealedNodeKey(nodePrivKeyPath)
nodePubKey, nodePubKeyRemoteReport, err := generateSealedNodeKey(nodePrivKeyPath)
if err != nil {
return fmt.Errorf("failed to generate node key pair: %w", err)
}

report, _ := enclave.VerifyRemoteReport(nodePubKeyRemoteReport)
_ = hex.EncodeToString(report.UniqueID)
uniqueID := hex.EncodeToString(report.UniqueID)

// request register oracle Tx to Panacea
// TODO: add register-oracle Tx
oracleCommissionRateStr, err := cmd.Flags().GetString(flagOracleCommissionRate)
if err != nil {
return err
}

oracleCommissionRate, err := sdk.NewDecFromStr(oracleCommissionRateStr)
if err != nil {
return err
}

endPoint, err := cmd.Flags().GetString(flagOracleEndpoint)
if err != nil {
return err
}

msgRegisterOracle := oracletypes.NewMsgRegisterOracle(uniqueID, oracleAccount.GetAddress(), nodePubKey, nodePubKeyRemoteReport, trustedBlockInfo.TrustedBlockHeight, trustedBlockInfo.TrustedBlockHash, endPoint, oracleCommissionRate)
txBuilder := panacea.NewTxBuilder(queryClient)
cli, err := panacea.NewGRPCClient(conf.Panacea.GRPCAddr)
if err != nil {
return fmt.Errorf("failed to generate gRPC client: %w", err)
}
defer cli.Close()

defaultFeeAmount, err := sdk.ParseCoinsNormalized(conf.Panacea.DefaultFeeAmount)
if err != nil {
return err
}

txBytes, err := txBuilder.GenerateSignedTxBytes(oracleAccount.GetPrivKey(), conf.Panacea.DefaultGasLimit, defaultFeeAmount, msgRegisterOracle)
if err != nil {
return fmt.Errorf("failed to generate signed Tx bytes: %w", err)
}

resp, err := cli.BroadcastTx(txBytes)
if err != nil {
return fmt.Errorf("failed to broadcast transaction: %w", err)
}

if resp.TxResponse.Code != 0 {
return fmt.Errorf("register oracle transaction failed: %v", resp.TxResponse.RawLog)
}

log.Infof("register-oracle transaction succeed. height(%v), hash(%s)", resp.TxResponse.Height, resp.TxResponse.TxHash)

// subscribe approval of oracle registration and handle it
client, err := rpchttp.New(conf.Panacea.RPCAddr, "/websocket")
Expand Down
59 changes: 59 additions & 0 deletions crypto/encrypt.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package crypto

import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"fmt"
"io"
)

// EncryptWithAES256 encrypts data using a AES256 cryptography.
func EncryptWithAES256(secretKey, data []byte) ([]byte, error) {
if len(secretKey) != 32 {
return nil, fmt.Errorf("secret key is not for AES-256: total %d bits", 8*len(secretKey))
}

block, err := aes.NewCipher(secretKey)
if err != nil {
return nil, err
}

aesGCM, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}

nonce := make([]byte, aesGCM.NonceSize())
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
return nil, err
}

cipherText := aesGCM.Seal(nonce, nonce, data, nil)

return cipherText, nil
}

// DecryptWithAES256 decrypts data using a AES256 cryptography.
func DecryptWithAES256(secretKey, ciphertext []byte) ([]byte, error) {
if len(secretKey) != 32 {
return nil, fmt.Errorf("secret key is not for AES-256: total %d bits", 8*len(secretKey))
}

block, err := aes.NewCipher(secretKey)
if err != nil {
return nil, err
}

aesgcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}

plainText, err := aesgcm.Open(nil, ciphertext[:aesgcm.NonceSize()], ciphertext[aesgcm.NonceSize():], nil)
if err != nil {
return nil, err
}

return plainText, nil
}
79 changes: 79 additions & 0 deletions event/oracle/event.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package oracle

import (
"bytes"
"encoding/hex"
"fmt"

"github.com/btcsuite/btcd/btcec"
oracletypes "github.com/medibloc/panacea-core/v2/x/oracle/types"
"github.com/medibloc/panacea-oracle/crypto"
"github.com/medibloc/panacea-oracle/panacea"
"github.com/tendermint/tendermint/light/provider"
)

func makeMsgApproveOracleRegistration(uniqueID, approverAddr, targetAddr string, oraclePrivKey, nodePubKey []byte) (*oracletypes.MsgApproveOracleRegistration, error) {
privKey, _ := crypto.PrivKeyFromBytes(oraclePrivKey)
pubKey, err := btcec.ParsePubKey(nodePubKey, btcec.S256())
if err != nil {
return nil, err
}

shareKey := crypto.DeriveSharedKey(privKey, pubKey, crypto.KDFSHA256)
encryptedOraclePrivKey, err := crypto.EncryptWithAES256(shareKey, oraclePrivKey)
if err != nil {
return nil, err
}

registrationApproval := &oracletypes.ApproveOracleRegistration{
UniqueId: uniqueID,
ApproverOracleAddress: approverAddr,
TargetOracleAddress: targetAddr,
EncryptedOraclePrivKey: encryptedOraclePrivKey,
}

return makeMsgApproveOracleRegistrationWithSignature(registrationApproval, oraclePrivKey)
}

func makeMsgApproveOracleRegistrationWithSignature(approveOracleRegistration *oracletypes.ApproveOracleRegistration, oraclePrivKey []byte) (*oracletypes.MsgApproveOracleRegistration, error) {
key, _ := crypto.PrivKeyFromBytes(oraclePrivKey)

marshaledApproveOracleRegistration, err := approveOracleRegistration.Marshal()
if err != nil {
return nil, err
}

sig, err := key.Sign(marshaledApproveOracleRegistration)
if err != nil {
return nil, err
}

msgApproveOracleRegistration := &oracletypes.MsgApproveOracleRegistration{
ApproveOracleRegistration: approveOracleRegistration,
Signature: sig.Serialize(),
}

return msgApproveOracleRegistration, nil
}

func verifyTrustedBlockInfo(queryClient panacea.QueryClient, height int64, blockHash []byte) error {
block, err := queryClient.GetLightBlock(height)
if err != nil {
switch err {
case provider.ErrLightBlockNotFound, provider.ErrHeightTooHigh:
return fmt.Errorf("not found light block. %w", err)
default:
return err
}
}

if !bytes.Equal(block.Hash().Bytes(), blockHash) {
return fmt.Errorf("failed to verify trusted block information. height(%v), expected block hash(%s), got block hash(%s)",
height,
hex.EncodeToString(block.Hash().Bytes()),
hex.EncodeToString(blockHash),
)
}

return nil
}
66 changes: 64 additions & 2 deletions event/oracle/register_oracle_event.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
package oracle

import (
"crypto/sha256"
"fmt"

oracletypes "github.com/medibloc/panacea-core/v2/x/oracle/types"
"github.com/medibloc/panacea-oracle/event"
"github.com/medibloc/panacea-oracle/panacea"
"github.com/medibloc/panacea-oracle/sgx"
log "github.com/sirupsen/logrus"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
)

Expand All @@ -20,6 +27,61 @@ func (e RegisterOracleEvent) GetEventQuery() string {
}

func (e RegisterOracleEvent) EventHandler(event ctypes.ResultEvent) error {
// TODO: https://github.com/medibloc/panacea-oracle/issues/3
panic("will be implemented in issue #3")
uniqueID := event.Events[oracletypes.EventTypeRegistration+"."+oracletypes.AttributeKeyUniqueID][0]
targetAddress := event.Events[oracletypes.EventTypeRegistration+"."+oracletypes.AttributeKeyOracleAddress][0]

msgApproveOracleRegistration, err := e.verifyAndGetMsgApproveOracleRegistration(uniqueID, targetAddress)
if err != nil {
return err
}

log.Infof("new oracle registration approval info. uniqueID(%s), approverAddress(%s), targetAddress(%s)",
msgApproveOracleRegistration.ApproveOracleRegistration.UniqueId,
msgApproveOracleRegistration.ApproveOracleRegistration.ApproverOracleAddress,
msgApproveOracleRegistration.ApproveOracleRegistration.TargetOracleAddress,
)

txBuilder := panacea.NewTxBuilder(e.reactor.QueryClient())
txBytes, err := txBuilder.GenerateTxBytes(e.reactor.OracleAcc().GetPrivKey(), e.reactor.Config(), msgApproveOracleRegistration)
if err != nil {
return err
}

txHeight, txHash, err := e.reactor.BroadcastTx(txBytes)
if err != nil {
return fmt.Errorf("failed to ApproveOracleRegistration transaction for new oracle registration: %v", err)
} else {
log.Infof("succeeded to ApproveOracleRegistration transaction for new oracle registration. height(%v), hash(%s)", txHeight, txHash)
}

return nil
}

func (e RegisterOracleEvent) verifyAndGetMsgApproveOracleRegistration(uniqueID, targetAddress string) (*oracletypes.MsgApproveOracleRegistration, error) {
queryClient := e.reactor.QueryClient()
approverAddress := e.reactor.OracleAcc().GetAddress()
oraclePrivKeyBz := e.reactor.OraclePrivKey().Serialize()
approverUniqueID := e.reactor.EnclaveInfo().UniqueIDHex()

if uniqueID != approverUniqueID {
return nil, fmt.Errorf("oracle's uniqueID does not match the requested uniqueID. expected(%s) got(%s)", approverUniqueID, uniqueID)
} else {
oracleRegistration, err := queryClient.GetOracleRegistration(uniqueID, targetAddress)
log.Errorf("err while get oracleRegistration: %v", err)

if err := verifyTrustedBlockInfo(e.reactor.QueryClient(), oracleRegistration.TrustedBlockHeight, oracleRegistration.TrustedBlockHash); err != nil {
log.Errorf("failed to verify trusted block. height(%d), hash(%s), err(%v)", oracleRegistration.TrustedBlockHeight, oracleRegistration.TrustedBlockHash, err)
return nil, err
}

nodePubKeyHash := sha256.Sum256(oracleRegistration.NodePubKey)

if err := sgx.VerifyRemoteReport(oracleRegistration.NodePubKeyRemoteReport, nodePubKeyHash[:], *e.reactor.EnclaveInfo()); err != nil {
log.Errorf("failed to verification report. uniqueID(%s), address(%s), err(%v)", oracleRegistration.UniqueId, oracleRegistration.OracleAddress, err)
return nil, err
}

return makeMsgApproveOracleRegistration(approverUniqueID, approverAddress, targetAddress, oraclePrivKeyBz, oracleRegistration.NodePubKey)
}

}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ require (
github.com/magiconair/properties v1.8.6 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
github.com/medibloc/panacea-core/v2 v2.0.6-0.20221208062148-f30a46f80881 // indirect
github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 // indirect
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 // indirect
github.com/minio/sha256-simd v0.1.1 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1382,6 +1382,8 @@ github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88J
github.com/mbilski/exhaustivestruct v1.2.0/go.mod h1:OeTBVxQWoEmB2J2JCHmXWPJ0aksxSUOUy+nvtVEfzXc=
github.com/medibloc/cosmos-sdk v0.45.9-panacea.1 h1:JTprXN6z/+6UjkjQU4OfDz7z+sUpzev1s9DywmWA2Sk=
github.com/medibloc/cosmos-sdk v0.45.9-panacea.1/go.mod h1:Z5M4TX7PsHNHlF/1XanI2DIpORQ+Q/st7oaeufEjnvU=
github.com/medibloc/panacea-core/v2 v2.0.6-0.20221208062148-f30a46f80881 h1:NllUbgXJzXVWpqSlfIt8NCOtcLAO5FXM/ixJbvmvIXk=
github.com/medibloc/panacea-core/v2 v2.0.6-0.20221208062148-f30a46f80881/go.mod h1:jDmkCB2vXq/Daq/XvdzF5ELGH9eT20WpCLdTB7zhMBY=
github.com/mgechev/dots v0.0.0-20210922191527-e955255bf517/go.mod h1:KQ7+USdGKfpPjXk4Ga+5XxQM4Lm4e3gAogrreFAYpOg=
github.com/mgechev/revive v1.2.1/go.mod h1:+Ro3wqY4vakcYNtkBWdZC7dBg1xSB6sp054wWwmeFm0=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
Expand Down
57 changes: 35 additions & 22 deletions panacea/query_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/cosmos/cosmos-sdk/std"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/cosmos/ibc-go/v2/modules/core/23-commitment/types"
oracletypes "github.com/medibloc/panacea-core/v2/x/oracle/types"
"github.com/medibloc/panacea-oracle/config"
sgxdb "github.com/medibloc/panacea-oracle/store/sgxleveldb"
log "github.com/sirupsen/logrus"
Expand All @@ -34,6 +35,10 @@ import (
type QueryClient interface {
Close() error
GetAccount(address string) (authtypes.AccountI, error)
GetOracleRegistration(uniqueID, oracleAddr string) (*oracletypes.OracleRegistration, error)
GetLightBlock(height int64) (*tmtypes.LightBlock, error)
GetCdc() *codec.ProtoCodec
GetChainID() string
}

const (
Expand Down Expand Up @@ -219,6 +224,14 @@ func refresh(ctx context.Context, lc *light.Client, trustPeriod time.Duration, m
return nil
}

func (q verifiedQueryClient) GetCdc() *codec.ProtoCodec {
return q.cdc
}

func (q verifiedQueryClient) GetChainID() string {
return q.chainID
}

// GetStoreData get data from panacea with storeKey and key, then verify queried data with light client and merkle proof.
// the returned data type is ResponseQuery.value ([]byte), so recommend to convert to expected type
func (q verifiedQueryClient) GetStoreData(ctx context.Context, storeKey string, key []byte) ([]byte, error) {
Expand Down Expand Up @@ -348,28 +361,28 @@ func (q verifiedQueryClient) GetAccount(address string) (authtypes.AccountI, err
return account, nil
}

//func (q verifiedQueryClient) GetOracleRegistration(oracleAddr, uniqueID, pubKey string) (*oracletypes.OracleRegistration, error) {
//
// acc, err := GetAccAddressFromBech32(oracleAddr)
// if err != nil {
// return nil, err
// }
//
// key := oracletypes.GetOracleRegistrationKey(uniqueID, acc, pubKey)
//
// bz, err := q.GetStoreData(context.Background(), oracletypes.StoreKey, key)
// if err != nil {
// return nil, err
// }
//
// var oracleRegistration oracletypes.OracleRegistration
// err = q.cdc.UnmarshalLengthPrefixed(bz, &oracleRegistration)
// if err != nil {
// return nil, err
// }
//
// return &oracleRegistration, nil
//}
func (q verifiedQueryClient) GetOracleRegistration(uniqueID, oracleAddr string) (*oracletypes.OracleRegistration, error) {

acc, err := GetAccAddressFromBech32(oracleAddr)
if err != nil {
return nil, err
}

key := oracletypes.GetOracleRegistrationKey(uniqueID, acc)

bz, err := q.GetStoreData(context.Background(), oracletypes.StoreKey, key)
if err != nil {
return nil, err
}

var oracleRegistration oracletypes.OracleRegistration
err = q.cdc.UnmarshalLengthPrefixed(bz, &oracleRegistration)
if err != nil {
return nil, err
}

return &oracleRegistration, nil
}

//func (q verifiedQueryClient) GetOracleParamsPublicKey() (*btcec.PublicKey, error) {
// pubKeyBase64Bz, err := q.GetStoreData(context.Background(), paramstypes.StoreKey, append(append([]byte(oracletypes.StoreKey), '/'), oracletypes.KeyOraclePublicKey...))
Expand Down
Loading