mirror of
https://github.com/fluencelabs/tendermint
synced 2025-05-01 17:42:16 +00:00
commit
713b1f0074
@ -104,25 +104,38 @@ func defaultConfig(moniker string) (defaultConfig string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// priv keys generated deterministically eg rpc/tests/helpers.go
|
||||||
var defaultGenesis = `{
|
var defaultGenesis = `{
|
||||||
"chain_id" : "tendermint_test",
|
"chain_id" : "tendermint_test",
|
||||||
"accounts": [
|
"accounts": [
|
||||||
{
|
{
|
||||||
"address": "1D7A91CB32F758A02EBB9BE1FB6F8DEE56F90D42",
|
"address": "E9B5D87313356465FAE33C406CE2C2979DE60BCB",
|
||||||
"amount": 200000000
|
"amount": 200000000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"address": "AC89A6DDF4C309A89A2C4078CE409A5A7B282270",
|
"address": "DFE4AFFA4CEE17CD01CB9E061D77C3ECED29BD88",
|
||||||
|
"amount": 200000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "F60D30722E7B497FA532FB3207C3FB29C31B1992",
|
||||||
|
"amount": 200000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "336CB40A5EB92E496E19B74FDFF2BA017C877FD6",
|
||||||
|
"amount": 200000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "D218F0F439BF0384F6F5EF8D0F8B398D941BD1DC",
|
||||||
"amount": 200000000
|
"amount": 200000000
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"validators": [
|
"validators": [
|
||||||
{
|
{
|
||||||
"pub_key": [1, "06FBAC4E285285D1D91FCBC7E91C780ADA11516F67462340B3980CE2B94940E8"],
|
"pub_key": [1, "583779C3BFA3F6C7E23C7D830A9C3D023A216B55079AD38BFED1207B94A19548"],
|
||||||
"amount": 1000000,
|
"amount": 1000000,
|
||||||
"unbond_to": [
|
"unbond_to": [
|
||||||
{
|
{
|
||||||
"address": "1D7A91CB32F758A02EBB9BE1FB6F8DEE56F90D42",
|
"address": "E9B5D87313356465FAE33C406CE2C2979DE60BCB",
|
||||||
"amount": 100000
|
"amount": 100000
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -39,8 +39,7 @@ func (p *Proposal) String() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *Proposal) WriteSignBytes(chainID string, w io.Writer, n *int64, err *error) {
|
func (p *Proposal) WriteSignBytes(chainID string, w io.Writer, n *int64, err *error) {
|
||||||
// We hex encode the chain_id name so we don't deal with escaping issues.
|
binary.WriteTo([]byte(Fmt(`{"chain_id":"%s"`, chainID)), w, n, err)
|
||||||
binary.WriteTo([]byte(Fmt(`{"chain_id":"%X"`, chainID)), w, n, err)
|
|
||||||
binary.WriteTo([]byte(`,"proposal":{"block_parts":`), w, n, err)
|
binary.WriteTo([]byte(`,"proposal":{"block_parts":`), w, n, err)
|
||||||
p.BlockParts.WriteSignBytes(w, n, err)
|
p.BlockParts.WriteSignBytes(w, n, err)
|
||||||
binary.WriteTo([]byte(Fmt(`,"height":%v,"pol_parts":`, p.Height)), w, n, err)
|
binary.WriteTo([]byte(Fmt(`,"height":%v,"pol_parts":`, p.Height)), w, n, err)
|
||||||
|
@ -19,7 +19,7 @@ func TestProposalSignable(t *testing.T) {
|
|||||||
}
|
}
|
||||||
signBytes := account.SignBytes(config.GetString("chain_id"), proposal)
|
signBytes := account.SignBytes(config.GetString("chain_id"), proposal)
|
||||||
signStr := string(signBytes)
|
signStr := string(signBytes)
|
||||||
expected := Fmt(`{"chain_id":"%X","proposal":{"block_parts":{"hash":"626C6F636B7061727473","total":111},"height":12345,"pol_parts":{"hash":"706F6C7061727473","total":222},"round":23456}}`,
|
expected := Fmt(`{"chain_id":"%s","proposal":{"block_parts":{"hash":"626C6F636B7061727473","total":111},"height":12345,"pol_parts":{"hash":"706F6C7061727473","total":222},"round":23456}}`,
|
||||||
config.GetString("chain_id"))
|
config.GetString("chain_id"))
|
||||||
if signStr != expected {
|
if signStr != expected {
|
||||||
t.Errorf("Got unexpected sign string for SendTx. Expected:\n%v\nGot:\n%v", expected, signStr)
|
t.Errorf("Got unexpected sign string for SendTx. Expected:\n%v\nGot:\n%v", expected, signStr)
|
||||||
|
@ -7,11 +7,11 @@ import (
|
|||||||
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GenPrivAccount() (*ctypes.ResponseGenPrivAccount, error) {
|
func GenPrivAccount() (*acm.PrivAccount, error) {
|
||||||
return &ctypes.ResponseGenPrivAccount{acm.GenPrivAccount()}, nil
|
return acm.GenPrivAccount(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetAccount(address []byte) (*ctypes.ResponseGetAccount, error) {
|
func GetAccount(address []byte) (*acm.Account, error) {
|
||||||
cache := mempoolReactor.Mempool.GetCache()
|
cache := mempoolReactor.Mempool.GetCache()
|
||||||
account := cache.GetAccount(address)
|
account := cache.GetAccount(address)
|
||||||
if account == nil {
|
if account == nil {
|
||||||
@ -24,7 +24,7 @@ func GetAccount(address []byte) (*ctypes.ResponseGetAccount, error) {
|
|||||||
StorageRoot: nil,
|
StorageRoot: nil,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &ctypes.ResponseGetAccount{account}, nil
|
return account, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetStorage(address, key []byte) (*ctypes.ResponseGetStorage, error) {
|
func GetStorage(address, key []byte) (*ctypes.ResponseGetStorage, error) {
|
||||||
|
@ -9,9 +9,8 @@ import (
|
|||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
// pass pointer?
|
|
||||||
// Note: tx must be signed
|
// Note: tx must be signed
|
||||||
func BroadcastTx(tx types.Tx) (*ctypes.ResponseBroadcastTx, error) {
|
func BroadcastTx(tx types.Tx) (*ctypes.Receipt, error) {
|
||||||
err := mempoolReactor.BroadcastTx(tx)
|
err := mempoolReactor.BroadcastTx(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Error broadcasting transaction: %v", err)
|
return nil, fmt.Errorf("Error broadcasting transaction: %v", err)
|
||||||
@ -27,10 +26,9 @@ func BroadcastTx(tx types.Tx) (*ctypes.ResponseBroadcastTx, error) {
|
|||||||
contractAddr = state.NewContractAddress(callTx.Input.Address, uint64(callTx.Input.Sequence))
|
contractAddr = state.NewContractAddress(callTx.Input.Address, uint64(callTx.Input.Sequence))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &ctypes.ResponseBroadcastTx{ctypes.Receipt{txHash, createsContract, contractAddr}}, nil
|
return &ctypes.Receipt{txHash, createsContract, contractAddr}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ListUnconfirmedTxs() (*ctypes.ResponseListUnconfirmedTxs, error) {
|
func ListUnconfirmedTxs() ([]types.Tx, error) {
|
||||||
txs := mempoolReactor.Mempool.GetProposalTxs()
|
return mempoolReactor.Mempool.GetProposalTxs(), nil
|
||||||
return &ctypes.ResponseListUnconfirmedTxs{txs}, nil
|
|
||||||
}
|
}
|
||||||
|
29
rpc/core/names.go
Normal file
29
rpc/core/names.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||||
|
"github.com/tendermint/tendermint/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetName(name string) (*types.NameRegEntry, error) {
|
||||||
|
st := consensusState.GetState() // performs a copy
|
||||||
|
entry := st.GetNameRegEntry(name)
|
||||||
|
if entry == nil {
|
||||||
|
return nil, fmt.Errorf("Name %s not found", name)
|
||||||
|
}
|
||||||
|
return entry, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ListNames() (*ctypes.ResponseListNames, error) {
|
||||||
|
var blockHeight uint
|
||||||
|
var names []*types.NameRegEntry
|
||||||
|
state := consensusState.GetState()
|
||||||
|
blockHeight = state.LastBlockHeight
|
||||||
|
state.GetNames().Iterate(func(key interface{}, value interface{}) bool {
|
||||||
|
names = append(names, value.(*types.NameRegEntry))
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
return &ctypes.ResponseListNames{blockHeight, names}, nil
|
||||||
|
}
|
@ -20,6 +20,8 @@ var Routes = map[string]*rpc.RPCFunc{
|
|||||||
"broadcast_tx": rpc.NewRPCFunc(BroadcastTx, []string{"tx"}),
|
"broadcast_tx": rpc.NewRPCFunc(BroadcastTx, []string{"tx"}),
|
||||||
"list_unconfirmed_txs": rpc.NewRPCFunc(ListUnconfirmedTxs, []string{}),
|
"list_unconfirmed_txs": rpc.NewRPCFunc(ListUnconfirmedTxs, []string{}),
|
||||||
"list_accounts": rpc.NewRPCFunc(ListAccounts, []string{}),
|
"list_accounts": rpc.NewRPCFunc(ListAccounts, []string{}),
|
||||||
|
"get_name": rpc.NewRPCFunc(GetName, []string{"name"}),
|
||||||
|
"list_names": rpc.NewRPCFunc(ListNames, []string{}),
|
||||||
"unsafe/gen_priv_account": rpc.NewRPCFunc(GenPrivAccount, []string{}),
|
"unsafe/gen_priv_account": rpc.NewRPCFunc(GenPrivAccount, []string{}),
|
||||||
"unsafe/sign_tx": rpc.NewRPCFunc(SignTx, []string{"tx", "privAccounts"}),
|
"unsafe/sign_tx": rpc.NewRPCFunc(SignTx, []string{"tx", "privAccounts"}),
|
||||||
}
|
}
|
||||||
|
@ -78,7 +78,7 @@ func CallCode(code, data []byte) (*ctypes.ResponseCall, error) {
|
|||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
func SignTx(tx types.Tx, privAccounts []*account.PrivAccount) (*ctypes.ResponseSignTx, error) {
|
func SignTx(tx types.Tx, privAccounts []*account.PrivAccount) (types.Tx, error) {
|
||||||
// more checks?
|
// more checks?
|
||||||
|
|
||||||
for i, privAccount := range privAccounts {
|
for i, privAccount := range privAccounts {
|
||||||
@ -113,5 +113,5 @@ func SignTx(tx types.Tx, privAccounts []*account.PrivAccount) (*ctypes.ResponseS
|
|||||||
rebondTx := tx.(*types.RebondTx)
|
rebondTx := tx.(*types.RebondTx)
|
||||||
rebondTx.Signature = privAccounts[0].Sign(config.GetString("chain_id"), rebondTx).(account.SignatureEd25519)
|
rebondTx.Signature = privAccounts[0].Sign(config.GetString("chain_id"), rebondTx).(account.SignatureEd25519)
|
||||||
}
|
}
|
||||||
return &ctypes.ResponseSignTx{tx}, nil
|
return tx, nil
|
||||||
}
|
}
|
||||||
|
@ -6,14 +6,6 @@ import (
|
|||||||
"github.com/tendermint/tendermint/types"
|
"github.com/tendermint/tendermint/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ResponseGenPrivAccount struct {
|
|
||||||
PrivAccount *account.PrivAccount `json:"priv_account"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ResponseGetAccount struct {
|
|
||||||
Account *account.Account `json:"account"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ResponseGetStorage struct {
|
type ResponseGetStorage struct {
|
||||||
Key []byte `json:"key"`
|
Key []byte `json:"key"`
|
||||||
Value []byte `json:"value"`
|
Value []byte `json:"value"`
|
||||||
@ -50,14 +42,6 @@ type ResponseGetBlock struct {
|
|||||||
Block *types.Block `json:"block"`
|
Block *types.Block `json:"block"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ResponseBroadcastTx struct {
|
|
||||||
Receipt Receipt `json:"receipt"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ResponseListUnconfirmedTxs struct {
|
|
||||||
Txs []types.Tx `json:"txs"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Receipt struct {
|
type Receipt struct {
|
||||||
TxHash []byte `json:"tx_hash"`
|
TxHash []byte `json:"tx_hash"`
|
||||||
CreatesContract uint8 `json:"creates_contract"`
|
CreatesContract uint8 `json:"creates_contract"`
|
||||||
@ -86,10 +70,6 @@ type Peer struct {
|
|||||||
IsOutbound bool `json:"is_outbound"`
|
IsOutbound bool `json:"is_outbound"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ResponseSignTx struct {
|
|
||||||
Tx types.Tx `json:"tx"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ResponseListValidators struct {
|
type ResponseListValidators struct {
|
||||||
BlockHeight uint `json:"block_height"`
|
BlockHeight uint `json:"block_height"`
|
||||||
BondedValidators []*sm.Validator `json:"bonded_validators"`
|
BondedValidators []*sm.Validator `json:"bonded_validators"`
|
||||||
@ -100,3 +80,8 @@ type ResponseDumpConsensusState struct {
|
|||||||
RoundState string `json:"round_state"`
|
RoundState string `json:"round_state"`
|
||||||
PeerRoundStates []string `json:"peer_round_states"`
|
PeerRoundStates []string `json:"peer_round_states"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ResponseListNames struct {
|
||||||
|
BlockHeight uint `json:"block_height"`
|
||||||
|
Names []*types.NameRegEntry `json:"names"`
|
||||||
|
}
|
||||||
|
@ -5,8 +5,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"github.com/tendermint/tendermint/binary"
|
"github.com/tendermint/tendermint/binary"
|
||||||
rpctypes "github.com/tendermint/tendermint/rpc/types"
|
rpctypes "github.com/tendermint/tendermint/rpc/types"
|
||||||
// NOTE: do not import rpc/core.
|
|
||||||
// What kind of client imports all of core logic? :P
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
@ -21,15 +19,20 @@ var reverseFuncMap = map[string]string{
|
|||||||
"Status": "status",
|
"Status": "status",
|
||||||
"NetInfo": "net_info",
|
"NetInfo": "net_info",
|
||||||
"BlockchainInfo": "blockchain",
|
"BlockchainInfo": "blockchain",
|
||||||
|
"Genesis": "genesis",
|
||||||
"GetBlock": "get_block",
|
"GetBlock": "get_block",
|
||||||
"GetAccount": "get_account",
|
"GetAccount": "get_account",
|
||||||
"GetStorage": "get_storage",
|
"GetStorage": "get_storage",
|
||||||
"Call": "call",
|
"Call": "call",
|
||||||
"CallCode": "call_code",
|
"CallCode": "call_code",
|
||||||
"ListValidators": "list_validators",
|
"ListValidators": "list_validators",
|
||||||
|
"DumpConsensusState": "dump_consensus_state",
|
||||||
"DumpStorage": "dump_storage",
|
"DumpStorage": "dump_storage",
|
||||||
"BroadcastTx": "broadcast_tx",
|
"BroadcastTx": "broadcast_tx",
|
||||||
|
"ListUnconfirmedTxs": "list_unconfirmed_txs",
|
||||||
"ListAccounts": "list_accounts",
|
"ListAccounts": "list_accounts",
|
||||||
|
"GetName": "get_name",
|
||||||
|
"ListNames": "list_names",
|
||||||
"GenPrivAccount": "unsafe/gen_priv_account",
|
"GenPrivAccount": "unsafe/gen_priv_account",
|
||||||
"SignTx": "unsafe/sign_tx",
|
"SignTx": "unsafe/sign_tx",
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ package core_client
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/tendermint/tendermint/account"
|
"github.com/tendermint/tendermint/account"
|
||||||
|
acm "github.com/tendermint/tendermint/account"
|
||||||
"github.com/tendermint/tendermint/binary"
|
"github.com/tendermint/tendermint/binary"
|
||||||
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||||
rpctypes "github.com/tendermint/tendermint/rpc/types"
|
rpctypes "github.com/tendermint/tendermint/rpc/types"
|
||||||
@ -15,21 +16,23 @@ import (
|
|||||||
|
|
||||||
type Client interface {
|
type Client interface {
|
||||||
BlockchainInfo(minHeight uint, maxHeight uint) (*ctypes.ResponseBlockchainInfo, error)
|
BlockchainInfo(minHeight uint, maxHeight uint) (*ctypes.ResponseBlockchainInfo, error)
|
||||||
BroadcastTx(tx types.Tx) (*ctypes.ResponseBroadcastTx, error)
|
BroadcastTx(tx types.Tx) (*ctypes.Receipt, error)
|
||||||
Call(address []byte, data []byte) (*ctypes.ResponseCall, error)
|
Call(address []byte, data []byte) (*ctypes.ResponseCall, error)
|
||||||
CallCode(code []byte, data []byte) (*ctypes.ResponseCall, error)
|
CallCode(code []byte, data []byte) (*ctypes.ResponseCall, error)
|
||||||
DumpConsensusState() (*ctypes.ResponseDumpConsensusState, error)
|
DumpConsensusState() (*ctypes.ResponseDumpConsensusState, error)
|
||||||
DumpStorage(address []byte) (*ctypes.ResponseDumpStorage, error)
|
DumpStorage(address []byte) (*ctypes.ResponseDumpStorage, error)
|
||||||
GenPrivAccount() (*ctypes.ResponseGenPrivAccount, error)
|
GenPrivAccount() (*acm.PrivAccount, error)
|
||||||
Genesis() (*string, error)
|
Genesis() (*string, error)
|
||||||
GetAccount(address []byte) (*ctypes.ResponseGetAccount, error)
|
GetAccount(address []byte) (*acm.Account, error)
|
||||||
GetBlock(height uint) (*ctypes.ResponseGetBlock, error)
|
GetBlock(height uint) (*ctypes.ResponseGetBlock, error)
|
||||||
|
GetName(name string) (*types.NameRegEntry, error)
|
||||||
GetStorage(address []byte, key []byte) (*ctypes.ResponseGetStorage, error)
|
GetStorage(address []byte, key []byte) (*ctypes.ResponseGetStorage, error)
|
||||||
ListAccounts() (*ctypes.ResponseListAccounts, error)
|
ListAccounts() (*ctypes.ResponseListAccounts, error)
|
||||||
ListUnconfirmedTxs() (*ctypes.ResponseListUnconfirmedTxs, error)
|
ListNames() (*ctypes.ResponseListNames, error)
|
||||||
|
ListUnconfirmedTxs() ([]types.Tx, error)
|
||||||
ListValidators() (*ctypes.ResponseListValidators, error)
|
ListValidators() (*ctypes.ResponseListValidators, error)
|
||||||
NetInfo() (*ctypes.ResponseNetInfo, error)
|
NetInfo() (*ctypes.ResponseNetInfo, error)
|
||||||
SignTx(tx types.Tx, privAccounts []*account.PrivAccount) (*ctypes.ResponseSignTx, error)
|
SignTx(tx types.Tx, privAccounts []*account.PrivAccount) (types.Tx, error)
|
||||||
Status() (*ctypes.ResponseStatus, error)
|
Status() (*ctypes.ResponseStatus, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,7 +66,7 @@ func (c *ClientHTTP) BlockchainInfo(minHeight uint, maxHeight uint) (*ctypes.Res
|
|||||||
return response.Result, nil
|
return response.Result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ClientHTTP) BroadcastTx(tx types.Tx) (*ctypes.ResponseBroadcastTx, error) {
|
func (c *ClientHTTP) BroadcastTx(tx types.Tx) (*ctypes.Receipt, error) {
|
||||||
values, err := argsToURLValues([]string{"tx"}, tx)
|
values, err := argsToURLValues([]string{"tx"}, tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -78,7 +81,7 @@ func (c *ClientHTTP) BroadcastTx(tx types.Tx) (*ctypes.ResponseBroadcastTx, erro
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var response struct {
|
var response struct {
|
||||||
Result *ctypes.ResponseBroadcastTx `json:"result"`
|
Result *ctypes.Receipt `json:"result"`
|
||||||
Error string `json:"error"`
|
Error string `json:"error"`
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
JSONRPC string `json:"jsonrpc"`
|
JSONRPC string `json:"jsonrpc"`
|
||||||
@ -213,7 +216,7 @@ func (c *ClientHTTP) DumpStorage(address []byte) (*ctypes.ResponseDumpStorage, e
|
|||||||
return response.Result, nil
|
return response.Result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ClientHTTP) GenPrivAccount() (*ctypes.ResponseGenPrivAccount, error) {
|
func (c *ClientHTTP) GenPrivAccount() (*acm.PrivAccount, error) {
|
||||||
values, err := argsToURLValues(nil)
|
values, err := argsToURLValues(nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -228,7 +231,7 @@ func (c *ClientHTTP) GenPrivAccount() (*ctypes.ResponseGenPrivAccount, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var response struct {
|
var response struct {
|
||||||
Result *ctypes.ResponseGenPrivAccount `json:"result"`
|
Result *acm.PrivAccount `json:"result"`
|
||||||
Error string `json:"error"`
|
Error string `json:"error"`
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
JSONRPC string `json:"jsonrpc"`
|
JSONRPC string `json:"jsonrpc"`
|
||||||
@ -273,7 +276,7 @@ func (c *ClientHTTP) Genesis() (*string, error) {
|
|||||||
return response.Result, nil
|
return response.Result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ClientHTTP) GetAccount(address []byte) (*ctypes.ResponseGetAccount, error) {
|
func (c *ClientHTTP) GetAccount(address []byte) (*acm.Account, error) {
|
||||||
values, err := argsToURLValues([]string{"address"}, address)
|
values, err := argsToURLValues([]string{"address"}, address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -288,7 +291,7 @@ func (c *ClientHTTP) GetAccount(address []byte) (*ctypes.ResponseGetAccount, err
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var response struct {
|
var response struct {
|
||||||
Result *ctypes.ResponseGetAccount `json:"result"`
|
Result *acm.Account `json:"result"`
|
||||||
Error string `json:"error"`
|
Error string `json:"error"`
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
JSONRPC string `json:"jsonrpc"`
|
JSONRPC string `json:"jsonrpc"`
|
||||||
@ -333,6 +336,36 @@ func (c *ClientHTTP) GetBlock(height uint) (*ctypes.ResponseGetBlock, error) {
|
|||||||
return response.Result, nil
|
return response.Result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *ClientHTTP) GetName(name string) (*types.NameRegEntry, error) {
|
||||||
|
values, err := argsToURLValues([]string{"name"}, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
resp, err := http.PostForm(c.addr+reverseFuncMap["GetName"], values)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var response struct {
|
||||||
|
Result *types.NameRegEntry `json:"result"`
|
||||||
|
Error string `json:"error"`
|
||||||
|
Id string `json:"id"`
|
||||||
|
JSONRPC string `json:"jsonrpc"`
|
||||||
|
}
|
||||||
|
binary.ReadJSON(&response, body, &err)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if response.Error != "" {
|
||||||
|
return nil, fmt.Errorf(response.Error)
|
||||||
|
}
|
||||||
|
return response.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *ClientHTTP) GetStorage(address []byte, key []byte) (*ctypes.ResponseGetStorage, error) {
|
func (c *ClientHTTP) GetStorage(address []byte, key []byte) (*ctypes.ResponseGetStorage, error) {
|
||||||
values, err := argsToURLValues([]string{"address", "key"}, address, key)
|
values, err := argsToURLValues([]string{"address", "key"}, address, key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -393,7 +426,37 @@ func (c *ClientHTTP) ListAccounts() (*ctypes.ResponseListAccounts, error) {
|
|||||||
return response.Result, nil
|
return response.Result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ClientHTTP) ListUnconfirmedTxs() (*ctypes.ResponseListUnconfirmedTxs, error) {
|
func (c *ClientHTTP) ListNames() (*ctypes.ResponseListNames, error) {
|
||||||
|
values, err := argsToURLValues(nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
resp, err := http.PostForm(c.addr+reverseFuncMap["ListNames"], values)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var response struct {
|
||||||
|
Result *ctypes.ResponseListNames `json:"result"`
|
||||||
|
Error string `json:"error"`
|
||||||
|
Id string `json:"id"`
|
||||||
|
JSONRPC string `json:"jsonrpc"`
|
||||||
|
}
|
||||||
|
binary.ReadJSON(&response, body, &err)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if response.Error != "" {
|
||||||
|
return nil, fmt.Errorf(response.Error)
|
||||||
|
}
|
||||||
|
return response.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientHTTP) ListUnconfirmedTxs() ([]types.Tx, error) {
|
||||||
values, err := argsToURLValues(nil)
|
values, err := argsToURLValues(nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -408,7 +471,7 @@ func (c *ClientHTTP) ListUnconfirmedTxs() (*ctypes.ResponseListUnconfirmedTxs, e
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var response struct {
|
var response struct {
|
||||||
Result *ctypes.ResponseListUnconfirmedTxs `json:"result"`
|
Result []types.Tx `json:"result"`
|
||||||
Error string `json:"error"`
|
Error string `json:"error"`
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
JSONRPC string `json:"jsonrpc"`
|
JSONRPC string `json:"jsonrpc"`
|
||||||
@ -483,7 +546,7 @@ func (c *ClientHTTP) NetInfo() (*ctypes.ResponseNetInfo, error) {
|
|||||||
return response.Result, nil
|
return response.Result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ClientHTTP) SignTx(tx types.Tx, privAccounts []*account.PrivAccount) (*ctypes.ResponseSignTx, error) {
|
func (c *ClientHTTP) SignTx(tx types.Tx, privAccounts []*account.PrivAccount) (types.Tx, error) {
|
||||||
values, err := argsToURLValues([]string{"tx", "privAccounts"}, tx, privAccounts)
|
values, err := argsToURLValues([]string{"tx", "privAccounts"}, tx, privAccounts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -498,7 +561,7 @@ func (c *ClientHTTP) SignTx(tx types.Tx, privAccounts []*account.PrivAccount) (*
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var response struct {
|
var response struct {
|
||||||
Result *ctypes.ResponseSignTx `json:"result"`
|
Result types.Tx `json:"result"`
|
||||||
Error string `json:"error"`
|
Error string `json:"error"`
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
JSONRPC string `json:"jsonrpc"`
|
JSONRPC string `json:"jsonrpc"`
|
||||||
@ -570,7 +633,7 @@ func (c *ClientJSON) BlockchainInfo(minHeight uint, maxHeight uint) (*ctypes.Res
|
|||||||
return response.Result, nil
|
return response.Result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ClientJSON) BroadcastTx(tx types.Tx) (*ctypes.ResponseBroadcastTx, error) {
|
func (c *ClientJSON) BroadcastTx(tx types.Tx) (*ctypes.Receipt, error) {
|
||||||
request := rpctypes.RPCRequest{
|
request := rpctypes.RPCRequest{
|
||||||
JSONRPC: "2.0",
|
JSONRPC: "2.0",
|
||||||
Method: reverseFuncMap["BroadcastTx"],
|
Method: reverseFuncMap["BroadcastTx"],
|
||||||
@ -582,7 +645,7 @@ func (c *ClientJSON) BroadcastTx(tx types.Tx) (*ctypes.ResponseBroadcastTx, erro
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var response struct {
|
var response struct {
|
||||||
Result *ctypes.ResponseBroadcastTx `json:"result"`
|
Result *ctypes.Receipt `json:"result"`
|
||||||
Error string `json:"error"`
|
Error string `json:"error"`
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
JSONRPC string `json:"jsonrpc"`
|
JSONRPC string `json:"jsonrpc"`
|
||||||
@ -705,7 +768,7 @@ func (c *ClientJSON) DumpStorage(address []byte) (*ctypes.ResponseDumpStorage, e
|
|||||||
return response.Result, nil
|
return response.Result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ClientJSON) GenPrivAccount() (*ctypes.ResponseGenPrivAccount, error) {
|
func (c *ClientJSON) GenPrivAccount() (*acm.PrivAccount, error) {
|
||||||
request := rpctypes.RPCRequest{
|
request := rpctypes.RPCRequest{
|
||||||
JSONRPC: "2.0",
|
JSONRPC: "2.0",
|
||||||
Method: reverseFuncMap["GenPrivAccount"],
|
Method: reverseFuncMap["GenPrivAccount"],
|
||||||
@ -717,7 +780,7 @@ func (c *ClientJSON) GenPrivAccount() (*ctypes.ResponseGenPrivAccount, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var response struct {
|
var response struct {
|
||||||
Result *ctypes.ResponseGenPrivAccount `json:"result"`
|
Result *acm.PrivAccount `json:"result"`
|
||||||
Error string `json:"error"`
|
Error string `json:"error"`
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
JSONRPC string `json:"jsonrpc"`
|
JSONRPC string `json:"jsonrpc"`
|
||||||
@ -759,7 +822,7 @@ func (c *ClientJSON) Genesis() (*string, error) {
|
|||||||
return response.Result, nil
|
return response.Result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ClientJSON) GetAccount(address []byte) (*ctypes.ResponseGetAccount, error) {
|
func (c *ClientJSON) GetAccount(address []byte) (*acm.Account, error) {
|
||||||
request := rpctypes.RPCRequest{
|
request := rpctypes.RPCRequest{
|
||||||
JSONRPC: "2.0",
|
JSONRPC: "2.0",
|
||||||
Method: reverseFuncMap["GetAccount"],
|
Method: reverseFuncMap["GetAccount"],
|
||||||
@ -771,7 +834,7 @@ func (c *ClientJSON) GetAccount(address []byte) (*ctypes.ResponseGetAccount, err
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var response struct {
|
var response struct {
|
||||||
Result *ctypes.ResponseGetAccount `json:"result"`
|
Result *acm.Account `json:"result"`
|
||||||
Error string `json:"error"`
|
Error string `json:"error"`
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
JSONRPC string `json:"jsonrpc"`
|
JSONRPC string `json:"jsonrpc"`
|
||||||
@ -813,6 +876,33 @@ func (c *ClientJSON) GetBlock(height uint) (*ctypes.ResponseGetBlock, error) {
|
|||||||
return response.Result, nil
|
return response.Result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *ClientJSON) GetName(name string) (*types.NameRegEntry, error) {
|
||||||
|
request := rpctypes.RPCRequest{
|
||||||
|
JSONRPC: "2.0",
|
||||||
|
Method: reverseFuncMap["GetName"],
|
||||||
|
Params: []interface{}{name},
|
||||||
|
Id: 0,
|
||||||
|
}
|
||||||
|
body, err := c.RequestResponse(request)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var response struct {
|
||||||
|
Result *types.NameRegEntry `json:"result"`
|
||||||
|
Error string `json:"error"`
|
||||||
|
Id string `json:"id"`
|
||||||
|
JSONRPC string `json:"jsonrpc"`
|
||||||
|
}
|
||||||
|
binary.ReadJSON(&response, body, &err)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if response.Error != "" {
|
||||||
|
return nil, fmt.Errorf(response.Error)
|
||||||
|
}
|
||||||
|
return response.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *ClientJSON) GetStorage(address []byte, key []byte) (*ctypes.ResponseGetStorage, error) {
|
func (c *ClientJSON) GetStorage(address []byte, key []byte) (*ctypes.ResponseGetStorage, error) {
|
||||||
request := rpctypes.RPCRequest{
|
request := rpctypes.RPCRequest{
|
||||||
JSONRPC: "2.0",
|
JSONRPC: "2.0",
|
||||||
@ -867,7 +957,34 @@ func (c *ClientJSON) ListAccounts() (*ctypes.ResponseListAccounts, error) {
|
|||||||
return response.Result, nil
|
return response.Result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ClientJSON) ListUnconfirmedTxs() (*ctypes.ResponseListUnconfirmedTxs, error) {
|
func (c *ClientJSON) ListNames() (*ctypes.ResponseListNames, error) {
|
||||||
|
request := rpctypes.RPCRequest{
|
||||||
|
JSONRPC: "2.0",
|
||||||
|
Method: reverseFuncMap["ListNames"],
|
||||||
|
Params: []interface{}{},
|
||||||
|
Id: 0,
|
||||||
|
}
|
||||||
|
body, err := c.RequestResponse(request)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var response struct {
|
||||||
|
Result *ctypes.ResponseListNames `json:"result"`
|
||||||
|
Error string `json:"error"`
|
||||||
|
Id string `json:"id"`
|
||||||
|
JSONRPC string `json:"jsonrpc"`
|
||||||
|
}
|
||||||
|
binary.ReadJSON(&response, body, &err)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if response.Error != "" {
|
||||||
|
return nil, fmt.Errorf(response.Error)
|
||||||
|
}
|
||||||
|
return response.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientJSON) ListUnconfirmedTxs() ([]types.Tx, error) {
|
||||||
request := rpctypes.RPCRequest{
|
request := rpctypes.RPCRequest{
|
||||||
JSONRPC: "2.0",
|
JSONRPC: "2.0",
|
||||||
Method: reverseFuncMap["ListUnconfirmedTxs"],
|
Method: reverseFuncMap["ListUnconfirmedTxs"],
|
||||||
@ -879,7 +996,7 @@ func (c *ClientJSON) ListUnconfirmedTxs() (*ctypes.ResponseListUnconfirmedTxs, e
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var response struct {
|
var response struct {
|
||||||
Result *ctypes.ResponseListUnconfirmedTxs `json:"result"`
|
Result []types.Tx `json:"result"`
|
||||||
Error string `json:"error"`
|
Error string `json:"error"`
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
JSONRPC string `json:"jsonrpc"`
|
JSONRPC string `json:"jsonrpc"`
|
||||||
@ -948,7 +1065,7 @@ func (c *ClientJSON) NetInfo() (*ctypes.ResponseNetInfo, error) {
|
|||||||
return response.Result, nil
|
return response.Result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ClientJSON) SignTx(tx types.Tx, privAccounts []*account.PrivAccount) (*ctypes.ResponseSignTx, error) {
|
func (c *ClientJSON) SignTx(tx types.Tx, privAccounts []*account.PrivAccount) (types.Tx, error) {
|
||||||
request := rpctypes.RPCRequest{
|
request := rpctypes.RPCRequest{
|
||||||
JSONRPC: "2.0",
|
JSONRPC: "2.0",
|
||||||
Method: reverseFuncMap["SignTx"],
|
Method: reverseFuncMap["SignTx"],
|
||||||
@ -960,7 +1077,7 @@ func (c *ClientJSON) SignTx(tx types.Tx, privAccounts []*account.PrivAccount) (*
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var response struct {
|
var response struct {
|
||||||
Result *ctypes.ResponseSignTx `json:"result"`
|
Result types.Tx `json:"result"`
|
||||||
Error string `json:"error"`
|
Error string `json:"error"`
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
JSONRPC string `json:"jsonrpc"`
|
JSONRPC string `json:"jsonrpc"`
|
||||||
|
@ -40,6 +40,10 @@ func TestHTTPCallContract(t *testing.T) {
|
|||||||
testCall(t, "HTTP")
|
testCall(t, "HTTP")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHTTPNameReg(t *testing.T) {
|
||||||
|
testNameReg(t, "HTTP")
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------
|
||||||
// Test the JSONRPC client
|
// Test the JSONRPC client
|
||||||
|
|
||||||
@ -74,3 +78,7 @@ func TestJSONCallCode(t *testing.T) {
|
|||||||
func TestJSONCallContract(t *testing.T) {
|
func TestJSONCallContract(t *testing.T) {
|
||||||
testCall(t, "JSONRPC")
|
testCall(t, "JSONRPC")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestJSONNameReg(t *testing.T) {
|
||||||
|
testNameReg(t, "JSONRPC")
|
||||||
|
}
|
||||||
|
@ -50,7 +50,7 @@ func TestWSBlockchainGrowth(t *testing.T) {
|
|||||||
|
|
||||||
// send a transaction and validate the events from listening for both sender and receiver
|
// send a transaction and validate the events from listening for both sender and receiver
|
||||||
func TestWSSend(t *testing.T) {
|
func TestWSSend(t *testing.T) {
|
||||||
toAddr := []byte{20, 143, 25, 63, 16, 177, 83, 29, 91, 91, 54, 23, 233, 46, 190, 121, 122, 34, 86, 54}
|
toAddr := user[1].Address
|
||||||
amt := uint64(100)
|
amt := uint64(100)
|
||||||
|
|
||||||
con := newWSCon(t)
|
con := newWSCon(t)
|
||||||
@ -80,7 +80,7 @@ func TestWSDoubleFire(t *testing.T) {
|
|||||||
con.Close()
|
con.Close()
|
||||||
}()
|
}()
|
||||||
amt := uint64(100)
|
amt := uint64(100)
|
||||||
toAddr := []byte{20, 143, 25, 63, 16, 177, 83, 29, 91, 91, 54, 23, 233, 46, 190, 121, 122, 34, 86, 54}
|
toAddr := user[1].Address
|
||||||
// broadcast the transaction, wait to hear about it
|
// broadcast the transaction, wait to hear about it
|
||||||
waitForEvent(t, con, eid, true, func() {
|
waitForEvent(t, con, eid, true, func() {
|
||||||
tx := makeDefaultSendTxSigned(t, wsTyp, toAddr, amt)
|
tx := makeDefaultSendTxSigned(t, wsTyp, toAddr, amt)
|
||||||
|
@ -2,7 +2,6 @@ package rpctest
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/hex"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@ -29,8 +28,7 @@ var (
|
|||||||
mempoolCount = 0
|
mempoolCount = 0
|
||||||
|
|
||||||
// make keys
|
// make keys
|
||||||
userPriv = "C453604BD6480D5538B4C6FD2E3E314B5BCE518D75ADE4DA3DA85AB8ADFD819606FBAC4E285285D1D91FCBC7E91C780ADA11516F67462340B3980CE2B94940E8"
|
user = makeUsers(5)
|
||||||
user = makeUsers(2)
|
|
||||||
|
|
||||||
chainID string
|
chainID string
|
||||||
|
|
||||||
@ -40,6 +38,7 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// deterministic account generation, synced with genesis file in config/tendermint_test/config.go
|
||||||
func makeUsers(n int) []*account.PrivAccount {
|
func makeUsers(n int) []*account.PrivAccount {
|
||||||
accounts := []*account.PrivAccount{}
|
accounts := []*account.PrivAccount{}
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
@ -47,13 +46,6 @@ func makeUsers(n int) []*account.PrivAccount {
|
|||||||
user := account.GenPrivAccountFromSecret(secret)
|
user := account.GenPrivAccountFromSecret(secret)
|
||||||
accounts = append(accounts, user)
|
accounts = append(accounts, user)
|
||||||
}
|
}
|
||||||
|
|
||||||
// include our validator
|
|
||||||
var byteKey [64]byte
|
|
||||||
userPrivByteSlice, _ := hex.DecodeString(userPriv)
|
|
||||||
copy(byteKey[:], userPrivByteSlice)
|
|
||||||
privAcc := account.GenPrivAccountFromKey(byteKey)
|
|
||||||
accounts[0] = privAcc
|
|
||||||
return accounts
|
return accounts
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,7 +94,7 @@ func init() {
|
|||||||
func makeDefaultSendTx(t *testing.T, typ string, addr []byte, amt uint64) *types.SendTx {
|
func makeDefaultSendTx(t *testing.T, typ string, addr []byte, amt uint64) *types.SendTx {
|
||||||
nonce := getNonce(t, typ, user[0].Address)
|
nonce := getNonce(t, typ, user[0].Address)
|
||||||
tx := types.NewSendTx()
|
tx := types.NewSendTx()
|
||||||
tx.AddInputWithNonce(user[0].PubKey, amt, nonce)
|
tx.AddInputWithNonce(user[0].PubKey, amt, nonce+1)
|
||||||
tx.AddOutput(addr, amt)
|
tx.AddOutput(addr, amt)
|
||||||
return tx
|
return tx
|
||||||
}
|
}
|
||||||
@ -115,7 +107,14 @@ func makeDefaultSendTxSigned(t *testing.T, typ string, addr []byte, amt uint64)
|
|||||||
|
|
||||||
func makeDefaultCallTx(t *testing.T, typ string, addr, code []byte, amt, gasLim, fee uint64) *types.CallTx {
|
func makeDefaultCallTx(t *testing.T, typ string, addr, code []byte, amt, gasLim, fee uint64) *types.CallTx {
|
||||||
nonce := getNonce(t, typ, user[0].Address)
|
nonce := getNonce(t, typ, user[0].Address)
|
||||||
tx := types.NewCallTxWithNonce(user[0].PubKey, addr, code, amt, gasLim, fee, nonce)
|
tx := types.NewCallTxWithNonce(user[0].PubKey, addr, code, amt, gasLim, fee, nonce+1)
|
||||||
|
tx.Sign(chainID, user[0])
|
||||||
|
return tx
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeDefaultNameTx(t *testing.T, typ string, name, value string, amt, fee uint64) *types.NameTx {
|
||||||
|
nonce := getNonce(t, typ, user[0].Address)
|
||||||
|
tx := types.NewNameTxWithNonce(user[0].PubKey, name, value, amt, fee, nonce+1)
|
||||||
tx.Sign(chainID, user[0])
|
tx.Sign(chainID, user[0])
|
||||||
return tx
|
return tx
|
||||||
}
|
}
|
||||||
@ -124,16 +123,16 @@ func makeDefaultCallTx(t *testing.T, typ string, addr, code []byte, amt, gasLim,
|
|||||||
// rpc call wrappers (fail on err)
|
// rpc call wrappers (fail on err)
|
||||||
|
|
||||||
// get an account's nonce
|
// get an account's nonce
|
||||||
func getNonce(t *testing.T, typ string, addr []byte) uint64 {
|
func getNonce(t *testing.T, typ string, addr []byte) uint {
|
||||||
client := clients[typ]
|
client := clients[typ]
|
||||||
ac, err := client.GetAccount(addr)
|
ac, err := client.GetAccount(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if ac.Account == nil {
|
if ac == nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
return uint64(ac.Account.Sequence)
|
return ac.Sequence
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the account
|
// get the account
|
||||||
@ -143,28 +142,28 @@ func getAccount(t *testing.T, typ string, addr []byte) *account.Account {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
return ac.Account
|
return ac
|
||||||
}
|
}
|
||||||
|
|
||||||
// sign transaction
|
// sign transaction
|
||||||
func signTx(t *testing.T, typ string, tx types.Tx, privAcc *account.PrivAccount) types.Tx {
|
func signTx(t *testing.T, typ string, tx types.Tx, privAcc *account.PrivAccount) types.Tx {
|
||||||
client := clients[typ]
|
client := clients[typ]
|
||||||
resp, err := client.SignTx(tx, []*account.PrivAccount{privAcc})
|
signedTx, err := client.SignTx(tx, []*account.PrivAccount{privAcc})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
return resp.Tx
|
return signedTx
|
||||||
}
|
}
|
||||||
|
|
||||||
// broadcast transaction
|
// broadcast transaction
|
||||||
func broadcastTx(t *testing.T, typ string, tx types.Tx) ctypes.Receipt {
|
func broadcastTx(t *testing.T, typ string, tx types.Tx) *ctypes.Receipt {
|
||||||
client := clients[typ]
|
client := clients[typ]
|
||||||
resp, err := client.BroadcastTx(tx)
|
rec, err := client.BroadcastTx(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
mempoolCount += 1
|
mempoolCount += 1
|
||||||
return resp.Receipt
|
return rec
|
||||||
}
|
}
|
||||||
|
|
||||||
// dump all storage for an account. currently unused
|
// dump all storage for an account. currently unused
|
||||||
@ -210,6 +209,16 @@ func callContract(t *testing.T, client cclient.Client, address, data, expected [
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get the namereg entry
|
||||||
|
func getNameRegEntry(t *testing.T, typ string, name string) *types.NameRegEntry {
|
||||||
|
client := clients[typ]
|
||||||
|
entry, err := client.GetName(name)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return entry
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------
|
||||||
// utility verification function
|
// utility verification function
|
||||||
|
|
||||||
|
@ -9,6 +9,8 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var doNothing = func(eid string, b []byte) error { return nil }
|
||||||
|
|
||||||
func testStatus(t *testing.T, typ string) {
|
func testStatus(t *testing.T, typ string) {
|
||||||
client := clients[typ]
|
client := clients[typ]
|
||||||
resp, err := client.Status()
|
resp, err := client.Status()
|
||||||
@ -23,11 +25,11 @@ func testStatus(t *testing.T, typ string) {
|
|||||||
|
|
||||||
func testGenPriv(t *testing.T, typ string) {
|
func testGenPriv(t *testing.T, typ string) {
|
||||||
client := clients[typ]
|
client := clients[typ]
|
||||||
resp, err := client.GenPrivAccount()
|
privAcc, err := client.GenPrivAccount()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if len(resp.PrivAccount.Address) == 0 {
|
if len(privAcc.Address) == 0 {
|
||||||
t.Fatal("Failed to generate an address")
|
t.Fatal("Failed to generate an address")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -44,13 +46,13 @@ func testGetAccount(t *testing.T, typ string) {
|
|||||||
|
|
||||||
func testSignedTx(t *testing.T, typ string) {
|
func testSignedTx(t *testing.T, typ string) {
|
||||||
amt := uint64(100)
|
amt := uint64(100)
|
||||||
toAddr := []byte{20, 143, 25, 63, 16, 177, 83, 29, 91, 91, 54, 23, 233, 46, 190, 121, 122, 34, 86, 54}
|
toAddr := user[1].Address
|
||||||
testOneSignTx(t, typ, toAddr, amt)
|
testOneSignTx(t, typ, toAddr, amt)
|
||||||
|
|
||||||
toAddr = []byte{20, 143, 24, 63, 16, 17, 83, 29, 90, 91, 52, 2, 0, 41, 190, 121, 122, 34, 86, 54}
|
toAddr = user[2].Address
|
||||||
testOneSignTx(t, typ, toAddr, amt)
|
testOneSignTx(t, typ, toAddr, amt)
|
||||||
|
|
||||||
toAddr = []byte{0, 0, 4, 0, 0, 4, 0, 0, 4, 91, 52, 2, 0, 41, 190, 121, 122, 34, 86, 54}
|
toAddr = user[3].Address
|
||||||
testOneSignTx(t, typ, toAddr, amt)
|
testOneSignTx(t, typ, toAddr, amt)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,7 +73,7 @@ func testOneSignTx(t *testing.T, typ string, addr []byte, amt uint64) {
|
|||||||
|
|
||||||
func testBroadcastTx(t *testing.T, typ string) {
|
func testBroadcastTx(t *testing.T, typ string) {
|
||||||
amt := uint64(100)
|
amt := uint64(100)
|
||||||
toAddr := []byte{20, 143, 25, 63, 16, 177, 83, 29, 91, 91, 54, 23, 233, 46, 190, 121, 122, 34, 86, 54}
|
toAddr := user[1].Address
|
||||||
tx := makeDefaultSendTxSigned(t, typ, toAddr, amt)
|
tx := makeDefaultSendTxSigned(t, typ, toAddr, amt)
|
||||||
receipt := broadcastTx(t, typ, tx)
|
receipt := broadcastTx(t, typ, tx)
|
||||||
if receipt.CreatesContract > 0 {
|
if receipt.CreatesContract > 0 {
|
||||||
@ -120,10 +122,7 @@ func testGetStorage(t *testing.T, typ string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// allow it to get mined
|
// allow it to get mined
|
||||||
waitForEvent(t, con, eid, true, func() {
|
waitForEvent(t, con, eid, true, func() {}, doNothing)
|
||||||
}, func(eid string, b []byte) error {
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
mempoolCount = 0
|
mempoolCount = 0
|
||||||
|
|
||||||
v := getStorage(t, typ, contractAddr, []byte{0x1})
|
v := getStorage(t, typ, contractAddr, []byte{0x1})
|
||||||
@ -179,10 +178,7 @@ func testCall(t *testing.T, typ string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// allow it to get mined
|
// allow it to get mined
|
||||||
waitForEvent(t, con, eid, true, func() {
|
waitForEvent(t, con, eid, true, func() {}, doNothing)
|
||||||
}, func(eid string, b []byte) error {
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
mempoolCount = 0
|
mempoolCount = 0
|
||||||
|
|
||||||
// run a call through the contract
|
// run a call through the contract
|
||||||
@ -190,3 +186,76 @@ func testCall(t *testing.T, typ string) {
|
|||||||
expected := []byte{0xb}
|
expected := []byte{0xb}
|
||||||
callContract(t, client, contractAddr, data, expected)
|
callContract(t, client, contractAddr, data, expected)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testNameReg(t *testing.T, typ string) {
|
||||||
|
client := clients[typ]
|
||||||
|
con := newWSCon(t)
|
||||||
|
eid := types.EventStringNewBlock()
|
||||||
|
subscribe(t, con, eid)
|
||||||
|
defer func() {
|
||||||
|
unsubscribe(t, con, eid)
|
||||||
|
con.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
types.MinNameRegistrationPeriod = 1
|
||||||
|
|
||||||
|
// register a new name, check if its there
|
||||||
|
// since entries ought to be unique and these run against different clients, we append the typ
|
||||||
|
name := "ye_old_domain_name_" + typ
|
||||||
|
data := "if not now, when"
|
||||||
|
fee := uint64(1000)
|
||||||
|
numDesiredBlocks := uint64(2)
|
||||||
|
amt := fee + numDesiredBlocks*types.NameCostPerByte*types.NameCostPerBlock*types.BaseEntryCost(name, data)
|
||||||
|
|
||||||
|
tx := makeDefaultNameTx(t, typ, name, data, amt, fee)
|
||||||
|
broadcastTx(t, typ, tx)
|
||||||
|
// commit block
|
||||||
|
waitForEvent(t, con, eid, true, func() {}, doNothing)
|
||||||
|
mempoolCount = 0
|
||||||
|
entry := getNameRegEntry(t, typ, name)
|
||||||
|
if entry.Data != data {
|
||||||
|
t.Fatal(fmt.Sprintf("Err on entry.Data: Got %s, expected %s", entry.Data, data))
|
||||||
|
}
|
||||||
|
if bytes.Compare(entry.Owner, user[0].Address) != 0 {
|
||||||
|
t.Fatal(fmt.Sprintf("Err on entry.Owner: Got %s, expected %s", entry.Owner, user[0].Address))
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the data as the owner, make sure still there
|
||||||
|
numDesiredBlocks = uint64(2)
|
||||||
|
data = "these are amongst the things I wish to bestow upon the youth of generations come: a safe supply of honey, and a better money. For what else shall they need"
|
||||||
|
amt = fee + numDesiredBlocks*types.NameCostPerByte*types.NameCostPerBlock*types.BaseEntryCost(name, data)
|
||||||
|
tx = makeDefaultNameTx(t, typ, name, data, amt, fee)
|
||||||
|
broadcastTx(t, typ, tx)
|
||||||
|
// commit block
|
||||||
|
waitForEvent(t, con, eid, true, func() {}, doNothing)
|
||||||
|
mempoolCount = 0
|
||||||
|
entry = getNameRegEntry(t, typ, name)
|
||||||
|
if entry.Data != data {
|
||||||
|
t.Fatal(fmt.Sprintf("Err on entry.Data: Got %s, expected %s", entry.Data, data))
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to update as non owner, should fail
|
||||||
|
nonce := getNonce(t, typ, user[1].Address)
|
||||||
|
data2 := "this is not my beautiful house"
|
||||||
|
tx = types.NewNameTxWithNonce(user[1].PubKey, name, data2, amt, fee, nonce+1)
|
||||||
|
tx.Sign(chainID, user[1])
|
||||||
|
_, err := client.BroadcastTx(tx)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("Expected error on NameTx")
|
||||||
|
}
|
||||||
|
|
||||||
|
// commit block
|
||||||
|
waitForEvent(t, con, eid, true, func() {}, doNothing)
|
||||||
|
|
||||||
|
// now the entry should be expired, so we can update as non owner
|
||||||
|
_, err = client.BroadcastTx(tx)
|
||||||
|
waitForEvent(t, con, eid, true, func() {}, doNothing)
|
||||||
|
mempoolCount = 0
|
||||||
|
entry = getNameRegEntry(t, typ, name)
|
||||||
|
if entry.Data != data2 {
|
||||||
|
t.Fatal(fmt.Sprintf("Error on entry.Data: Got %s, expected %s", entry.Data, data2))
|
||||||
|
}
|
||||||
|
if bytes.Compare(entry.Owner, user[1].Address) != 0 {
|
||||||
|
t.Fatal(fmt.Sprintf("Err on entry.Owner: Got %s, expected %s", entry.Owner, user[1].Address))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
. "github.com/tendermint/tendermint/common"
|
. "github.com/tendermint/tendermint/common"
|
||||||
dbm "github.com/tendermint/tendermint/db"
|
dbm "github.com/tendermint/tendermint/db"
|
||||||
"github.com/tendermint/tendermint/merkle"
|
"github.com/tendermint/tendermint/merkle"
|
||||||
|
"github.com/tendermint/tendermint/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func makeStorage(db dbm.DB, root []byte) merkle.Tree {
|
func makeStorage(db dbm.DB, root []byte) merkle.Tree {
|
||||||
@ -27,6 +28,7 @@ type BlockCache struct {
|
|||||||
backend *State
|
backend *State
|
||||||
accounts map[string]accountInfo
|
accounts map[string]accountInfo
|
||||||
storages map[Tuple256]storageInfo
|
storages map[Tuple256]storageInfo
|
||||||
|
names map[string]nameInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBlockCache(backend *State) *BlockCache {
|
func NewBlockCache(backend *State) *BlockCache {
|
||||||
@ -35,6 +37,7 @@ func NewBlockCache(backend *State) *BlockCache {
|
|||||||
backend: backend,
|
backend: backend,
|
||||||
accounts: make(map[string]accountInfo),
|
accounts: make(map[string]accountInfo),
|
||||||
storages: make(map[Tuple256]storageInfo),
|
storages: make(map[Tuple256]storageInfo),
|
||||||
|
names: make(map[string]nameInfo),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,6 +126,44 @@ func (cache *BlockCache) SetStorage(addr Word256, key Word256, value Word256) {
|
|||||||
|
|
||||||
// BlockCache.storage
|
// BlockCache.storage
|
||||||
//-------------------------------------
|
//-------------------------------------
|
||||||
|
// BlockCache.names
|
||||||
|
|
||||||
|
func (cache *BlockCache) GetNameRegEntry(name string) *types.NameRegEntry {
|
||||||
|
entry, removed, _ := cache.names[name].unpack()
|
||||||
|
if removed {
|
||||||
|
return nil
|
||||||
|
} else if entry != nil {
|
||||||
|
return entry
|
||||||
|
} else {
|
||||||
|
entry = cache.backend.GetNameRegEntry(name)
|
||||||
|
cache.names[name] = nameInfo{entry, false, false}
|
||||||
|
return entry
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cache *BlockCache) UpdateNameRegEntry(entry *types.NameRegEntry) {
|
||||||
|
name := entry.Name
|
||||||
|
// SANITY CHECK
|
||||||
|
_, removed, _ := cache.names[name].unpack()
|
||||||
|
if removed {
|
||||||
|
panic("UpdateNameRegEntry on a removed name")
|
||||||
|
}
|
||||||
|
// SANITY CHECK END
|
||||||
|
cache.names[name] = nameInfo{entry, false, true}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cache *BlockCache) RemoveNameRegEntry(name string) {
|
||||||
|
// SANITY CHECK
|
||||||
|
_, removed, _ := cache.names[name].unpack()
|
||||||
|
if removed {
|
||||||
|
panic("RemoveNameRegEntry on a removed entry")
|
||||||
|
}
|
||||||
|
// SANITY CHECK END
|
||||||
|
cache.names[name] = nameInfo{nil, true, false}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BlockCache.names
|
||||||
|
//-------------------------------------
|
||||||
|
|
||||||
// CONTRACT the updates are in deterministic order.
|
// CONTRACT the updates are in deterministic order.
|
||||||
func (cache *BlockCache) Sync() {
|
func (cache *BlockCache) Sync() {
|
||||||
@ -202,6 +243,32 @@ func (cache *BlockCache) Sync() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Determine order for names
|
||||||
|
// note names may be of any length less than some limit
|
||||||
|
nameStrs := []string{}
|
||||||
|
for nameStr := range cache.names {
|
||||||
|
nameStrs = append(nameStrs, nameStr)
|
||||||
|
}
|
||||||
|
sort.Strings(nameStrs)
|
||||||
|
|
||||||
|
// Update or delete names.
|
||||||
|
for _, nameStr := range nameStrs {
|
||||||
|
entry, removed, dirty := cache.names[nameStr].unpack()
|
||||||
|
if removed {
|
||||||
|
removed := cache.backend.RemoveNameRegEntry(nameStr)
|
||||||
|
if !removed {
|
||||||
|
panic(Fmt("Could not remove namereg entry to be removed: %s", nameStr))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if entry == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if dirty {
|
||||||
|
cache.backend.UpdateNameRegEntry(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
@ -225,3 +292,13 @@ type storageInfo struct {
|
|||||||
func (stjInfo storageInfo) unpack() (Word256, bool) {
|
func (stjInfo storageInfo) unpack() (Word256, bool) {
|
||||||
return stjInfo.value, stjInfo.dirty
|
return stjInfo.value, stjInfo.dirty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type nameInfo struct {
|
||||||
|
name *types.NameRegEntry
|
||||||
|
removed bool
|
||||||
|
dirty bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nInfo nameInfo) unpack() (*types.NameRegEntry, bool, bool) {
|
||||||
|
return nInfo.name, nInfo.removed, nInfo.dirty
|
||||||
|
}
|
||||||
|
@ -296,7 +296,7 @@ func ExecTx(blockCache *BlockCache, tx_ types.Tx, runCall bool, evc events.Firea
|
|||||||
|
|
||||||
// TODO: do something with fees
|
// TODO: do something with fees
|
||||||
fees := uint64(0)
|
fees := uint64(0)
|
||||||
_s := blockCache.State() // hack to access validators and event switch.
|
_s := blockCache.State() // hack to access validators and block height
|
||||||
|
|
||||||
// Exec tx
|
// Exec tx
|
||||||
switch tx := tx_.(type) {
|
switch tx := tx_.(type) {
|
||||||
@ -478,6 +478,120 @@ func ExecTx(blockCache *BlockCache, tx_ types.Tx, runCall bool, evc events.Firea
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
|
case *types.NameTx:
|
||||||
|
var inAcc *account.Account
|
||||||
|
|
||||||
|
// Validate input
|
||||||
|
inAcc = blockCache.GetAccount(tx.Input.Address)
|
||||||
|
if inAcc == nil {
|
||||||
|
log.Debug(Fmt("Can't find in account %X", tx.Input.Address))
|
||||||
|
return types.ErrTxInvalidAddress
|
||||||
|
}
|
||||||
|
// pubKey should be present in either "inAcc" or "tx.Input"
|
||||||
|
if err := checkInputPubKey(inAcc, tx.Input); err != nil {
|
||||||
|
log.Debug(Fmt("Can't find pubkey for %X", tx.Input.Address))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
signBytes := account.SignBytes(_s.ChainID, tx)
|
||||||
|
err := validateInput(inAcc, signBytes, tx.Input)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug(Fmt("validateInput failed on %X:", tx.Input.Address))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// fee is in addition to the amount which is used to determine the TTL
|
||||||
|
if tx.Input.Amount < tx.Fee {
|
||||||
|
log.Debug(Fmt("Sender did not send enough to cover the fee %X", tx.Input.Address))
|
||||||
|
return types.ErrTxInsufficientFunds
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate the input strings
|
||||||
|
if err := tx.ValidateStrings(); err != nil {
|
||||||
|
log.Debug(err.Error())
|
||||||
|
return types.ErrTxInvalidString
|
||||||
|
}
|
||||||
|
|
||||||
|
value := tx.Input.Amount - tx.Fee
|
||||||
|
|
||||||
|
// let's say cost of a name for one block is len(data) + 32
|
||||||
|
costPerBlock := types.NameCostPerBlock * types.NameCostPerByte * tx.BaseEntryCost()
|
||||||
|
expiresIn := value / uint64(costPerBlock)
|
||||||
|
lastBlockHeight := uint64(_s.LastBlockHeight)
|
||||||
|
|
||||||
|
log.Debug("New NameTx", "value", value, "costPerBlock", costPerBlock, "expiresIn", expiresIn, "lastBlock", lastBlockHeight)
|
||||||
|
|
||||||
|
// check if the name exists
|
||||||
|
entry := blockCache.GetNameRegEntry(tx.Name)
|
||||||
|
|
||||||
|
if entry != nil {
|
||||||
|
var expired bool
|
||||||
|
// if the entry already exists, and hasn't expired, we must be owner
|
||||||
|
if entry.Expires > lastBlockHeight {
|
||||||
|
// ensure we are owner
|
||||||
|
if bytes.Compare(entry.Owner, tx.Input.Address) != 0 {
|
||||||
|
log.Debug(Fmt("Sender %X is trying to update a name (%s) for which he is not owner", tx.Input.Address, tx.Name))
|
||||||
|
return types.ErrIncorrectOwner
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
expired = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// no value and empty data means delete the entry
|
||||||
|
if value == 0 && len(tx.Data) == 0 {
|
||||||
|
// maybe we reward you for telling us we can delete this crap
|
||||||
|
// (owners if not expired, anyone if expired)
|
||||||
|
log.Debug("Removing namereg entry", "name", entry.Name)
|
||||||
|
blockCache.RemoveNameRegEntry(entry.Name)
|
||||||
|
} else {
|
||||||
|
// update the entry by bumping the expiry
|
||||||
|
// and changing the data
|
||||||
|
if expired {
|
||||||
|
if expiresIn < types.MinNameRegistrationPeriod {
|
||||||
|
return fmt.Errorf("Names must be registered for at least %d blocks", types.MinNameRegistrationPeriod)
|
||||||
|
}
|
||||||
|
entry.Expires = lastBlockHeight + expiresIn
|
||||||
|
entry.Owner = tx.Input.Address
|
||||||
|
log.Debug("An old namereg entry has expired and been reclaimed", "name", entry.Name, "expiresIn", expiresIn, "owner", entry.Owner)
|
||||||
|
} else {
|
||||||
|
// since the size of the data may have changed
|
||||||
|
// we use the total amount of "credit"
|
||||||
|
oldCredit := (entry.Expires - lastBlockHeight) * types.BaseEntryCost(entry.Name, entry.Data)
|
||||||
|
credit := oldCredit + value
|
||||||
|
expiresIn = credit / costPerBlock
|
||||||
|
if expiresIn < types.MinNameRegistrationPeriod {
|
||||||
|
return fmt.Errorf("Names must be registered for at least %d blocks", types.MinNameRegistrationPeriod)
|
||||||
|
}
|
||||||
|
entry.Expires = lastBlockHeight + expiresIn
|
||||||
|
log.Debug("Updated namereg entry", "name", entry.Name, "expiresIn", expiresIn, "oldCredit", oldCredit, "value", value, "credit", credit)
|
||||||
|
}
|
||||||
|
entry.Data = tx.Data
|
||||||
|
blockCache.UpdateNameRegEntry(entry)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if expiresIn < types.MinNameRegistrationPeriod {
|
||||||
|
return fmt.Errorf("Names must be registered for at least %d blocks", types.MinNameRegistrationPeriod)
|
||||||
|
}
|
||||||
|
// entry does not exist, so create it
|
||||||
|
entry = &types.NameRegEntry{
|
||||||
|
Name: tx.Name,
|
||||||
|
Owner: tx.Input.Address,
|
||||||
|
Data: tx.Data,
|
||||||
|
Expires: lastBlockHeight + expiresIn,
|
||||||
|
}
|
||||||
|
log.Debug("Creating namereg entry", "name", entry.Name, "expiresIn", expiresIn)
|
||||||
|
blockCache.UpdateNameRegEntry(entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: something with the value sent?
|
||||||
|
|
||||||
|
// Good!
|
||||||
|
inAcc.Sequence += 1
|
||||||
|
inAcc.Balance -= value
|
||||||
|
blockCache.UpdateAccount(inAcc)
|
||||||
|
|
||||||
|
// TODO: maybe we want to take funds on error and allow txs in that don't do anythingi?
|
||||||
|
|
||||||
|
return nil
|
||||||
|
|
||||||
case *types.BondTx:
|
case *types.BondTx:
|
||||||
valInfo := blockCache.State().GetValidatorInfo(tx.PubKey.Address())
|
valInfo := blockCache.State().GetValidatorInfo(tx.PubKey.Address())
|
||||||
if valInfo != nil {
|
if valInfo != nil {
|
||||||
|
@ -100,9 +100,14 @@ func MakeGenesisState(db dbm.DB, genDoc *GenesisDoc) *State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make namereg tree
|
||||||
|
nameReg := merkle.NewIAVLTree(binary.BasicCodec, NameRegCodec, 0, db)
|
||||||
|
// TODO: add names to genesis.json
|
||||||
|
|
||||||
// IAVLTrees must be persisted before copy operations.
|
// IAVLTrees must be persisted before copy operations.
|
||||||
accounts.Save()
|
accounts.Save()
|
||||||
validatorInfos.Save()
|
validatorInfos.Save()
|
||||||
|
nameReg.Save()
|
||||||
|
|
||||||
return &State{
|
return &State{
|
||||||
DB: db,
|
DB: db,
|
||||||
@ -116,5 +121,6 @@ func MakeGenesisState(db dbm.DB, genDoc *GenesisDoc) *State {
|
|||||||
UnbondingValidators: NewValidatorSet(nil),
|
UnbondingValidators: NewValidatorSet(nil),
|
||||||
accounts: accounts,
|
accounts: accounts,
|
||||||
validatorInfos: validatorInfos,
|
validatorInfos: validatorInfos,
|
||||||
|
nameReg: nameReg,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package state
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/tendermint/tendermint/account"
|
"github.com/tendermint/tendermint/account"
|
||||||
@ -36,6 +37,7 @@ type State struct {
|
|||||||
UnbondingValidators *ValidatorSet
|
UnbondingValidators *ValidatorSet
|
||||||
accounts merkle.Tree // Shouldn't be accessed directly.
|
accounts merkle.Tree // Shouldn't be accessed directly.
|
||||||
validatorInfos merkle.Tree // Shouldn't be accessed directly.
|
validatorInfos merkle.Tree // Shouldn't be accessed directly.
|
||||||
|
nameReg merkle.Tree // Shouldn't be accessed directly.
|
||||||
|
|
||||||
evc events.Fireable // typically an events.EventCache
|
evc events.Fireable // typically an events.EventCache
|
||||||
}
|
}
|
||||||
@ -61,6 +63,9 @@ func LoadState(db dbm.DB) *State {
|
|||||||
validatorInfosHash := binary.ReadByteSlice(r, n, err)
|
validatorInfosHash := binary.ReadByteSlice(r, n, err)
|
||||||
s.validatorInfos = merkle.NewIAVLTree(binary.BasicCodec, ValidatorInfoCodec, 0, db)
|
s.validatorInfos = merkle.NewIAVLTree(binary.BasicCodec, ValidatorInfoCodec, 0, db)
|
||||||
s.validatorInfos.Load(validatorInfosHash)
|
s.validatorInfos.Load(validatorInfosHash)
|
||||||
|
nameRegHash := binary.ReadByteSlice(r, n, err)
|
||||||
|
s.nameReg = merkle.NewIAVLTree(binary.BasicCodec, NameRegCodec, 0, db)
|
||||||
|
s.nameReg.Load(nameRegHash)
|
||||||
if *err != nil {
|
if *err != nil {
|
||||||
panic(*err)
|
panic(*err)
|
||||||
}
|
}
|
||||||
@ -72,6 +77,7 @@ func LoadState(db dbm.DB) *State {
|
|||||||
func (s *State) Save() {
|
func (s *State) Save() {
|
||||||
s.accounts.Save()
|
s.accounts.Save()
|
||||||
s.validatorInfos.Save()
|
s.validatorInfos.Save()
|
||||||
|
s.nameReg.Save()
|
||||||
buf, n, err := new(bytes.Buffer), new(int64), new(error)
|
buf, n, err := new(bytes.Buffer), new(int64), new(error)
|
||||||
binary.WriteString(s.ChainID, buf, n, err)
|
binary.WriteString(s.ChainID, buf, n, err)
|
||||||
binary.WriteUvarint(s.LastBlockHeight, buf, n, err)
|
binary.WriteUvarint(s.LastBlockHeight, buf, n, err)
|
||||||
@ -83,6 +89,7 @@ func (s *State) Save() {
|
|||||||
binary.WriteBinary(s.UnbondingValidators, buf, n, err)
|
binary.WriteBinary(s.UnbondingValidators, buf, n, err)
|
||||||
binary.WriteByteSlice(s.accounts.Hash(), buf, n, err)
|
binary.WriteByteSlice(s.accounts.Hash(), buf, n, err)
|
||||||
binary.WriteByteSlice(s.validatorInfos.Hash(), buf, n, err)
|
binary.WriteByteSlice(s.validatorInfos.Hash(), buf, n, err)
|
||||||
|
binary.WriteByteSlice(s.nameReg.Hash(), buf, n, err)
|
||||||
if *err != nil {
|
if *err != nil {
|
||||||
panic(*err)
|
panic(*err)
|
||||||
}
|
}
|
||||||
@ -105,6 +112,7 @@ func (s *State) Copy() *State {
|
|||||||
UnbondingValidators: s.UnbondingValidators.Copy(), // copy the valSet lazily.
|
UnbondingValidators: s.UnbondingValidators.Copy(), // copy the valSet lazily.
|
||||||
accounts: s.accounts.Copy(),
|
accounts: s.accounts.Copy(),
|
||||||
validatorInfos: s.validatorInfos.Copy(),
|
validatorInfos: s.validatorInfos.Copy(),
|
||||||
|
nameReg: s.nameReg.Copy(),
|
||||||
evc: nil,
|
evc: nil,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -116,6 +124,7 @@ func (s *State) Hash() []byte {
|
|||||||
s.UnbondingValidators,
|
s.UnbondingValidators,
|
||||||
s.accounts,
|
s.accounts,
|
||||||
s.validatorInfos,
|
s.validatorInfos,
|
||||||
|
s.nameReg,
|
||||||
}
|
}
|
||||||
return merkle.HashFromHashables(hashables)
|
return merkle.HashFromHashables(hashables)
|
||||||
}
|
}
|
||||||
@ -272,6 +281,45 @@ func (s *State) LoadStorage(hash []byte) (storage merkle.Tree) {
|
|||||||
|
|
||||||
// State.storage
|
// State.storage
|
||||||
//-------------------------------------
|
//-------------------------------------
|
||||||
|
// State.nameReg
|
||||||
|
|
||||||
|
func (s *State) GetNameRegEntry(name string) *types.NameRegEntry {
|
||||||
|
_, value := s.nameReg.Get(name)
|
||||||
|
if value == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
entry := value.(*types.NameRegEntry)
|
||||||
|
return entry.Copy()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *State) UpdateNameRegEntry(entry *types.NameRegEntry) bool {
|
||||||
|
return s.nameReg.Set(entry.Name, entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *State) RemoveNameRegEntry(name string) bool {
|
||||||
|
_, removed := s.nameReg.Remove(name)
|
||||||
|
return removed
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *State) GetNames() merkle.Tree {
|
||||||
|
return s.nameReg.Copy()
|
||||||
|
}
|
||||||
|
|
||||||
|
func NameRegEncoder(o interface{}, w io.Writer, n *int64, err *error) {
|
||||||
|
binary.WriteBinary(o.(*types.NameRegEntry), w, n, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NameRegDecoder(r io.Reader, n *int64, err *error) interface{} {
|
||||||
|
return binary.ReadBinary(&types.NameRegEntry{}, r, n, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var NameRegCodec = binary.Codec{
|
||||||
|
Encode: NameRegEncoder,
|
||||||
|
Decode: NameRegDecoder,
|
||||||
|
}
|
||||||
|
|
||||||
|
// State.nameReg
|
||||||
|
//-------------------------------------
|
||||||
|
|
||||||
// Implements events.Eventable. Typically uses events.EventCache
|
// Implements events.Eventable. Typically uses events.EventCache
|
||||||
func (s *State) SetFireable(evc events.Fireable) {
|
func (s *State) SetFireable(evc events.Fireable) {
|
||||||
|
@ -21,6 +21,15 @@ func execTxWithState(state *State, tx types.Tx, runCall bool) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func execTxWithStateNewBlock(state *State, tx types.Tx, runCall bool) error {
|
||||||
|
if err := execTxWithState(state, tx, runCall); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
state.LastBlockHeight += 1
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func TestCopyState(t *testing.T) {
|
func TestCopyState(t *testing.T) {
|
||||||
// Generate a random state
|
// Generate a random state
|
||||||
s0, privAccounts, _ := RandGenesisState(10, true, 1000, 5, true, 1000)
|
s0, privAccounts, _ := RandGenesisState(10, true, 1000, 5, true, 1000)
|
||||||
@ -166,31 +175,13 @@ func TestTxSequence(t *testing.T) {
|
|||||||
acc0PubKey := privAccounts[0].PubKey
|
acc0PubKey := privAccounts[0].PubKey
|
||||||
acc1 := state.GetAccount(privAccounts[1].PubKey.Address())
|
acc1 := state.GetAccount(privAccounts[1].PubKey.Address())
|
||||||
|
|
||||||
// Try executing a SendTx with various sequence numbers.
|
|
||||||
makeSendTx := func(sequence uint) *types.SendTx {
|
|
||||||
return &types.SendTx{
|
|
||||||
Inputs: []*types.TxInput{
|
|
||||||
&types.TxInput{
|
|
||||||
Address: acc0.Address,
|
|
||||||
Amount: 1,
|
|
||||||
Sequence: sequence,
|
|
||||||
PubKey: acc0PubKey,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Outputs: []*types.TxOutput{
|
|
||||||
&types.TxOutput{
|
|
||||||
Address: acc1.Address,
|
|
||||||
Amount: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test a variety of sequence numbers for the tx.
|
// Test a variety of sequence numbers for the tx.
|
||||||
// The tx should only pass when i == 1.
|
// The tx should only pass when i == 1.
|
||||||
for i := -1; i < 3; i++ {
|
for i := -1; i < 3; i++ {
|
||||||
sequence := acc0.Sequence + uint(i)
|
sequence := acc0.Sequence + uint(i)
|
||||||
tx := makeSendTx(sequence)
|
tx := types.NewSendTx()
|
||||||
|
tx.AddInputWithNonce(acc0PubKey, 1, sequence)
|
||||||
|
tx.AddOutput(acc1.Address, 1)
|
||||||
tx.Inputs[0].Signature = privAccounts[0].Sign(state.ChainID, tx)
|
tx.Inputs[0].Signature = privAccounts[0].Sign(state.ChainID, tx)
|
||||||
stateCopy := state.Copy()
|
stateCopy := state.Copy()
|
||||||
err := execTxWithState(stateCopy, tx, true)
|
err := execTxWithState(stateCopy, tx, true)
|
||||||
@ -220,6 +211,168 @@ func TestTxSequence(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNameTxs(t *testing.T) {
|
||||||
|
state, privAccounts, _ := RandGenesisState(3, true, 1000, 1, true, 1000)
|
||||||
|
|
||||||
|
types.MinNameRegistrationPeriod = 5
|
||||||
|
startingBlock := uint64(state.LastBlockHeight)
|
||||||
|
|
||||||
|
// try some bad names. these should all fail
|
||||||
|
names := []string{"", "\n", "123#$%", "\x00", string([]byte{20, 40, 60, 80}), "baffledbythespectacleinallofthisyouseeehesaidwithouteyes", "no spaces please"}
|
||||||
|
data := "something about all this just doesn't feel right."
|
||||||
|
fee := uint64(1000)
|
||||||
|
numDesiredBlocks := uint64(5)
|
||||||
|
for _, name := range names {
|
||||||
|
amt := fee + numDesiredBlocks*types.NameCostPerByte*types.NameCostPerBlock*types.BaseEntryCost(name, data)
|
||||||
|
tx, _ := types.NewNameTx(state, privAccounts[0].PubKey, name, data, amt, fee)
|
||||||
|
tx.Sign(state.ChainID, privAccounts[0])
|
||||||
|
|
||||||
|
if err := execTxWithState(state, tx, true); err == nil {
|
||||||
|
t.Fatalf("Expected invalid name error from %s", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// try some bad data. these should all fail
|
||||||
|
name := "hold_it_chum"
|
||||||
|
datas := []string{"cold&warm", "!@#$%^&*()", "<<<>>>>", "because why would you ever need a ~ or a & or even a % in a json file? make your case and we'll talk"}
|
||||||
|
for _, data := range datas {
|
||||||
|
amt := fee + numDesiredBlocks*types.NameCostPerByte*types.NameCostPerBlock*types.BaseEntryCost(name, data)
|
||||||
|
tx, _ := types.NewNameTx(state, privAccounts[0].PubKey, name, data, amt, fee)
|
||||||
|
tx.Sign(state.ChainID, privAccounts[0])
|
||||||
|
|
||||||
|
if err := execTxWithState(state, tx, true); err == nil {
|
||||||
|
t.Fatalf("Expected invalid data error from %s", data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
validateEntry := func(t *testing.T, entry *types.NameRegEntry, name, data string, addr []byte, expires uint64) {
|
||||||
|
|
||||||
|
if entry == nil {
|
||||||
|
t.Fatalf("Could not find name %s", name)
|
||||||
|
}
|
||||||
|
if bytes.Compare(entry.Owner, addr) != 0 {
|
||||||
|
t.Fatalf("Wrong owner. Got %X expected %X", entry.Owner, addr)
|
||||||
|
}
|
||||||
|
if data != entry.Data {
|
||||||
|
t.Fatalf("Wrong data. Got %s expected %s", entry.Data, data)
|
||||||
|
}
|
||||||
|
if name != entry.Name {
|
||||||
|
t.Fatalf("Wrong name. Got %s expected %s", entry.Name, name)
|
||||||
|
}
|
||||||
|
if expires != entry.Expires {
|
||||||
|
t.Fatalf("Wrong expiry. Got %d, expected %d", entry.Expires, expires)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// try a good one, check data, owner, expiry
|
||||||
|
name = "looking_good/karaoke_bar"
|
||||||
|
data = "on this side of neptune there are 1234567890 people: first is OMNIVORE. Or is it. Ok this is pretty restrictive. No exclamations :(. Faces tho :')"
|
||||||
|
amt := fee + numDesiredBlocks*types.NameCostPerByte*types.NameCostPerBlock*types.BaseEntryCost(name, data)
|
||||||
|
tx, _ := types.NewNameTx(state, privAccounts[0].PubKey, name, data, amt, fee)
|
||||||
|
tx.Sign(state.ChainID, privAccounts[0])
|
||||||
|
if err := execTxWithState(state, tx, true); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
entry := state.GetNameRegEntry(name)
|
||||||
|
validateEntry(t, entry, name, data, privAccounts[0].Address, startingBlock+numDesiredBlocks)
|
||||||
|
|
||||||
|
// fail to update it as non-owner, in same block
|
||||||
|
tx, _ = types.NewNameTx(state, privAccounts[1].PubKey, name, data, amt, fee)
|
||||||
|
tx.Sign(state.ChainID, privAccounts[1])
|
||||||
|
if err := execTxWithState(state, tx, true); err == nil {
|
||||||
|
t.Fatal("Expected error")
|
||||||
|
}
|
||||||
|
|
||||||
|
// update it as owner, just to increase expiry, in same block
|
||||||
|
// NOTE: we have to resend the data or it will clear it (is this what we want?)
|
||||||
|
tx, _ = types.NewNameTx(state, privAccounts[0].PubKey, name, data, amt, fee)
|
||||||
|
tx.Sign(state.ChainID, privAccounts[0])
|
||||||
|
if err := execTxWithStateNewBlock(state, tx, true); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
entry = state.GetNameRegEntry(name)
|
||||||
|
validateEntry(t, entry, name, data, privAccounts[0].Address, startingBlock+numDesiredBlocks*2)
|
||||||
|
|
||||||
|
// update it as owner, just to increase expiry, in next block
|
||||||
|
tx, _ = types.NewNameTx(state, privAccounts[0].PubKey, name, data, amt, fee)
|
||||||
|
tx.Sign(state.ChainID, privAccounts[0])
|
||||||
|
if err := execTxWithStateNewBlock(state, tx, true); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
entry = state.GetNameRegEntry(name)
|
||||||
|
validateEntry(t, entry, name, data, privAccounts[0].Address, startingBlock+numDesiredBlocks*3)
|
||||||
|
|
||||||
|
// fail to update it as non-owner
|
||||||
|
state.LastBlockHeight = uint(entry.Expires - 1)
|
||||||
|
tx, _ = types.NewNameTx(state, privAccounts[1].PubKey, name, data, amt, fee)
|
||||||
|
tx.Sign(state.ChainID, privAccounts[1])
|
||||||
|
if err := execTxWithState(state, tx, true); err == nil {
|
||||||
|
t.Fatal("Expected error")
|
||||||
|
}
|
||||||
|
|
||||||
|
// once expires, non-owner succeeds
|
||||||
|
state.LastBlockHeight = uint(entry.Expires)
|
||||||
|
tx, _ = types.NewNameTx(state, privAccounts[1].PubKey, name, data, amt, fee)
|
||||||
|
tx.Sign(state.ChainID, privAccounts[1])
|
||||||
|
if err := execTxWithState(state, tx, true); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
entry = state.GetNameRegEntry(name)
|
||||||
|
validateEntry(t, entry, name, data, privAccounts[1].Address, uint64(state.LastBlockHeight)+numDesiredBlocks)
|
||||||
|
|
||||||
|
// update it as new owner, with new data (longer), but keep the expiry!
|
||||||
|
data = "In the beginning there was no thing, not even the beginning. It hadn't been here, no there, nor for that matter anywhere, not especially because it had not to even exist, let alone to not. Nothing especially odd about that."
|
||||||
|
oldCredit := amt - fee
|
||||||
|
numDesiredBlocks = 10
|
||||||
|
amt = fee + (numDesiredBlocks*types.NameCostPerByte*types.NameCostPerBlock*types.BaseEntryCost(name, data) - oldCredit)
|
||||||
|
tx, _ = types.NewNameTx(state, privAccounts[1].PubKey, name, data, amt, fee)
|
||||||
|
tx.Sign(state.ChainID, privAccounts[1])
|
||||||
|
if err := execTxWithState(state, tx, true); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
entry = state.GetNameRegEntry(name)
|
||||||
|
validateEntry(t, entry, name, data, privAccounts[1].Address, uint64(state.LastBlockHeight)+numDesiredBlocks)
|
||||||
|
|
||||||
|
// test removal
|
||||||
|
amt = fee
|
||||||
|
data = ""
|
||||||
|
tx, _ = types.NewNameTx(state, privAccounts[1].PubKey, name, data, amt, fee)
|
||||||
|
tx.Sign(state.ChainID, privAccounts[1])
|
||||||
|
if err := execTxWithStateNewBlock(state, tx, true); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
entry = state.GetNameRegEntry(name)
|
||||||
|
if entry != nil {
|
||||||
|
t.Fatal("Expected removed entry to be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
// create entry by key0,
|
||||||
|
// test removal by key1 after expiry
|
||||||
|
name = "looking_good/karaoke_bar"
|
||||||
|
data = "some data"
|
||||||
|
amt = fee + numDesiredBlocks*types.NameCostPerByte*types.NameCostPerBlock*types.BaseEntryCost(name, data)
|
||||||
|
tx, _ = types.NewNameTx(state, privAccounts[0].PubKey, name, data, amt, fee)
|
||||||
|
tx.Sign(state.ChainID, privAccounts[0])
|
||||||
|
if err := execTxWithState(state, tx, true); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
entry = state.GetNameRegEntry(name)
|
||||||
|
validateEntry(t, entry, name, data, privAccounts[0].Address, uint64(state.LastBlockHeight)+numDesiredBlocks)
|
||||||
|
state.LastBlockHeight = uint(entry.Expires)
|
||||||
|
|
||||||
|
amt = fee
|
||||||
|
data = ""
|
||||||
|
tx, _ = types.NewNameTx(state, privAccounts[1].PubKey, name, data, amt, fee)
|
||||||
|
tx.Sign(state.ChainID, privAccounts[1])
|
||||||
|
if err := execTxWithStateNewBlock(state, tx, true); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
entry = state.GetNameRegEntry(name)
|
||||||
|
if entry != nil {
|
||||||
|
t.Fatal("Expected removed entry to be nil")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: test overflows.
|
// TODO: test overflows.
|
||||||
// TODO: test for unbonding validators.
|
// TODO: test for unbonding validators.
|
||||||
func TestTxs(t *testing.T) {
|
func TestTxs(t *testing.T) {
|
||||||
@ -268,6 +421,100 @@ func TestTxs(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CallTx. Just runs through it and checks the transfer. See vm, rpc tests for more
|
||||||
|
{
|
||||||
|
state := state.Copy()
|
||||||
|
newAcc1 := state.GetAccount(acc1.Address)
|
||||||
|
newAcc1.Code = []byte{0x60}
|
||||||
|
state.UpdateAccount(newAcc1)
|
||||||
|
tx := &types.CallTx{
|
||||||
|
Input: &types.TxInput{
|
||||||
|
Address: acc0.Address,
|
||||||
|
Amount: 1,
|
||||||
|
Sequence: acc0.Sequence + 1,
|
||||||
|
PubKey: acc0PubKey,
|
||||||
|
},
|
||||||
|
Address: acc1.Address,
|
||||||
|
GasLimit: 10,
|
||||||
|
}
|
||||||
|
|
||||||
|
tx.Input.Signature = privAccounts[0].Sign(state.ChainID, tx)
|
||||||
|
err := execTxWithState(state, tx, true)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Got error in executing call transaction, %v", err)
|
||||||
|
}
|
||||||
|
newAcc0 := state.GetAccount(acc0.Address)
|
||||||
|
if acc0.Balance-1 != newAcc0.Balance {
|
||||||
|
t.Errorf("Unexpected newAcc0 balance. Expected %v, got %v",
|
||||||
|
acc0.Balance-1, newAcc0.Balance)
|
||||||
|
}
|
||||||
|
newAcc1 = state.GetAccount(acc1.Address)
|
||||||
|
if acc1.Balance+1 != newAcc1.Balance {
|
||||||
|
t.Errorf("Unexpected newAcc1 balance. Expected %v, got %v",
|
||||||
|
acc1.Balance+1, newAcc1.Balance)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NameTx.
|
||||||
|
{
|
||||||
|
entryName := "satoshi"
|
||||||
|
entryData := `
|
||||||
|
A purely peer-to-peer version of electronic cash would allow online
|
||||||
|
payments to be sent directly from one party to another without going through a
|
||||||
|
financial institution. Digital signatures provide part of the solution, but the main
|
||||||
|
benefits are lost if a trusted third party is still required to prevent double-spending.
|
||||||
|
We propose a solution to the double-spending problem using a peer-to-peer network.
|
||||||
|
The network timestamps transactions by hashing them into an ongoing chain of
|
||||||
|
hash-based proof-of-work, forming a record that cannot be changed without redoing
|
||||||
|
the proof-of-work. The longest chain not only serves as proof of the sequence of
|
||||||
|
events witnessed, but proof that it came from the largest pool of CPU power. As
|
||||||
|
long as a majority of CPU power is controlled by nodes that are not cooperating to
|
||||||
|
attack the network, they'll generate the longest chain and outpace attackers. The
|
||||||
|
network itself requires minimal structure. Messages are broadcast on a best effort
|
||||||
|
basis, and nodes can leave and rejoin the network at will, accepting the longest
|
||||||
|
proof-of-work chain as proof of what happened while they were gone `
|
||||||
|
entryAmount := uint64(10000)
|
||||||
|
|
||||||
|
state := state.Copy()
|
||||||
|
tx := &types.NameTx{
|
||||||
|
Input: &types.TxInput{
|
||||||
|
Address: acc0.Address,
|
||||||
|
Amount: entryAmount,
|
||||||
|
Sequence: acc0.Sequence + 1,
|
||||||
|
PubKey: acc0PubKey,
|
||||||
|
},
|
||||||
|
Name: entryName,
|
||||||
|
Data: entryData,
|
||||||
|
}
|
||||||
|
|
||||||
|
tx.Input.Signature = privAccounts[0].Sign(state.ChainID, tx)
|
||||||
|
err := execTxWithState(state, tx, true)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Got error in executing call transaction, %v", err)
|
||||||
|
}
|
||||||
|
newAcc0 := state.GetAccount(acc0.Address)
|
||||||
|
if acc0.Balance-entryAmount != newAcc0.Balance {
|
||||||
|
t.Errorf("Unexpected newAcc0 balance. Expected %v, got %v",
|
||||||
|
acc0.Balance-entryAmount, newAcc0.Balance)
|
||||||
|
}
|
||||||
|
entry := state.GetNameRegEntry(entryName)
|
||||||
|
if entry == nil {
|
||||||
|
t.Errorf("Expected an entry but got nil")
|
||||||
|
}
|
||||||
|
if entry.Data != entryData {
|
||||||
|
t.Errorf("Wrong data stored")
|
||||||
|
}
|
||||||
|
|
||||||
|
// test a bad string
|
||||||
|
tx.Data = string([]byte{0, 1, 2, 3, 127, 128, 129, 200, 251})
|
||||||
|
tx.Input.Sequence += 1
|
||||||
|
tx.Input.Signature = privAccounts[0].Sign(state.ChainID, tx)
|
||||||
|
err = execTxWithState(state, tx, true)
|
||||||
|
if err != types.ErrTxInvalidString {
|
||||||
|
t.Errorf("Expected invalid string error. Got: %s", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// BondTx.
|
// BondTx.
|
||||||
{
|
{
|
||||||
state := state.Copy()
|
state := state.Copy()
|
||||||
|
48
types/names.go
Normal file
48
types/names.go
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
MinNameRegistrationPeriod uint64 = 5
|
||||||
|
|
||||||
|
// cost for storing a name for a block is
|
||||||
|
// CostPerBlock*CostPerByte*(len(data) + 32)
|
||||||
|
NameCostPerByte uint64 = 1
|
||||||
|
NameCostPerBlock uint64 = 1
|
||||||
|
|
||||||
|
MaxNameLength = 32
|
||||||
|
MaxDataLength = 1 << 16
|
||||||
|
|
||||||
|
// Name should be alphanum, underscore, slash
|
||||||
|
// Data should be anything permitted in JSON
|
||||||
|
regexpAlphaNum = regexp.MustCompile("^[a-zA-Z0-9_/]*$")
|
||||||
|
regexpJSON = regexp.MustCompile(`^[a-zA-Z0-9_/ \-"':,\n\t.{}()\[\]]*$`)
|
||||||
|
)
|
||||||
|
|
||||||
|
// filter strings
|
||||||
|
func validateNameRegEntryName(name string) bool {
|
||||||
|
return regexpAlphaNum.Match([]byte(name))
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateNameRegEntryData(data string) bool {
|
||||||
|
return regexpJSON.Match([]byte(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
// base cost is "effective" number of bytes
|
||||||
|
func BaseEntryCost(name, data string) uint64 {
|
||||||
|
return uint64(len(data) + 32)
|
||||||
|
}
|
||||||
|
|
||||||
|
type NameRegEntry struct {
|
||||||
|
Name string `json:"name"` // registered name for the entry
|
||||||
|
Owner []byte `json:"owner"` // address that created the entry
|
||||||
|
Data string `json:"data"` // data to store under this name
|
||||||
|
Expires uint64 `json:"expires"` // block at which this entry expires
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *NameRegEntry) Copy() *NameRegEntry {
|
||||||
|
entryCopy := *entry
|
||||||
|
return &entryCopy
|
||||||
|
}
|
80
types/tx.go
80
types/tx.go
@ -1,6 +1,7 @@
|
|||||||
package types
|
package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
@ -18,6 +19,8 @@ var (
|
|||||||
ErrTxUnknownPubKey = errors.New("Error unknown pubkey")
|
ErrTxUnknownPubKey = errors.New("Error unknown pubkey")
|
||||||
ErrTxInvalidPubKey = errors.New("Error invalid pubkey")
|
ErrTxInvalidPubKey = errors.New("Error invalid pubkey")
|
||||||
ErrTxInvalidSignature = errors.New("Error invalid signature")
|
ErrTxInvalidSignature = errors.New("Error invalid signature")
|
||||||
|
ErrTxInvalidString = errors.New("Error invalid string")
|
||||||
|
ErrIncorrectOwner = errors.New("Error incorrect owner")
|
||||||
)
|
)
|
||||||
|
|
||||||
type ErrTxInvalidSequence struct {
|
type ErrTxInvalidSequence struct {
|
||||||
@ -35,6 +38,7 @@ Tx (Transaction) is an atomic operation on the ledger state.
|
|||||||
Account Txs:
|
Account Txs:
|
||||||
- SendTx Send coins to address
|
- SendTx Send coins to address
|
||||||
- CallTx Send a msg to a contract that runs in the vm
|
- CallTx Send a msg to a contract that runs in the vm
|
||||||
|
- NameTx Store some value under a name in the global namereg
|
||||||
|
|
||||||
Validation Txs:
|
Validation Txs:
|
||||||
- BondTx New validator posts a bond
|
- BondTx New validator posts a bond
|
||||||
@ -50,6 +54,7 @@ const (
|
|||||||
// Account transactions
|
// Account transactions
|
||||||
TxTypeSend = byte(0x01)
|
TxTypeSend = byte(0x01)
|
||||||
TxTypeCall = byte(0x02)
|
TxTypeCall = byte(0x02)
|
||||||
|
TxTypeName = byte(0x03)
|
||||||
|
|
||||||
// Validation transactions
|
// Validation transactions
|
||||||
TxTypeBond = byte(0x11)
|
TxTypeBond = byte(0x11)
|
||||||
@ -63,6 +68,7 @@ var _ = binary.RegisterInterface(
|
|||||||
struct{ Tx }{},
|
struct{ Tx }{},
|
||||||
binary.ConcreteType{&SendTx{}, TxTypeSend},
|
binary.ConcreteType{&SendTx{}, TxTypeSend},
|
||||||
binary.ConcreteType{&CallTx{}, TxTypeCall},
|
binary.ConcreteType{&CallTx{}, TxTypeCall},
|
||||||
|
binary.ConcreteType{&NameTx{}, TxTypeName},
|
||||||
binary.ConcreteType{&BondTx{}, TxTypeBond},
|
binary.ConcreteType{&BondTx{}, TxTypeBond},
|
||||||
binary.ConcreteType{&UnbondTx{}, TxTypeUnbond},
|
binary.ConcreteType{&UnbondTx{}, TxTypeUnbond},
|
||||||
binary.ConcreteType{&RebondTx{}, TxTypeRebond},
|
binary.ConcreteType{&RebondTx{}, TxTypeRebond},
|
||||||
@ -130,8 +136,7 @@ type SendTx struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (tx *SendTx) WriteSignBytes(chainID string, w io.Writer, n *int64, err *error) {
|
func (tx *SendTx) WriteSignBytes(chainID string, w io.Writer, n *int64, err *error) {
|
||||||
// We hex encode the chain_id so we don't deal with escaping issues.
|
binary.WriteTo([]byte(Fmt(`{"chain_id":%s`, jsonEscape(chainID))), w, n, err)
|
||||||
binary.WriteTo([]byte(Fmt(`{"chain_id":"%X"`, chainID)), w, n, err)
|
|
||||||
binary.WriteTo([]byte(Fmt(`,"tx":[%v,{"inputs":[`, TxTypeSend)), w, n, err)
|
binary.WriteTo([]byte(Fmt(`,"tx":[%v,{"inputs":[`, TxTypeSend)), w, n, err)
|
||||||
for i, in := range tx.Inputs {
|
for i, in := range tx.Inputs {
|
||||||
in.WriteSignBytes(w, n, err)
|
in.WriteSignBytes(w, n, err)
|
||||||
@ -164,8 +169,7 @@ type CallTx struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (tx *CallTx) WriteSignBytes(chainID string, w io.Writer, n *int64, err *error) {
|
func (tx *CallTx) WriteSignBytes(chainID string, w io.Writer, n *int64, err *error) {
|
||||||
// We hex encode the chain_id so we don't deal with escaping issues.
|
binary.WriteTo([]byte(Fmt(`{"chain_id":%s`, jsonEscape(chainID))), w, n, err)
|
||||||
binary.WriteTo([]byte(Fmt(`{"chain_id":"%X"`, chainID)), w, n, err)
|
|
||||||
binary.WriteTo([]byte(Fmt(`,"tx":[%v,{"address":"%X","data":"%X"`, TxTypeCall, tx.Address, tx.Data)), w, n, err)
|
binary.WriteTo([]byte(Fmt(`,"tx":[%v,{"address":"%X","data":"%X"`, TxTypeCall, tx.Address, tx.Data)), w, n, err)
|
||||||
binary.WriteTo([]byte(Fmt(`,"fee":%v,"gas_limit":%v,"input":`, tx.Fee, tx.GasLimit)), w, n, err)
|
binary.WriteTo([]byte(Fmt(`,"fee":%v,"gas_limit":%v,"input":`, tx.Fee, tx.GasLimit)), w, n, err)
|
||||||
tx.Input.WriteSignBytes(w, n, err)
|
tx.Input.WriteSignBytes(w, n, err)
|
||||||
@ -178,6 +182,54 @@ func (tx *CallTx) String() string {
|
|||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
type NameTx struct {
|
||||||
|
Input *TxInput `json:"input"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Data string `json:"data"`
|
||||||
|
Fee uint64 `json:"fee"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tx *NameTx) WriteSignBytes(chainID string, w io.Writer, n *int64, err *error) {
|
||||||
|
binary.WriteTo([]byte(Fmt(`{"chain_id":%s`, jsonEscape(chainID))), w, n, err)
|
||||||
|
binary.WriteTo([]byte(Fmt(`,"tx":[%v,{"data":%s,"fee":%v`, TxTypeName, jsonEscape(tx.Data), tx.Fee)), w, n, err)
|
||||||
|
binary.WriteTo([]byte(Fmt(`,"input":`, tx.Input)), w, n, err)
|
||||||
|
tx.Input.WriteSignBytes(w, n, err)
|
||||||
|
binary.WriteTo([]byte(Fmt(`,"name":%s`, jsonEscape(tx.Name))), w, n, err)
|
||||||
|
binary.WriteTo([]byte(`}]}`), w, n, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tx *NameTx) ValidateStrings() error {
|
||||||
|
if len(tx.Name) == 0 {
|
||||||
|
return errors.New("Name must not be empty")
|
||||||
|
}
|
||||||
|
if len(tx.Name) > MaxNameLength {
|
||||||
|
return errors.New(Fmt("Name is too long. Max %d bytes", MaxNameLength))
|
||||||
|
}
|
||||||
|
if len(tx.Data) > MaxDataLength {
|
||||||
|
return errors.New(Fmt("Data is too long. Max %d bytes", MaxDataLength))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !validateNameRegEntryName(tx.Name) {
|
||||||
|
return errors.New(Fmt("Invalid characters found in NameTx.Name (%s). Only alphanumeric, underscores, and forward slashes allowed", tx.Name))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !validateNameRegEntryData(tx.Data) {
|
||||||
|
return errors.New(Fmt("Invalid characters found in NameTx.Data (%s). Only the kind of things found in a JSON file are allowed", tx.Data))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tx *NameTx) BaseEntryCost() uint64 {
|
||||||
|
return BaseEntryCost(tx.Name, tx.Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tx *NameTx) String() string {
|
||||||
|
return Fmt("NameTx{%v -> %s: %s}", tx.Input, tx.Name, tx.Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
type BondTx struct {
|
type BondTx struct {
|
||||||
PubKey account.PubKeyEd25519 `json:"pub_key"`
|
PubKey account.PubKeyEd25519 `json:"pub_key"`
|
||||||
Signature account.SignatureEd25519 `json:"signature"`
|
Signature account.SignatureEd25519 `json:"signature"`
|
||||||
@ -186,8 +238,7 @@ type BondTx struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (tx *BondTx) WriteSignBytes(chainID string, w io.Writer, n *int64, err *error) {
|
func (tx *BondTx) WriteSignBytes(chainID string, w io.Writer, n *int64, err *error) {
|
||||||
// We hex encode the chain_id so we don't deal with escaping issues.
|
binary.WriteTo([]byte(Fmt(`{"chain_id":%s`, jsonEscape(chainID))), w, n, err)
|
||||||
binary.WriteTo([]byte(Fmt(`{"chain_id":"%X"`, chainID)), w, n, err)
|
|
||||||
binary.WriteTo([]byte(Fmt(`,"tx":[%v,{"inputs":[`, TxTypeBond)), w, n, err)
|
binary.WriteTo([]byte(Fmt(`,"tx":[%v,{"inputs":[`, TxTypeBond)), w, n, err)
|
||||||
for i, in := range tx.Inputs {
|
for i, in := range tx.Inputs {
|
||||||
in.WriteSignBytes(w, n, err)
|
in.WriteSignBytes(w, n, err)
|
||||||
@ -220,8 +271,7 @@ type UnbondTx struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (tx *UnbondTx) WriteSignBytes(chainID string, w io.Writer, n *int64, err *error) {
|
func (tx *UnbondTx) WriteSignBytes(chainID string, w io.Writer, n *int64, err *error) {
|
||||||
// We hex encode the chain_id so we don't deal with escaping issues.
|
binary.WriteTo([]byte(Fmt(`{"chain_id":%s`, jsonEscape(chainID))), w, n, err)
|
||||||
binary.WriteTo([]byte(Fmt(`{"chain_id":"%X"`, chainID)), w, n, err)
|
|
||||||
binary.WriteTo([]byte(Fmt(`,"tx":[%v,{"address":"%X","height":%v}]}`, TxTypeUnbond, tx.Address, tx.Height)), w, n, err)
|
binary.WriteTo([]byte(Fmt(`,"tx":[%v,{"address":"%X","height":%v}]}`, TxTypeUnbond, tx.Address, tx.Height)), w, n, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -238,8 +288,7 @@ type RebondTx struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (tx *RebondTx) WriteSignBytes(chainID string, w io.Writer, n *int64, err *error) {
|
func (tx *RebondTx) WriteSignBytes(chainID string, w io.Writer, n *int64, err *error) {
|
||||||
// We hex encode the chain_id so we don't deal with escaping issues.
|
binary.WriteTo([]byte(Fmt(`{"chain_id":%s`, jsonEscape(chainID))), w, n, err)
|
||||||
binary.WriteTo([]byte(Fmt(`{"chain_id":"%X"`, chainID)), w, n, err)
|
|
||||||
binary.WriteTo([]byte(Fmt(`,"tx":[%v,{"address":"%X","height":%v}]}`, TxTypeRebond, tx.Address, tx.Height)), w, n, err)
|
binary.WriteTo([]byte(Fmt(`,"tx":[%v,{"address":"%X","height":%v}]}`, TxTypeRebond, tx.Address, tx.Height)), w, n, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,3 +318,14 @@ func TxId(chainID string, tx Tx) []byte {
|
|||||||
signBytes := account.SignBytes(chainID, tx)
|
signBytes := account.SignBytes(chainID, tx)
|
||||||
return binary.BinaryRipemd160(signBytes)
|
return binary.BinaryRipemd160(signBytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Contract: This function is deterministic and completely reversible.
|
||||||
|
func jsonEscape(str string) string {
|
||||||
|
escapedBytes, err := json.Marshal(str)
|
||||||
|
if err != nil {
|
||||||
|
panic(Fmt("Error json-escaping a string", str))
|
||||||
|
}
|
||||||
|
return string(escapedBytes)
|
||||||
|
}
|
||||||
|
@ -41,7 +41,7 @@ func TestSendTxSignable(t *testing.T) {
|
|||||||
}
|
}
|
||||||
signBytes := account.SignBytes(chainID, sendTx)
|
signBytes := account.SignBytes(chainID, sendTx)
|
||||||
signStr := string(signBytes)
|
signStr := string(signBytes)
|
||||||
expected := Fmt(`{"chain_id":"%X","tx":[1,{"inputs":[{"address":"696E70757431","amount":12345,"sequence":67890},{"address":"696E70757432","amount":111,"sequence":222}],"outputs":[{"address":"6F757470757431","amount":333},{"address":"6F757470757432","amount":444}]}]}`,
|
expected := Fmt(`{"chain_id":"%s","tx":[1,{"inputs":[{"address":"696E70757431","amount":12345,"sequence":67890},{"address":"696E70757432","amount":111,"sequence":222}],"outputs":[{"address":"6F757470757431","amount":333},{"address":"6F757470757432","amount":444}]}]}`,
|
||||||
config.GetString("chain_id"))
|
config.GetString("chain_id"))
|
||||||
if signStr != expected {
|
if signStr != expected {
|
||||||
t.Errorf("Got unexpected sign string for SendTx. Expected:\n%v\nGot:\n%v", expected, signStr)
|
t.Errorf("Got unexpected sign string for SendTx. Expected:\n%v\nGot:\n%v", expected, signStr)
|
||||||
@ -62,7 +62,7 @@ func TestCallTxSignable(t *testing.T) {
|
|||||||
}
|
}
|
||||||
signBytes := account.SignBytes(chainID, callTx)
|
signBytes := account.SignBytes(chainID, callTx)
|
||||||
signStr := string(signBytes)
|
signStr := string(signBytes)
|
||||||
expected := Fmt(`{"chain_id":"%X","tx":[2,{"address":"636F6E747261637431","data":"6461746131","fee":222,"gas_limit":111,"input":{"address":"696E70757431","amount":12345,"sequence":67890}}]}`,
|
expected := Fmt(`{"chain_id":"%s","tx":[2,{"address":"636F6E747261637431","data":"6461746131","fee":222,"gas_limit":111,"input":{"address":"696E70757431","amount":12345,"sequence":67890}}]}`,
|
||||||
config.GetString("chain_id"))
|
config.GetString("chain_id"))
|
||||||
if signStr != expected {
|
if signStr != expected {
|
||||||
t.Errorf("Got unexpected sign string for CallTx. Expected:\n%v\nGot:\n%v", expected, signStr)
|
t.Errorf("Got unexpected sign string for CallTx. Expected:\n%v\nGot:\n%v", expected, signStr)
|
||||||
@ -98,7 +98,7 @@ func TestBondTxSignable(t *testing.T) {
|
|||||||
}
|
}
|
||||||
signBytes := account.SignBytes(chainID, bondTx)
|
signBytes := account.SignBytes(chainID, bondTx)
|
||||||
signStr := string(signBytes)
|
signStr := string(signBytes)
|
||||||
expected := Fmt(`{"chain_id":"%X","tx":[17,{"inputs":[{"address":"696E70757431","amount":12345,"sequence":67890},{"address":"696E70757432","amount":111,"sequence":222}],"pub_key":[1,"3B6A27BCCEB6A42D62A3A8D02A6F0D73653215771DE243A63AC048A18B59DA29"],"unbond_to":[{"address":"6F757470757431","amount":333},{"address":"6F757470757432","amount":444}]}]}`,
|
expected := Fmt(`{"chain_id":"%s","tx":[17,{"inputs":[{"address":"696E70757431","amount":12345,"sequence":67890},{"address":"696E70757432","amount":111,"sequence":222}],"pub_key":[1,"3B6A27BCCEB6A42D62A3A8D02A6F0D73653215771DE243A63AC048A18B59DA29"],"unbond_to":[{"address":"6F757470757431","amount":333},{"address":"6F757470757432","amount":444}]}]}`,
|
||||||
config.GetString("chain_id"))
|
config.GetString("chain_id"))
|
||||||
if signStr != expected {
|
if signStr != expected {
|
||||||
t.Errorf("Got unexpected sign string for BondTx")
|
t.Errorf("Got unexpected sign string for BondTx")
|
||||||
@ -112,7 +112,7 @@ func TestUnbondTxSignable(t *testing.T) {
|
|||||||
}
|
}
|
||||||
signBytes := account.SignBytes(chainID, unbondTx)
|
signBytes := account.SignBytes(chainID, unbondTx)
|
||||||
signStr := string(signBytes)
|
signStr := string(signBytes)
|
||||||
expected := Fmt(`{"chain_id":"%X","tx":[18,{"address":"6164647265737331","height":111}]}`,
|
expected := Fmt(`{"chain_id":"%s","tx":[18,{"address":"6164647265737331","height":111}]}`,
|
||||||
config.GetString("chain_id"))
|
config.GetString("chain_id"))
|
||||||
if signStr != expected {
|
if signStr != expected {
|
||||||
t.Errorf("Got unexpected sign string for UnbondTx")
|
t.Errorf("Got unexpected sign string for UnbondTx")
|
||||||
@ -126,7 +126,7 @@ func TestRebondTxSignable(t *testing.T) {
|
|||||||
}
|
}
|
||||||
signBytes := account.SignBytes(chainID, rebondTx)
|
signBytes := account.SignBytes(chainID, rebondTx)
|
||||||
signStr := string(signBytes)
|
signStr := string(signBytes)
|
||||||
expected := Fmt(`{"chain_id":"%X","tx":[19,{"address":"6164647265737331","height":111}]}`,
|
expected := Fmt(`{"chain_id":"%s","tx":[19,{"address":"6164647265737331","height":111}]}`,
|
||||||
config.GetString("chain_id"))
|
config.GetString("chain_id"))
|
||||||
if signStr != expected {
|
if signStr != expected {
|
||||||
t.Errorf("Got unexpected sign string for RebondTx")
|
t.Errorf("Got unexpected sign string for RebondTx")
|
||||||
|
@ -25,23 +25,15 @@ func (tx *SendTx) AddInput(st AccountGetter, pubkey account.PubKey, amt uint64)
|
|||||||
if acc == nil {
|
if acc == nil {
|
||||||
return fmt.Errorf("Invalid address %X from pubkey %X", addr, pubkey)
|
return fmt.Errorf("Invalid address %X from pubkey %X", addr, pubkey)
|
||||||
}
|
}
|
||||||
|
return tx.AddInputWithNonce(pubkey, amt, acc.Sequence+1)
|
||||||
tx.Inputs = append(tx.Inputs, &TxInput{
|
|
||||||
Address: addr,
|
|
||||||
Amount: amt,
|
|
||||||
Sequence: uint(acc.Sequence) + 1,
|
|
||||||
Signature: account.SignatureEd25519{},
|
|
||||||
PubKey: pubkey,
|
|
||||||
})
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tx *SendTx) AddInputWithNonce(pubkey account.PubKey, amt, nonce uint64) error {
|
func (tx *SendTx) AddInputWithNonce(pubkey account.PubKey, amt uint64, nonce uint) error {
|
||||||
addr := pubkey.Address()
|
addr := pubkey.Address()
|
||||||
tx.Inputs = append(tx.Inputs, &TxInput{
|
tx.Inputs = append(tx.Inputs, &TxInput{
|
||||||
Address: addr,
|
Address: addr,
|
||||||
Amount: amt,
|
Amount: amt,
|
||||||
Sequence: uint(nonce) + 1,
|
Sequence: nonce,
|
||||||
Signature: account.SignatureEd25519{},
|
Signature: account.SignatureEd25519{},
|
||||||
PubKey: pubkey,
|
PubKey: pubkey,
|
||||||
})
|
})
|
||||||
@ -75,16 +67,16 @@ func NewCallTx(st AccountGetter, from account.PubKey, to, data []byte, amt, gasL
|
|||||||
return nil, fmt.Errorf("Invalid address %X from pubkey %X", addr, from)
|
return nil, fmt.Errorf("Invalid address %X from pubkey %X", addr, from)
|
||||||
}
|
}
|
||||||
|
|
||||||
nonce := uint64(acc.Sequence)
|
nonce := acc.Sequence + 1
|
||||||
return NewCallTxWithNonce(from, to, data, amt, gasLimit, fee, nonce), nil
|
return NewCallTxWithNonce(from, to, data, amt, gasLimit, fee, nonce), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCallTxWithNonce(from account.PubKey, to, data []byte, amt, gasLimit, fee, nonce uint64) *CallTx {
|
func NewCallTxWithNonce(from account.PubKey, to, data []byte, amt, gasLimit, fee uint64, nonce uint) *CallTx {
|
||||||
addr := from.Address()
|
addr := from.Address()
|
||||||
input := &TxInput{
|
input := &TxInput{
|
||||||
Address: addr,
|
Address: addr,
|
||||||
Amount: amt,
|
Amount: amt,
|
||||||
Sequence: uint(nonce) + 1,
|
Sequence: nonce,
|
||||||
Signature: account.SignatureEd25519{},
|
Signature: account.SignatureEd25519{},
|
||||||
PubKey: from,
|
PubKey: from,
|
||||||
}
|
}
|
||||||
@ -103,6 +95,43 @@ func (tx *CallTx) Sign(chainID string, privAccount *account.PrivAccount) {
|
|||||||
tx.Input.Signature = privAccount.Sign(chainID, tx)
|
tx.Input.Signature = privAccount.Sign(chainID, tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
// NameTx interface for creating tx
|
||||||
|
|
||||||
|
func NewNameTx(st AccountGetter, from account.PubKey, name, data string, amt, fee uint64) (*NameTx, error) {
|
||||||
|
addr := from.Address()
|
||||||
|
acc := st.GetAccount(addr)
|
||||||
|
if acc == nil {
|
||||||
|
return nil, fmt.Errorf("Invalid address %X from pubkey %X", addr, from)
|
||||||
|
}
|
||||||
|
|
||||||
|
nonce := acc.Sequence + 1
|
||||||
|
return NewNameTxWithNonce(from, name, data, amt, fee, nonce), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNameTxWithNonce(from account.PubKey, name, data string, amt, fee uint64, nonce uint) *NameTx {
|
||||||
|
addr := from.Address()
|
||||||
|
input := &TxInput{
|
||||||
|
Address: addr,
|
||||||
|
Amount: amt,
|
||||||
|
Sequence: nonce,
|
||||||
|
Signature: account.SignatureEd25519{},
|
||||||
|
PubKey: from,
|
||||||
|
}
|
||||||
|
|
||||||
|
return &NameTx{
|
||||||
|
Input: input,
|
||||||
|
Name: name,
|
||||||
|
Data: data,
|
||||||
|
Fee: fee,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tx *NameTx) Sign(chainID string, privAccount *account.PrivAccount) {
|
||||||
|
tx.Input.PubKey = privAccount.PubKey
|
||||||
|
tx.Input.Signature = privAccount.Sign(chainID, tx)
|
||||||
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
// BondTx interface for adding inputs/outputs and adding signatures
|
// BondTx interface for adding inputs/outputs and adding signatures
|
||||||
|
|
||||||
@ -124,23 +153,15 @@ func (tx *BondTx) AddInput(st AccountGetter, pubkey account.PubKey, amt uint64)
|
|||||||
if acc == nil {
|
if acc == nil {
|
||||||
return fmt.Errorf("Invalid address %X from pubkey %X", addr, pubkey)
|
return fmt.Errorf("Invalid address %X from pubkey %X", addr, pubkey)
|
||||||
}
|
}
|
||||||
|
return tx.AddInputWithNonce(pubkey, amt, acc.Sequence+1)
|
||||||
tx.Inputs = append(tx.Inputs, &TxInput{
|
|
||||||
Address: addr,
|
|
||||||
Amount: amt,
|
|
||||||
Sequence: uint(acc.Sequence) + 1,
|
|
||||||
Signature: account.SignatureEd25519{},
|
|
||||||
PubKey: pubkey,
|
|
||||||
})
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tx *BondTx) AddInputWithNonce(pubkey account.PubKey, amt, nonce uint64) error {
|
func (tx *BondTx) AddInputWithNonce(pubkey account.PubKey, amt uint64, nonce uint) error {
|
||||||
addr := pubkey.Address()
|
addr := pubkey.Address()
|
||||||
tx.Inputs = append(tx.Inputs, &TxInput{
|
tx.Inputs = append(tx.Inputs, &TxInput{
|
||||||
Address: addr,
|
Address: addr,
|
||||||
Amount: amt,
|
Amount: amt,
|
||||||
Sequence: uint(nonce) + 1,
|
Sequence: nonce,
|
||||||
Signature: account.SignatureEd25519{},
|
Signature: account.SignatureEd25519{},
|
||||||
PubKey: pubkey,
|
PubKey: pubkey,
|
||||||
})
|
})
|
||||||
|
@ -46,8 +46,7 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (vote *Vote) WriteSignBytes(chainID string, w io.Writer, n *int64, err *error) {
|
func (vote *Vote) WriteSignBytes(chainID string, w io.Writer, n *int64, err *error) {
|
||||||
// We hex encode the chain_id name so we don't deal with escaping issues.
|
binary.WriteTo([]byte(Fmt(`{"chain_id":"%s"`, chainID)), w, n, err)
|
||||||
binary.WriteTo([]byte(Fmt(`{"chain_id":"%X"`, chainID)), w, n, err)
|
|
||||||
binary.WriteTo([]byte(Fmt(`,"vote":{"block_hash":"%X","block_parts":%v`, vote.BlockHash, vote.BlockParts)), w, n, err)
|
binary.WriteTo([]byte(Fmt(`,"vote":{"block_hash":"%X","block_parts":%v`, vote.BlockHash, vote.BlockParts)), w, n, err)
|
||||||
binary.WriteTo([]byte(Fmt(`,"height":%v,"round":%v,"type":%v}}`, vote.Height, vote.Round, vote.Type)), w, n, err)
|
binary.WriteTo([]byte(Fmt(`,"height":%v,"round":%v,"type":%v}}`, vote.Height, vote.Round, vote.Type)), w, n, err)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user