Skip to content

Commit

Permalink
Merge pull request #12 from tellor-io/commit-reveal
Browse files Browse the repository at this point in the history
Add commit msg & logic to subVal to reveal value
  • Loading branch information
themandalore authored Sep 29, 2023
2 parents 3f52d91 + dcafdc8 commit 65a43b4
Show file tree
Hide file tree
Showing 18 changed files with 1,187 additions and 74 deletions.
1 change: 1 addition & 0 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,7 @@ func New(
keys[oraclemoduletypes.MemStoreKey],
app.GetSubspace(oraclemoduletypes.ModuleName),

app.AccountKeeper,
app.BankKeeper,
app.StakingKeeper,
app.RegistryKeeper,
Expand Down
11 changes: 11 additions & 0 deletions proto/layer/oracle/commit_value.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
syntax = "proto3";
package layer.oracle;

import "layer/oracle/tx.proto";
option go_package = "github.com/tellor-io/layer/x/oracle/types";

message CommitValue {

MsgCommitReport report = 1;
int64 block = 2;
}
2 changes: 1 addition & 1 deletion proto/layer/oracle/reports.proto
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ option go_package = "github.com/tellor-io/layer/x/oracle/types";

message Reports {

repeated MicroReport reports = 1;
repeated MicroReport microReports = 1;
}
13 changes: 11 additions & 2 deletions proto/layer/oracle/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,22 @@ option go_package = "github.com/tellor-io/layer/x/oracle/types";

// Msg defines the Msg service.
service Msg {
rpc SubmitValue (MsgSubmitValue) returns (MsgSubmitValueResponse);
rpc SubmitValue (MsgSubmitValue ) returns (MsgSubmitValueResponse );
rpc CommitReport (MsgCommitReport) returns (MsgCommitReportResponse);
}
message MsgSubmitValue {
string creator = 1;
string qid = 2;
string qid = 2;
string value = 3;
}

message MsgSubmitValueResponse {}

message MsgCommitReport {
string creator = 1;
string queryId = 2;
string signature = 3; //signature of value in submit value
}

message MsgCommitReportResponse {}

1 change: 1 addition & 0 deletions x/oracle/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ func GetTxCmd() *cobra.Command {
}

cmd.AddCommand(CmdSubmitValue())
cmd.AddCommand(CmdCommitReport())
// this line is used by starport scaffolding # 1

return cmd
Expand Down
56 changes: 56 additions & 0 deletions x/oracle/client/cli/tx_commit_report.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package cli

import (
"encoding/hex"
"strconv"

"github.com/cometbft/cometbft/libs/bytes"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/client/tx"
"github.com/spf13/cobra"
"github.com/tellor-io/layer/x/oracle/types"
)

var _ = strconv.Itoa(0)

func CmdCommitReport() *cobra.Command {
cmd := &cobra.Command{
Use: "commit-report [query-id] [signature]",
Short: "Broadcast message commitReport",
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) (err error) {
argQueryId := args[0]
value := args[1]

clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}
valueDecoded, err := hex.DecodeString(value)
if err != nil {
return err
}
// leaving this here for convenience to input value thru cli
// then is signed by the keys here
data, _, err := clientCtx.Keyring.SignByAddress(clientCtx.GetFromAddress(), valueDecoded)
if err != nil {
return err
}

msg := types.NewMsgCommitReport(
clientCtx.GetFromAddress().String(),
argQueryId,
bytes.HexBytes(data).String(),
)
if err := msg.ValidateBasic(); err != nil {
return err
}
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
},
}

flags.AddTxFlagsToCmd(cmd)

return cmd
}
3 changes: 3 additions & 0 deletions x/oracle/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type (
memKey storetypes.StoreKey
paramstore paramtypes.Subspace

accountKeeper types.AccountKeeper
bankKeeper types.BankKeeper
stakingKeeper types.StakingKeeper
registryKeeper types.RegistryKeeper
Expand All @@ -31,6 +32,7 @@ func NewKeeper(
memKey storetypes.StoreKey,
ps paramtypes.Subspace,

accountKeeper types.AccountKeeper,
bankKeeper types.BankKeeper,
stakingKeeper types.StakingKeeper,
registryKeeper types.RegistryKeeper,
Expand All @@ -46,6 +48,7 @@ func NewKeeper(
memKey: memKey,
paramstore: ps,

accountKeeper: accountKeeper,
bankKeeper: bankKeeper,
stakingKeeper: stakingKeeper,
registryKeeper: registryKeeper,
Expand Down
44 changes: 44 additions & 0 deletions x/oracle/keeper/msg_server_commit_report.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package keeper

import (
"context"
"encoding/hex"

"github.com/cosmos/cosmos-sdk/store/prefix"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/tellor-io/layer/x/oracle/types"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)

func (k msgServer) CommitReport(goCtx context.Context, msg *types.MsgCommitReport) (*types.MsgCommitReportResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.CommitReportKey))
report := types.CommitValue{
Report: msg,
Block: ctx.BlockHeight(),
}
qIdBytes, err := hex.DecodeString(msg.QueryId)
if err != nil {
return nil, err
}
store.Set(append([]byte(msg.Creator), qIdBytes...), k.cdc.MustMarshal(&report))

return &types.MsgCommitReportResponse{}, nil
}

func (k Keeper) getCommit(ctx sdk.Context, reporter, queryId string) (*types.CommitValue, error) {

commitStore := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.CommitReportKey))
qIdBytes, err := hex.DecodeString(queryId)
if err != nil {
return nil, err
}
commit := commitStore.Get(append([]byte(reporter), qIdBytes...))
if commit == nil {
return nil, status.Error(codes.NotFound, "no commits to reveal found")
}
var commitValue types.CommitValue
k.cdc.Unmarshal(commit, &commitValue)
return &commitValue, nil
}
136 changes: 93 additions & 43 deletions x/oracle/keeper/msg_server_submit_value.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
registryKeeper "github.com/tellor-io/layer/x/registry/keeper"
registryTypes "github.com/tellor-io/layer/x/registry/types"

"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/store/prefix"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/ethereum/go-ethereum/accounts/abi"
Expand All @@ -18,45 +19,58 @@ import (

func (k msgServer) SubmitValue(goCtx context.Context, msg *types.MsgSubmitValue) (*types.MsgSubmitValueResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)
if !k.IsSenderStaked(ctx, msg.Creator) {
// convert reporter address from bech32 to sdk.AccAddress
reporter, err := sdk.AccAddressFromBech32(msg.Creator)
if err != nil {
return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("failed to decode reporter address: %v", err))
}
// check if sender is bonded
if !k.IsReporterStaked(ctx, reporter) {
return nil, status.Error(codes.Unauthenticated, "sender is not staked")
}
// check if query id is valid
// check if query id field is valid
if !registryKeeper.IsQueryIdValid(msg.Qid) {
return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("invalid query id: %s", msg.Qid))
}
// decode query id hex string to bytes
qIdBytes, err := hex.DecodeString(msg.Qid)
// get commit from store
commitValue, err := k.getCommit(ctx, msg.Creator, msg.Qid)
if err != nil {
return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("failed to decode query ID string: %v", err))
return nil, err
}
// check if value is being revealed in the one block after commit
if ctx.BlockHeight()-1 != commitValue.Block {
return nil, status.Error(codes.InvalidArgument, "missed block height to reveal")
}
// if commitValue.Block < ctx.BlockHeight()-5 || commitValue.Block > ctx.BlockHeight() {
// return nil, status.Error(codes.InvalidArgument, "missed block height window to reveal")
// }
// verify value signature
if !k.verifySignature(ctx, reporter, msg.Value, commitValue.Report.Signature) {
return nil, status.Error(codes.InvalidArgument, "unable to verify signature")
}
// set value
k.setValue(ctx, msg)
return &types.MsgSubmitValueResponse{}, nil
}

func (k Keeper) setValue(ctx sdk.Context, msg *types.MsgSubmitValue) (*types.MsgSubmitValueResponse, error) {
// get query data from registry by query id
queryData, err := k.registryKeeper.QueryData(ctx, msg.Qid)
if err != nil {
return nil, status.Error(codes.NotFound, fmt.Sprintf("query data not found: %v", err))
}
// decode query data hex to get query type
decodedQueryType, err := decodeQueryType(queryData)
// decode query data hex to get query type, returns interface array
queryType, err := decodeQueryType(queryData)
if err != nil {
return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("failed to decode query type: %v", err))
}
queryType := decodedQueryType[0].(string)
// get data spec from registry by query type to validate value
dataSpecBytes := k.registryKeeper.Spec(ctx, queryType)
if dataSpecBytes == nil {
return nil, status.Error(codes.NotFound, fmt.Sprintf("data spec not found for query type: %s", queryType))
}

var dataSpec registryTypes.DataSpec
k.cdc.Unmarshal(dataSpecBytes, &dataSpec)
decodedSpec := &dataSpec
valueType := decodedSpec.ValueType
valueBytes, err := hex.DecodeString(msg.Value)
valueType, err := getValueType(k.registryKeeper, k.cdc, ctx, queryType)
if err != nil {
return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("failed to decode value string: %v", err))
return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("failed to get value type: %v", err))
}
// decode value using value type from data spec
value, err := decodeValue(valueBytes, valueType)
// decode value using value type from data spec and check if decodes successfully
// value is not used, only used to check if it decodes successfully
value, err := decodeValue(msg.Value, valueType)
ctx.Logger().Info(fmt.Sprintf("value: %v", value[0]))
if err != nil {
return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("failed to decode value: %v", err))
Expand All @@ -68,37 +82,82 @@ func (k msgServer) SubmitValue(goCtx context.Context, msg *types.MsgSubmitValue)
Value: msg.Value,
Timestamp: uint64(ctx.BlockTime().Unix()),
}
// decode query id hex string to bytes
qIdBytes, err := hex.DecodeString(msg.Qid)
if err != nil {
return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("failed to decode query ID string: %v", err))
}
var reportsList types.Reports
if err := k.cdc.Unmarshal(store.Get(qIdBytes), &reportsList); err != nil {
return nil, fmt.Errorf("failed to unmarshal reports: %v", err)
}
reportsList.Reports = append(reportsList.Reports, report)
reportsList.MicroReports = append(reportsList.MicroReports, report)
store.Set(qIdBytes, k.cdc.MustMarshal(&reportsList))
return &types.MsgSubmitValueResponse{}, nil
}
func getValueType(registry types.RegistryKeeper, cdc codec.BinaryCodec, ctx sdk.Context, queryType string) (string, error) {
// get data spec from registry by query type to validate value
dataSpecBytes := registry.Spec(ctx, queryType)
if dataSpecBytes == nil {
return "", status.Error(codes.NotFound, fmt.Sprintf("data spec not found for query type: %s", queryType))
}
var dataSpec registryTypes.DataSpec
cdc.Unmarshal(dataSpecBytes, &dataSpec)

return dataSpec.ValueType, nil
}

func (k Keeper) IsReporterStaked(ctx sdk.Context, reporter sdk.AccAddress) bool {
delegations := k.stakingKeeper.GetAllDelegatorDelegations(ctx, reporter)

func decodeQueryType(data []byte) ([]interface{}, error) {
var totalStakedTokens sdk.Dec = sdk.ZeroDec()
for _, delegation := range delegations {
totalStakedTokens = totalStakedTokens.Add(delegation.Shares)
}
return totalStakedTokens.GT(sdk.ZeroDec())
}

func (k Keeper) verifySignature(ctx sdk.Context, reporter sdk.AccAddress, value, signature string) bool {
reporterAccount := k.accountKeeper.GetAccount(ctx, reporter)
pubKey := reporterAccount.GetPubKey()
sigBytes, err := hex.DecodeString(signature)
if err != nil {
return false
}
// decode value from hex string
valBytes, err := hex.DecodeString(value)
if err != nil {
return false
}
// verify signature
if !pubKey.VerifySignature(valBytes, sigBytes) {
return false
}
return true
}

func decodeQueryType(data []byte) (string, error) {
// Create an ABI arguments object based on the types
strArg, err := abi.NewType("string", "string", nil)
if err != nil {
return nil, fmt.Errorf("failed to create new ABI type when decoding query type: %v", err)
return "", fmt.Errorf("failed to create new ABI type when decoding query type: %v", err)
}
bytesArg, err := abi.NewType("bytes", "bytes", nil)
if err != nil {
return nil, fmt.Errorf("failed to create new ABI type when decoding query type: %v", err)
return "", fmt.Errorf("failed to create new ABI type when decoding query type: %v", err)
}
args := abi.Arguments{
abi.Argument{Type: strArg},
abi.Argument{Type: bytesArg},
}
result, err := args.UnpackValues(data)
if err != nil {
return nil, fmt.Errorf("failed to unpack query type: %v", err)
return "", fmt.Errorf("failed to unpack query type: %v", err)
}
return result, nil
return result[0].(string), nil
}

func decodeValue(data []byte, dataType string) ([]interface{}, error) {
func decodeValue(value, dataType string) ([]interface{}, error) {
argType, err := abi.NewType(dataType, dataType, nil)
if err != nil {
return nil, fmt.Errorf("failed to create new ABI type when decoding value: %v", err)
Expand All @@ -107,27 +166,18 @@ func decodeValue(data []byte, dataType string) ([]interface{}, error) {
Type: argType,
}
args := abi.Arguments{arg}
valueBytes, err := hex.DecodeString(value)
if err != nil {
return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("failed to decode value string: %v", err))
}
var result []interface{}
result, err = args.Unpack(data)
result, err = args.Unpack(valueBytes)
if err != nil {
return nil, fmt.Errorf("failed to unpack value: %v", err)
}
return result, nil
}

func (k Keeper) IsSenderStaked(ctx sdk.Context, sender string) bool {
accAddr, err := sdk.AccAddressFromBech32(sender)
if err != nil {
return false
}
delegations := k.stakingKeeper.GetAllDelegatorDelegations(ctx, accAddr)
var totalStakedTokens sdk.Dec
for _, delegation := range delegations {
totalStakedTokens = totalStakedTokens.Add(delegation.GetShares())
}
return totalStakedTokens.GT(sdk.ZeroDec())
}

// cleanup reports list
// func (k Keeper) CleanupReports(ctx sdk.Context, qid string) {
// qIdBytes, err := hex.DecodeString(qid)
Expand Down
Loading

0 comments on commit 65a43b4

Please sign in to comment.