layout | title |
---|---|
default |
Adding new operation |
To provide TSS signature core uses operations and confirmation entities. Operation entity represents some data to be signed by threshold signature producers. Confirmation entity represents information about signature: indexes list, merkle root based on provided list and signature.
Operation entity contains the following fields:
message Operation {
string index = 1;
opType operationType = 2;
google.protobuf.Any details = 3;
opStatus status = 4;
string creator = 5;
uint64 timestamp = 6;
}
index
is the unique string that should be deterministic created depending on operation dataoperationType
defines the type of operation detailsdetails
contain any necessary information about operation to provide signature forstatus
defines the current status of operation (signed, approved, initialize, etc.)creator
defines the creator of certain operationtimestamp
contains the unix block timestamp then operation was created (the timestamp should be received from the context)
To add new operation developer should lead the following steps:
-
Add operation data definition in the
proto/rarimocore
.Example: proto/ratimocore/op_fee_token_management.proto
enum FeeTokenManagementType { ADD_FEE_TOKEN = 0; REMOVE_FEE_TOKEN = 1; UPDATE_FEE_TOKEN = 2; WITHDRAW_FEE_TOKEN = 3; } message FeeTokenManagement { FeeTokenManagementType opType = 1; rarimo.rarimocore.tokenmanager.FeeToken token = 2 [(gogoproto.nullable) = false]; string chain = 3; string receiver = 4; }
Also, add new operation type in
proto/rarimocore/operation.proto
.Example: proto/rarimocore/operation.proto
enum opType { TRANSFER = 0; CHANGE_PARTIES = 1; FEE_TOKEN_MANAGEMENT = 2; }
-
In
x/rarimocore/crypto/operation
define the operation content that should implementmerkle.Content
interface frommerkle "github.com/rarimo/go-merkle"
.Example: x/rarimocore/crypto/operation/op_fee_token_management.go
package operation import ( "bytes" eth "github.com/ethereum/go-ethereum/crypto" merkle "github.com/rarimo/go-merkle" ) // FeeTokenManagementContent implements the Content interface provided by go-merkle and represents the content stored in the tree. type FeeTokenManagementContent struct { // Hash of the deposit tx info Origin OriginData TargetNetwork string // Receiver address on target network Receiver []byte // Target bridge contract TargetContract []byte // Can contain any specific data for target chain to validate. Data ContentData } var _ merkle.Content = FeeTokenManagementContent{} func (f FeeTokenManagementContent) CalculateHash() []byte { return eth.Keccak256(f.Data, f.Origin[:], []byte(f.TargetNetwork), f.Receiver, f.TargetContract) } // Equals tests for equality of two Contents func (f FeeTokenManagementContent) Equals(other merkle.Content) bool { if oc, ok := other.(ChangePartiesContent); ok { return bytes.Equal(oc.CalculateHash(), f.CalculateHash()) } return false }
-
In
x/rarimocore/crypto/pkg
define the following methods:Get{op name}
andGet{op name} content
.Example: x/rarimocore/crypto/operation/op_fee_token_management.go
package operation func GetFeeTokenManagement(operation types.Operation) (*types.FeeTokenManagement, error) { if operation.OperationType == types.OpType_FEE_TOKEN_MANAGEMENT { op := new(types.FeeTokenManagement) return op, proto.Unmarshal(operation.Details.Value, op) } return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidType, "invalid operation type") } func GetFeeTokenManagementContent(strOrigin string, params tokentypes.NetworkParams, op *types.FeeTokenManagement) (*operation.FeeTokenManagementContent, error) { return &operation.FeeTokenManagementContent{ Origin: origin.NewStringOriginBuilder().SetString(strOrigin).Build().GetOrigin(), TargetNetwork: params.Name, Receiver: hexutil.MustDecode(op.Receiver), TargetContract: hexutil.MustDecode(params.Contract), Data: data.NewFeeTokenDataBuilder().SetOpType(op.OpType).SetAmount(op.Token.Amount).SetAmount(op.Token.Amount).Build().GetContent(), }, nil }
Tips: explore the
x/rarimocore/crypto/operation/data
andx/rarimocore/crypto/operation/origin
packages to use some useful utils from it or add the new if required. -
Add Get{op name}Content method and the corresponding case block to the
x/rarimocore/crypto/pkg/content/main.go
Example: x/rarimocore/crypto/pkg/content/main.go
case types.OpType_FEE_TOKEN_MANAGEMENT: content, err := GetFeeManagementContent(client, op) if err != nil { return nil, err } if content != nil { contents = append(contents, content) }
package content func GetFeeManagementContent(client *grpc.ClientConn, op *types.Operation) (merkle.Content, error) { manage, err := pkg.GetFeeTokenManagement(*op) if err != nil { return nil, errors.Wrap(err, "error parsing operation details") } networkResp, err := token.NewQueryClient(client).NetworkParams(context.TODO(), &token.QueryNetworkParamsRequest{Name: manage.Chain}) if err != nil { return nil, errors.Wrap(err, "error getting network param entry") } feeparams := networkResp.Params.GetFeeParams() if err != nil { return nil, errors.New("bridge params not found") } content, err := pkg.GetFeeTokenManagementContent(feeparams, manage) return content, errors.Wrap(err, "error creating content") }
-
In the
x/rarimocore/keeper
define function that creates the operation and define function call where it is required. -
In the
x/rarimocore/keeper/msg_server_confirmation.go
extend the existing logic ofgetContent(ctx sdk.Context, op types.Operation) (merkle.Content, error)
method. For example add:case types.OpType_FEE_TOKEN_MANAGEMENT: manage, err := pkg.GetFeeTokenManagement(op) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "failed to unmarshal details") } return k.getFeeTokenManagementContent(ctx, op.Index, manage)
Example:
func (k msgServer) getContent(ctx sdk.Context, op types.Operation) (merkle.Content, error) { switch op.OperationType { case types.OpType_TRANSFER: transfer, err := pkg.GetTransfer(op) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "failed to unmarshal details") } return k.getTransferOperationContent(ctx, transfer) case types.OpType_CHANGE_PARTIES: change, err := pkg.GetChangeParties(op) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "failed to unmarshal details") } return pkg.GetChangePartiesContent(change) case types.OpType_FEE_TOKEN_MANAGEMENT: manage, err := pkg.GetFeeTokenManagement(op) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "failed to unmarshal details") } return k.getFeeTokenManagementContent(ctx, op.Index, manage) default: return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "invalid operation") } }
-
Also, you can provide additional logic in
ApplyOperation(ctx sdk.Context, op types.Operation) error
to execute some stuff after signing if required. -
Update the core dependency version in
tss-svc
. (It will use methods that you've defined in 4)