Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*.dll
*.so
*.dylib

.idea
# Tests
*.test

Expand Down
9 changes: 9 additions & 0 deletions api/gas/gas.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ import (
"github.com/renproject/pack"
)

type TxType uint8

const (
ETHTransfer = TxType(0)
)

// The Estimator interface defines the functionality required to know the
// current recommended gas prices.
type Estimator interface {
Expand All @@ -22,4 +28,7 @@ type Estimator interface {
// required to get a transaction into one of the next few blocks (because
// blocks happen a lot faster).
EstimateGasPrice(context.Context) (pack.U256, error)

// EstimateGasLimit ...
EstimateGasLimit(TxType) (pack.U256, error)
}
146 changes: 146 additions & 0 deletions chain/iotex/account.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package iotex

import (
"errors"
"math/big"

"github.com/iotexproject/go-pkgs/crypto"
"github.com/iotexproject/go-pkgs/hash"
iotexaddr "github.com/iotexproject/iotex-address/address"
"github.com/iotexproject/iotex-proto/golang/iotextypes"

"github.com/renproject/multichain/api/account"
"github.com/renproject/multichain/api/address"
"github.com/renproject/multichain/api/contract"
"github.com/renproject/pack"

"github.com/golang/protobuf/proto"
)

type Tx struct {
from address.Address
to address.Address
value, nonce pack.U256
gasLimit, gasPrice pack.U256
payload pack.Bytes
sig pack.Bytes65
publicKey pack.Bytes
}

func (t *Tx) ToIoTeXTransfer() *iotextypes.Action {
return &iotextypes.Action{
Core: &iotextypes.ActionCore{
GasLimit: t.gasLimit.Int().Uint64(),
GasPrice: t.gasPrice.Int().String(),
Nonce: t.nonce.Int().Uint64(),
Action: &iotextypes.ActionCore_Transfer{
Transfer: &iotextypes.Transfer{
Amount: t.value.String(),
Recipient: string(t.to),
Payload: t.payload,
},
},
},
SenderPubKey: t.publicKey,
Signature: t.sig[:],
}
}

func (t *Tx) Hash() pack.Bytes {
sealed, err := t.Serialize()
if err != nil {
return nil
}
h := hash.Hash256b(sealed)
return h[:]
}

func (t *Tx) From() address.Address { return t.from }

func (t *Tx) To() address.Address { return t.to }

func (t *Tx) Value() pack.U256 { return t.value }

func (t *Tx) Nonce() pack.U256 { return t.nonce }

func (t *Tx) Payload() contract.CallData { return contract.CallData(t.payload) }

func (t *Tx) PublicKey() pack.Bytes { return t.publicKey }

func (t *Tx) Sighashes() ([]pack.Bytes32, error) {
act := t.ToIoTeXTransfer()
core, err := proto.Marshal(act.GetCore())
if err != nil {
return nil, err
}
h := hash.Hash256b(core)
return []pack.Bytes32{pack.Bytes32(h)}, nil
}

func (t *Tx) Sign(sig []pack.Bytes65, publicKey pack.Bytes) error {
copy(t.sig[:], sig[0][:])

pub, err := crypto.BytesToPublicKey(publicKey)
if err != nil {
return err
}
pubBytes := pub.Bytes()
t.publicKey = make([]byte, len(pubBytes))
copy(t.publicKey[:], pubBytes[:])
return nil
}

func (t *Tx) Serialize() (pack.Bytes, error) {
return proto.Marshal(t.ToIoTeXTransfer())
}

func (t *Tx) Deserialize(ser pack.Bytes) (account.Tx, error) {
act := &iotextypes.Action{}
if err := proto.Unmarshal(ser, act); err != nil {
return nil, err
}
pub, err := crypto.BytesToPublicKey(act.GetSenderPubKey())
if err != nil {
return nil, err
}
from, err := iotexaddr.FromBytes(pub.Hash())
if err != nil {
return nil, err
}
sig := pack.Bytes65{}
copy(sig[:], act.GetSignature())
amount, ok := new(big.Int).SetString(act.GetCore().GetTransfer().GetAmount(), 10)
if !ok {
return nil, errors.New("amount convert error")
}
gasPrice, ok := new(big.Int).SetString(act.GetCore().GetGasPrice(), 10)
if !ok {
return nil, errors.New("gas price convert error")
}
return &Tx{
from: address.Address(from.String()),
to: address.Address(act.GetCore().GetTransfer().GetRecipient()),
value: pack.NewU256FromInt(amount),
nonce: pack.NewU256FromU64(pack.U64(act.GetCore().GetNonce())),
gasLimit: pack.NewU256FromU64(pack.U64(act.GetCore().GetGasLimit())),
gasPrice: pack.NewU256FromInt(gasPrice),
payload: act.GetCore().GetTransfer().GetPayload(),
sig: sig,
publicKey: act.GetSenderPubKey(),
}, nil
}

type TxBuilder struct{}

func (t *TxBuilder) BuildTx(from, to address.Address, value, nonce pack.U256, gasPrice, gasLimit pack.U256, payload pack.Bytes) (account.Tx, error) {
return &Tx{
from: from,
to: to,
value: value,
nonce: nonce,
gasLimit: gasLimit,
gasPrice: gasPrice,
payload: payload,
sig: pack.NewBytes65([65]byte{0}),
}, nil
}
89 changes: 89 additions & 0 deletions chain/iotex/account_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package iotex_test

import (
"context"
"encoding/hex"
"fmt"
"math/big"
"os"
"time"

"github.com/iotexproject/go-pkgs/crypto"
"github.com/iotexproject/iotex-proto/golang/iotexapi"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/renproject/multichain/api/address"
"github.com/renproject/multichain/chain/iotex"
"github.com/renproject/pack"
"google.golang.org/grpc"
)

var _ = Describe("IoTeX", func() {
Context("when decoding address", func() {
Context("when decoding IoTeX address", func() {
It("should work", func() {
decoder := iotex.NewAddressDecoder()
addrStr := "io17ch0jth3dxqa7w9vu05yu86mqh0n6502d92lmp"
_, err := decoder.DecodeAddress(address.Address(pack.NewString(addrStr)))
Expect(err).ToNot(HaveOccurred())
})
})
})
})
var _ = Describe("IoTeX", func() {
Context("when submitting transactions", func() {
Context("when sending IOTX", func() {
It("should work", func() {
pkEnv := os.Getenv("pk")
if pkEnv == "" {
panic("pk is undefined")
}
gopts := []grpc.DialOption{}
gopts = append(gopts, grpc.WithInsecure())
endpoint := "api.testnet.iotex.one:80"
conn, err := grpc.Dial(endpoint, gopts...)
c := iotexapi.NewAPIServiceClient(conn)
request := &iotexapi.GetAccountRequest{Address: "io1vdtfpzkwpyngzvx7u2mauepnzja7kd5rryp0sg"}
res, err := c.GetAccount(context.Background(), request)
Expect(err).ToNot(HaveOccurred())

opts := iotex.ClientOptions{
Endpoint: endpoint,
Secure: false,
}
client := iotex.NewClient(opts)
builder := iotex.TxBuilder{}
gasPrice, _ := new(big.Int).SetString("1000000000000", 10)
tx, err := builder.BuildTx("io1vdtfpzkwpyngzvx7u2mauepnzja7kd5rryp0sg", "io1vdtfpzkwpyngzvx7u2mauepnzja7kd5rryp0sg", pack.NewU256FromU64(pack.NewU64(1)), pack.NewU256FromU64(pack.NewU64(res.AccountMeta.PendingNonce)), pack.NewU256FromInt(gasPrice), pack.NewU256FromU64(pack.NewU64(1000000)), nil)
Expect(err).ToNot(HaveOccurred())
sh, err := tx.Sighashes()
Expect(err).ToNot(HaveOccurred())
sk, err := crypto.HexStringToPrivateKey(pkEnv)
Expect(err).ToNot(HaveOccurred())
sig, err := sk.Sign(sh[0][:])
Expect(err).ToNot(HaveOccurred())
var sig65 [65]byte
copy(sig65[:], sig[:])
tx.Sign([]pack.Bytes65{pack.NewBytes65(sig65)}, sk.PublicKey().Bytes())
sigHash, err := tx.Sighashes()
Expect(err).ToNot(HaveOccurred())

sHash := hex.EncodeToString(sigHash[0][:])
Expect(err).ToNot(HaveOccurred())
fmt.Println("sig hash:", sHash)
fmt.Println("public key:", hex.EncodeToString(sk.PublicKey().Bytes()))
fmt.Println("transaction hash:", hex.EncodeToString(tx.Hash()))

err = client.SubmitTx(context.Background(), tx)
Expect(err).ToNot(HaveOccurred())

// We wait for 10 s before beginning to check transaction.
time.Sleep(10 * time.Second)
returnedTx, n, err := client.Tx(context.Background(), tx.Hash())
Expect(err).ToNot(HaveOccurred())
Expect(n).To(Equal(pack.NewU64(1)))
Expect(returnedTx.Nonce()).To(Equal(pack.NewU256FromU64(pack.NewU64(res.AccountMeta.PendingNonce))))
})
})
})
})
56 changes: 56 additions & 0 deletions chain/iotex/address.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package iotex

import (
iotexaddr "github.com/iotexproject/iotex-address/address"

"github.com/renproject/multichain/api/address"
"github.com/renproject/pack"
)

type AddressEncodeDecoder struct {
AddressEncoder
AddressDecoder
}

type AddressEncoder interface {
EncodeAddress(address.RawAddress) (address.Address, error)
}

type addressEncoder struct{}

func NewAddressEncodeDecoder() address.EncodeDecoder {
return AddressEncodeDecoder{
AddressEncoder: NewAddressEncoder(),
AddressDecoder: NewAddressDecoder(),
}
}

type AddressDecoder interface {
DecodeAddress(address.Address) (address.RawAddress, error)
}

type addressDecoder struct{}

func NewAddressDecoder() AddressDecoder {
return addressDecoder{}
}

func NewAddressEncoder() AddressEncoder {
return addressEncoder{}
}

func (addressDecoder) DecodeAddress(encoded address.Address) (address.RawAddress, error) {
addr, err := iotexaddr.FromString(string(encoded))
if err != nil {
return nil, err
}
return address.RawAddress(pack.Bytes(addr.Bytes())), nil
}

func (addressEncoder) EncodeAddress(rawAddr address.RawAddress) (address.Address, error) {
addr, err := iotexaddr.FromBytes(rawAddr)
if err != nil {
return address.Address(pack.NewString("")), err
}
return address.Address(pack.NewString(addr.String())), nil
}
1 change: 1 addition & 0 deletions chain/iotex/address_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package iotex_test
Loading