Skip to content

Commit

Permalink
feat: Implement Mercury SDK
Browse files Browse the repository at this point in the history
  • Loading branch information
zhengjianhui committed Jun 20, 2021
1 parent 4d76309 commit 2b29782
Show file tree
Hide file tree
Showing 18 changed files with 754 additions and 1 deletion.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ require (
github.com/golang/mock v1.3.1
github.com/google/go-cmp v0.3.1
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1
github.com/opentracing/opentracing-go v1.1.0
github.com/pkg/errors v0.8.1
github.com/stretchr/testify v1.4.0
)
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ github.com/olekukonko/tablewriter v0.0.2-0.20190409134802-7e037d187b0c/go.mod h1
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pborman/uuid v0.0.0-20170112150404-1b00554d8222/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34=
Expand Down
66 changes: 66 additions & 0 deletions mercury/api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package mercury

import (
"github.com/ethereum/go-ethereum/rpc"
"github.com/nervosnetwork/ckb-sdk-go/mercury/model"
"github.com/nervosnetwork/ckb-sdk-go/mercury/model/action"
"github.com/nervosnetwork/ckb-sdk-go/mercury/model/resp"
"github.com/pkg/errors"
)

type MercuryApi interface {
GetBalance(udthash interface{}, ident string) (*resp.Balance, error)
BuildTransferTransaction(payload *model.TransferPayload) (*resp.TransferCompletionResponse, error)
BuildWalletCreationTransaction(payload *model.CreateWalletPayload) (*resp.TransferCompletionResponse, error)
}

type DefaultMercuryApi struct {
c *rpc.Client
}

func (cli *DefaultMercuryApi) GetBalance(udthash interface{}, ident string) (*resp.Balance, error) {
var balance resp.Balance
err := cli.c.Call(&balance, "get_balance", udthash, ident)
if err != nil {
return &balance, err
}

return &balance, err
}

func (cli *DefaultMercuryApi) BuildTransferTransaction(payload *model.TransferPayload) (*resp.TransferCompletionResponse, error) {
var resp resp.TransferCompletionResponse
if payload.UdtHash == "" {
for _, item := range payload.Items {
if item.To.Action == action.Lend_by_from || item.To.Action == action.Pay_by_to {
return &resp, errors.New("The transaction does not support ckb")
}
}
}

err := cli.c.Call(&resp, "build_transfer_transaction", payload)
if err != nil {
return &resp, err
}

return &resp, err
}

func (cli *DefaultMercuryApi) BuildWalletCreationTransaction(payload *model.CreateWalletPayload) (*resp.TransferCompletionResponse, error) {
var resp resp.TransferCompletionResponse
err := cli.c.Call(&resp, "build_wallet_creation_transaction", payload)
if err != nil {
return &resp, err
}

return &resp, err
}

func NewMercuryApi(address string) (MercuryApi, error) {
dial, err := rpc.Dial(address)
if err != nil {
return &DefaultMercuryApi{}, err
}

return &DefaultMercuryApi{dial}, err
}
7 changes: 7 additions & 0 deletions mercury/model/action/action.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package action

const (
Pay_by_from = "pay_by_from"
Lend_by_from = "lend_by_from"
Pay_by_to = "pay_by_to"
)
38 changes: 38 additions & 0 deletions mercury/model/create_wallet_payload.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package model

type CreateWalletPayload struct {
Ident string `json:"ident"`
Info []*WalletInfo `json:"info"`
Fee uint `json:"fee"`
}
type WalletInfo struct {
UdtHash string `json:"udt_hash"`
}

type CreateWalletPayloadBuilder struct {
Ident string `json:"ident"`
Info []*WalletInfo `json:"info"`
Fee uint `json:"fee"`
}

func (walletPayload *CreateWalletPayload) AddIdent(ident string) {
walletPayload.Ident = ident
}

func (walletPayload *CreateWalletPayload) AddFee(fee uint) {
walletPayload.Fee = fee
}

func (walletPayload *CreateWalletPayload) AddInfo(udtHash string) {
walletPayload.Info = append(walletPayload.Info, &WalletInfo{
UdtHash: udtHash,
})
}

func (walletPayload *CreateWalletPayload) Build() *CreateWalletPayload {
return &CreateWalletPayload{
Ident: walletPayload.Ident,
Info: walletPayload.Info,
Fee: walletPayload.Fee,
}
}
7 changes: 7 additions & 0 deletions mercury/model/resp/balance.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package resp

type Balance struct {
Unconstrained string `json:"owned"`
Fleeting string `json:"claimable"`
locked string `json:"locked"`
}
130 changes: 130 additions & 0 deletions mercury/model/resp/transcation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
package resp

import (
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/nervosnetwork/ckb-sdk-go/transaction"
"github.com/nervosnetwork/ckb-sdk-go/types"
)

type TransferCompletionResponse struct {
TxView transactionResp `json:"tx_view"`
SigsEntry []*SignatureEntry `json:"sigs_entry"`
}

type SignatureEntry struct {
Type string `json:"type"`
Index int `json:"index"`
PubKey string `json:"pub_key"`
GroupLen int `json:"group_len"`
}

type ScriptGroup struct {
Group []int
WitnessArgs *types.WitnessArgs
PubKey string
}

func (self *TransferCompletionResponse) GetTransaction() *types.Transaction {
return toTransaction(self.TxView)
}

func (self *TransferCompletionResponse) GetScriptGroup() []*ScriptGroup {
groupScripts := make([]*ScriptGroup, len(self.SigsEntry))

self.TxView.Witnesses = make([]hexutil.Bytes, len(self.TxView.Inputs))
for i, _ := range self.TxView.Witnesses {
self.TxView.Witnesses[i] = []byte{}
}

for index, entry := range self.SigsEntry {
group := make([]int, entry.GroupLen)
groupIndex := 0
for i := entry.Index; i < entry.Index+entry.GroupLen; i++ {
group[groupIndex] = i
groupIndex += 1
}

groupScripts[index] = &ScriptGroup{
Group: group,
WitnessArgs: transaction.EmptyWitnessArg,
PubKey: entry.PubKey,
}

self.TxView.Witnesses[entry.Index] = transaction.EmptyWitnessArgPlaceholder
}
return groupScripts
}

func toTransaction(tx transactionResp) *types.Transaction {
return &types.Transaction{
Version: uint(tx.Version),
Hash: tx.Hash,
CellDeps: toCellDeps(tx.CellDeps),
HeaderDeps: tx.HeaderDeps,
Inputs: toInputs(tx.Inputs),
Outputs: toOutputs(tx.Outputs),
OutputsData: toBytesArray(tx.OutputsData),
Witnesses: toBytesArray(tx.Witnesses),
}
}

func toCellDeps(deps []cellDep) []*types.CellDep {
result := make([]*types.CellDep, len(deps))
for i := 0; i < len(deps); i++ {
dep := deps[i]
result[i] = &types.CellDep{
OutPoint: &types.OutPoint{
TxHash: dep.OutPoint.TxHash,
Index: uint(dep.OutPoint.Index),
},
DepType: dep.DepType,
}
}
return result
}

func toInputs(inputs []cellInput) []*types.CellInput {
result := make([]*types.CellInput, len(inputs))
for i := 0; i < len(inputs); i++ {
input := inputs[i]
result[i] = &types.CellInput{
Since: uint64(input.Since),
PreviousOutput: &types.OutPoint{
TxHash: input.PreviousOutput.TxHash,
Index: uint(input.PreviousOutput.Index),
},
}
}
return result
}

func toOutputs(outputs []cellOutput) []*types.CellOutput {
result := make([]*types.CellOutput, len(outputs))
for i := 0; i < len(outputs); i++ {
output := outputs[i]
result[i] = &types.CellOutput{
Capacity: uint64(output.Capacity),
Lock: &types.Script{
CodeHash: output.Lock.CodeHash,
HashType: output.Lock.HashType,
Args: output.Lock.Args,
},
}
if output.Type != nil {
result[i].Type = &types.Script{
CodeHash: output.Type.CodeHash,
HashType: output.Type.HashType,
Args: output.Type.Args,
}
}
}
return result
}

func toBytesArray(bytes []hexutil.Bytes) [][]byte {
result := make([][]byte, len(bytes))
for i, data := range bytes {
result[i] = data
}
return result
}
44 changes: 44 additions & 0 deletions mercury/model/resp/transcation_resp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package resp

import (
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/nervosnetwork/ckb-sdk-go/types"
)

type transactionResp struct {
Version hexutil.Uint `json:"version"`
Hash types.Hash `json:"hash"`
CellDeps []cellDep `json:"cell_deps"`
HeaderDeps []types.Hash `json:"header_deps"`
Inputs []cellInput `json:"inputs"`
Outputs []cellOutput `json:"outputs"`
OutputsData []hexutil.Bytes `json:"outputs_data"`
Witnesses []hexutil.Bytes `json:"witnesses"`
}

type cellDep struct {
OutPoint outPoint `json:"out_point"`
DepType types.DepType `json:"dep_type"`
}

type outPoint struct {
TxHash types.Hash `json:"tx_hash"`
Index hexutil.Uint `json:"index"`
}

type cellInput struct {
Since hexutil.Uint64 `json:"since"`
PreviousOutput outPoint `json:"previous_output"`
}

type cellOutput struct {
Capacity hexutil.Uint64 `json:"capacity"`
Lock *script `json:"lock"`
Type *script `json:"type"`
}

type script struct {
CodeHash types.Hash `json:"code_hash"`
HashType types.ScriptHashType `json:"hash_type"`
Args hexutil.Bytes `json:"args"`
}
6 changes: 6 additions & 0 deletions mercury/model/source/source.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package source

const (
Unconstrained = "owned"
Fleeting = "claimable"
)
72 changes: 72 additions & 0 deletions mercury/model/transfer_payload.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package model

type TransferPayload struct {
UdtHash string `json:"udt_hash,omitempty"`
From *FromAccount `json:"from"`
Items []*TransferItem `json:"items"`
Change string `json:"change,omitempty"`
Fee uint `json:"fee"`
}

type FromAccount struct {
Idents []string `json:"idents"`
Source string `json:"source"`
}

type TransferItem struct {
To *ToAccount `json:"to"`
Amount uint `json:"amount"`
}

type ToAccount struct {
Ident string `json:"ident"`
Action string `json:"action"`
}

type TransferBuilder struct {
UdtHash string `json:"udt_hash,omitempty"`
From *FromAccount `json:"from"`
Items []*TransferItem `json:"items"`
Change string `json:"change,omitempty"`
Fee uint `json:"fee"`
}

func (builder *TransferBuilder) AddUdtHash(udtHash string) {
builder.UdtHash = udtHash
}

func (builder *TransferBuilder) AddFrom(idents []string, source string) {
form := &FromAccount{
Idents: idents,
Source: source,
}
builder.From = form
}

func (builder *TransferBuilder) AddItem(ident, action string, amount uint) {
to := &ToAccount{
Ident: ident,
Action: action}
item := &TransferItem{Amount: amount}
item.To = to
builder.Items = append(builder.Items, item)
}

func (builder *TransferBuilder) AddChange(change string) {
builder.Change = change
}

func (builder *TransferBuilder) AddFee(fee uint) {
builder.Fee = fee
}

func (builder *TransferBuilder) Build() *TransferPayload {
return &TransferPayload{
builder.UdtHash,
builder.From,
builder.Items,
builder.Change,
builder.Fee,
}

}
Loading

0 comments on commit 2b29782

Please sign in to comment.