Skip to content

Commit

Permalink
Merge pull request #76 from gmajor-encrypt/master
Browse files Browse the repository at this point in the history
Add extrinsic encode support
  • Loading branch information
freehere107 authored Apr 24, 2023
2 parents 66bc58a + fb80fa8 commit 6e3a3be
Show file tree
Hide file tree
Showing 10 changed files with 177 additions and 46 deletions.
9 changes: 9 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
FROM golang:1.18

WORKDIR app

COPY . .

RUN go mod download

CMD go test -v ./...
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,13 @@ go get -u github.com/itering/scale.go
## Test

```bash
go test ./... --cover -v
go test ./... --cover -v
```

Docker
```bash
docker build -t scale_go .
docker run -it scale_go
```

## Resources
Expand Down
3 changes: 0 additions & 3 deletions contract/ABI_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"testing"

"github.com/itering/scale.go/types"
"github.com/itering/scale.go/utiles"
"github.com/stretchr/testify/assert"
)

Expand All @@ -16,12 +15,10 @@ func Test_AbiParse(t *testing.T) {
abi, err := InitAbi(c)
assert.NoError(t, err)
assert.Greater(t, len(abi.Types), 1)
utiles.Debug(abi)

sc := types.ScaleDecoder{DuplicateName: make(map[string]int), RegisteredSiType: make(map[int]string)}
abi.Register(&sc, "pre")

utiles.Debug(abi.RegisteredSiType)
assert.Equal(t, len(abi.RegisteredSiType), 8)

}
135 changes: 109 additions & 26 deletions extrinsic.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package scalecodec

import (
"encoding/json"
"errors"
"fmt"

scaleType "github.com/itering/scale.go/types"
"github.com/itering/scale.go/types/scaleBytes"
"github.com/itering/scale.go/utiles"
"github.com/shopspring/decimal"
"golang.org/x/crypto/blake2b"
)

Expand Down Expand Up @@ -73,6 +76,25 @@ func (e *ExtrinsicDecoder) generateHash() string {
return utiles.BytesToHex(h)
}

type GenericExtrinsic struct {
VersionInfo string `json:"version_info"`
ExtrinsicLength int `json:"extrinsic_length"`
AddressType string `json:"address_type"`
Tip decimal.Decimal `json:"tip"`
SignedExtensions map[string]interface{} `json:"signed_extensions"`
AccountId interface{} `json:"accountId"`
Signer interface{} `json:"signer"` // map[string]interface or string
Signature string `json:"signature"`
SignatureRaw interface{} `json:"signature_raw"` // map[string]interface or string
Nonce int `json:"nonce"`
Era string `json:"era"`
ExtrinsicHash string `json:"extrinsic_hash"`
CallModuleFunction string `json:"call_module_function"`
CallCode string `json:"call_code"`
CallModule string `json:"call_module"`
Params []ExtrinsicParam `json:"params"`
}

func (e *ExtrinsicDecoder) Process() {
e.ExtrinsicLength = e.ProcessAndUpdateData("Compact<u32>").(int)
if e.ExtrinsicLength != e.Data.GetRemainingLength() {
Expand All @@ -84,27 +106,28 @@ func (e *ExtrinsicDecoder) Process() {

e.ContainsTransaction = utiles.U256(e.VersionInfo).Int64() >= 80

result := map[string]interface{}{
"extrinsic_length": e.ExtrinsicLength,
"version_info": e.VersionInfo,
result := GenericExtrinsic{
ExtrinsicLength: e.ExtrinsicLength,
VersionInfo: e.VersionInfo,
}

if e.VersionInfo == "04" || e.VersionInfo == "84" {
if e.ContainsTransaction {
// Address
address := e.ProcessAndUpdateData(utiles.TrueOrElse(e.Metadata.MetadataVersion >= 14 && scaleType.HasReg("ExtrinsicSigner"), "ExtrinsicSigner", "Address"))
switch v := address.(type) {
result.Signer = e.ProcessAndUpdateData(utiles.TrueOrElse(e.Metadata.MetadataVersion >= 14 && scaleType.HasReg("ExtrinsicSigner"), "ExtrinsicSigner", "Address"))
switch v := result.Signer.(type) {
case string:
e.Address = v
result["address_type"] = "AccountId"
result.AddressType = "AccountId"
case map[string]interface{}:
for name, value := range v {
result["address_type"] = name
result.AddressType = name
e.Address = value
}
}
// ExtrinsicSignature
signature := e.ProcessAndUpdateData("ExtrinsicSignature")
switch v := signature.(type) {
result.SignatureRaw = e.ProcessAndUpdateData("ExtrinsicSignature")
switch v := result.SignatureRaw.(type) {
case string:
e.Signature = v
case map[string]interface{}:
Expand All @@ -116,25 +139,25 @@ func (e *ExtrinsicDecoder) Process() {
e.Nonce = int(e.ProcessAndUpdateData("Compact<U64>").(uint64))
if e.Metadata.Extrinsic != nil {
if utiles.SliceIndex("ChargeTransactionPayment", e.Metadata.Extrinsic.SignedIdentifier) != -1 {
result["tip"] = e.ProcessAndUpdateData("Compact<Balance>")
result.Tip = e.ProcessAndUpdateData("Compact<Balance>").(decimal.Decimal)
}
} else {
result["tip"] = e.ProcessAndUpdateData("Compact<Balance>")
result.Tip = e.ProcessAndUpdateData("Compact<Balance>").(decimal.Decimal)
}
// spec SignedExtensions
if len(e.SignedExtensions) > 0 {
for _, extension := range e.SignedExtensions {
if utiles.SliceIndex(extension.Name, e.Metadata.Extrinsic.SignedIdentifier) != -1 {
for _, v := range extension.AdditionalSigned {
result[v.Name] = e.ProcessAndUpdateData(v.Type)
result.SignedExtensions[v.Name] = e.ProcessAndUpdateData(v.Type)
}
}
}
} else {
if e.Metadata.MetadataVersion >= 14 {
for _, ext := range e.Metadata.Extrinsic.SignedExtensions {
if enable, ok := signedExts[ext.Identifier]; ok && enable {
result[ext.Identifier] = e.ProcessAndUpdateData(ext.TypeString)
result.SignedExtensions[ext.Identifier] = e.ProcessAndUpdateData(ext.TypeString)
}
}
}
Expand All @@ -160,21 +183,81 @@ func (e *ExtrinsicDecoder) Process() {
}

if e.ContainsTransaction {
result["account_id"] = e.Address
result["signature"] = e.Signature
result["nonce"] = e.Nonce
result["era"] = e.Era
result["extrinsic_hash"] = e.ExtrinsicHash
result.AccountId = e.Address
result.Signature = e.Signature
result.Nonce = e.Nonce
result.Era = e.Era
result.ExtrinsicHash = e.ExtrinsicHash
}
result.CallCode = e.CallIndex
result.CallModuleFunction = call.Call.Name
result.CallModule = call.Module.Name
result.Params = e.Params
e.Value = &result
}

/*
Encode extrinsic with option
opt.Metadata is required
return hex string, if error, return empty string
if e.CallIndex != "" {
result["call_code"] = e.CallIndex
result["call_module_function"] = call.Call.Name
result["call_module"] = call.Module.Name
Example:
m := scalecodec.MetadataDecoder{}
m.Init(utiles.HexToBytes(Kusama9370))
_ = m.Process()
option := types.ScaleDecoderOption{Metadata: &m.Metadata}
genericExtrinsic := scalecodec.GenericExtrinsic{
VersionInfo: "84",
CallCode: "0400",
Nonce: 0,
Era: "00",
Signer: map[string]interface{}{"Id": "0xe673cb35ffaaf7ab98c4e9268bfa9b4a74e49d41c8225121c346db7a7dd06d88"},
SignatureRaw: map[string]interface{}{"Ed25519": "0xfce9453b1442bba86c2781e755a29c8a215ccf4b65ce81eeaa5b5a04dcdb79a54525cc86969f910c71c05f84aeab9c205022ecd4aa2abb4a3c3667f09dd16e0b"},
Params: []scalecodec.ExtrinsicParam{
{Value: map[string]interface{}{"Id": "0x0770e0831a275b534f7507c8ebd9f5f982a55053c9dc672da886ef41a6b5c628"}}, {Value: "1094000000000"},
},
}
fmt.Println(genericExtrinsic.Encode(&option))
*/

func (g *GenericExtrinsic) Encode(opt *scaleType.ScaleDecoderOption) (string, error) {
if opt.Metadata == nil {
return "", errors.New("invalid metadata")
}
data := g.VersionInfo
if g.VersionInfo == "84" {
data = data + scaleType.Encode(utiles.TrueOrElse(opt.Metadata.MetadataVersion >= 14 && scaleType.HasReg("ExtrinsicSigner"), "ExtrinsicSigner", "AccountId"), g.Signer) // accountId
data = data + scaleType.Encode("ExtrinsicSignature", g.SignatureRaw) // signature
data = data + scaleType.Encode("EraExtrinsic", g.Era) // era
data = data + scaleType.Encode("Compact<U64>", g.Nonce) // nonce
data = data + scaleType.Encode("Compact<Balance>", g.Tip) // tip
}

result["nonce"] = e.Nonce
result["era"] = e.Era
result["params"] = e.Params
e.Value = result
data = data + g.CallCode
call, ok := opt.Metadata.CallIndex[g.CallCode]

if !ok {
return "", fmt.Errorf("not find Extrinsic Lookup %s, please check metadata info", g.CallCode)
}

if len(g.Params) != len(call.Call.Args) {
return "", fmt.Errorf("extrinsic params length not match, expect %d, got %d", len(call.Call.Args), len(g.Params))
}

for index, arg := range call.Call.Args {
data = data + utiles.TrimHex(scaleType.EncodeWithOpt(arg.Type, g.Params[index].Value, opt))
}

return scaleType.Encode("Compact<u32>", len(utiles.HexToBytes(data))) + data, nil
}

// ToMap GenericExtrinsic convert to map[string]interface
func (g *GenericExtrinsic) ToMap() map[string]interface{} {
var r map[string]interface{}
b, err := json.Marshal(g)
if err != nil {
return nil
}
_ = json.Unmarshal(b, &r)
return r
}
29 changes: 29 additions & 0 deletions extrinsic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,32 @@ func TestCallEncode(t *testing.T) {

assert.Equal(t, "0400009094c424429709a324e65c64f151630e6c3700192bba8abd3c8e2218b61c0a7a13000064a7b3b6e00d", types.EncodeWithOpt("Call", call, &types.ScaleDecoderOption{Metadata: &m.Metadata}), "")
}

func TestExtrinsicEncode(t *testing.T) {
m := scalecodec.MetadataDecoder{}
m.Init(utiles.HexToBytes(Kusama9370))
_ = m.Process()
option := types.ScaleDecoderOption{Metadata: &m.Metadata}

extrinsicRaw := []string{
"280402000b91bdddd38601",
"3d028400e673cb35ffaaf7ab98c4e9268bfa9b4a74e49d41c8225121c346db7a7dd06d8800fce9453b1442bba86c2781e755a29c8a215ccf4b65ce81eeaa5b5a04dcdb79a54525cc86969f910c71c05f84aeab9c205022ecd4aa2abb4a3c3667f09dd16e0b0000000400000770e0831a275b534f7507c8ebd9f5f982a55053c9dc672da886ef41a6b5c62807003c7bb7fe",
"4d0384000ed3eb9c7be61e0f8e13abb04857d82be899c1d7c3203998e02538720052ad34019edcf93212fc47fd9934311d10363a2cdbaa6efb52660bfa335325a2eb78c52d1052ff7b3d4c926ec2ed02e9ce1ddacf34211a97d467b8e1fe9e8b5f63fa9f8d550005040000009d01524d524b3a3a45515549503a3a322e302e303a3a31333235343234332d3865646566396635326238316339306132612d434c4f544845532d53555350454e444552532d30303030303030333a3a626173652d31333235323835322d444742502e434c4f54484553",
"bd1384001641233add39a0bfb8941c27f3091e320fab4032db6153c4597408dc2733e52e01ae293222b8a9e7356522429a0bfa448bd5aeedeb8fafd214f5e36507c8e2b8221b19fff42d179072667ecfa8eade81d9be9ae80e7890f991c7ceb6fbfe4d638f350065070018023000007901524d524b3a3a4c4953543a3a322e302e303a3a31353837383636302d3334326631323130366561623664393034632d594f55444c4543484553542d594f55444c4543484553542d30303031313231343a3a3836353030303030303030303000006901524d524b3a3a4c4953543a3a322e302e303a3a31353838303530312d3334326631323130366561623664393034632d594f55444c454143432d594f55444c454143432d30303032333638353a3a3433323530303030303030303000007101524d524b3a3a4c4953543a3a322e302e303a3a31353838303237352d3334326631323130366561623664393034632d594f55444c45484149522d594f55444c45484149522d30303032323339353a3a3137333030303030303030303000007101524d524b3a3a4c4953543a3a322e302e303a3a31353837393232312d3334326631323130366561623664393034632d594f55444c45464143452d594f55444c45464143452d30303031353432383a3a3334363030303030303030303000007101524d524b3a3a4c4953543a3a322e302e303a3a31353837373734332d3334326631323130366561623664393034632d594f55444c45534b494e2d594f55444c45534b494e2d30303030363934363a3a3137333030303030303030303000007101524d524b3a3a4c4953543a3a322e302e303a3a31353837393732372d3334326631323130366561623664393034632d594f55444c45455945532d594f55444c45455945532d30303031383537383a3a3639323030303030303030303000007101524d524b3a3a4c4953543a3a322e302e303a3a31353837383839372d3334326631323130366561623664393034632d594f55444c45454152532d594f55444c45454152532d30303031333131303a3a3235393530303030303030303000007101524d524b3a3a4c4953543a3a322e302e303a3a31353837393835382d3334326631323130366561623664393034632d594f55444c45484149522d594f55444c45484149522d30303031393636373a3a3433323530303030303030303000006d01524d524b3a3a4c4953543a3a322e302e303a3a31353838303835302d3334326631323130366561623664393034632d594f55444c454143432d594f55444c454143432d30303032353530383a3a32313632353030303030303030300000a101524d524b3a3a4c4953543a3a322e302e303a3a31353837363832362d3334326631323130366561623664393034632d594f55444c454241434b47524f554e442d594f55444c454241434b47524f554e442d30303030313231393a3a3433323530303030303030303000007101524d524b3a3a4c4953543a3a322e302e303a3a31353838363037372d3334326631323130366561623664393034632d594f55444c4541524d2d594f55444c4541524d532d30303030303639393a3a313833333530303030303030303000005501524d524b3a3a4c4953543a3a322e302e303a3a31353837353238362d3334326631323130366561623664393034632d594f55444c452d594f55444c452d30303030303737303a3a3839303935303030303030303030",
"ad0284004a7c11337fdf53dd1ac3cdbfa86f90a059684f0c01a00827ab00259d946d5f5b01aed0bd7afbf2ad7a926bbab1157d3de28aa60f9e268e0400ba1165b1af4f5b56daa7faf62cd8219a1644268fc6c52946c749d840779b0677d2fd220019f71d8fc500d902001e06004e6709193edd9ad1773ef48e549fe565ea7d6e75ee1dfc1f631dbca7106ee26fc17cbec65603a1e6d434f8e3651db8020f453b8fad25d98b7822b9ef55f77665",
}

for _, raw := range extrinsicRaw {
e := scalecodec.ExtrinsicDecoder{}
e.Init(scaleBytes.ScaleBytes{Data: utiles.HexToBytes(raw)}, &option)
e.Process()
encode, err := e.Value.(*scalecodec.GenericExtrinsic).Encode(&option)
assert.NoError(t, err)
assert.Equal(t, raw, encode)
}

// will raise extrinsic params length not match error
genericExtrinsic := scalecodec.GenericExtrinsic{VersionInfo: "04", CallCode: "0200", Params: nil}
_, err := genericExtrinsic.Encode(&option)
assert.Error(t, err)
}
2 changes: 2 additions & 0 deletions metadata_test.go

Large diffs are not rendered by default.

15 changes: 11 additions & 4 deletions types/Bytes.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package types

import (
"strings"

"github.com/itering/scale.go/utiles"
)

Expand All @@ -16,11 +18,16 @@ func (b *Bytes) Process() {
}

func (b *Bytes) Encode(value string) string {
value = utiles.TrimHex(value)
if len(value)%2 == 1 {
value += "0"
var bytes []byte
if strings.HasPrefix(value, "0x") {
value = utiles.TrimHex(value)
if len(value)%2 == 1 {
value += "0"
}
} else {
value = utiles.BytesToHex([]byte(value))
}
bytes := utiles.HexToBytes(value)
bytes = utiles.HexToBytes(value)
return Encode("Compact<u32>", len(bytes)) + value
}

Expand Down
3 changes: 2 additions & 1 deletion types/Vectors.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"strings"

"github.com/itering/scale.go/types/scaleBytes"
"github.com/itering/scale.go/utiles"
)

type Vec struct {
Expand Down Expand Up @@ -42,7 +43,7 @@ func (v *Vec) Encode(value interface{}) string {
s := reflect.ValueOf(value)
raw += Encode("Compact<u32>", s.Len())
for i := 0; i < s.Len(); i++ {
raw += EncodeWithOpt(v.SubType, s.Index(i).Interface(), &ScaleDecoderOption{Spec: v.Spec, Metadata: v.Metadata})
raw += utiles.TrimHex(EncodeWithOpt(v.SubType, s.Index(i).Interface(), &ScaleDecoderOption{Spec: v.Spec, Metadata: v.Metadata}))
}
return raw
default:
Expand Down
4 changes: 4 additions & 0 deletions types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@ func (e *Era) Process() {
}
}

func (e *Era) Encode(era string) string {
return era
}

type EraExtrinsic struct{ Era }

type CompactMoment struct {
Expand Down
Loading

0 comments on commit 6e3a3be

Please sign in to comment.