diff --git a/.gitignore b/.gitignore deleted file mode 100644 index d1bd73b..0000000 --- a/.gitignore +++ /dev/null @@ -1,26 +0,0 @@ -# Compiled Object files, Static and Dynamic libs (Shared Objects) -*.o -*.a -*.so - -# Folders -_obj -_test - -# Architecture specific extensions/prefixes -*.[568vq] -[568vq].out - -*.cgo1.go -*.cgo2.c -_cgo_defun.c -_cgo_gotypes.go -_cgo_export.* - -_testmain.go - -*.exe -*.test -*.prof - -*.sh diff --git a/LICENSE b/LICENSE index def0efa..d651b08 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2016 go-steem +Copyright (c) 2016 steem-go Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index ce6d811..c6ca89c 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,16 @@ -# go-steem/rpc +# asuleymanov/steem-go -[![GoDoc](https://godoc.org/github.com/go-steem/rpc?status.svg)](https://godoc.org/github.com/go-steem/rpc) +[![GoDoc](https://godoc.org/github.com/asuleymanov/steem-go?status.svg)](https://godoc.org/github.com/asuleymanov/steem-go) +[![Go Report Card](https://goreportcard.com/badge/github.com/asuleymanov/steem-go)](https://goreportcard.com/report/github.com/asuleymanov/steem-go) -Golang RPC client library for [Steem](https://steem.io). - -## Compatibility - -`steemd 0.13.0` +Golang RPC client library for [Steem](https://steemit.com). ## Usage ```go -import "github.com/go-steem/rpc" +import "github.com/asuleymanov/steem-go" ``` -This package is still very much in development, so `gopkg.in` is not yet available. - -## Installation - -This package calls [bitcoin-core/secp256k1](https://github.com/bitcoin-core/secp256k1) -using CGO to implement signed transactions, so you need to install `secp256k1` first. -Then it will be possible to build `go-steem/rpc`. - -In case you don't need signed transactions, i.e. you don't need to use -`network_broadcast_api`, it is possible to build the package with `nosigning` -tag to exclude the functionality: - -```bash -$ go build -tags nosigning -``` ## Example @@ -36,58 +18,65 @@ This is just a code snippet. Please check the `examples` directory for more complete and ready to use examples. ```go -// Instantiate the WebSocket transport. -t, _ := websocket.NewTransport("ws://localhost:8090") - -// Use the transport to create an RPC client. -client, _ := rpc.NewClient(t) -defer client.Close() - -// Call "get_config". -config, _ := client.Database.GetConfig() - -// Start processing blocks. -lastBlock := 1800000 -for { - // Call "get_dynamic_global_properties". - props, _ := client.Database.GetDynamicGlobalProperties() - - for props.LastIrreversibleBlockNum-lastBlock > 0 { - // Call "get_block". - block, _ := client.Database.GetBlock(lastBlock) - - // Process the transactions. - for _, tx := range block.Transactions { - for _, op := range tx.Operations { - switch body := op.Data().(type) { - // Comment operation. - case *types.CommentOperation: - content, _ := client.Database.GetContent(body.Author, body.Permlink) - fmt.Printf("COMMENT @%v %v\n", content.Author, content.URL) - - // Vote operation. + cls,_ := client.NewClient([]string{"ws://localhost:8090"},"steem") + defer cls.Close() + + // Get config. + log.Println("---> GetConfig()") + config, err := cls.Database.GetConfig() + if err != nil { + return err + } + + // Use the last irreversible block number as the initial last block number. + props, err := cls.Database.GetDynamicGlobalProperties() + if err != nil { + return err + } + lastBlock := props.LastIrreversibleBlockNum + + // Keep processing incoming blocks forever. + log.Printf("---> Entering the block processing loop (last block = %v)\n", lastBlock) + for { + // Get current properties. + props, err := cls.Database.GetDynamicGlobalProperties() + if err != nil { + return err + } + + // Process new blocks. + for props.LastIrreversibleBlockNum-lastBlock > 0 { + block, err := cls.Database.GetBlock(lastBlock) + if err != nil { + return err + } + + // Process the transactions. + for _, tx := range block.Transactions { + for _, operation := range tx.Operations { + switch op := operation.Data().(type) { case *types.VoteOperation: - fmt.Printf("VOTE @%v @%v/%v\n", body.Voter, body.Author, body.Permlink) + log.Printf("@%v voted for @%v/%v\n", op.Voter, op.Author, op.Permlink) - // You can add more cases, it depends on what - // operations you actually need to process. + // You can add more cases here, it depends on + // what operations you actually need to process. + } } } + + lastBlock++ } - lastBlock++ + // Sleep for STEEMIT_BLOCK_INTERVAL seconds before the next iteration. + time.Sleep(time.Duration(config.BlockInterval) * time.Second) } - - time.Sleep(time.Duration(config.SteemitBlockInterval) * time.Second) -} ``` ## Package Organisation -You need to create a `Client` object to be able to do anything. To be able to -instantiate a `Client`, you first need to create a transport to be used to -execute RPC calls. The WebSocket transport is available in `transports/websocket`. -Then you just need to call `NewClient(transport)`. + +You need to create a `Client` object to be able to do anything. +Then you just need to call `NewClient()`. Once you create a `Client` object, you can start calling the methods exported via `steemd`'s RPC endpoint by invoking associated methods on the client object. @@ -99,33 +88,6 @@ For example, to call `get_block` from `database_api`, you need to use When looking for a method to call, all you need is to turn the method name into CamelCase, e.g. `get_config` becomes `Client.Database.GetConfig`. -### Raw and Full Methods - -There are two methods implemented for every method exported via the RPC endpoint. -The regular version and the raw version. Let's see an example for `get_config`: - -```go -func (client *Client) GetConfig() (*Config, error) { - ... -} - -func (client *Client) GetConfigRaw() (*json.RawMessage, error) { - ... -} -``` - -As we can see, the difference is that the raw version returns `*json.RawMessage`, -so it is not trying to unmarshall the response into a properly typed response. - -There are two reasons for this: - -1. To be able to see raw data. -2. To be able to call most of the remote methods even though the response - object is not yet known or specified. - -It is already benefitial to just have the raw version because at least -the method parameters are statically typed. - ## Status This package is still under rapid development and it is by no means complete. diff --git a/api/README.md b/api/README.md new file mode 100644 index 0000000..e44cb93 --- /dev/null +++ b/api/README.md @@ -0,0 +1,99 @@ +# API + +This package adds support for `steem api`. + +## State + +| **ID** | **Command Name** | **Version** | +| :-: | :-: | :-: | +| **account_by_key** | +| 1 | get_key_references | **RAW** | +| **account_history** | +| 1 | get_account_history | **DONE** | +| **committee_api** | +| 1 | get_committee_request | **DONE** | +| 2 | get_committee_request_votes | **DONE** | +| 3 | get_committee_requests_list | **DONE** | +| **database_api** | +| 1 | get_account_count | **DONE** | +| 2 | get_accounts | **DONE** | +| 3 | get_block | **DONE** | +| 4 | get_block_header | **DONE** | +| 5 | get_chain_properties | **DONE** | +| 6 | get_config | **DONE** | +| 7 | get_database_info | **DONE** | +| 8 | get_dynamic_global_properties | **DONE** | +| 9 | get_escrow | **DONE** | +| 10 | get_expiring_vesting_delegations | **DONE** | +| 11| get_hardfork_version | **DONE** | +| 12| get_next_scheduled_hardfork | **DONE** | +| 13 | get_owner_history | **DONE** | +| 14 | get_potential_signatures | **DONE** | +| 15 | get_proposed_transaction | **DONE** | +| 16 | get_recovery_request | **DONE** | +| 17 | get_required_signatures | **DONE** | +| 18 | get_transaction_hex | **DONE** | +| 19 | get_vesting_delegations | **DONE** | +| 20 | get_withdraw_routes | **DONE** | +| 21 | lookup_account_names | **DONE** | +| 22 | lookup_accounts | **DONE** | +| 23 | verify_account_authority | **DONE** | +| 24 | verify_authority | **DONE** | +| **follow** | +| 1 | get_blog | **DONE** | +| 2 | get_blog_authors | **DONE** | +| 3 | get_blog_entries | **DONE** | +| 4 | get_feed | **DONE** | +| 5 | get_feed_entries | **DONE** | +| 6 | get_follow_count | **DONE** | +| 7 | get_followers | **DONE** | +| 8 | get_following | **DONE** | +| 9 | get_reblogged_by | **DONE** | +| **invite_api** | +| 1 | get_invites_list | **DONE** | +| 2 | get_invite_by_id | **DONE** | +| 3 | get_invite_by_key | **DONE** | +| **network_broadcast_api** | +| 1 | broadcast_block | **NONE** | +| 2 | broadcast_transaction | **DONE** | +| 3 | broadcast_transaction_synchronous | **DONE** | +| 4 | broadcast_transaction_with_callback | **NONE** | +| **operation_history** | +| 1 | get_ops_in_block | **DONE** | +| 2 | get_transaction | **DONE** | +| **social_network** | +| 1 | get_account_votes | **DONE** | +| 2 | get_active_votes | **DONE** | +| 3 | get_all_content_replies | **DONE** | +| 4 | get_content | **DONE** | +| 5 | get_content_replies | **DONE** | +| 6 | get_replies_by_last_update | **DONE** | +| **tags** | +| 1 | get_discussions_by_active | **DONE** | +| 2 | get_discussions_by_author_before_date | **DONE** | +| 3 | get_discussions_by_blog | **DONE** | +| 4 | get_discussions_by_cashout | **DONE** | +| 5 | get_discussions_by_children | **DONE** | +| 6 | get_discussions_by_contents | **DONE** | +| 7 | get_discussions_by_created | **DONE** | +| 8 | get_discussions_by_feed | **DONE** | +| 9 | get_discussions_by_hot | **DONE** | +| 10 | get_discussions_by_payout | **DONE** | +| 11 | get_discussions_by_trending | **DONE** | +| 12 | get_discussions_by_votes | **DONE** | +| 13 | get_languages | **DONE** | +| 14 | get_tags_used_by_author | **RAW** | +| 15 | get_trending_tags | **DONE** | +| **witness_api** | +| 1 | get_active_witnesses | **DONE** | +| 2 | get_miner_queue | **DONE** | +| 3 | get_witness_by_account | **DONE** | +| 4 | get_witness_count | **DONE** | +| 5 | get_witness_schedule | **DONE** | +| 6 | get_witnesses | **DONE** | +| 7 | get_witnesses_by_vote | **DONE** | +| 8 | lookup_witness_accounts | **DONE** | + +## License + +MIT, see the `LICENSE` file. diff --git a/api/api.go b/api/api.go new file mode 100644 index 0000000..0e76d41 --- /dev/null +++ b/api/api.go @@ -0,0 +1,25 @@ +package api + +import ( + "encoding/json" + + "github.com/asuleymanov/steem-go/transports" +) + +//API plug-in structure +type API struct { + caller transports.Caller +} + +//NewAPI plug-in initialization +func NewAPI(caller transports.Caller) *API { + return &API{caller} +} + +func (api *API) call(apiID string, method string, params, resp interface{}) error { + return api.caller.Call("call", []interface{}{apiID, method, params}, resp) +} + +func (api *API) setCallback(apiID string, method string, callback func(raw json.RawMessage)) error { + return api.caller.SetCallback(apiID, method, callback) +} diff --git a/api/data.go b/api/data.go new file mode 100644 index 0000000..a2ad986 --- /dev/null +++ b/api/data.go @@ -0,0 +1,122 @@ +package api + +import ( + "github.com/asuleymanov/steem-go/types" +) + +//BroadcastResponse structure for the BroadcastTransactionSynchronous function +type BroadcastResponse struct { + ID string `json:"id"` + BlockNum int32 `json:"block_num"` + TrxNum int32 `json:"trx_num"` + Expired bool `json:"expired"` +} + +//Config structure for the GetConfig function. +type Config struct { + Percent100 int `json:"CHAIN_100_PERCENT"` + Percent1 *types.Int `json:"CHAIN_1_PERCENT"` + AddressPrefix string `json:"CHAIN_ADDRESS_PREFIX"` + BandwidthAverageWindowSeconds *types.Int `json:"CHAIN_BANDWIDTH_AVERAGE_WINDOW_SECONDS"` + BandwidthPrecision *types.Int `json:"CHAIN_BANDWIDTH_PRECISION"` + ConsensusBandwidthReservePercent *types.Int `json:"CONSENSUS_BANDWIDTH_RESERVE_PERCENT"` + ConsensusBandwidthReserveBelow *types.Int `json:"CONSENSUS_BANDWIDTH_RESERVE_BELOW"` + HardforkVersion string `json:"CHAIN_HARDFORK_VERSION"` + Version string `json:"CHAIN_VERSION"` + BlockInterval uint `json:"CHAIN_BLOCK_INTERVAL"` + BlocksPerDay *types.Int `json:"CHAIN_BLOCKS_PER_DAY"` + BlocksPerYear *types.Int `json:"CHAIN_BLOCKS_PER_YEAR"` + CashoutWindowSeconds *types.Int `json:"CHAIN_CASHOUT_WINDOW_SECONDS"` + ChainID string `json:"CHAIN_ID"` + HardforkRequiredWitnesses *types.Int `json:"CHAIN_HARDFORK_REQUIRED_WITNESSES"` + InitiatorName string `json:"CHAIN_INITIATOR_NAME"` + InitiatorPublicKey string `json:"CHAIN_INITIATOR_PUBLIC_KEY_STR"` + InitSupply *types.UInt32 `json:"CHAIN_INIT_SUPPLY"` + CommitteeAccount string `json:"CHAIN_COMMITTEE_ACCOUNT"` + CommitteePublicKey string `json:"CHAIN_COMMITTEE_PUBLIC_KEY_STR"` + IrreversibleThreshold *types.Int `json:"CHAIN_IRREVERSIBLE_THRESHOLD"` + MaxAccountNameLength *types.Int `json:"CHAIN_MAX_ACCOUNT_NAME_LENGTH"` + MaxAccountWitnessVotes *types.Int `json:"CHAIN_MAX_ACCOUNT_WITNESS_VOTES"` + BlockSize *types.Int `json:"CHAIN_BLOCK_SIZE"` + MaxCommentDepth *types.Int `json:"CHAIN_MAX_COMMENT_DEPTH"` + MaxMemoSize *types.Int `json:"CHAIN_MAX_MEMO_SIZE"` + MaxWitnesses *types.Int `json:"CHAIN_MAX_WITNESSES"` + MaxProxyRecursionDepth *types.Int `json:"CHAIN_MAX_PROXY_RECURSION_DEPTH"` + MaxReserveRatio *types.Int `json:"CHAIN_MAX_RESERVE_RATIO"` + MaxSupportWitnesses *types.Int `json:"CHAIN_MAX_SUPPORT_WITNESSES"` + MaxShareSupply string `json:"CHAIN_MAX_SHARE_SUPPLY"` + MaxSigCheckDepth *types.Int `json:"CHAIN_MAX_SIG_CHECK_DEPTH"` + MaxTimeUntilExpiration *types.Int `json:"CHAIN_MAX_TIME_UNTIL_EXPIRATION"` + MaxTransactionSize *types.Int `json:"CHAIN_MAX_TRANSACTION_SIZE"` + MaxUndoHistory *types.Int `json:"CHAIN_MAX_UNDO_HISTORY"` + MaxVoteChanges *types.Int `json:"CHAIN_MAX_VOTE_CHANGES"` + MaxTopWitnesses *types.Int `json:"CHAIN_MAX_TOP_WITNESSES"` + MaxWithdrawRoutes *types.Int `json:"CHAIN_MAX_WITHDRAW_ROUTES"` + MaxWitnessURLLength *types.Int `json:"CHAIN_MAX_WITNESS_URL_LENGTH"` + MinAccountCreationFee *types.Int `json:"CHAIN_MIN_ACCOUNT_CREATION_FEE"` + MinAccountNameLength *types.Int `json:"CHAIN_MIN_ACCOUNT_NAME_LENGTH"` + MinBlockSizeLimit *types.Int `json:"CHAIN_MIN_BLOCK_SIZE_LIMIT"` + MaxBlockSizeLimit *types.Int `json:"CHAIN_MAX_BLOCK_SIZE_LIMIT"` + NullAccount string `json:"CHAIN_NULL_ACCOUNT"` + NumInitiators *types.Int `json:"CHAIN_NUM_INITIATORS"` + ProxyToSelfAccount string `json:"CHAIN_PROXY_TO_SELF_ACCOUNT"` + SecondsPerYear *types.Int `json:"CHAIN_SECONDS_PER_YEAR"` + VestingWithdrawIntervals *types.Int `json:"CHAIN_VESTING_WITHDRAW_INTERVALS"` + VestingWithdrawIntervalSeconds *types.Int `json:"CHAIN_VESTING_WITHDRAW_INTERVAL_SECONDS"` + EnergyRegenerationSeconds int `json:"CHAIN_ENERGY_REGENERATION_SECONDS"` + TokenSymbol *types.Int `json:"TOKEN_SYMBOL"` + SharesSymbol *types.Int `json:"SHARES_SYMBOL"` + ChainName string `json:"CHAIN_NAME"` +} + +//DynamicGlobalProperties structure for the GetDynamicGlobalProperties function. +type DynamicGlobalProperties struct { + ID *types.Int `json:"id"` + HeadBlockNumber uint32 `json:"head_block_number"` + HeadBlockID string `json:"head_block_id"` + GenesisTime *types.Time `json:"genesis_time"` + Time *types.Time `json:"time"` + CurrentWitness string `json:"current_witness"` + CommitteeFund *types.Asset `json:"committee_fund"` + CommitteeRequests uint32 `json:"committee_requests"` + CurrentSupply *types.Asset `json:"current_supply"` + TotalVersingFund *types.Asset `json:"total_vesting_fund"` + TotalVestingShares *types.Asset `json:"total_vesting_shares"` + TotalRewardFund *types.Asset `json:"total_reward_fund"` + TotalRewardShares *types.Int64 `json:"total_reward_shares"` + InflationCalcBlockNum uint32 `json:"inflation_calc_block_num"` + InflationWitnessPercent int16 `json:"inflation_witness_percent"` + InflationRatio int16 `json:"inflation_ratio"` + AverageBlockSize uint32 `json:"average_block_size"` + MaximumBlockSize uint32 `json:"maximum_block_size"` + CurrentAslot uint64 `json:"current_aslot"` + RecentSlotsFilled *types.Int64 `json:"recent_slots_filled"` + ParticipationCount uint8 `json:"participation_count"` + LastIrreversibleBlockNum uint32 `json:"last_irreversible_block_num"` + MaxVirtualBandwidth uint64 `json:"max_virtual_bandwidth"` + CurrentReserveRatio uint64 `json:"current_reserve_ratio"` + VoteRegenerationPerDay uint32 `json:"vote_regeneration_per_day"` + BandwidthReserveCandidates uint32 `json:"bandwidth_reserve_candidates"` +} + +//BlockHeader structure for the GetBlockHeader and SetBlockAppliedCallback functions +type BlockHeader struct { + Number uint32 `json:"-"` + Previous string `json:"previous"` + Timestamp string `json:"timestamp"` + Witness string `json:"witness"` + TransactionMerkleRoot string `json:"transaction_merkle_root"` + Extensions []interface{} `json:"extensions"` +} + +//Block structure for the GetBlock function +type Block struct { + Number uint32 `json:"-"` + Timestamp *types.Time `json:"timestamp"` + Witness string `json:"witness"` + WitnessSignature string `json:"witness_signature"` + TransactionMerkleRoot string `json:"transaction_merkle_root"` + Previous string `json:"previous"` + Extensions [][]interface{} `json:"extensions"` + Transactions []*types.Transaction `json:"transactions"` +} diff --git a/api/database_api.go b/api/database_api.go new file mode 100644 index 0000000..8b27d5a --- /dev/null +++ b/api/database_api.go @@ -0,0 +1,52 @@ +package api + +import ( + "encoding/json" + + "github.com/asuleymanov/steem-go/transports" + _ "github.com/asuleymanov/steem-go/types" +) + +//GetConfig api request get_config +func (api *API) GetConfig() (*Config, error) { + var resp Config + err := api.call("database_api", "get_config", transports.EmptyParams, &resp) + return &resp, err +} + +//GetDynamicGlobalProperties api request get_dynamic_global_properties +func (api *API) GetDynamicGlobalProperties() (*DynamicGlobalProperties, error) { + var resp DynamicGlobalProperties + err := api.call("database_api", "get_dynamic_global_properties", transports.EmptyParams, &resp) + return &resp, err +} + +//GetBlock api request get_block +func (api *API) GetBlock(blockNum uint32) (*Block, error) { + var resp Block + err := api.call("database_api", "get_block", []uint32{blockNum}, &resp) + resp.Number = blockNum + return &resp, err +} + +//GetBlockHeader api request get_block_header +func (api *API) GetBlockHeader(blockNum uint32) (*BlockHeader, error) { + var resp BlockHeader + err := api.call("database_api", "get_block_header", []uint32{blockNum}, &resp) + resp.Number = blockNum + return &resp, err +} + +// Set callback to invoke as soon as a new block is applied +func (api *API) SetBlockAppliedCallback(notice func(header *BlockHeader, error error)) (err error) { + err = api.setCallback("database_api", "set_block_applied_callback", func(raw json.RawMessage) { + var header []BlockHeader + if err := json.Unmarshal(raw, &header); err != nil { + notice(nil, err) + } + for _, b := range header { + notice(&b, nil) + } + }) + return +} diff --git a/api/network_broadcast_api.go b/api/network_broadcast_api.go new file mode 100644 index 0000000..7f115ca --- /dev/null +++ b/api/network_broadcast_api.go @@ -0,0 +1,19 @@ +package api + +import ( + "github.com/asuleymanov/steem-go/types" +) + +//network_broadcast_api + +//BroadcastTransaction api request broadcast_transaction +func (api *API) BroadcastTransaction(tx *types.Transaction) error { + return api.call("network_broadcast_api", "broadcast_transaction", []interface{}{tx}, nil) +} + +//BroadcastTransactionSynchronous api request broadcast_transaction_synchronous +func (api *API) BroadcastTransactionSynchronous(tx *types.Transaction) (*BroadcastResponse, error) { + var resp BroadcastResponse + err := api.call("network_broadcast_api", "broadcast_transaction_synchronous", []interface{}{tx}, &resp) + return &resp, err +} diff --git a/apis/database/README.md b/apis/database/README.md deleted file mode 100644 index a4d7a50..0000000 --- a/apis/database/README.md +++ /dev/null @@ -1,130 +0,0 @@ -# Database API - -This package adds support for `database_api`. - -## State - -The following subsections document the API completion. The method names -are taken from `database_api.hpp` in `steemit/steem`. - -### Subscriptions - -**TODO:** Is this actually callable over the RPC endpoint? -It is a bit confusing to see `set_` prefix. Needs research. - -``` - (set_subscribe_callback) - (set_pending_transaction_callback) - (set_block_applied_callback) - (cancel_all_subscriptions) -``` - -### Tags - -| Method Name | Raw Version | Full Version | -| --------------------------- |:-----------:|:------------:| -| get_trending_tags | DONE | | -| get_discussions_by_trending | DONE | | -| get_discussions_by_created | DONE | | -| get_discussions_by_active | DONE | | -| get_discussions_by_cashout | DONE | | -| get_discussions_by_payout | DONE | | -| get_discussions_by_votes | DONE | | -| get_discussions_by_children | DONE | | -| get_discussions_by_hot | DONE | | -| get_recommended_for | DONE | | - -### Blocks and Transactions - -| Method Name | Raw Version | Full Version | -| ----------------------- |:-----------:|:--------------:| -| get_block_header | DONE | | -| get_block | DONE | PARTIALLY DONE | -| get_ops_in_block | DONE | DONE | -| get_state | DONE | | -| get_trending_categories | DONE | | -| get_best_categories | DONE | | -| get_active_categories | DONE | | -| get_recent_categories | DONE | | -| get_ops_in_block | DONE | | - -### Globals - -| Method Name | Raw Version | Full Version | -| -------------------------------- |:-----------:|:--------------:| -| get_config | DONE | PARTIALLY DONE | -| get_dynamic_global_properties | DONE | DONE | -| get_chain_properties | DONE | | -| get_feed_history | DONE | | -| get_current_median_history_price | DONE | | -| get_witness_schedule | DONE | | -| get_hardfork_version | DONE | DONE | -| get_next_scheduled_hardfork | DONE | | - -### Keys - -| Method Name | Raw Version | Full Version | -| ----------------- |:-----------:|:------------:| -| get_key_reference | | | - -### Accounts - -| Method Name | Raw Version | Full Version | -| ------------------------- |:-----------:|:------------:| -| get_accounts | DONE | | -| get_account_references | | | -| lookup_account_names | DONE | | -| lookup_accounts | DONE | | -| get_account_count | DONE | | -| get_conversation_requests | DONE | | -| get_account_history | DONE | | - -### Market - -| Method Name | Raw Version | Full Version | -| --------------- |:-----------:|:------------:| -| get_order_book | | | -| get_open_orders | | | - -### Authority / Validation - -| Method Name | Raw Version | Full Version | -| ------------------------ |:-----------:|:------------:| -| get_transaction_hex | | | -| get_transaction | | | -| get_required_signatures | | | -| get_potential_signatures | | | -| verify_authority | | | -| verity_account_authority | | | - -### Votes - -| Method Name | Raw Version | Full Version | -| ----------------- |:-----------:|:------------:| -| get_active_votes | DONE | DONE | -| get_account_votes | DONE | | - -### Cotent - -| Method Name | Raw Version | Full Version | -| ------------------------------------- |:-----------:|:--------------:| -| get_content | DONE | PARTIALLY DONE | -| get_content_replies | DONE | PARTIALLY DONE | -| get_discussions_by_author_before_date | | | -| get_replies_by_last_update | DONE | | - -### Witnesses - -| Method Name | Raw Version | Full Version | -| ----------------------- |:-----------:|:------------:| -| get_witnesses | | | -| get_witness_by_account | | | -| get_witnesses_by_vote | | | -| lookup_witness_accounts | | | -| get_witness_count | | | -| get_active_witnesses | | | -| get_miner_queue | | | - -## License - -MIT, see the `LICENSE` file. diff --git a/apis/database/api.go b/apis/database/api.go deleted file mode 100644 index f33573d..0000000 --- a/apis/database/api.go +++ /dev/null @@ -1,380 +0,0 @@ -package database - -import ( - // Stdlib - "encoding/json" - "errors" - - // RPC - "github.com/go-steem/rpc/interfaces" - "github.com/go-steem/rpc/internal/call" - "github.com/go-steem/rpc/types" -) - -const ( - APIID = "database_api" - NumbericAPIID = 0 -) - -type API struct { - caller interfaces.Caller -} - -func NewAPI(caller interfaces.Caller) *API { - return &API{caller} -} - -/* - // Subscriptions - (set_subscribe_callback) - (set_pending_transaction_callback) - (set_block_applied_callback) - (cancel_all_subscriptions) -*/ - -/* - // Tags - (get_trending_tags) - (get_discussions_by_trending) - (get_discussions_by_created) - (get_discussions_by_active) - (get_discussions_by_cashout) - (get_discussions_by_payout) - (get_discussions_by_votes) - (get_discussions_by_children) - (get_discussions_by_hot) - (get_recommended_for) -*/ - -func (api *API) GetTrendingTagsRaw(afterTag string, limit uint32) (*json.RawMessage, error) { - return call.Raw(api.caller, "get_trending_tags", []interface{}{afterTag, limit}) -} - -type DiscussionQuery struct { - Tag string `json:"tag"` - Limit uint32 `json:"limit"` - // XXX: Not sure about the type here. - FilterTags []string `json:"filter_tags"` - StartAuthor string `json:"start_author,omitempty"` - StartPermlink string `json:"start_permlink,omitempty"` - ParentAuthor string `json:"parent_author,omitempty"` - ParentPermlink string `json:"parent_permlink"` -} - -func (api *API) GetDiscussionsByTrendingRaw(query *DiscussionQuery) (*json.RawMessage, error) { - return call.Raw(api.caller, "get_discussions_by_trending", query) -} - -func (api *API) GetDiscussionsByCreatedRaw(query *DiscussionQuery) (*json.RawMessage, error) { - return call.Raw(api.caller, "get_discussions_by_created", query) -} - -func (api *API) GetDiscussionsByActiveRaw(query *DiscussionQuery) (*json.RawMessage, error) { - return call.Raw(api.caller, "get_discussions_by_active", query) -} - -func (api *API) GetDiscussionsByCashoutRaw(query *DiscussionQuery) (*json.RawMessage, error) { - return call.Raw(api.caller, "get_discussions_by_cashout", query) -} - -func (api *API) GetDiscussionsByPayoutRaw(query *DiscussionQuery) (*json.RawMessage, error) { - return call.Raw(api.caller, "get_discussions_by_payout", query) -} - -func (api *API) GetDiscussionsByVotesRaw(query *DiscussionQuery) (*json.RawMessage, error) { - return call.Raw(api.caller, "get_discussions_by_votes", query) -} - -func (api *API) GetDiscussionsByChildrenRaw(query *DiscussionQuery) (*json.RawMessage, error) { - return call.Raw(api.caller, "get_discussions_by_children", query) -} - -func (api *API) GetDiscussionsByHotRaw(query *DiscussionQuery) (*json.RawMessage, error) { - return call.Raw(api.caller, "get_discussions_by_hot", query) -} - -func (api *API) GetRecommendedForRaw(user string, limit uint32) (*json.RawMessage, error) { - return call.Raw(api.caller, "get_discussions_by_votes", []interface{}{user, limit}) -} - -/* - // Blocks and transactions - (get_block_header) - (get_block) - (get_state) - (get_trending_categories) - (get_best_categories) - (get_active_categories) - (get_recent_categories) -*/ - -func (api *API) GetBlockHeaderRaw(blockNum uint32) (*json.RawMessage, error) { - return call.Raw(api.caller, "get_block_header", []uint32{blockNum}) -} - -func (api *API) GetBlockRaw(blockNum uint32) (*json.RawMessage, error) { - return call.Raw(api.caller, "get_block", []uint32{blockNum}) -} - -func (api *API) GetBlock(blockNum uint32) (*Block, error) { - var resp Block - if err := api.caller.Call("get_block", []uint32{blockNum}, &resp); err != nil { - return nil, err - } - resp.Number = blockNum - return &resp, nil -} - -func (api *API) GetStateRaw(path string) (*json.RawMessage, error) { - return call.Raw(api.caller, "get_state", []string{path}) -} - -func (api *API) GetTrendingCategoriesRaw(after string, limit uint32) (*json.RawMessage, error) { - return call.Raw(api.caller, "get_trending_categories", []interface{}{after, limit}) -} - -func (api *API) GetBestCategoriesRaw(after string, limit uint32) (*json.RawMessage, error) { - return call.Raw(api.caller, "get_best_categories", []interface{}{after, limit}) -} - -func (api *API) GetActiveCategoriesRaw(after string, limit uint32) (*json.RawMessage, error) { - return call.Raw(api.caller, "get_active_categories", []interface{}{after, limit}) -} - -func (api *API) GetRecentCategoriesRaw(after string, limit uint32) (*json.RawMessage, error) { - return call.Raw(api.caller, "get_recent_categories", []interface{}{after, limit}) -} - -/* - // Globals - (get_config) - (get_dynamic_global_properties) - (get_chain_properties) - (get_feed_history) - (get_current_median_history_price) - (get_witness_schedule) - (get_hardfork_version) - (get_next_scheduled_hardfork) -*/ - -func (api *API) GetConfigRaw() (*json.RawMessage, error) { - return call.Raw(api.caller, "get_config", call.EmptyParams) -} - -func (api *API) GetConfig() (*Config, error) { - var resp Config - if err := api.caller.Call("get_config", call.EmptyParams, &resp); err != nil { - return nil, err - } - return &resp, nil -} - -func (api *API) GetDynamicGlobalPropertiesRaw() (*json.RawMessage, error) { - return call.Raw(api.caller, "get_dynamic_global_properties", call.EmptyParams) -} - -func (api *API) GetDynamicGlobalProperties() (*DynamicGlobalProperties, error) { - var resp DynamicGlobalProperties - if err := api.caller.Call("get_dynamic_global_properties", call.EmptyParams, &resp); err != nil { - return nil, err - } - return &resp, nil -} - -func (api *API) GetChainPropertiesRaw() (*json.RawMessage, error) { - return call.Raw(api.caller, "get_chain_properties", call.EmptyParams) -} - -func (api *API) GetFeedHistoryRaw() (*json.RawMessage, error) { - return call.Raw(api.caller, "get_feed_history", call.EmptyParams) -} - -func (api *API) GetCurrentMedianHistoryPriceRaw() (*json.RawMessage, error) { - return call.Raw(api.caller, "get_current_median_history_price", call.EmptyParams) -} - -func (api *API) GetWitnessScheduleRaw() (*json.RawMessage, error) { - return call.Raw(api.caller, "get_witness_schedule", call.EmptyParams) -} - -func (api *API) GetHardforkVersionRaw() (*json.RawMessage, error) { - return call.Raw(api.caller, "get_hardfork_version", call.EmptyParams) -} - -func (api *API) GetHardforkVersion() (string, error) { - var resp string - if err := api.caller.Call("get_hardfork_version", call.EmptyParams, &resp); err != nil { - return "", err - } - return resp, nil -} - -func (api *API) GetNextScheduledHardforkRaw() (*json.RawMessage, error) { - return call.Raw(api.caller, "get_next_scheduled_hardfork", call.EmptyParams) -} - -/* - // Keys - (get_key_references) -*/ - -// XXX: Not sure about params. -//func (api *API) GetKeyReferencesRaw(key []string) (*json.RawMessage, error) { -// return call.Raw(api.caller, "get_key_references", [][]string{key}) -//} - -/* - // Accounts - (get_accounts) - (get_account_references) - (lookup_account_names) - (lookup_accounts) - (get_account_count) - (get_conversion_requests) - (get_account_history) -*/ - -func (api *API) GetAccountsRaw(accountNames []string) (*json.RawMessage, error) { - return call.Raw(api.caller, "get_accounts", [][]string{accountNames}) -} - -// XXX: Not sure about params. -//func (api *API) GetAccountReferenceRaw(id string) (*json.RawMessage, error) { -// return call.Raw(api.caller, "get_account_reference", []string{id}) -//} - -func (api *API) LookupAccountNamesRaw(accountNames []string) (*json.RawMessage, error) { - return call.Raw(api.caller, "lookup_account_names", [][]string{accountNames}) -} - -func (api *API) LookupAccountsRaw(lowerBoundName string, limit uint32) (*json.RawMessage, error) { - return call.Raw(api.caller, "lookup_accounts", []interface{}{lowerBoundName, limit}) -} - -func (api *API) GetAccountCountRaw() (*json.RawMessage, error) { - return call.Raw(api.caller, "get_account_count", call.EmptyParams) -} - -func (api *API) GetConversionRequestsRaw(accountName string) (*json.RawMessage, error) { - return call.Raw(api.caller, "get_conversion_requests", []string{accountName}) -} - -func (api *API) GetAccountHistoryRaw(account string, from uint64, limit uint32) (*json.RawMessage, error) { - return call.Raw(api.caller, "get_account_history", []interface{}{account, from, limit}) -} - -/* - // Market - (get_order_book) -*/ - -func (api *API) GetOrderBookRaw(limit uint32) (*json.RawMessage, error) { - if limit > 1000 { - return nil, errors.New("GetOrderBook: limit must not exceed 1000") - } - return call.Raw(api.caller, "get_order_book", []interface{}{limit}) -} - -/* - // Authority / validation - (get_transaction_hex) - (get_transaction) - (get_required_signatures) - (get_potential_signatures) - (verify_authority) - (verify_account_authority) -*/ - -/* - // Votes - (get_active_votes) - (get_account_votes) -*/ - -func (api *API) GetActiveVotesRaw(author, permlink string) (*json.RawMessage, error) { - return call.Raw(api.caller, "get_active_votes", []string{author, permlink}) -} - -func (api *API) GetActiveVotes(author, permlink string) ([]*VoteState, error) { - var resp []*VoteState - if err := api.caller.Call("get_active_votes", []string{author, permlink}, &resp); err != nil { - return nil, err - } - return resp, nil -} - -func (api *API) GetAccountVotesRaw(voter string) (*json.RawMessage, error) { - return call.Raw(api.caller, "get_account_votes", []string{voter}) -} - -/* - // Content - (get_content) - (get_content_replies) - (get_discussions_by_author_before_date) - MISSING - (get_replies_by_last_update) -*/ - -func (api *API) GetContentRaw(author, permlink string) (*json.RawMessage, error) { - return call.Raw(api.caller, "get_content", []string{author, permlink}) -} - -func (api *API) GetContent(author, permlink string) (*Content, error) { - var resp Content - if err := api.caller.Call("get_content", []string{author, permlink}, &resp); err != nil { - return nil, err - } - return &resp, nil -} - -func (api *API) GetContentRepliesRaw(parentAuthor, parentPermlink string) (*json.RawMessage, error) { - return call.Raw(api.caller, "get_content_replies", []string{parentAuthor, parentPermlink}) -} - -func (api *API) GetContentReplies(parentAuthor, parentPermlink string) ([]*Content, error) { - var resp []*Content - err := api.caller.Call("get_content_replies", []string{parentAuthor, parentPermlink}, &resp) - if err != nil { - return nil, err - } - return resp, nil -} - -func (api *API) GetRepliesByLastUpdateRaw( - startAuthor string, - startPermlink string, - limit uint32, -) (*json.RawMessage, error) { - - return call.Raw( - api.caller, "get_replies_by_last_update", []interface{}{startAuthor, startPermlink, limit}) -} - -/* - // Witnesses - (get_witnesses) - (get_witness_by_account) - (get_witnesses_by_vote) - (lookup_witness_accounts) - (get_witness_count) - (get_active_witnesses) - (get_miner_queue) -*/ - -// -// Some randomly added functions. -// - -func (api *API) GetOpsInBlockRaw(blockNum uint32, onlyVirtual bool) (*json.RawMessage, error) { - return call.Raw( - api.caller, "get_ops_in_block", []interface{}{blockNum, onlyVirtual}) -} - -func (api *API) GetOpsInBlock(blockNum uint32, onlyVirtual bool) ([]*types.OperationObject, error) { - var resp []*types.OperationObject - err := api.caller.Call("get_ops_in_block", []interface{}{blockNum, onlyVirtual}, &resp) - if err != nil { - return nil, err - } - return resp, nil -} diff --git a/apis/database/data.go b/apis/database/data.go deleted file mode 100644 index 8bd2d94..0000000 --- a/apis/database/data.go +++ /dev/null @@ -1,149 +0,0 @@ -package database - -import ( - // Stdlib - "encoding/json" - "strconv" - "strings" - - // RPC - "github.com/go-steem/rpc/types" -) - -type Config struct { - SteemitBlockchainHardforkVersion string `json:"STEEMIT_BLOCKCHAIN_HARDFORK_VERSION"` - SteemitBlockchainVersion string `json:"STEEMIT_BLOCKCHAIN_VERSION"` - SteemitBlockInterval uint `json:"STEEMIT_BLOCK_INTERVAL"` -} - -type DynamicGlobalProperties struct { - Time *types.Time `json:"time"` - TotalPow *types.Int `json:"total_pow"` - NumPowWitnesses *types.Int `json:"num_pow_witnesses"` - CurrentReserveRatio *types.Int `json:"current_reserve_ratio"` - ID *types.ID `json:"id"` - CurrentSupply string `json:"current_supply"` - CurrentSBDSupply string `json:"current_sbd_supply"` - MaximumBlockSize *types.Int `json:"maximum_block_size"` - RecentSlotsFilled *types.Int `json:"recent_slots_filled"` - CurrentWitness string `json:"current_witness"` - TotalRewardShares2 *types.Int `json:"total_reward_shares2"` - AverageBlockSize *types.Int `json:"average_block_size"` - CurrentAslot *types.Int `json:"current_aslot"` - LastIrreversibleBlockNum uint32 `json:"last_irreversible_block_num"` - TotalVestingShares string `json:"total_vesting_shares"` - TotalVersingFundSteem string `json:"total_vesting_fund_steem"` - HeadBlockID string `json:"head_block_id"` - HeadBlockNumber types.UInt32 `json:"head_block_number"` - VirtualSupply string `json:"virtual_supply"` - ConfidentialSupply string `json:"confidential_supply"` - ConfidentialSBDSupply string `json:"confidential_sbd_supply"` - TotalRewardFundSteem string `json:"total_reward_fund_steem"` - TotalActivityFundSteem string `json:"total_activity_fund_steem"` - TotalActivityFundShares *types.Int `json:"total_activity_fund_shares"` - SBDInterestRate *types.Int `json:"sbd_interest_rate"` - MaxVirtualBandwidth *types.Int `json:"max_virtual_bandwidth"` -} - -type Block struct { - Number uint32 `json:"-"` - Timestamp *types.Time `json:"timestamp"` - Witness string `json:"witness"` - WitnessSignature string `json:"witness_signature"` - TransactionMerkleRoot string `json:"transaction_merkle_root"` - Previous string `json:"previous"` - Extensions [][]interface{} `json:"extensions"` - Transactions []*types.Transaction `json:"transactions"` -} - -type Content struct { - Id *types.ID `json:"id"` - RootTitle string `json:"root_title"` - Active *types.Time `json:"active"` - AbsRshares *types.Int `json:"abs_rshares"` - PendingPayoutValue string `json:"pending_payout_value"` - TotalPendingPayoutValue string `json:"total_pending_payout_value"` - Category string `json:"category"` - Title string `json:"title"` - LastUpdate *types.Time `json:"last_update"` - Stats string `json:"stats"` - Body string `json:"body"` - Created *types.Time `json:"created"` - Replies []*Content `json:"replies"` - Permlink string `json:"permlink"` - JsonMetadata *ContentMetadata `json:"json_metadata"` - Children *types.Int `json:"children"` - NetRshares *types.Int `json:"net_rshares"` - URL string `json:"url"` - ActiveVotes []*VoteState `json:"active_votes"` - ParentPermlink string `json:"parent_permlink"` - CashoutTime *types.Time `json:"cashout_time"` - TotalPayoutValue string `json:"total_payout_value"` - ParentAuthor string `json:"parent_author"` - ChildrenRshares2 *types.Int `json:"children_rshares2"` - Author string `json:"author"` - Depth *types.Int `json:"depth"` - TotalVoteWeight *types.Int `json:"total_vote_weight"` -} - -func (content *Content) IsStory() bool { - return content.ParentAuthor == "" -} - -type ContentMetadata struct { - Flag bool - Value string - Users []string - Tags []string - Image []string -} - -type ContentMetadataRaw struct { - Users types.StringSlice `json:"users"` - Tags types.StringSlice `json:"tags"` - Image types.StringSlice `json:"image"` -} - -func (metadata *ContentMetadata) UnmarshalJSON(data []byte) error { - unquoted, err := strconv.Unquote(string(data)) - if err != nil { - return err - } - - switch unquoted { - case "true": - metadata.Flag = true - return nil - case "false": - metadata.Flag = false - return nil - } - - if len(unquoted) == 0 { - return nil - } - - if unquoted[0] == '"' { - metadata.Value = unquoted - return nil - } - - var raw ContentMetadataRaw - if err := json.NewDecoder(strings.NewReader(unquoted)).Decode(&raw); err != nil { - return err - } - - metadata.Users = raw.Users - metadata.Tags = raw.Tags - metadata.Image = raw.Image - - return nil -} - -type VoteState struct { - Voter string `json:"voter"` - Weight *types.Int `json:"weight"` - Rshares *types.Int `json:"rshares"` - Percent *types.Int `json:"percent"` - Time *types.Time `json:"time"` -} diff --git a/apis/follow/README.md b/apis/follow/README.md deleted file mode 100644 index 6195c7f..0000000 --- a/apis/follow/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Follow API - -This package adds support for `follow_api`. - -## State - -| Method Name | Raw Version | Full Version | -| ------------------------- |:-----------:|:------------:| -| `get_followers` | DONE | DONE | -| `get_following` | DONE | DONE | -| `get_feed_entries` | DONE | DONE | -| `get_feed` | DONE | | -| `get_account_reputations` | DONE | | diff --git a/apis/follow/api.go b/apis/follow/api.go deleted file mode 100644 index f4c01dc..0000000 --- a/apis/follow/api.go +++ /dev/null @@ -1,176 +0,0 @@ -package follow - -import ( - // Stdlib - "encoding/json" - - // RPC - "github.com/go-steem/rpc/interfaces" - "github.com/go-steem/rpc/internal/rpc" - - // Vendor - "github.com/pkg/errors" -) - -const APIID = "follow_api" - -type API struct { - id int - caller interfaces.Caller -} - -func NewAPI(caller interfaces.Caller) (*API, error) { - id, err := rpc.GetNumericAPIID(caller, APIID) - if err != nil { - return nil, err - } - return &API{id, caller}, nil -} - -func (api *API) call(method string, params, resp interface{}) error { - return api.caller.Call("call", []interface{}{"follow_api", method, params}, resp) -} - -func (api *API) GetFollowersRaw( - accountName string, - start string, - kind string, - limit uint16, -) (*json.RawMessage, error) { - - var resp json.RawMessage - params := []interface{}{accountName, start, kind, limit} - if err := api.call("get_followers", params, &resp); err != nil { - return nil, errors.Wrap(err, "go-steem/rpc: follow_api: failed to call get_followers") - } - return &resp, nil -} - -func (api *API) GetFollowers( - accountName string, - start string, - kind string, - limit uint16, -) ([]*FollowObject, error) { - - raw, err := api.GetFollowersRaw(accountName, start, kind, limit) - if err != nil { - return nil, err - } - - var resp []*FollowObject - if err := json.Unmarshal([]byte(*raw), &resp); err != nil { - return nil, errors.Wrap( - err, "go-steem/rpc: follow_api: failed to unmarshal get_followers response") - } - return resp, nil - -} - -func (api *API) GetFollowingRaw( - accountName string, - start string, - kind string, - limit uint16, -) (*json.RawMessage, error) { - - var resp json.RawMessage - params := []interface{}{accountName, start, kind, limit} - if err := api.call("get_following", params, &resp); err != nil { - return nil, errors.Wrap(err, "go-steem/rpc: follow_api: failed to call get_following") - } - return &resp, nil -} - -func (api *API) GetFollowing( - accountName string, - start string, - kind string, - limit uint16, -) ([]*FollowObject, error) { - - raw, err := api.GetFollowingRaw(accountName, start, kind, limit) - if err != nil { - return nil, err - } - - var resp []*FollowObject - if err := json.Unmarshal([]byte(*raw), &resp); err != nil { - return nil, errors.Wrap( - err, "go-steem/rpc: follow_api: failed to unmarshal get_following response") - } - return resp, nil -} - -func (api *API) GetFeedEntriesRaw( - accountName string, - entryID uint32, - limit uint16, -) (*json.RawMessage, error) { - - if limit == 0 { - limit = 500 - } - - var resp json.RawMessage - params := []interface{}{accountName, entryID, limit} - if err := api.call("get_feed_entries", params, &resp); err != nil { - return nil, errors.Wrap(err, "go-steem/rpc: follow_api: failed to call get_feed_entries") - } - return &resp, nil -} - -func (api *API) GetFeedEntries( - accountName string, - entryID uint32, - limit uint16, -) ([]*FeedEntry, error) { - - raw, err := api.GetFeedEntriesRaw(accountName, entryID, limit) - if err != nil { - return nil, err - } - - var resp []*FeedEntry - if err := json.Unmarshal([]byte(*raw), &resp); err != nil { - return nil, errors.Wrap( - err, "go-steem/rpc: follow_api: failed to unmarshal get_feed_entries response") - } - return resp, nil -} - -func (api *API) GetFeedRaw( - accountName string, - entryID uint32, - limit uint16, -) (*json.RawMessage, error) { - - if limit == 0 { - limit = 500 - } - - var resp json.RawMessage - params := []interface{}{accountName, entryID, limit} - if err := api.call("get_feed", params, &resp); err != nil { - return nil, errors.Wrap(err, "go-steem/rpc: follow_api: failed to call get_feed") - } - return &resp, nil -} - -func (api *API) GetAccountReputationsRaw( - lowerBoundName string, - limit uint32, -) (*json.RawMessage, error) { - - if limit == 0 { - limit = 1000 - } - - var resp json.RawMessage - params := []interface{}{lowerBoundName, limit} - if err := api.call("get_account_reputations", params, &resp); err != nil { - return nil, errors.Wrap( - err, "go-steem/rpc: follow_api: failed to call get_account_reputations") - } - return &resp, nil -} diff --git a/apis/follow/data.go b/apis/follow/data.go deleted file mode 100644 index c489f32..0000000 --- a/apis/follow/data.go +++ /dev/null @@ -1,32 +0,0 @@ -package follow - -const ( - FollowKindFollow = "blog" - FollowKindIgnore = "ignore" -) - -type FollowObject struct { - Follower string `json:"follower"` - Following string `json:"following"` - What []string `json:"what"` -} - -type FeedEntry struct { - Author string `json:"string"` - Permlink string `json:"permlink"` - EntryID uint32 `json:"entry_id"` -} - -/* -type CommentFeedEntry struct { - Comment *CommentObject `json:"comment"` - EntryID uint32 `json:"entry_id"` -} -*/ - -/* -type AccountReputation struct { - Account string `json:"account"` - Reputation ??? `json:"reputation"` -} -*/ diff --git a/apis/login/README.md b/apis/login/README.md deleted file mode 100644 index c502f35..0000000 --- a/apis/login/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# Login API - -This package adds support for `login_api`. - -## State - -| Method Name | Raw Version | Full Version | -| ----------------- |:-----------:|:------------:| -| `login` | DONE | DONE | -| `get_api_by_name` | DONE | DONE | diff --git a/apis/login/api.go b/apis/login/api.go deleted file mode 100644 index 86ca903..0000000 --- a/apis/login/api.go +++ /dev/null @@ -1,65 +0,0 @@ -package login - -import ( - // Stdlib - "encoding/json" - - // RPC - "github.com/go-steem/rpc/interfaces" - - // Vendor - "github.com/pkg/errors" -) - -const ( - APIID = "login_api" - NumbericAPIID = 1 -) - -type API struct { - caller interfaces.Caller -} - -func NewAPI(caller interfaces.Caller) *API { - return &API{caller} -} - -func (api *API) call(method string, params, resp interface{}) error { - return api.caller.Call("call", []interface{}{NumbericAPIID, method, params}, resp) -} - -func (api *API) LoginRaw(username, password string) (*json.RawMessage, error) { - var resp json.RawMessage - params := []interface{}{username, password} - if err := api.call("login", params, &resp); err != nil { - return nil, errors.Wrap(err, "go-steem/rpc: login_api: failed to call login") - } - return &resp, nil -} - -func (api *API) Login(username, password string) (bool, error) { - var resp bool - params := []interface{}{username, password} - if err := api.call("login", params, &resp); err != nil { - return false, errors.Wrap(err, "go-steem/rpc: login_api: failed to call login") - } - return resp, nil -} - -func (api *API) GetAPIByNameRaw(apiName string) (*json.RawMessage, error) { - var resp json.RawMessage - params := []interface{}{apiName} - if err := api.call("get_api_by_name", params, &resp); err != nil { - return nil, errors.Wrap(err, "go-steem/rpc: login_api: failed to call get_api_by_name") - } - return &resp, nil -} - -func (api *API) GetAPIByName(apiName string) (int, error) { - var resp int - params := []interface{}{apiName} - if err := api.call("get_api_by_name", params, &resp); err != nil { - return 0, errors.Wrap(err, "go-steem/rpc: login_api: failed to call get_api_by_name") - } - return resp, nil -} diff --git a/apis/networkbroadcast/README.md b/apis/networkbroadcast/README.md deleted file mode 100644 index 0fa7d99..0000000 --- a/apis/networkbroadcast/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Network Broadcast API - -This package adds support for `network_broadcast_api`. - -## State - -| Method Name | Raw Version | Full Version | -| ------------------------------------- |:-----------:|:------------:| -| `broadcast_transaction` | SKIPPED | DONE | -| `broadcast_transaction_with_callback` | | | -| `broadcast_transaction_synchronous` | DONE | DONE | -| `broadcast_block` | | | -| `set_bcd_trigger` | | | diff --git a/apis/networkbroadcast/api.go b/apis/networkbroadcast/api.go deleted file mode 100644 index 7c610bb..0000000 --- a/apis/networkbroadcast/api.go +++ /dev/null @@ -1,76 +0,0 @@ -package networkbroadcast - -import ( - // Stdlib - "encoding/json" - - // RPC - "github.com/go-steem/rpc/interfaces" - "github.com/go-steem/rpc/internal/rpc" - "github.com/go-steem/rpc/types" - - // Vendor - "github.com/pkg/errors" -) - -const APIID = "network_broadcast_api" - -type API struct { - id int - caller interfaces.Caller -} - -func NewAPI(caller interfaces.Caller) (*API, error) { - id, err := rpc.GetNumericAPIID(caller, APIID) - if err != nil { - return nil, err - } - return &API{id, caller}, nil -} - -func (api *API) call(method string, params, resp interface{}) error { - return api.caller.Call("call", []interface{}{api.id, method, params}, resp) -} - -/* - * broadcast_transaction - */ - -func (api *API) BroadcastTransaction(tx *types.Transaction) error { - params := []interface{}{tx} - return api.call("broadcast_transaction", params, nil) -} - -/* - * broadcast_transaction_synchronous - */ - -func (api *API) BroadcastTransactionSynchronousRaw(tx *types.Transaction) (*json.RawMessage, error) { - params := []interface{}{tx} - - var resp json.RawMessage - if err := api.call("broadcast_transaction_synchronous", params, &resp); err != nil { - return nil, err - } - return &resp, nil -} - -type BroadcastResponse struct { - ID string `json:"id"` - BlockNum uint32 `json:"block_num"` - TrxNum uint32 `json:"trx_num"` - Expired bool `json:"expired"` -} - -func (api *API) BroadcastTransactionSynchronous(tx *types.Transaction) (*BroadcastResponse, error) { - raw, err := api.BroadcastTransactionSynchronousRaw(tx) - if err != nil { - return nil, err - } - - var resp BroadcastResponse - if err := json.Unmarshal([]byte(*raw), &resp); err != nil { - return nil, errors.Wrapf(err, "failed to unmarshal BroadcastResponse: %v", string(*raw)) - } - return &resp, nil -} diff --git a/client.go b/client.go index 21071db..aa54e82 100644 --- a/client.go +++ b/client.go @@ -1,51 +1,72 @@ -package rpc +package client import ( - // RPC - "github.com/go-steem/rpc/apis/database" - "github.com/go-steem/rpc/apis/follow" - "github.com/go-steem/rpc/apis/login" - "github.com/go-steem/rpc/apis/networkbroadcast" - "github.com/go-steem/rpc/interfaces" + "net/url" + + "github.com/asuleymanov/steem-go/api" + "github.com/asuleymanov/steem-go/transports" + "github.com/asuleymanov/steem-go/transports/http" + "github.com/asuleymanov/steem-go/transports/websocket" + "github.com/pkg/errors" +) + +var ( + ErrInitializeTransport = errors.New("Failed to initialize transport.") ) -// Client can be used to access Steem remote APIs. -// -// There is a public field for every Steem API available, +// Client can be used to access STEEM remote APIs. +// There is a public field for every STEEM API available, // e.g. Client.Database corresponds to database_api. type Client struct { - cc interfaces.CallCloser + cc transports.CallCloser - // Login represents login_api. - Login *login.API + chainID string - // Database represents database_api. - Database *database.API + AsyncProtocol bool - // Follow represents follow_api. - Follow *follow.API + // Database represents database_api. + API *api.API - // NetworkBroadcast represents network_broadcast_api. - NetworkBroadcast *networkbroadcast.API + // Current keys for operations + CurrentKeys *Keys } // NewClient creates a new RPC client that use the given CallCloser internally. -func NewClient(cc interfaces.CallCloser) (*Client, error) { - client := &Client{cc: cc} - client.Login = login.NewAPI(client.cc) - client.Database = database.NewAPI(client.cc) - - followAPI, err := follow.NewAPI(client.cc) +// Initialize only server present API. Absent API initialized as nil value. +func NewClient(s string) (*Client, error) { + // Parse URL + u, err := url.Parse(s) if err != nil { return nil, err } - client.Follow = followAPI - networkBroadcastAPI, err := networkbroadcast.NewAPI(client.cc) + // Initializing Transport + var call transports.CallCloser + switch u.Scheme { + case "wss", "ws": + call, err = websocket.NewTransport(s) + if err != nil { + return nil, err + } + case "https", "http": + call, err = http.NewTransport(s) + if err != nil { + return nil, err + } + default: + return nil, ErrInitializeTransport + } + client := &Client{cc: call} + + client.AsyncProtocol = false + + client.API = api.NewAPI(client.cc) + + chainID, err := client.API.GetConfig() if err != nil { return nil, err } - client.NetworkBroadcast = networkBroadcastAPI + client.chainID = chainID.ChainID return client, nil } diff --git a/encoding/transaction/encoder.go b/encoding/transaction/encoder.go index f38d282..ec9be5c 100644 --- a/encoding/transaction/encoder.go +++ b/encoding/transaction/encoder.go @@ -2,22 +2,31 @@ package transaction import ( // Stdlib + "bytes" "encoding/binary" "io" + "regexp" + "strconv" "strings" // Vendor + "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcutil/base58" "github.com/pkg/errors" + "golang.org/x/crypto/ripemd160" ) +//Encoder structure for the converter type Encoder struct { w io.Writer } +//NewEncoder initializing a new converter func NewEncoder(w io.Writer) *Encoder { return &Encoder{w} } +//EncodeVarint converting int64 to byte func (encoder *Encoder) EncodeVarint(i int64) error { if i >= 0 { return encoder.EncodeUVarint(uint64(i)) @@ -28,12 +37,14 @@ func (encoder *Encoder) EncodeVarint(i int64) error { return encoder.writeBytes(b[:n]) } +//EncodeUVarint converting uint64 to byte func (encoder *Encoder) EncodeUVarint(i uint64) error { b := make([]byte, binary.MaxVarintLen64) n := binary.PutUvarint(b, i) return encoder.writeBytes(b[:n]) } +//EncodeNumber converting number to byte func (encoder *Encoder) EncodeNumber(v interface{}) error { if err := binary.Write(encoder.w, binary.LittleEndian, v); err != nil { return errors.Wrapf(err, "encoder: failed to write number: %v", v) @@ -41,6 +52,23 @@ func (encoder *Encoder) EncodeNumber(v interface{}) error { return nil } +//EncodeArrString converting []string to byte +func (encoder *Encoder) EncodeArrString(v []string) error { + if err := encoder.EncodeUVarint(uint64(len(v))); err != nil { + return errors.Wrapf(err, "encoder: failed to write string: %v", v) + } + for _, val := range v { + if err := encoder.EncodeUVarint(uint64(len(val))); err != nil { + return errors.Wrapf(err, "encoder: failed to write string: %v", val) + } + if _, err := io.Copy(encoder.w, strings.NewReader(val)); err != nil { + return errors.Wrapf(err, "encoder: failed to write string: %v", val) + } + } + return nil +} + +//Encode function that determines the input values of which converter to use func (encoder *Encoder) Encode(v interface{}) error { if marshaller, ok := v.(TransactionMarshaller); ok { return marshaller.MarshalTransaction(encoder) @@ -70,14 +98,16 @@ func (encoder *Encoder) Encode(v interface{}) error { return encoder.EncodeNumber(v) case string: - return encoder.encodeString(v) - + return encoder.EncodeString(v) + case []byte: + return encoder.writeBytes(v) default: return errors.Errorf("encoder: unsupported type encountered") } } -func (encoder *Encoder) encodeString(v string) error { +//EncodeString converting string to byte +func (encoder *Encoder) EncodeString(v string) error { if err := encoder.EncodeUVarint(uint64(len(v))); err != nil { return errors.Wrapf(err, "encoder: failed to write string: %v", v) } @@ -98,3 +128,73 @@ func (encoder *Encoder) writeString(s string) error { } return nil } + +//EncodeBool converting bool to byte +func (encoder *Encoder) EncodeBool(b bool) error { + if b { + return encoder.EncodeNumber(byte(1)) + } + return encoder.EncodeNumber(byte(0)) +} + +//EncodeMoney converting Asset to byte +func (encoder *Encoder) EncodeMoney(s string) error { + r, _ := regexp.Compile(`^[0-9]+\.?[0-9]* [A-Za-z0-9]+$`) + if r.MatchString(s) { + asset := strings.Split(s, " ") + amm, errParsInt := strconv.ParseInt(strings.Replace(asset[0], ".", "", -1), 10, 64) + if errParsInt != nil { + return errParsInt + } + ind := strings.Index(asset[0], ".") + var perc int + if ind == -1 { + perc = 0 + } else { + perc = len(asset[0]) - ind - 1 + } + if err := binary.Write(encoder.w, binary.LittleEndian, amm); err != nil { + return errors.Wrapf(err, "encoder: failed to write number: %v", amm) + } + if err := binary.Write(encoder.w, binary.LittleEndian, byte(perc)); err != nil { + return errors.Wrapf(err, "encoder: failed to write number: %v", perc) + } + + if _, err := io.Copy(encoder.w, strings.NewReader(asset[1])); err != nil { + return errors.Wrapf(err, "encoder: failed to write string: %v", asset[1]) + } + + for i := byte(len(asset[1])); i < 7; i++ { + if err := binary.Write(encoder.w, binary.LittleEndian, byte(0)); err != nil { + return errors.Wrapf(err, "encoder: failed to write number: %v", 0) + } + } + return nil + } + return errors.New("Expecting amount like '99.000 SYMBOL'") +} + +//EncodePubKey converting PubKey to byte +func (encoder *Encoder) EncodePubKey(s string) error { + pkn1 := strings.Join(strings.Split(s, "")[3:], "") + b58 := base58.Decode(pkn1) + chs := b58[len(b58)-4:] + pkn2 := b58[:len(b58)-4] + chHash := ripemd160.New() + _, errHash := chHash.Write(pkn2) + if errHash != nil { + return errHash + } + nchs := chHash.Sum(nil)[:4] + if bytes.Equal(chs, nchs) { + if string(pkn2) == string(make([]byte, 33)) { + return encoder.writeBytes(pkn2) + } + pkn3, _ := btcec.ParsePubKey(pkn2, btcec.S256()) + if _, err := encoder.w.Write(pkn3.SerializeCompressed()); err != nil { + return errors.Wrapf(err, "encoder: failed to write bytes: %v", pkn3.SerializeCompressed()) + } + return nil + } + return errors.New("Public key is incorrect") +} diff --git a/encoding/transaction/encoder_rolling.go b/encoding/transaction/encoder_rolling.go index 172b0ba..c4fd06a 100644 --- a/encoding/transaction/encoder_rolling.go +++ b/encoding/transaction/encoder_rolling.go @@ -1,38 +1,80 @@ package transaction +//RollingEncoder structure for the chain of converters type RollingEncoder struct { next *Encoder err error } +//NewRollingEncoder initializing the chain of converters func NewRollingEncoder(next *Encoder) *RollingEncoder { return &RollingEncoder{next, nil} } +//EncodeVarint adding int64 to the converted value func (encoder *RollingEncoder) EncodeVarint(i int64) { if encoder.err == nil { encoder.err = encoder.next.EncodeVarint(i) } } +//EncodeUVarint adding uint64 to the converted value func (encoder *RollingEncoder) EncodeUVarint(i uint64) { if encoder.err == nil { encoder.err = encoder.next.EncodeUVarint(i) } } +//EncodeNumber adding number to the converted value func (encoder *RollingEncoder) EncodeNumber(v interface{}) { if encoder.err == nil { encoder.err = encoder.next.EncodeNumber(v) } } +//EncodeBool adding bool to the converted value +func (encoder *RollingEncoder) EncodeBool(v bool) { + if encoder.err == nil { + encoder.err = encoder.next.EncodeBool(v) + } +} + +//EncodeMoney adding Asset to the converted value +func (encoder *RollingEncoder) EncodeMoney(v string) { + if encoder.err == nil { + encoder.err = encoder.next.EncodeMoney(v) + } +} + +//EncodeString adding string to the converted value +func (encoder *RollingEncoder) EncodeString(v string) { + if encoder.err == nil { + encoder.err = encoder.next.EncodeString(v) + } +} + +//EncodePubKey adding PubKey to the converted value +func (encoder *RollingEncoder) EncodePubKey(v string) { + if encoder.err == nil { + encoder.err = encoder.next.EncodePubKey(v) + } +} + +//EncodeArrString adding []string to the converted value +func (encoder *RollingEncoder) EncodeArrString(v []string) { + if encoder.err == nil { + encoder.err = encoder.next.EncodeArrString(v) + } +} + +//Encode adding to a chain of other values func (encoder *RollingEncoder) Encode(v interface{}) { if encoder.err == nil { encoder.err = encoder.next.Encode(v) } } +//Err function that returns an error (if any) from the cup of converters func (encoder *RollingEncoder) Err() error { return encoder.err } diff --git a/encoding/transaction/encoding.go b/encoding/transaction/encoding.go index e19fb0a..d0f5502 100644 --- a/encoding/transaction/encoding.go +++ b/encoding/transaction/encoding.go @@ -1,5 +1,6 @@ package transaction +//TransactionMarshaller interface for converting data into byte type TransactionMarshaller interface { MarshalTransaction(*Encoder) error } diff --git a/encoding/wif/data_test.go b/encoding/wif/data_test.go deleted file mode 100644 index 6fdeb64..0000000 --- a/encoding/wif/data_test.go +++ /dev/null @@ -1,17 +0,0 @@ -package wif - -type testData struct { - WIF string - PrivateKeyHex string -} - -var data = []testData{ - { - WIF: "5JWHY5DxTF6qN5grTtChDCYBmWHfY9zaSsw4CxEKN5eZpH9iBma", - PrivateKeyHex: "5ad2b8df2c255d4a2996ee7d065e013e1bbb35c075ee6e5208aca44adc9a9d4c", - }, - { - WIF: "5KPipdRzoxrp6dDqsBfMD6oFZG356trVHV5QBGx3rABs1zzWWs8", - PrivateKeyHex: "cf9d6121ed458f24ea456ad7ff700da39e86688988cfe5c6ed6558642cf1e32f", - }, -} diff --git a/encoding/wif/decode_test.go b/encoding/wif/decode_test.go index 64200c4..8b10eb9 100644 --- a/encoding/wif/decode_test.go +++ b/encoding/wif/decode_test.go @@ -6,6 +6,22 @@ import ( "testing" ) +type testData struct { + WIF string + PrivateKeyHex string +} + +var data = []testData{ + { + WIF: "5JWHY5DxTF6qN5grTtChDCYBmWHfY9zaSsw4CxEKN5eZpH9iBma", + PrivateKeyHex: "5ad2b8df2c255d4a2996ee7d065e013e1bbb35c075ee6e5208aca44adc9a9d4c", + }, + { + WIF: "5KPipdRzoxrp6dDqsBfMD6oFZG356trVHV5QBGx3rABs1zzWWs8", + PrivateKeyHex: "cf9d6121ed458f24ea456ad7ff700da39e86688988cfe5c6ed6558642cf1e32f", + }, +} + func TestDecode(t *testing.T) { for _, d := range data { privKey, err := Decode(d.WIF) diff --git a/examples/upvote/.gitignore b/examples/upvote/.gitignore deleted file mode 100644 index 0774a23..0000000 --- a/examples/upvote/.gitignore +++ /dev/null @@ -1 +0,0 @@ -upvote diff --git a/examples/upvote/Makefile b/examples/upvote/Makefile deleted file mode 100644 index 9c42536..0000000 --- a/examples/upvote/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -.PHONY: build - -build: - go build -ldflags='-r /usr/local/lib' diff --git a/examples/upvote/README.md b/examples/upvote/README.md deleted file mode 100644 index a5ff1b4..0000000 --- a/examples/upvote/README.md +++ /dev/null @@ -1,17 +0,0 @@ -# Upvote - -Simply upvote whatever you like. - -## Usage - -To vote for a post made by `somebody`, post permlink `somepermlink` as user `me`, execute: - -```bash -$ ./upvote somebody somepermlink me -``` - -For example - -```bash -./upvote hr1 our-journey-ends-dream-coming-true-and-the-day-when-everything-went-as-planned me -``` diff --git a/examples/upvote/main.go b/examples/upvote/main.go deleted file mode 100644 index f51a899..0000000 --- a/examples/upvote/main.go +++ /dev/null @@ -1,144 +0,0 @@ -package main - -import ( - // Stdlib - "flag" - "fmt" - "log" - "os" - "os/signal" - "syscall" - - // RPC - "github.com/go-steem/rpc" - "github.com/go-steem/rpc/encoding/wif" - "github.com/go-steem/rpc/transactions" - "github.com/go-steem/rpc/transports/websocket" - "github.com/go-steem/rpc/types" - - // Vendor - "github.com/pkg/errors" - "golang.org/x/crypto/ssh/terminal" -) - -func main() { - if err := run(); err != nil { - log.Fatalln("Error:", err) - } -} - -func run() (err error) { - // Process flags. - flagAddress := flag.String("rpc_endpoint", "ws://localhost:8090", "steemd RPC endpoint address") - flag.Parse() - - url := *flagAddress - - // Process args. - args := flag.Args() - if len(args) != 3 { - return errors.New("3 arguments required") - } - author, permlink, voter := args[0], args[1], args[2] - - // Prompt for WIF. - wifKey, err := promptWIF(voter) - if err != nil { - return err - } - - // Start catching signals. - var interrupted bool - signalCh := make(chan os.Signal, 1) - signal.Notify(signalCh, syscall.SIGINT, syscall.SIGTERM) - - // Drop the error in case it is a request being interrupted. - defer func() { - if err == websocket.ErrClosing && interrupted { - err = nil - } - }() - - // Instantiate the WebSocket transport. - t, err := websocket.NewTransport(url) - if err != nil { - return err - } - - // Use the transport to get an RPC client. - client, err := rpc.NewClient(t) - if err != nil { - return err - } - defer func() { - if !interrupted { - client.Close() - } - }() - - // Start processing signals. - go func() { - <-signalCh - fmt.Println() - log.Println("Signal received, exiting...") - signal.Stop(signalCh) - interrupted = true - client.Close() - }() - - // Get the props to get the head block number and ID - // so that we can use that for the transaction. - props, err := client.Database.GetDynamicGlobalProperties() - if err != nil { - return err - } - - // Prepare the transaction. - refBlockPrefix, err := transactions.RefBlockPrefix(props.HeadBlockID) - if err != nil { - return err - } - - tx := transactions.NewSignedTransaction(&types.Transaction{ - RefBlockNum: transactions.RefBlockNum(props.HeadBlockNumber), - RefBlockPrefix: refBlockPrefix, - }) - - tx.PushOperation(&types.VoteOperation{ - Voter: voter, - Author: author, - Permlink: permlink, - Weight: 10000, - }) - - // Sign. - privKey, err := wif.Decode(wifKey) - if err != nil { - return err - } - privKeys := [][]byte{privKey} - - if err := tx.Sign(privKeys, transactions.SteemChain); err != nil { - return err - } - - // Broadcast. - resp, err := client.NetworkBroadcast.BroadcastTransactionSynchronous(tx.Transaction) - if err != nil { - return err - } - fmt.Printf("%+v\n", *resp) - - // Success! - return nil -} - -func promptWIF(accountName string) (string, error) { - fmt.Printf("Please insert WIF for account @%v: ", accountName) - passwd, err := terminal.ReadPassword(syscall.Stdin) - if err != nil { - return "", errors.Wrap(err, "failed to read WIF from the terminal") - } - fmt.Println() - return string(passwd), nil -} diff --git a/examples/voting_monitor/.gitignore b/examples/voting_monitor/.gitignore deleted file mode 100644 index 8339c4a..0000000 --- a/examples/voting_monitor/.gitignore +++ /dev/null @@ -1 +0,0 @@ -voting_monitor diff --git a/examples/voting_monitor/README.md b/examples/voting_monitor/README.md deleted file mode 100644 index 8b2eb6c..0000000 --- a/examples/voting_monitor/README.md +++ /dev/null @@ -1,17 +0,0 @@ -# Voting Monitor - -In this example we connect to `steemd` and watch operations as they -are happening. Every time we see a `vote` operation, we print a message -into the console. - -``` -$ ./monitor_voting -rpc_endpoint="ws://$(docker-machine ip default):8090" -2016/05/29 10:42:56 ---> Dial("ws://192.168.99.100:8090") -2016/05/29 10:42:56 ---> GetConfig() -2016/05/29 10:42:56 ---> Entering the block processing loop (last block = 1866869) -@easteagle13 voted for @easteagle13/another-article-discussing-some-inherent-flaws-of-the-dao -@easteagle13 voted for @easteagle13/to-your-loss-of-a-friend-my-condolences-and-other-thoughts -@yefet voted for @alexgr/planning-for-long-term-success-of-steemit-identifying-areas-of-improvement -@dke voted for @steemrollin/steem-meme -... -``` diff --git a/examples/voting_monitor/main.go b/examples/voting_monitor/main.go deleted file mode 100644 index b02657b..0000000 --- a/examples/voting_monitor/main.go +++ /dev/null @@ -1,139 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "log" - "os" - "os/signal" - "syscall" - "time" - - "github.com/go-steem/rpc" - "github.com/go-steem/rpc/transports/websocket" - "github.com/go-steem/rpc/types" -) - -func main() { - if err := run(); err != nil { - log.Fatalln("Error:", err) - } -} - -func run() (err error) { - // Process flags. - flagAddress := flag.String("rpc_endpoint", "ws://localhost:8090", "steemd RPC endpoint address") - flagReconnect := flag.Bool("reconnect", false, "enable auto-reconnect mode") - flag.Parse() - - var ( - url = *flagAddress - reconnect = *flagReconnect - ) - - // Start catching signals. - var interrupted bool - signalCh := make(chan os.Signal, 1) - signal.Notify(signalCh, syscall.SIGINT, syscall.SIGTERM) - - // Drop the error in case it is a request being interrupted. - defer func() { - if err == websocket.ErrClosing && interrupted { - err = nil - } - }() - - // Start the connection monitor. - monitorChan := make(chan interface{}, 1) - if reconnect { - go func() { - for { - event, ok := <-monitorChan - if ok { - log.Println(event) - } - } - }() - } - - // Instantiate the WebSocket transport. - log.Printf("---> Dial(\"%v\")\n", url) - t, err := websocket.NewTransport(url, - websocket.SetAutoReconnectEnabled(reconnect), - websocket.SetAutoReconnectMaxDelay(30*time.Second), - websocket.SetMonitor(monitorChan)) - if err != nil { - return err - } - - // Use the transport to get an RPC client. - client, err := rpc.NewClient(t) - if err != nil { - return err - } - defer func() { - if !interrupted { - client.Close() - } - }() - - // Start processing signals. - go func() { - <-signalCh - fmt.Println() - log.Println("Signal received, exiting...") - signal.Stop(signalCh) - interrupted = true - client.Close() - }() - - // Get config. - log.Println("---> GetConfig()") - config, err := client.Database.GetConfig() - if err != nil { - return err - } - - // Use the last irreversible block number as the initial last block number. - props, err := client.Database.GetDynamicGlobalProperties() - if err != nil { - return err - } - lastBlock := props.LastIrreversibleBlockNum - - // Keep processing incoming blocks forever. - log.Printf("---> Entering the block processing loop (last block = %v)\n", lastBlock) - for { - // Get current properties. - props, err := client.Database.GetDynamicGlobalProperties() - if err != nil { - return err - } - - // Process new blocks. - for props.LastIrreversibleBlockNum-lastBlock > 0 { - block, err := client.Database.GetBlock(lastBlock) - if err != nil { - return err - } - - // Process the transactions. - for _, tx := range block.Transactions { - for _, operation := range tx.Operations { - switch op := operation.Data().(type) { - case *types.VoteOperation: - fmt.Printf("@%v voted for @%v/%v\n", op.Voter, op.Author, op.Permlink) - - // You can add more cases here, it depends on - // what operations you actually need to process. - } - } - } - - lastBlock++ - } - - // Sleep for STEEMIT_BLOCK_INTERVAL seconds before the next iteration. - time.Sleep(time.Duration(config.SteemitBlockInterval) * time.Second) - } -} diff --git a/helper.go b/helper.go new file mode 100644 index 0000000..f6249ab --- /dev/null +++ b/helper.go @@ -0,0 +1,56 @@ +package client + +import ( + "strconv" + + "github.com/asuleymanov/steem-go/transactions" + "github.com/asuleymanov/steem-go/types" +) + +//SetAsset returns data of type Asset +func SetAsset(amount float64, symbol string) *types.Asset { + return &types.Asset{Amount: amount, Symbol: symbol} +} + +//PerMvest returns the ratio of TotalVersingFund to TotalVestingShares. +func (client *Client) PerMvest() (float64, error) { + dgp, errdgp := client.API.GetDynamicGlobalProperties() + if errdgp != nil { + return 0, errdgp + } + + tvfs := dgp.TotalVersingFund.Amount + tvs := dgp.TotalVestingShares.Amount + + spmtmp := (tvfs / tvs) * 1000000 + + str := strconv.FormatFloat(spmtmp, 'f', 3, 64) + spm, errspm := strconv.ParseFloat(str, 64) + if errspm != nil { + return 0, errspm + } + + return spm, nil +} + +//JSONTrxString generate Trx to String +func JSONTrxString(v *transactions.SignedTransaction) (string, error) { + ans, err := types.JSONMarshal(v) + if err != nil { + return "", err + } + return string(ans), nil +} + +//JSONOpString generate Operations to String +func JSONOpString(v []types.Operation) (string, error) { + var tx types.Operations + + tx = append(tx, v...) + + ans, err := types.JSONMarshal(tx) + if err != nil { + return "", err + } + return string(ans), nil +} diff --git a/interfaces/callcloser.go b/interfaces/callcloser.go deleted file mode 100644 index 978f1fa..0000000 --- a/interfaces/callcloser.go +++ /dev/null @@ -1,8 +0,0 @@ -package interfaces - -import "io" - -type CallCloser interface { - Caller - io.Closer -} diff --git a/interfaces/caller.go b/interfaces/caller.go deleted file mode 100644 index 238c30b..0000000 --- a/interfaces/caller.go +++ /dev/null @@ -1,5 +0,0 @@ -package interfaces - -type Caller interface { - Call(method string, params, response interface{}) error -} diff --git a/internal/call/utils.go b/internal/call/utils.go deleted file mode 100644 index 8886e44..0000000 --- a/internal/call/utils.go +++ /dev/null @@ -1,19 +0,0 @@ -package call - -import ( - // Stdlib - "encoding/json" - - // Vendor - "github.com/go-steem/rpc/interfaces" -) - -var EmptyParams = []string{} - -func Raw(caller interfaces.Caller, method string, params interface{}) (*json.RawMessage, error) { - var resp json.RawMessage - if err := caller.Call(method, params, &resp); err != nil { - return nil, err - } - return &resp, nil -} diff --git a/internal/rpc/rpc.go b/internal/rpc/rpc.go deleted file mode 100644 index 4254b92..0000000 --- a/internal/rpc/rpc.go +++ /dev/null @@ -1,31 +0,0 @@ -package rpc - -import ( - // Stdlib - "encoding/json" - - // RPC - "github.com/go-steem/rpc/interfaces" - - // Vendor - "github.com/pkg/errors" -) - -func GetNumericAPIID(caller interfaces.Caller, apiName string) (int, error) { - params := []interface{}{apiName} - - var resp json.RawMessage - if err := caller.Call("call", []interface{}{1, "get_api_by_name", params}, &resp); err != nil { - return 0, err - } - - if string(resp) == "null" { - return 0, errors.Errorf("API not available: %v", apiName) - } - - var id int - if err := json.Unmarshal([]byte(resp), &id); err != nil { - return 0, err - } - return id, nil -} diff --git a/keys.go b/keys.go new file mode 100644 index 0000000..47d3719 --- /dev/null +++ b/keys.go @@ -0,0 +1,114 @@ +package client + +import ( + "github.com/asuleymanov/steem-go/encoding/wif" + "github.com/asuleymanov/steem-go/types" + "github.com/pkg/errors" +) + +var ( + //OpTypeKey include a description of the operation and the key needed to sign it + OpTypeKey = make(map[types.OpType][]string) +) + +//Keys is used as a keystroke for a specific user. +//Only a few keys can be set. +type Keys struct { + PKey []string + AKey []string + OKey []string + MKey []string +} + +func init() { + OpTypeKey["vote"] = []string{"posting"} + OpTypeKey["content"] = []string{"posting"} + OpTypeKey["transfer"] = []string{"active"} + OpTypeKey["transfer_to_vesting"] = []string{"active"} + OpTypeKey["withdraw_vesting"] = []string{"active"} + OpTypeKey["account_update"] = []string{"active"} + OpTypeKey["witness_update"] = []string{"active"} + OpTypeKey["account_witness_vote"] = []string{"posting"} + OpTypeKey["account_witness_proxy"] = []string{"posting"} + OpTypeKey["delete_content"] = []string{"posting"} + OpTypeKey["custom"] = []string{"posting"} + OpTypeKey["set_withdraw_vesting_route"] = []string{"active"} + OpTypeKey["request_account_recovery"] = []string{"active"} + OpTypeKey["recover_account"] = []string{"owner"} + OpTypeKey["change_recovery_account"] = []string{"owner"} + OpTypeKey["escrow_transfer"] = []string{"active"} + OpTypeKey["escrow_dispute"] = []string{"active"} + OpTypeKey["escrow_release"] = []string{"active"} + OpTypeKey["escrow_approve"] = []string{"active"} + OpTypeKey["delegate_vesting_shares"] = []string{"active"} + OpTypeKey["account_create"] = []string{"active"} + OpTypeKey["account_metadata"] = []string{"posting"} + OpTypeKey["proposal_create"] = []string{"active"} + OpTypeKey["proposal_update"] = []string{"active"} + OpTypeKey["proposal_delete"] = []string{"active"} + OpTypeKey["chain_properties_update"] = []string{"active"} + OpTypeKey["committee_worker_create_request"] = []string{"posting"} + OpTypeKey["committee_worker_cancel_request"] = []string{"posting"} + OpTypeKey["committee_vote_request"] = []string{"posting"} + OpTypeKey["create_invite"] = []string{"active"} + OpTypeKey["claim_invite_balance"] = []string{"active"} + OpTypeKey["invite_registration"] = []string{"active"} + OpTypeKey["versioned_chain_properties_update"] = []string{"active"} + OpTypeKey["award"] = []string{"posting"} + OpTypeKey["set_paid_subscription"] = []string{"active"} + OpTypeKey["paid_subscribe"] = []string{"active"} +} + +//SetKeys you can specify keys for signing transactions. +func (client *Client) SetKeys(keys *Keys) { + client.CurrentKeys = keys +} + +//SigningKeys returns the key from the CurrentKeys +func (client *Client) SigningKeys(trx types.Operation) ([][]byte, error) { + var keys [][]byte + + if client.CurrentKeys == nil { + return nil, errors.New("Client Keys not initialized. Use SetKeys method") + } + + opKeys := OpTypeKey[trx.Type()] + for _, val := range opKeys { + switch val { + case "posting": + for _, keyStr := range client.CurrentKeys.PKey { + privKey, err := wif.Decode(keyStr) + if err != nil { + return nil, errors.New("error decode Posting Key: " + err.Error()) + } + keys = append(keys, privKey) + } + case "active": + for _, keyStr := range client.CurrentKeys.AKey { + privKey, err := wif.Decode(keyStr) + if err != nil { + return nil, errors.New("error decode Active Key: " + err.Error()) + } + keys = append(keys, privKey) + } + case "owner": + for _, keyStr := range client.CurrentKeys.OKey { + privKey, err := wif.Decode(keyStr) + if err != nil { + return nil, errors.New("error decode Owner Key: " + err.Error()) + } + keys = append(keys, privKey) + } + case "memo": + for _, keyStr := range client.CurrentKeys.MKey { + privKey, err := wif.Decode(keyStr) + if err != nil { + return nil, errors.New("error decode Memo Key: " + err.Error()) + } + keys = append(keys, privKey) + } + } + } + + return keys, nil +} diff --git a/transactions/chains.go b/transactions/chains.go deleted file mode 100644 index 14745a8..0000000 --- a/transactions/chains.go +++ /dev/null @@ -1,13 +0,0 @@ -package transactions - -type Chain struct { - ID string -} - -var SteemChain = &Chain{ - ID: "0000000000000000000000000000000000000000000000000000000000000000", -} - -var TestChain = &Chain{ - ID: "18dcf0a285365fc58b71f18b3d3fec954aa0c141c44e4e5cb4cf777b9eab274e", -} diff --git a/transactions/rfc6979/ecdsa.go b/transactions/rfc6979/ecdsa.go new file mode 100644 index 0000000..298e2a7 --- /dev/null +++ b/transactions/rfc6979/ecdsa.go @@ -0,0 +1,67 @@ +package rfc6979 + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "hash" + "math/big" +) + +// SignECDSA signs an arbitrary length hash (which should be the result of +// hashing a larger message) using the private key, priv. It returns the +// signature as a pair of integers. +// +// Note that FIPS 186-3 section 4.6 specifies that the hash should be truncated +// to the byte-length of the subgroup. This function does not perform that +// truncation itself. +func SignECDSA(priv *ecdsa.PrivateKey, hash []byte, alg func() hash.Hash, nonce int) (r, s *big.Int, err error) { + c := priv.PublicKey.Curve + n := c.Params().N + + var hashClone = make([]byte, len(hash)) + copy(hashClone, hash) + + err = generateSecret(priv /* N, priv.D, */, alg, hashClone, func(k *big.Int) bool { + inv := new(big.Int).ModInverse(k, n) + r, _ = priv.Curve.ScalarBaseMult(k.Bytes()) + r.Mod(r, n) + + if r.Sign() == 0 { + return false + } + + e := hashToInt(hashClone, c) + s = new(big.Int).Mul(priv.D, r) + s.Add(s, e) + s.Mul(s, inv) + s.Mod(s, n) + + return s.Sign() != 0 + }, nonce) + + nOverTwo := new(big.Int).Div(n, big.NewInt(2)) + if s.Cmp(nOverTwo) > 0 { + s = new(big.Int).Sub(n, s) + } + + return +} + +// copied from crypto/ecdsa +func hashToInt(hash []byte, c elliptic.Curve) *big.Int { + var hashClone = make([]byte, len(hash)) + copy(hashClone, hash) + + orderBits := c.Params().N.BitLen() + orderBytes := (orderBits + 7) / 8 + if len(hashClone) > orderBytes { + hashClone = hashClone[:orderBytes] + } + + ret := new(big.Int).SetBytes(hashClone) + excess := len(hashClone)*8 - orderBits + if excess > 0 { + ret.Rsh(ret, uint(excess)) + } + return ret +} diff --git a/transactions/rfc6979/rfc6979.go b/transactions/rfc6979/rfc6979.go new file mode 100644 index 0000000..e71fd52 --- /dev/null +++ b/transactions/rfc6979/rfc6979.go @@ -0,0 +1,141 @@ +/* +Package rfc6979 is an implementation of RFC 6979's deterministic DSA. + Such signatures are compatible with standard Digital Signature Algorithm + (DSA) and Elliptic Curve Digital Signature Algorithm (ECDSA) digital + signatures and can be processed with unmodified verifiers, which need not be + aware of the procedure described therein. Deterministic signatures retain + the cryptographic security features associated with digital signatures but + can be more easily implemented in various environments, since they do not + need access to a source of high-quality randomness. +(https://tools.ietf.org/html/rfc6979) +Provides functions similar to crypto/dsa and crypto/ecdsa. +*/ +package rfc6979 + +import ( + "bytes" + "crypto/hmac" + "hash" + "math/big" + //"log" + //"encoding/hex" + "crypto/ecdsa" + "crypto/sha256" + "encoding/binary" + "math/rand" +) + +//var one = big.NewInt(1) +var oneInitializer = []byte{0x01} + +func RandStringBytes(n int) string { + letterBytes := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + + b := make([]byte, n) + for i := range b { + b[i] = letterBytes[rand.Intn(len(letterBytes))] + } + return string(b) +} + +// https://tools.ietf.org/html/rfc6979#section-3.2 +func generateSecret(priv *ecdsa.PrivateKey, alg func() hash.Hash, hash []byte, test func(*big.Int) bool, nonce int) error { + var hashClone = make([]byte, len(hash)) + var err error + copy(hashClone, hash) + + if nonce > 0 { + nonceA := make([]byte, 4) + binary.BigEndian.PutUint32(nonceA, uint32(nonce)) + hashClone = append(hashClone, nonceA...) + hs := sha256.New() + _, err = hs.Write(hashClone) + if err != nil { + return err + } + hashClone = hs.Sum(nil) + } + + c := priv.PublicKey.Curve + x := priv.D.Bytes() + q := c.Params().N + + // Step B + v := bytes.Repeat(oneInitializer, 32) + + // Step C (Go zeroes the all allocated memory) + k := make([]byte, 32) + + // Step D + + m := append(append(append(v, 0x00), x...), hashClone...) + + k, err = HmacSHA256(m, k) + if err != nil { + return err + } + + // Step E + v, err = HmacSHA256(v, k) + if err != nil { + return err + } + + // Step F + k, err = HmacSHA256(append(append(append(v, 0x01), x...), hashClone...), k) + if err != nil { + return err + } + + // Step G + v, err = HmacSHA256(v, k) + if err != nil { + return err + } + + // Step H1/H2a, ignored as tlen === qlen (256 bit) + // Step H2b + v, err = HmacSHA256(v, k) + if err != nil { + return err + } + + var t = hashToInt(v, c) + if err != nil { + return err + } + + // Step H3, repeat until T is within the interval [1, n - 1] + for t.Sign() <= 0 || t.Cmp(q) >= 0 || !test(t) { + + k, err = HmacSHA256(append(v, 0x00), k) + if err != nil { + return err + } + + v, err = HmacSHA256(v, k) + if err != nil { + return err + } + + // Step H1/H2a, again, ignored as tlen === qlen (256 bit) + // Step H2b again + v, err = HmacSHA256(v, k) + if err != nil { + return err + } + + t = hashToInt(v, c) + } + return nil +} + +func HmacSHA256(m, k []byte) ([]byte, error) { + mac := hmac.New(sha256.New, k) + _, err := mac.Write(m) + if err != nil { + return []byte{}, err + } + expectedMAC := mac.Sum(nil) + return expectedMAC, nil +} diff --git a/transactions/sign_the_shit.go b/transactions/sign_the_shit.go new file mode 100644 index 0000000..52f14a4 --- /dev/null +++ b/transactions/sign_the_shit.go @@ -0,0 +1,210 @@ +package transactions + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/sha256" + //"encoding/hex" + //"log" + "math/big" + + "github.com/asuleymanov/steem-go/transactions/rfc6979" + secp256k1 "github.com/btcsuite/btcd/btcec" + "github.com/pkg/errors" +) + +//SignSingle signature of the transaction by one of the keys +func (tx *SignedTransaction) SignSingle(privB, data []byte) ([]byte, error) { + privKeyBytes := [32]byte{} + copy(privKeyBytes[:], privB) + + priv, _ := secp256k1.PrivKeyFromBytes(secp256k1.S256(), privKeyBytes[:]) + //priv, _ := btcec.PrivKeyFromBytes(btcec.S256(), privKeyBytes[:]) + priEcdsa := priv.ToECDSA() + + return signBuffer(data, priEcdsa) +} + +func signBuffer(buf []byte, privateKey *ecdsa.PrivateKey) ([]byte, error) { + //log.Println("signBuffer buf=", hex.EncodeToString(buf)) + + // Hash a message. + alg := sha256.New() + _, errAlg := alg.Write(buf) + if errAlg != nil { + return []byte{}, errAlg + } + + _hash := alg.Sum(nil) + + return signBufferSha256(_hash, privateKey) +} + +func signBufferSha256(bufSha256 []byte, privateKey *ecdsa.PrivateKey) ([]byte, error) { + var bufSha256Clone = make([]byte, len(bufSha256)) + copy(bufSha256Clone, bufSha256) + + nonce := 0 + // key := (*btcec.PrivateKey)(privateKey) + + for { + r, s, err := rfc6979.SignECDSA(privateKey, bufSha256Clone, sha256.New, nonce) + // ecsignature, err := key.Sign(bufSha256Clone) + + nonce++ + if err != nil { + return nil, errors.Wrapf(err, "SignSingle[signBufferSha256]: ") + } + + ecsignature := &secp256k1.Signature{R: r, S: s} + + der := ecsignature.Serialize() + lenR := der[3] + lenS := der[5+lenR] + //log.Println("lenR=", lenR, "lenS", lenS) + + if lenR == 32 && lenS == 32 { + ////////////////////////////////////// + // bitcoind checks the bit length of R and S here. The ecdsa signature + // algorithm returns R and S mod N therefore they will be the bitsize of + // the curve, and thus correctly sized. + + key := (*secp256k1.PrivateKey)(privateKey) + curve := secp256k1.S256() + // curve := btcec.S256() + maxCounter := 4 + for i := 0; i < maxCounter; i++ { + pk, err := recoverKeyFromSignature(curve, ecsignature, bufSha256Clone, i, true) + + if err == nil && pk.X.Cmp(key.X) == 0 && pk.Y.Cmp(key.Y) == 0 { + + byteSize := curve.BitSize / 8 + result := make([]byte, 1, 2*byteSize+1) + result[0] = 27 + byte(i) + if true { // isCompressedKey + result[0] += 4 + } + // Not sure this needs rounding but safer to do so. + curvelen := (curve.BitSize + 7) / 8 + + // Pad R and S to curvelen if needed. + bytelen := (ecsignature.R.BitLen() + 7) / 8 + if bytelen < curvelen { + result = append(result, make([]byte, curvelen-bytelen)...) + } + result = append(result, ecsignature.R.Bytes()...) + + bytelen = (ecsignature.S.BitLen() + 7) / 8 + if bytelen < curvelen { + result = append(result, make([]byte, curvelen-bytelen)...) + } + result = append(result, ecsignature.S.Bytes()...) + + return result, nil + } else if err != nil { + return nil, err + } + } + } + } +} + +func recoverKeyFromSignature(curve *secp256k1.KoblitzCurve, sig *secp256k1.Signature, msg []byte, iter int, doChecks bool) (*secp256k1.PublicKey, error) { + rx := new(big.Int).Mul(curve.Params().N, + new(big.Int).SetInt64(int64(iter/2))) + rx.Add(rx, sig.R) + if rx.Cmp(curve.Params().P) != -1 { + return nil, errors.New("SignSingle[recoverKeyFromSignature]: calculated Rx is larger than curve P") + } + + // convert 02 to point R. (step 1.2 and 1.3). If we are on an odd + // iteration then 1.6 will be done with -R, so we calculate the other + // term when uncompressing the point. + ry, err := decompressPoint(curve, rx, iter%2 == 1) + if err != nil { + return nil, err + } + + // 1.4 Check n*R is point at infinity + if doChecks { + nRx, nRy := curve.ScalarMult(rx, ry, curve.Params().N.Bytes()) + if nRx.Sign() != 0 || nRy.Sign() != 0 { + return nil, errors.New("SignSingle[recoverKeyFromSignature]: n*R does not equal the point at infinity") + } + } + + // 1.5 calculate e from message using the same algorithm as ecdsa + // signature calculation. + e := hashToInt(msg, curve) + + // Step 1.6.1: + // We calculate the two terms sR and eG separately multiplied by the + // inverse of r (from the signature). We then add them to calculate + // Q = r^-1(sR-eG) + invr := new(big.Int).ModInverse(sig.R, curve.Params().N) + + // first term. + invrS := new(big.Int).Mul(invr, sig.S) + invrS.Mod(invrS, curve.Params().N) + sRx, sRy := curve.ScalarMult(rx, ry, invrS.Bytes()) + + // second term. + e.Neg(e) + e.Mod(e, curve.Params().N) + e.Mul(e, invr) + e.Mod(e, curve.Params().N) + minuseGx, minuseGy := curve.ScalarBaseMult(e.Bytes()) + + // TODO: this would be faster if we did a mult and add in one + // step to prevent the jacobian conversion back and forth. + qx, qy := curve.Add(sRx, sRy, minuseGx, minuseGy) + + return &secp256k1.PublicKey{ + Curve: curve, + X: qx, + Y: qy, + }, nil +} + +func decompressPoint(curve *secp256k1.KoblitzCurve, x *big.Int, ybit bool) (*big.Int, error) { + // TODO: This will probably only work for secp256k1 due to + // optimizations. + + // Y = +-sqrt(x^3 + B) + x3 := new(big.Int).Mul(x, x) + x3.Mul(x3, x) + x3.Add(x3, curve.Params().B) + + // now calculate sqrt mod p of x2 + B + // This code used to do a full sqrt based on tonelli/shanks, + // but this was replaced by the algorithms referenced in + // https://bitcointalk.org/index.php?topic=162805.msg1712294#msg1712294 + y := new(big.Int).Exp(x3, curve.QPlus1Div4(), curve.Params().P) + + if ybit != isOdd(y) { + y.Sub(curve.Params().P, y) + } + if ybit != isOdd(y) { + return nil, errors.New("SignSingle[decompressPoint]: ybit doesn't match oddness") + } + return y, nil +} + +func isOdd(a *big.Int) bool { + return a.Bit(0) == 1 +} + +func hashToInt(hash []byte, c elliptic.Curve) *big.Int { + orderBits := c.Params().N.BitLen() + orderBytes := (orderBits + 7) / 8 + if len(hash) > orderBytes { + hash = hash[:orderBytes] + } + + ret := new(big.Int).SetBytes(hash) + excess := len(hash)*8 - orderBits + if excess > 0 { + ret.Rsh(ret, uint(excess)) + } + return ret +} diff --git a/transactions/signed_transaction.go b/transactions/signed_transaction.go index 59e4127..7ab41b1 100644 --- a/transactions/signed_transaction.go +++ b/transactions/signed_transaction.go @@ -8,34 +8,31 @@ import ( "crypto/sha256" "encoding/hex" "time" - "unsafe" // RPC - "github.com/go-steem/rpc/encoding/transaction" - "github.com/go-steem/rpc/types" + "github.com/asuleymanov/steem-go/encoding/transaction" + "github.com/asuleymanov/steem-go/types" // Vendor "github.com/pkg/errors" ) -// #cgo LDFLAGS: -lsecp256k1 -// #include -// #include "signing.h" -import "C" - +//SignedTransaction structure of a signed transaction type SignedTransaction struct { *types.Transaction } +//NewSignedTransaction initialization of a new signed transaction func NewSignedTransaction(tx *types.Transaction) *SignedTransaction { if tx.Expiration == nil { - expiration := time.Now().Add(30 * time.Second) - tx.Expiration = &types.Time{&expiration} + expiration := time.Now().Add(30 * time.Second).UTC() + tx.Expiration = &types.Time{Time: &expiration} } return &SignedTransaction{tx} } +//Serialize function serializes a transaction func (tx *SignedTransaction) Serialize() ([]byte, error) { var b bytes.Buffer encoder := transaction.NewEncoder(&b) @@ -46,13 +43,14 @@ func (tx *SignedTransaction) Serialize() ([]byte, error) { return b.Bytes(), nil } -func (tx *SignedTransaction) Digest(chain *Chain) ([]byte, error) { +//Digest function that returns a digest from a serialized transaction +func (tx *SignedTransaction) Digest(chain string) ([]byte, error) { var msgBuffer bytes.Buffer // Write the chain ID. - rawChainID, err := hex.DecodeString(chain.ID) + rawChainID, err := hex.DecodeString(chain) if err != nil { - return nil, errors.Wrapf(err, "failed to decode chain ID: %v", chain.ID) + return nil, errors.Wrapf(err, "failed to decode chain ID: %v", chain) } if _, err := msgBuffer.Write(rawChainID); err != nil { @@ -74,105 +72,34 @@ func (tx *SignedTransaction) Digest(chain *Chain) ([]byte, error) { return digest[:], nil } -func (tx *SignedTransaction) Sign(privKeys [][]byte, chain *Chain) error { - digest, err := tx.Digest(chain) - if err != nil { - return err - } - - // Sign. - cDigest := C.CBytes(digest) - defer C.free(cDigest) - - cKeys := make([]unsafe.Pointer, 0, len(privKeys)) - for _, key := range privKeys { - cKeys = append(cKeys, C.CBytes(key)) - } - defer func() { - for _, cKey := range cKeys { - C.free(cKey) - } - }() - - sigs := make([][]byte, 0, len(privKeys)) - for _, cKey := range cKeys { - var ( - signature [64]byte - recid C.int - ) - - code := C.sign_transaction( - (*C.uchar)(cDigest), (*C.uchar)(cKey), (*C.uchar)(&signature[0]), &recid) - if code == 0 { - return errors.New("sign_transaction returned 0") - } - - sig := make([]byte, 65) - sig[0] = byte(recid) - copy(sig[1:], signature[:]) - - sigs = append(sigs, sig) +//Sign function directly generating transaction signature +func (tx *SignedTransaction) Sign(privKeys [][]byte, chain string) error { + var buf bytes.Buffer + chainid, errdec := hex.DecodeString(chain) + if errdec != nil { + return errdec } - // Set the signature array in the transaction. - sigsHex := make([]string, 0, len(sigs)) - for _, sig := range sigs { - sigsHex = append(sigsHex, hex.EncodeToString(sig)) - } - - tx.Transaction.Signatures = sigsHex - return nil -} - -func (tx *SignedTransaction) Verify(pubKeys [][]byte, chain *Chain) (bool, error) { - // Compute the digest, again. - digest, err := tx.Digest(chain) + txRaw, err := tx.Serialize() if err != nil { - return false, err + return err } - cDigest := C.CBytes(digest) - defer C.free(cDigest) + buf.Write(chainid) + buf.Write(txRaw) + data := buf.Bytes() + //msg_sha := crypto.Sha256(buf.Bytes()) - // Make sure to free memory. - cSigs := make([]unsafe.Pointer, 0, len(tx.Signatures)) - defer func() { - for _, cSig := range cSigs { - C.free(cSig) - } - }() + var sigsHex []string - // Collect verified public keys. - pubKeysFound := make([][]byte, len(pubKeys)) - for i, signature := range tx.Signatures { - sig, err := hex.DecodeString(signature) + for _, privB := range privKeys { + sigBytes, err := tx.SignSingle(privB, data) if err != nil { - return false, errors.Wrap(err, "failed to decode signature hex") - } - - recoverParameter := sig[0] - 27 - 4 - sig = sig[1:] - - cSig := C.CBytes(sig) - cSigs = append(cSigs, cSig) - - var publicKey [33]byte - - code := C.verify_recoverable_signature( - (*C.uchar)(cDigest), - (*C.uchar)(cSig), - (C.int)(recoverParameter), - (*C.uchar)(&publicKey[0]), - ) - if code == 1 { - pubKeysFound[i] = publicKey[:] + return err } + sigsHex = append(sigsHex, hex.EncodeToString(sigBytes)) } - for i := range pubKeys { - if !bytes.Equal(pubKeysFound[i], pubKeys[i]) { - return false, nil - } - } - return true, nil + tx.Transaction.Signatures = sigsHex + return nil } diff --git a/transactions/signed_transaction_test.go b/transactions/signed_transaction_test.go deleted file mode 100644 index 1863bc9..0000000 --- a/transactions/signed_transaction_test.go +++ /dev/null @@ -1,98 +0,0 @@ -package transactions - -import ( - // Stdlib - "encoding/hex" - "testing" - "time" - - // RPC - "github.com/go-steem/rpc/encoding/wif" - "github.com/go-steem/rpc/types" -) - -var tx *types.Transaction - -func init() { - // Prepare the transaction. - expiration := time.Date(2016, 8, 8, 12, 24, 17, 0, time.UTC) - tx = &types.Transaction{ - RefBlockNum: 36029, - RefBlockPrefix: 1164960351, - Expiration: &types.Time{&expiration}, - } - tx.PushOperation(&types.VoteOperation{ - Voter: "xeroc", - Author: "xeroc", - Permlink: "piston", - Weight: 10000, - }) -} - -var wifs = []string{ - "5JLw5dgQAx6rhZEgNN5C2ds1V47RweGshynFSWFbaMohsYsBvE8", -} - -var privateKeys = make([][]byte, 0, len(wifs)) - -func init() { - for _, v := range wifs { - privKey, err := wif.Decode(v) - if err != nil { - panic(err) - } - privateKeys = append(privateKeys, privKey) - } -} - -var publicKeys = make([][]byte, 0, len(wifs)) - -func init() { - for _, v := range wifs { - pubKey, err := wif.GetPublicKey(v) - if err != nil { - panic(err) - } - publicKeys = append(publicKeys, pubKey) - } -} - -func TestTransaction_Digest(t *testing.T) { - expected := "582176b1daf89984bc8b4fdcb24ff1433d1eb114a8c4bf20fb22ad580d035889" - - stx := NewSignedTransaction(tx) - - digest, err := stx.Digest(SteemChain) - if err != nil { - t.Error(err) - } - - got := hex.EncodeToString(digest) - if got != expected { - t.Errorf("got %v, expected %v", got, expected) - } -} - -func TestTransaction_SignAndVerify(t *testing.T) { - tx.Signatures = nil - defer func() { - tx.Signatures = nil - }() - - stx := NewSignedTransaction(tx) - if err := stx.Sign(privateKeys, SteemChain); err != nil { - t.Error(err) - } - - if len(tx.Signatures) != 1 { - t.Error("expected signatures not appended to the transaction") - } - - ok, err := stx.Verify(publicKeys, SteemChain) - if err != nil { - t.Error(err) - } - if !ok { - t.Error("verification failed") - } -} diff --git a/transactions/signing.c b/transactions/signing.c deleted file mode 100644 index be2be0f..0000000 --- a/transactions/signing.c +++ /dev/null @@ -1,138 +0,0 @@ -#include -#include -#include - -#include "secp256k1.h" -#include "secp256k1_recovery.h" - -#include "signing.h" - -static int sign( - const secp256k1_context* ctx, - const unsigned char *digest, - const unsigned char *privkey, - const void *ndata, - unsigned char *signature, - int *recid -); - -static bool is_canonical(const unsigned char *signature); - -void dump(const unsigned char *array, int len) { - for (int i = 0; i < len; i++) { - printf("%d ", array[i]); - } - printf("\n"); -} - -int sign_transaction( - const unsigned char *digest, - const unsigned char *privkey, - unsigned char *signature, - int *recid -) { - secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); - - int ndata = 1; - - while (1) { - // Sign the transaction. - if (!sign(ctx, digest, privkey, &ndata, signature, recid)) { - secp256k1_context_destroy(ctx); - return 0; - } - - // Check whether the signiture is canonical. - if (is_canonical(signature)) { - *recid += 4; // compressed - *recid += 27; // compact - break; - } - - ndata++; - } - - secp256k1_context_destroy(ctx); - return 1; -} - -static int sign( - const secp256k1_context* ctx, - const unsigned char *digest, - const unsigned char *privkey, - const void *ndata, - unsigned char *signature, - int *recid -) { - //printf("DIGEST:\n"); - //dump(digest, 32); - //printf("KEY:\n"); - //dump(privkey, 32); - - // Prepare a signature. - secp256k1_ecdsa_recoverable_signature sig; - - // Sign the digest using the given private key. - if (!secp256k1_ecdsa_sign_recoverable(ctx, &sig, digest, privkey, NULL, ndata)) { - return 0; - } - - //printf("SIGNATURE DATA:\n"); - //dump(sig.data, 65); - - // Serialize and return success. - secp256k1_ecdsa_recoverable_signature_serialize_compact(ctx, signature, recid, &sig); - return 1; -} - -static bool is_canonical(const unsigned char *sig) { - return (!(sig[0] & 0x80) && - !(sig[0] == 0 && !(sig[1] & 0x80)) && - !(sig[32] & 0x80) && - !(sig[32] == 0 && !(sig[33] & 0x80))); -} - -int verify_recoverable_signature( - const unsigned char *digest, - const unsigned char *signature, - int recid, - unsigned char *rawpubkey -) { - // Get a context. - secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); - - // Parse the signature. - secp256k1_ecdsa_recoverable_signature sig; - - if (!secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &sig, signature, recid)) { - secp256k1_context_destroy(ctx); - return 0; - } - - // Recover the public key. - secp256k1_pubkey pubkey; - - if (!secp256k1_ecdsa_recover(ctx, &pubkey, &sig, digest)) { - secp256k1_context_destroy(ctx); - return 0; - } - - // Conver recoverable signature to normal signature. - secp256k1_ecdsa_signature normsig; - - secp256k1_ecdsa_recoverable_signature_convert(ctx, &normsig, &sig); - - // Verify. - if (!secp256k1_ecdsa_verify(ctx, &normsig, digest, &pubkey)) { - secp256k1_context_destroy(ctx); - return 0; - } - - // Pass the public key back. - size_t len = 33; - secp256k1_ec_pubkey_serialize(ctx, rawpubkey, &len, &pubkey, SECP256K1_EC_COMPRESSED); - - // Clean up. - secp256k1_context_destroy(ctx); - return 1; -} diff --git a/transactions/signing.h b/transactions/signing.h deleted file mode 100644 index 121b600..0000000 --- a/transactions/signing.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef GOSTEEMRPC_SIGNING_H -#define GOSTEEMRPC_SIGNING_H - -int sign_transaction( - const unsigned char *digest, - const unsigned char *privkey, - unsigned char *signature, - int *recid -); - -// pubkey is expected to be 33 bytes long so that a compressed public key fits. -int verify_recoverable_signature( - const unsigned char *digest, - const unsigned char *signature, - int recid, - unsigned char *pubkey -); - -#endif diff --git a/transactions/transactions.go b/transactions/transactions.go index 05d91f3..d986218 100644 --- a/transactions/transactions.go +++ b/transactions/transactions.go @@ -7,16 +7,18 @@ import ( "encoding/hex" // RPC - "github.com/go-steem/rpc/types" + "github.com/asuleymanov/steem-go/types" // Vendor "github.com/pkg/errors" ) -func RefBlockNum(blockNumber types.UInt32) types.UInt16 { +//RefBlockNum function returns blockNumber +func RefBlockNum(blockNumber uint32) types.UInt16 { return types.UInt16(blockNumber) } +//RefBlockPrefix function returns block prefix func RefBlockPrefix(blockID string) (types.UInt32, error) { // Block ID is hex-encoded. rawBlockID, err := hex.DecodeString(blockID) diff --git a/transports/caller.go b/transports/caller.go new file mode 100644 index 0000000..1c073b1 --- /dev/null +++ b/transports/caller.go @@ -0,0 +1,22 @@ +package transports + +import ( + "encoding/json" + "io" +) + +var ( + EmptyParams = []struct{}{} +) + +//Caller interface for sending a request to a network transport +type Caller interface { + Call(method string, args []interface{}, reply interface{}) error + SetCallback(api string, method string, callback func(raw json.RawMessage)) error +} + +//CallCloser network transport interface +type CallCloser interface { + Caller + io.Closer +} diff --git a/transports/http/transport.go b/transports/http/transport.go new file mode 100644 index 0000000..15cef39 --- /dev/null +++ b/transports/http/transport.go @@ -0,0 +1,111 @@ +package http + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "math" + "net/http" + "sync" + + "time" + + "github.com/asuleymanov/steem-go/types" + "github.com/pkg/errors" +) + +type Transport struct { + Url string + client http.Client + + requestID uint64 + reqMutex sync.Mutex +} + +func NewTransport(url string) (*Transport, error) { + timeout := time.Duration(5 * time.Second) + + return &Transport{ + client: http.Client{ + Timeout: timeout, + }, + Url: url, + }, check(url) +} + +func (caller *Transport) Call(method string, args []interface{}, reply interface{}) error { + caller.reqMutex.Lock() + defer caller.reqMutex.Unlock() + + // increase request id + if caller.requestID == math.MaxUint64 { + caller.requestID = 0 + } + caller.requestID++ + + request := types.RPCRequest{ + Method: method, + JSON: "2.0", + ID: caller.requestID, + Params: args, + } + + reqBody, err := json.Marshal(request) + if err != nil { + return err + } + + resp, err := caller.client.Post(caller.Url, "application/json", bytes.NewBuffer(reqBody)) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("unexpected status code: %d", resp.StatusCode) + } + + respBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return errors.Wrap(err, "failed to read body") + } + + var rpcResponse types.RPCResponse + if err = json.Unmarshal(respBody, &rpcResponse); err != nil { + return errors.Wrapf(err, "failed to unmarshal response: %+v", string(respBody)) + } + + if rpcResponse.Error != nil { + return rpcResponse.Error + } + + if rpcResponse.Result != nil { + if err := json.Unmarshal(*rpcResponse.Result, reply); err != nil { + return errors.Wrapf(err, "failed to unmarshal rpc result: %+v", string(*rpcResponse.Result)) + } + } + + return nil +} + +func (caller *Transport) SetCallback(api string, method string, notice func(args json.RawMessage)) error { + panic("not supported") +} + +func (caller *Transport) Close() error { + return nil +} + +func check(url string) error { + resp, err := http.Get(url) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode != 200 { + return errors.Errorf("Error. URL: %s STATUS: %s\n", url, resp.StatusCode) + } + return nil +} diff --git a/transports/websocket/errors.go b/transports/websocket/errors.go deleted file mode 100644 index 09f4e09..0000000 --- a/transports/websocket/errors.go +++ /dev/null @@ -1,5 +0,0 @@ -package websocket - -import "errors" - -var ErrClosing = errors.New("closing") diff --git a/transports/websocket/events.go b/transports/websocket/events.go deleted file mode 100644 index d376eaf..0000000 --- a/transports/websocket/events.go +++ /dev/null @@ -1,33 +0,0 @@ -package websocket - -import ( - "fmt" -) - -// ConnectingEvent is emitted when a new connection is being established. -type ConnectingEvent struct { - URL string -} - -func (e *ConnectingEvent) String() string { - return fmt.Sprintf("CONNECTING [url=%v]", e.URL) -} - -// ConnectedEvent is emitted when the WebSocket connection is established. -type ConnectedEvent struct { - URL string -} - -func (e *ConnectedEvent) String() string { - return fmt.Sprintf("CONNECTED [url=%v]", e.URL) -} - -// DisconnectedEvent is emitted when the WebSocket connection is lost. -type DisconnectedEvent struct { - URL string - Err error -} - -func (e *DisconnectedEvent) String() string { - return fmt.Sprintf("DISCONNECTED [url=%v, err=%v]", e.URL, e.Err) -} diff --git a/transports/websocket/object_stream.go b/transports/websocket/object_stream.go deleted file mode 100644 index ba16a13..0000000 --- a/transports/websocket/object_stream.go +++ /dev/null @@ -1,36 +0,0 @@ -package websocket - -import ( - "time" - - "github.com/gorilla/websocket" - jsonrpc2websocket "github.com/sourcegraph/jsonrpc2/websocket" -) - -// ObjectStream implements jsonrpc2.ObjectStream that uses a WebSocket. -// It extends jsonrpc2/websocket.ObjectStream with read/write timeouts. -type ObjectStream struct { - conn *websocket.Conn - stream jsonrpc2websocket.ObjectStream - - writeTimeout time.Duration - readTimeout time.Duration -} - -func NewObjectStream(conn *websocket.Conn, writeTimeout, readTimeout time.Duration) *ObjectStream { - return &ObjectStream{conn, jsonrpc2websocket.NewObjectStream(conn), writeTimeout, readTimeout} -} - -func (stream *ObjectStream) WriteObject(v interface{}) error { - stream.conn.SetWriteDeadline(time.Now().Add(stream.writeTimeout)) - return stream.stream.WriteObject(v) -} - -func (stream *ObjectStream) ReadObject(v interface{}) error { - stream.conn.SetReadDeadline(time.Now().Add(stream.readTimeout)) - return stream.stream.ReadObject(v) -} - -func (stream *ObjectStream) Close() error { - return stream.stream.Close() -} diff --git a/transports/websocket/transport.go b/transports/websocket/transport.go index c4e00ed..2057e2a 100644 --- a/transports/websocket/transport.go +++ b/transports/websocket/transport.go @@ -1,306 +1,241 @@ package websocket import ( - "io" - // Stdlib - "context" - "net" + "encoding/json" + "fmt" + "log" + "math" + "sync" "time" - // Vendor + "github.com/asuleymanov/steem-go/types" "github.com/gorilla/websocket" "github.com/pkg/errors" - "github.com/sourcegraph/jsonrpc2" - tomb "gopkg.in/tomb.v2" ) -const ( - DefaultHandshakeTimeout = 30 * time.Second - DefaultWriteTimeout = 10 * time.Second - DefaultReadTimeout = 20 * time.Second - DefaultAutoReconnectMaxDelay = 1 * time.Minute - - InitialAutoReconnectDelay = 1 * time.Second - AutoReconnectBackoffCoefficient = 1.5 +var ( + ErrShutdown = errors.New("connection is shut down") + writeWait = 10 * time.Second + pongWait = 60 * time.Second + pingPeriod = (pongWait * 9) / 10 ) -var netDialer net.Dialer - -// Transport implements a CallCloser accessing the Steem RPC endpoint over WebSocket. type Transport struct { - // URLs as passed into the constructor. - urls []string - nextURLIndex int - currentURL string + conn *websocket.Conn - // Options. - handshakeTimeout time.Duration - readTimeout time.Duration - writeTimeout time.Duration + reqMutex sync.Mutex + requestID uint64 + pending map[uint64]*callRequest - autoReconnectEnabled bool - autoReconnectMaxDelay time.Duration + callbackMutex sync.Mutex + callbackID uint64 + callbacks map[uint64]func(args json.RawMessage) - monitorChan chan<- interface{} + closing bool // user has called Close + shutdown bool // server has told us to stop - // The underlying JSON-RPC connection. - connCh chan chan *jsonrpc2.Conn - errCh chan error - - t *tomb.Tomb + mutex sync.Mutex } -// Option represents an option that can be passed into the transport constructor. -type Option func(*Transport) - -// SetDialTimeout can be used to set the timeout when establishing a new connection. -// -// This function is deprecated, please use SetHandshakeTimeout. -func SetDialTimeout(timeout time.Duration) Option { - return SetHandshakeTimeout(timeout) +// Represent an async call +type callRequest struct { + Error error // after completion, the error status. + Done chan bool // strobes when call is complete. + Reply *json.RawMessage // reply message } -// SetHandshakeTimeout can be used to set the timeout for WebSocket handshake. -func SetHandshakeTimeout(timeout time.Duration) Option { - return func(t *Transport) { - t.handshakeTimeout = timeout +func NewTransport(url string) (*Transport, error) { + ws, _, err := websocket.DefaultDialer.Dial(url, nil) + if err != nil { + return nil, err } -} -// SetReadTimeout sets the connection read timeout. -// The timeout is implemented using net.Conn.SetReadDeadline. -func SetReadTimeout(timeout time.Duration) Option { - return func(t *Transport) { - t.readTimeout = timeout + client := &Transport{ + conn: ws, + pending: make(map[uint64]*callRequest), + callbacks: make(map[uint64]func(args json.RawMessage)), } -} -// SetWriteTimeout sets the connection read timeout. -// The timeout is implemented using net.Conn.SetWriteDeadline. -func SetWriteTimeout(timeout time.Duration) Option { - return func(t *Transport) { - t.writeTimeout = timeout - } + go ping(ws) + go client.input() + return client, nil } -// SetReadWriteTimeout sets the connection read and write timeout. -// The timeout is implemented using net.Conn.SetDeadline. -func SetReadWriteTimeout(timeout time.Duration) Option { - return func(t *Transport) { - t.readTimeout = timeout - t.writeTimeout = timeout +func (caller *Transport) Call(method string, args []interface{}, reply interface{}) error { + caller.reqMutex.Lock() + defer caller.reqMutex.Unlock() + + caller.mutex.Lock() + if caller.closing || caller.shutdown { + caller.mutex.Unlock() + return ErrShutdown } -} -// SetAutoReconnectEnabled can be used to enable automatic reconnection to the RPC endpoint. -// Exponential backoff is used when the connection cannot be established repetitively. -// -// See SetAutoReconnectMaxDelay to set the maximum delay between the reconnection attempts. -func SetAutoReconnectEnabled(enabled bool) Option { - return func(t *Transport) { - t.autoReconnectEnabled = enabled + // increase request id + if caller.requestID == math.MaxUint64 { + caller.requestID = 0 } -} + caller.requestID++ + seq := caller.requestID -// SetAutoReconnectMaxDelay can be used to set the maximum delay between the reconnection attempts. -// -// This option only takes effect when the auto-reconnect mode is enabled. -// -// The default value is 5 minutes. -func SetAutoReconnectMaxDelay(delay time.Duration) Option { - return func(t *Transport) { - t.autoReconnectMaxDelay = delay + c := &callRequest{ + Done: make(chan bool, 1), + } + caller.pending[seq] = c + caller.mutex.Unlock() + + request := types.RPCRequest{ + Method: method, + JSON: "2.0", + ID: caller.requestID, + Params: args, } -} -// SetMonitor can be used to set the monitoring channel that can be used to watch -// connection-related state changes. -// -// All channel send operations are happening synchronously, so not receiving messages -// from the channel will lead to the whole thing getting stuck completely. -// -// This option only takes effect when the auto-reconnect mode is enabled. -// -// The channel is closed when the transport is closed. -func SetMonitor(monitorChan chan<- interface{}) Option { - return func(t *Transport) { - t.monitorChan = monitorChan + // send Json Rcp request + if err := caller.WriteJSON(request); err != nil { + caller.mutex.Lock() + delete(caller.pending, seq) + caller.mutex.Unlock() + return err } -} -// NewTransport creates a new transport that connects to the given WebSocket URLs. -// -// It is possible to specify multiple WebSocket endpoint URLs. -// In case the transport is configured to reconnect automatically, -// the URL to connect to is rotated on every connect attempt using round-robin. -func NewTransport(urls []string, options ...Option) (*Transport, error) { - // Prepare a transport instance. - t := &Transport{ - urls: urls, - handshakeTimeout: DefaultHandshakeTimeout, - readTimeout: DefaultReadTimeout, - writeTimeout: DefaultWriteTimeout, - autoReconnectMaxDelay: DefaultAutoReconnectMaxDelay, - connCh: make(chan chan *jsonrpc2.Conn), - errCh: make(chan error), - t: &tomb.Tomb{}, + // wait for the call to complete + <-c.Done + if c.Error != nil { + return c.Error } - // Apply the options. - for _, opt := range options { - opt(t) + if c.Reply != nil { + if err := json.Unmarshal(*c.Reply, reply); err != nil { + return err + } } + return nil +} - t.t.Go(t.dialer) +func (caller *Transport) SetCallback(api string, method string, notice func(args json.RawMessage)) error { + var ans map[string]interface{} + // increase callback id + caller.callbackMutex.Lock() + if caller.callbackID == math.MaxUint64 { + caller.callbackID = 0 + } + //caller.callbackID++ + caller.callbackID = caller.requestID + 1 + caller.callbacks[caller.callbackID] = notice + caller.callbackMutex.Unlock() - // Return the new transport. - return t, nil + return caller.Call("call", []interface{}{api, method, []interface{}{caller.callbackID}}, ans) } -// Call implements interfaces.CallCloser. -func (t *Transport) Call(method string, params, result interface{}) error { - // Limit the request context with the tomb context. - ctx := t.t.Context(nil) - -Loop: +func (caller *Transport) input() { + caller.conn.SetPongHandler(func(string) error { _ = caller.conn.SetReadDeadline(time.Now().Add(pongWait)); return nil }) for { - // Request a connection. - connCh := make(chan *jsonrpc2.Conn, 1) - select { - case t.connCh <- connCh: - case <-ctx.Done(): - return errors.Wrap(ctx.Err(), "context closed") + _, message, err := caller.conn.ReadMessage() + if err != nil { + caller.stop(err) + return } - // Receive the connection. - conn := <-connCh - - // Perform the call. - err := conn.Call(ctx, method, params, result) - if err == nil { - return nil - } - - // In case this is a context error, return immediately. - if err := ctx.Err(); err != nil { - return errors.Wrap(err, "context closed") - } - - // In case auto-reconnect is disabled, fail immediately. - if !t.autoReconnectEnabled { - return errors.Wrap(err, "call failed") - } - - // In case this is a connection error, request a new connection. - err = errors.Cause(err) - if _, ok := err.(*websocket.CloseError); ok || err == io.ErrUnexpectedEOF { - select { - case t.errCh <- errors.Wrap(err, "WebSocket closed"): - continue Loop - case <-ctx.Done(): - return errors.Wrap(ctx.Err(), "context closed") + var response types.RPCResponse + if err := json.Unmarshal(message, &response); err != nil { + caller.stop(err) + return + } else { + if call, ok := caller.pending[response.ID]; ok { + caller.onCallResponse(response, call) + } else { + //the message is not a pending call, but probably a callback notice + var incoming types.RPCIncoming + if err := json.Unmarshal(message, &incoming); err != nil { + caller.stop(err) + return + } + if _, ok := caller.callbacks[incoming.ID]; ok { + if err := caller.onNotice(incoming); err != nil { + caller.stop(err) + return + } + } else { + log.Printf("protocol error: unknown message received: %+v\n", incoming) + log.Printf("Answer: %+v\n", string(message)) + } } } - - // Some other error occurred, return it immediately. - return errors.Wrap(err, "call failed") } } -func (t *Transport) dialer() error { - ctx := t.t.Context(nil) - - var conn *jsonrpc2.Conn - defer func() { - if conn != nil { - conn.Close() - err := errors.Wrap(ctx.Err(), "context closed") - t.emit(&DisconnectedEvent{t.currentURL, err}) - } - }() - - connect := func() { - delay := InitialAutoReconnectDelay - - for { - var err error - conn, err = t.dial(ctx) - if err == nil { - break - } - - select { - case <-time.After(delay): - delay = time.Duration(float64(delay) * AutoReconnectBackoffCoefficient) - if delay > t.autoReconnectMaxDelay { - delay = t.autoReconnectMaxDelay - } - - case <-ctx.Done(): - return - } - } +// Return pending clients and shutdown the client +func (caller *Transport) stop(err error) { + caller.reqMutex.Lock() + caller.shutdown = true + for _, call := range caller.pending { + call.Error = err + call.Done <- true } + caller.reqMutex.Unlock() +} - // Establish the initial connection. - connect() - - for { - select { - case connCh := <-t.connCh: - connCh <- conn - - case err := <-t.errCh: - conn.Close() - t.emit(&DisconnectedEvent{t.currentURL, err}) - connect() - - case <-ctx.Done(): - return nil - } +// Call response handler +func (caller *Transport) onCallResponse(response types.RPCResponse, call *callRequest) { + caller.mutex.Lock() + delete(caller.pending, response.ID) + if response.Error != nil { + call.Error = response.Error } + call.Reply = response.Result + call.Done <- true + caller.mutex.Unlock() } -func (t *Transport) dial(ctx context.Context) (*jsonrpc2.Conn, error) { - // Set up a dialer. - dialer := websocket.Dialer{ - NetDial: func(network, addr string) (net.Conn, error) { - return netDialer.DialContext(ctx, network, addr) - }, - HandshakeTimeout: t.handshakeTimeout, +// Incoming notice handler +func (caller *Transport) onNotice(incoming types.RPCIncoming) error { + notice := caller.callbacks[incoming.ID] + if notice == nil { + return fmt.Errorf("callback %d is not registered", incoming.ID) } - // Get the next URL to try. - u := t.urls[t.nextURLIndex] - t.nextURLIndex = (t.nextURLIndex + 1) % len(t.urls) - t.currentURL = u + // invoke callback + notice(incoming.Result) - // Connect the WebSocket. - t.emit(&ConnectingEvent{u}) - ws, _, err := dialer.Dial(u, nil) - if err != nil { - err = errors.Wrapf(err, "failed to dial %v", u) - t.emit(&DisconnectedEvent{u, err}) - return nil, err - } - t.emit(&ConnectedEvent{u}) + return nil +} - // Wrap the WebSocket with JSON-RPC2. - stream := NewObjectStream(ws, t.writeTimeout, t.readTimeout) - return jsonrpc2.NewConn(ctx, stream, nil), nil +// Close calls the underlying web socket Close method. If the connection is already +// shutting down, ErrShutdown is returned. +func (caller *Transport) Close() error { + caller.mutex.Lock() + if caller.closing { + caller.mutex.Unlock() + return ErrShutdown + } + caller.closing = true + caller.mutex.Unlock() + return caller.conn.Close() } -func (t *Transport) emit(v interface{}) { - if t.monitorChan != nil { - select { - case t.monitorChan <- v: - default: +func ping(ws *websocket.Conn) { + ticker := time.NewTicker(pingPeriod) + defer ticker.Stop() + for { + <-ticker.C + if err := ws.WriteControl(websocket.PingMessage, []byte{}, time.Now().Add(writeWait)); err != nil { + log.Println("ping:", err) } } } -// Close implements interfaces.CallCloser. -func (t *Transport) Close() error { - t.t.Kill(nil) - return t.t.Wait() +func (caller *Transport) WriteJSON(v interface{}) error { + w, err := caller.conn.NextWriter(1) + if err != nil { + return err + } + enc := json.NewEncoder(w) + enc.SetEscapeHTML(false) + err1 := enc.Encode(v) + err2 := w.Close() + if err1 != nil { + return err1 + } + return err2 } diff --git a/trx.go b/trx.go new file mode 100644 index 0000000..7211304 --- /dev/null +++ b/trx.go @@ -0,0 +1,116 @@ +package client + +import ( + "time" + + "github.com/asuleymanov/steem-go/api" + "github.com/asuleymanov/steem-go/transactions" + "github.com/asuleymanov/steem-go/types" +) + +//BResp of response when sending a transaction. +type BResp struct { + ID string + BlockNum int32 + TrxNum int32 + Expired bool + JSONTrx string +} + +//SendTrx generates and sends an array of transactions to STEEM. +func (client *Client) SendTrx(username string, strx []types.Operation) (*BResp, error) { + var bresp BResp + + // Getting the necessary parameters + props, err := client.API.GetDynamicGlobalProperties() + if err != nil { + return nil, err + } + + // Creating a Transaction + refBlockPrefix, err := transactions.RefBlockPrefix(props.HeadBlockID) + if err != nil { + return nil, err + } + tx := transactions.NewSignedTransaction(&types.Transaction{ + RefBlockNum: transactions.RefBlockNum(props.HeadBlockNumber), + RefBlockPrefix: refBlockPrefix, + }) + + // Adding Operations to a Transaction + for _, val := range strx { + tx.PushOperation(val) + } + + expTime := time.Now().Add(59 * time.Minute).UTC() + tm := types.Time{ + Time: &expTime, + } + tx.Expiration = &tm + + // Obtain the key required for signing + privKeys, err := client.SigningKeys(strx[0]) + if err != nil { + return nil, err + } + + // Sign the transaction + if err := tx.Sign(privKeys, client.chainID); err != nil { + return nil, err + } + + // Sending a transaction + var resp *api.BroadcastResponse + if client.AsyncProtocol { + err = client.API.BroadcastTransaction(tx.Transaction) + } else { + resp, err = client.API.BroadcastTransactionSynchronous(tx.Transaction) + } + + bresp.JSONTrx, _ = JSONTrxString(tx) + + if err != nil { + return &bresp, err + } + if resp != nil && !client.AsyncProtocol { + bresp.ID = resp.ID + bresp.BlockNum = resp.BlockNum + bresp.TrxNum = resp.TrxNum + bresp.Expired = resp.Expired + + return &bresp, nil + } + + return &bresp, nil +} + +func (client *Client) GetTrx(strx []types.Operation) (*types.Transaction, error) { + // Getting the necessary parameters + props, err := client.API.GetDynamicGlobalProperties() + if err != nil { + return nil, err + } + + // Creating a Transaction + refBlockPrefix, err := transactions.RefBlockPrefix(props.HeadBlockID) + if err != nil { + return nil, err + } + tx := &types.Transaction{ + RefBlockNum: transactions.RefBlockNum(props.HeadBlockNumber), + RefBlockPrefix: refBlockPrefix, + } + + // Adding Operations to a Transaction + for _, val := range strx { + tx.PushOperation(val) + } + + expTime := time.Now().Add(59 * time.Minute).UTC() + tm := types.Time{ + Time: &expTime, + } + tx.Expiration = &tm + + return tx, nil +} diff --git a/types/asset.go b/types/asset.go new file mode 100644 index 0000000..7f3225b --- /dev/null +++ b/types/asset.go @@ -0,0 +1,69 @@ +package types + +import ( + "encoding/json" + "strconv" + "strings" + + "github.com/asuleymanov/steem-go/encoding/transaction" +) + +//Asset type from parameter JSON +type Asset struct { + Amount float64 + Symbol string +} + +//UnmarshalJSON unpacking the JSON parameter in the Asset type. +func (op *Asset) UnmarshalJSON(data []byte) error { + str, errUnq := strconv.Unquote(string(data)) + if errUnq != nil { + return errUnq + } + param := strings.Split(str, " ") + + s, errpf := strconv.ParseFloat(param[0], 64) + if errpf != nil { + return errpf + } + + op.Amount = s + op.Symbol = param[1] + + return nil +} + +//MarshalJSON function for packing the Asset type in JSON. +func (op *Asset) MarshalJSON() ([]byte, error) { + return json.Marshal(op.String()) +} + +//MarshalTransaction is a function of converting type Asset to bytes. +func (op *Asset) MarshalTransaction(encoder *transaction.Encoder) error { + ans, err := json.Marshal(op) + if err != nil { + return err + } + + str, err := strconv.Unquote(string(ans)) + if err != nil { + return err + } + return encoder.EncodeMoney(str) +} + +//String function convert type Asset to string. +func (op *Asset) String() string { + var ammf string + if op.Symbol != "SHARES" { + ammf = strconv.FormatFloat(op.Amount, 'f', 3, 64) + } else { + ammf = strconv.FormatFloat(op.Amount, 'f', 6, 64) + } + return ammf + " " + op.Symbol +} + +//StringAmount function convert type Asset.Amount to string. +func (op *Asset) StringAmount() string { + return strconv.FormatFloat(op.Amount, 'f', 3, 64) +} diff --git a/types/authority.go b/types/authority.go new file mode 100644 index 0000000..4726467 --- /dev/null +++ b/types/authority.go @@ -0,0 +1,31 @@ +package types + +import ( + "github.com/asuleymanov/steem-go/encoding/transaction" +) + +//Authority is an additional structure used by other structures. +type Authority struct { + AccountAuths StringInt64Map `json:"account_auths"` + KeyAuths StringInt64Map `json:"key_auths"` + WeightThreshold uint32 `json:"weight_threshold"` +} + +//MarshalTransaction is a function of converting type Authority to bytes. +func (auth *Authority) MarshalTransaction(encoder *transaction.Encoder) error { + enc := transaction.NewRollingEncoder(encoder) + enc.EncodeNumber(uint32(auth.WeightThreshold)) + // encode AccountAuths as map[string]uint16 + enc.EncodeUVarint(uint64(len(auth.AccountAuths))) + for k, v := range auth.AccountAuths { + enc.EncodeString(k) + enc.EncodeNumber(uint16(v)) + } + // encode KeyAuths as map[PubKey]uint16 + enc.EncodeUVarint(uint64(len(auth.KeyAuths))) + for k, v := range auth.KeyAuths { + enc.EncodePubKey(k) + enc.EncodeNumber(uint16(v)) + } + return enc.Err() +} diff --git a/types/chain_properties.go b/types/chain_properties.go new file mode 100644 index 0000000..88675b3 --- /dev/null +++ b/types/chain_properties.go @@ -0,0 +1,21 @@ +package types + +import ( + "github.com/asuleymanov/steem-go/encoding/transaction" +) + +//ChainProperties is an additional structure used by other structures. +type ChainProperties struct { + AccountCreationFee *Asset `json:"account_creation_fee"` + MaximumBlockSize uint32 `json:"maximum_block_size"` + SbdInterestRate uint16 `json:"sbd_interest_rate"` +} + +//MarshalTransaction is a function of converting type ChainProperties to bytes. +func (cp *ChainProperties) MarshalTransaction(encoder *transaction.Encoder) error { + enc := transaction.NewRollingEncoder(encoder) + enc.Encode(cp.AccountCreationFee) + enc.Encode(cp.MaximumBlockSize) + enc.Encode(cp.SbdInterestRate) + return enc.Err() +} diff --git a/types/exchrate.go b/types/exchrate.go new file mode 100644 index 0000000..3b8c4b7 --- /dev/null +++ b/types/exchrate.go @@ -0,0 +1,19 @@ +package types + +import ( + "github.com/asuleymanov/steem-go/encoding/transaction" +) + +//ExchRate is an additional structure used by other structures. +type ExchRate struct { + Base *Asset `json:"base"` + Quote *Asset `json:"quote"` +} + +//MarshalTransaction is a function of converting type ExchRate to bytes. +func (exch *ExchRate) MarshalTransaction(encoder *transaction.Encoder) error { + enc := transaction.NewRollingEncoder(encoder) + enc.Encode(exch.Base) + enc.Encode(exch.Quote) + return enc.Err() +} diff --git a/types/extensions_comment_options.go b/types/extensions_comment_options.go new file mode 100644 index 0000000..9122e84 --- /dev/null +++ b/types/extensions_comment_options.go @@ -0,0 +1,30 @@ +package types + +import ( + "github.com/asuleymanov/steem-go/encoding/transaction" +) + +//Beneficiary is an additional structure used by other structures. +type Beneficiary struct { + Account string `json:"account"` + Weight uint16 `json:"weight"` +} + +//CommentPayoutBeneficiaries is an additional structure used by other structures. +type CommentPayoutBeneficiaries struct { + Beneficiaries []Beneficiary `json:"beneficiaries"` +} + +//MarshalTransaction is a function of converting type CommentPayoutBeneficiaries to bytes. +func (cp *CommentPayoutBeneficiaries) MarshalTransaction(encoder *transaction.Encoder) error { + enc := transaction.NewRollingEncoder(encoder) + enc.Encode(byte(0)) + enc.EncodeUVarint(uint64(len(cp.Beneficiaries))) + for _, val := range cp.Beneficiaries { + enc.Encode(val.Account) + enc.Encode(val.Weight) + } + return enc.Err() +} + +//AllowedVoteAssets diff --git a/types/id.go b/types/id.go index e810e03..871382b 100644 --- a/types/id.go +++ b/types/id.go @@ -5,6 +5,7 @@ import ( "encoding/json" ) +//ID type from parameter JSON type ID struct { ValueInt *Int ValueString string @@ -12,6 +13,7 @@ type ID struct { var dot = []byte{'.'} +//UnmarshalJSON unpacking the JSON parameter in the ID type. func (id *ID) UnmarshalJSON(data []byte) error { if bytes.Contains(data, dot) { id.ValueString = string(data) diff --git a/types/int.go b/types/int.go index b3682b3..5034ad3 100644 --- a/types/int.go +++ b/types/int.go @@ -1,14 +1,10 @@ package types import ( - // Stdlib "encoding/json" "strconv" - // RPC - "github.com/go-steem/rpc/encoding/transaction" - - // Vendor + "github.com/asuleymanov/steem-go/encoding/transaction" "github.com/pkg/errors" ) @@ -31,22 +27,10 @@ func unmarshalInt(data []byte) (int64, error) { return i, errors.Wrapf(err, "types: failed to unmarshal integer: %v", data) } -/* -type Int int - -func (num *Int) UnmarshalJSON(data []byte) error { - v, err := unmarshalInt(data) - if err != nil { - return err - } - - *num = Int(v) - return nil -} -*/ - +//Int8 type from parameter JSON type Int8 int8 +//UnmarshalJSON unpacking the JSON parameter in the Int8 type. func (num *Int8) UnmarshalJSON(data []byte) error { v, err := unmarshalInt(data) if err != nil { @@ -57,12 +41,15 @@ func (num *Int8) UnmarshalJSON(data []byte) error { return nil } +//MarshalTransaction is a function of converting type Int8 to bytes. func (num Int8) MarshalTransaction(encoder *transaction.Encoder) error { return encoder.EncodeNumber(int(num)) } +//Int16 type from parameter JSON type Int16 int16 +//UnmarshalJSON unpacking the JSON parameter in the Int16 type. func (num *Int16) UnmarshalJSON(data []byte) error { v, err := unmarshalInt(data) if err != nil { @@ -73,12 +60,15 @@ func (num *Int16) UnmarshalJSON(data []byte) error { return nil } +//MarshalTransaction is a function of converting type Int16 to bytes. func (num Int16) MarshalTransaction(encoder *transaction.Encoder) error { return encoder.EncodeNumber(int16(num)) } +//Int32 type from parameter JSON type Int32 int32 +//UnmarshalJSON unpacking the JSON parameter in the Int32 type. func (num *Int32) UnmarshalJSON(data []byte) error { v, err := unmarshalInt(data) if err != nil { @@ -89,12 +79,15 @@ func (num *Int32) UnmarshalJSON(data []byte) error { return nil } +//MarshalTransaction is a function of converting type Int32 to bytes. func (num Int32) MarshalTransaction(encoder *transaction.Encoder) error { return encoder.EncodeNumber(int32(num)) } +//Int64 type from parameter JSON type Int64 int64 +//UnmarshalJSON unpacking the JSON parameter in the Int64 type. func (num *Int64) UnmarshalJSON(data []byte) error { v, err := unmarshalInt(data) if err != nil { @@ -105,6 +98,7 @@ func (num *Int64) UnmarshalJSON(data []byte) error { return nil } +//MarshalTransaction is a function of converting type Int64 to bytes. func (num Int64) MarshalTransaction(encoder *transaction.Encoder) error { return encoder.EncodeNumber(int64(num)) } diff --git a/types/int_deprecated.go b/types/int_deprecated.go index 340f58b..4920d25 100644 --- a/types/int_deprecated.go +++ b/types/int_deprecated.go @@ -5,10 +5,12 @@ import ( "math/big" ) +//Int type from parameter JSON type Int struct { *big.Int } +//UnmarshalJSON unpacking the JSON parameter in the Int type. func (num *Int) UnmarshalJSON(data []byte) error { if data[0] == '"' { data = data[1:] diff --git a/types/operation.go b/types/operation.go index 1dc1a2d..386f1e6 100644 --- a/types/operation.go +++ b/types/operation.go @@ -2,6 +2,7 @@ package types import ( // Stdlib + "bytes" "encoding/json" "reflect" @@ -12,37 +13,62 @@ import ( // dataObjects keeps mapping operation type -> operation data object. // This is used later on to unmarshal operation data based on the operation type. var dataObjects = map[OpType]Operation{ - TypeVote: &VoteOperation{}, - TypeComment: &CommentOperation{}, - TypeTransfer: &TransferOperation{}, - TypeTransferToVesting: &TransferToVestingOperation{}, - TypeWithdrawVesting: &WithdrawVestingOperation{}, - TypeLimitOrderCreate: &LimitOrderCreateOperation{}, - TypeLimitOrderCancel: &LimitOrderCancelOperation{}, - TypeFeedPublish: &FeedPublishOperation{}, - TypeConvert: &ConvertOperation{}, - TypeAccountCreate: &AccountCreateOperation{}, - TypeAccountUpdate: &AccountUpdateOperation{}, - // TypeWitnessUpdate: &WitnessUpdateOperation{}, - TypeAccountWitnessVote: &AccountWitnessVoteOperation{}, - TypeAccountWitnessProxy: &AccountWitnessProxyOperation{}, - TypePOW: &POWOperation{}, - // TypeCustom: &CustomOperation{}, - TypeReportOverProduction: &ReportOverProductionOperation{}, - TypeDeleteComment: &DeleteCommentOperation{}, - TypeCustomJSON: &CustomJSONOperation{}, - TypeCommentOptions: &CommentOptionsOperation{}, - // TypeSetWithdrawVestingRoute: &SetWithdrawVestingRouteOperation{}, - // TypeLimitOrderCreate2: &LimitOrderCreate2Operation{}, - // TypeChallengeAuthority: &ChallengeAuthorityOperation{}, - // TypeProveAuthority: &ProveAuthorityOperation{}, - // TypeRequestAccountRecovery: &RequestAccountRecoveryOperation{}, - // TypeRecoverAccount: &RecoverAccountOperation{}, - // TypeChangeRecoveryAccount: &ChangeRecoverAccountOperation{}, - // TypeEscrowTransfer: &EscrowTransferOperation{}, - // TypeEscrowDispute: &EscrowDisputeOperation{}, - // TypeEscrowRelease: &EescrowReleaseOperation{}, - // TypePOW2: &POW2Operation{}, + TypeVote: &VoteOperation{}, + TypeComment: &CommentOperation{}, + TypeTransfer: &TransferOperation{}, + TypeTransferToVesting: &TransferToVestingOperation{}, + TypeWithdrawVesting: &WithdrawVestingOperation{}, + TypeLimitOrderCreate: &LimitOrderCreateOperation{}, + TypeLimitOrderCancel: &LimitOrderCancelOperation{}, + TypeFeedPublish: &FeedPublishOperation{}, + TypeConvert: &ConvertOperation{}, + TypeAccountCreate: &AccountCreateOperation{}, + TypeAccountUpdate: &AccountUpdateOperation{}, + TypeWitnessUpdate: &WitnessUpdateOperation{}, + TypeAccountWitnessVote: &AccountWitnessVoteOperation{}, + TypeAccountWitnessProxy: &AccountWitnessProxyOperation{}, + TypePOW: &POWOperation{}, + TypeCustom: &CustomOperation{}, + TypeReportOverProduction: &ReportOverProductionOperation{}, + TypeDeleteComment: &DeleteCommentOperation{}, + TypeCustomJSON: &CustomJSONOperation{}, + TypeCommentOptions: &CommentOptionsOperation{}, + TypeSetWithdrawVestingRoute: &SetWithdrawVestingRouteOperation{}, + TypeLimitOrderCreate2: &LimitOrderCreate2Operation{}, + TypeClaimAccount: &ClaimAccountOperation{}, + TypeCreateClaimedAccount: &CreateClaimedAccountOperation{}, + TypeRequestAccountRecovery: &RequestAccountRecoveryOperation{}, + TypeRecoverAccount: &RecoverAccountOperation{}, + TypeChangeRecoveryAccount: &ChangeRecoveryAccountOperation{}, + TypeEscrowTransfer: &EscrowTransferOperation{}, + TypeEscrowDispute: &EscrowDisputeOperation{}, + TypeEscrowRelease: &EscrowReleaseOperation{}, + TypePOW2: &POW2Operation{}, + TypeEscrowApprove: &EscrowApproveOperation{}, + TypeTransferToSavings: &TransferToSavingsOperation{}, + TypeTransferFromSavings: &TransferFromSavingsOperation{}, + TypeCancelTransferFromSavings: &CancelTransferFromSavingsOperation{}, + TypeCustomBinary: &CustomBinaryOperation{}, + TypeDeclineVotingRights: &DeclineVotingRightsOperation{}, + TypeResetAccount: &ResetAccountOperation{}, + TypeSetResetAccount: &SetResetAccountOperation{}, + TypeClaimRewardBalance: &ClaimRewardBalanceOperation{}, + TypeDelegateVestingShares: &DelegateVestingSharesOperation{}, + TypeAccountCreateWithDelegation: &AccountCreateWithDelegationOperation{}, + TypeFillConvertRequest: &FillConvertRequestOperation{}, //Virtual Operation + TypeAuthorReward: &AuthorRewardOperation{}, //Virtual Operation + TypeCurationReward: &CurationRewardOperation{}, //Virtual Operation + TypeCommentReward: &CommentRewardOperation{}, //Virtual Operation + TypeLiquidityReward: &LiquidityRewardOperation{}, //Virtual Operation + TypeInterest: &InterestOperation{}, //Virtual Operation + TypeFillVestingWithdraw: &FillVestingWithdrawOperation{}, //Virtual Operation + TypeFillOrder: &FillOrderOperation{}, //Virtual Operation + TypeShutdownWitness: &ShutdownWitnessOperation{}, //Virtual Operation + TypeFillTransferFromSavings: &FillTransferFromSavingsOperation{}, //Virtual Operation + TypeHardfork: &HardforkOperation{}, //Virtual Operation + TypeCommentPayoutUpdate: &CommentPayoutUpdateOperation{}, //Virtual Operation + TypeReturnVestingDelegation: &ReturnVestingDelegationOperation{}, //Virtual Operation + TypeCommentBenefactorReward: &CommentBenefactorRewardOperation{}, //Virtual Operation } // Operation represents an operation stored in a transaction. @@ -59,8 +85,10 @@ type Operation interface { Data() interface{} } +//Operations structure from the set Operation. type Operations []Operation +//UnmarshalJSON unpacking the JSON parameter in the Operations type. func (ops *Operations) UnmarshalJSON(data []byte) error { var tuples []*operationTuple if err := json.Unmarshal(data, &tuples); err != nil { @@ -76,6 +104,7 @@ func (ops *Operations) UnmarshalJSON(data []byte) error { return nil } +//MarshalJSON function for packing the Operations type in JSON. func (ops Operations) MarshalJSON() ([]byte, error) { tuples := make([]*operationTuple, 0, len(ops)) for _, op := range ops { @@ -84,7 +113,7 @@ func (ops Operations) MarshalJSON() ([]byte, error) { Data: op.Data().(Operation), }) } - return json.Marshal(tuples) + return JSONMarshal(tuples) } type operationTuple struct { @@ -92,13 +121,15 @@ type operationTuple struct { Data Operation } +//MarshalJSON function for packing the operationTuple type in JSON. func (op *operationTuple) MarshalJSON() ([]byte, error) { - return json.Marshal([]interface{}{ + return JSONMarshal([]interface{}{ op.Type, op.Data, }) } +//UnmarshalJSON unpacking the JSON parameter in the operationTuple type. func (op *operationTuple) UnmarshalJSON(data []byte) error { // The operation object is [opType, opBody]. raw := make([]*json.RawMessage, 2) @@ -135,3 +166,12 @@ func (op *operationTuple) UnmarshalJSON(data []byte) error { op.Data = opData return nil } + +//JSONMarshal the function of packing with the processing of HTML tags. +func JSONMarshal(t interface{}) ([]byte, error) { + buffer := &bytes.Buffer{} + encoder := json.NewEncoder(buffer) + encoder.SetEscapeHTML(false) + err := encoder.Encode(t) + return buffer.Bytes(), err +} diff --git a/types/operation_account_create.go b/types/operation_account_create.go new file mode 100644 index 0000000..93caaaa --- /dev/null +++ b/types/operation_account_create.go @@ -0,0 +1,42 @@ +package types + +import ( + "github.com/asuleymanov/steem-go/encoding/transaction" +) + +//AccountCreateOperation represents account_create operation data. +type AccountCreateOperation struct { + Fee *Asset `json:"fee"` + Creator string `json:"creator"` + NewAccountName string `json:"new_account_name"` + Owner *Authority `json:"owner"` + Active *Authority `json:"active"` + Posting *Authority `json:"posting"` + MemoKey string `json:"memo_key"` + JSONMetadata string `json:"json_metadata"` +} + +//Type function that defines the type of operation AccountCreateOperation. +func (op *AccountCreateOperation) Type() OpType { + return TypeAccountCreate +} + +//Data returns the operation data AccountCreateOperation. +func (op *AccountCreateOperation) Data() interface{} { + return op +} + +//MarshalTransaction is a function of converting type AccountCreateOperation to bytes. +func (op *AccountCreateOperation) MarshalTransaction(encoder *transaction.Encoder) error { + enc := transaction.NewRollingEncoder(encoder) + enc.EncodeUVarint(uint64(TypeAccountCreate.Code())) + enc.Encode(op.Fee) + enc.EncodeString(op.Creator) + enc.EncodeString(op.NewAccountName) + enc.Encode(op.Owner) + enc.Encode(op.Active) + enc.Encode(op.Posting) + enc.EncodePubKey(op.MemoKey) + enc.Encode(op.JSONMetadata) + return enc.Err() +} diff --git a/types/operation_account_create_with_delegation.go b/types/operation_account_create_with_delegation.go new file mode 100644 index 0000000..f7c3507 --- /dev/null +++ b/types/operation_account_create_with_delegation.go @@ -0,0 +1,47 @@ +package types + +import ( + "github.com/asuleymanov/steem-go/encoding/transaction" +) + +//AccountCreateWithDelegationOperation represents account_create_with_delegation operation data. +type AccountCreateWithDelegationOperation struct { + Fee *Asset `json:"fee"` + Delegation *Asset `json:"delegation"` + Creator string `json:"creator"` + NewAccountName string `json:"new_account_name"` + Owner *Authority `json:"owner"` + Active *Authority `json:"active"` + Posting *Authority `json:"posting"` + MemoKey string `json:"memo_key"` + JSONMetadata string `json:"json_metadata"` + Extensions []interface{} `json:"extensions"` +} + +//Type function that defines the type of operation AccountCreateWithDelegationOperation. +func (op *AccountCreateWithDelegationOperation) Type() OpType { + return TypeAccountCreateWithDelegation +} + +//Data returns the operation data AccountCreateWithDelegationOperation. +func (op *AccountCreateWithDelegationOperation) Data() interface{} { + return op +} + +//MarshalTransaction is a function of converting type AccountCreateWithDelegationOperation to bytes. +func (op *AccountCreateWithDelegationOperation) MarshalTransaction(encoder *transaction.Encoder) error { + enc := transaction.NewRollingEncoder(encoder) + enc.EncodeUVarint(uint64(TypeAccountCreateWithDelegation.Code())) + enc.Encode(op.Fee) + enc.Encode(op.Delegation) + enc.Encode(op.Creator) + enc.Encode(op.NewAccountName) + enc.Encode(op.Owner) + enc.Encode(op.Active) + enc.Encode(op.Posting) + enc.EncodePubKey(op.MemoKey) + enc.Encode(op.JSONMetadata) + //enc.Encode(op.Extensions) + enc.Encode(byte(0)) + return enc.Err() +} diff --git a/types/operation_account_update.go b/types/operation_account_update.go new file mode 100644 index 0000000..480bc3d --- /dev/null +++ b/types/operation_account_update.go @@ -0,0 +1,53 @@ +package types + +import ( + "github.com/asuleymanov/steem-go/encoding/transaction" +) + +//AccountUpdateOperation represents account_update operation data. +type AccountUpdateOperation struct { + Account string `json:"account"` + Owner *Authority `json:"owner,omitempty"` + Active *Authority `json:"active,omitempty"` + Posting *Authority `json:"posting,omitempty"` + MemoKey string `json:"memo_key"` + JSONMetadata string `json:"json_metadata"` +} + +//Type function that defines the type of operation AccountUpdateOperation. +func (op *AccountUpdateOperation) Type() OpType { + return TypeAccountUpdate +} + +//Data returns the operation data AccountUpdateOperation. +func (op *AccountUpdateOperation) Data() interface{} { + return op +} + +//MarshalTransaction is a function of converting type AccountUpdateOperation to bytes. +func (op *AccountUpdateOperation) MarshalTransaction(encoder *transaction.Encoder) error { + enc := transaction.NewRollingEncoder(encoder) + enc.EncodeUVarint(uint64(TypeAccountUpdate.Code())) + enc.EncodeString(op.Account) + if op.Owner != nil { + enc.Encode(byte(1)) + enc.Encode(op.Owner) + } else { + enc.Encode(byte(0)) + } + if op.Active != nil { + enc.Encode(byte(1)) + enc.Encode(op.Active) + } else { + enc.Encode(byte(0)) + } + if op.Posting != nil { + enc.Encode(byte(1)) + enc.Encode(op.Posting) + } else { + enc.Encode(byte(0)) + } + enc.EncodePubKey(op.MemoKey) + enc.Encode(op.JSONMetadata) + return enc.Err() +} diff --git a/types/operation_account_witness_proxy.go b/types/operation_account_witness_proxy.go new file mode 100644 index 0000000..50f9ce3 --- /dev/null +++ b/types/operation_account_witness_proxy.go @@ -0,0 +1,30 @@ +package types + +import ( + "github.com/asuleymanov/steem-go/encoding/transaction" +) + +//AccountWitnessProxyOperation represents account_witness_proxy operation data. +type AccountWitnessProxyOperation struct { + Account string `json:"account"` + Proxy string `json:"proxy"` +} + +//Type function that defines the type of operation AccountWitnessProxyOperation. +func (op *AccountWitnessProxyOperation) Type() OpType { + return TypeAccountWitnessProxy +} + +//Data returns the operation data AccountWitnessProxyOperation. +func (op *AccountWitnessProxyOperation) Data() interface{} { + return op +} + +//MarshalTransaction is a function of converting type AccountWitnessProxyOperation to bytes. +func (op *AccountWitnessProxyOperation) MarshalTransaction(encoder *transaction.Encoder) error { + enc := transaction.NewRollingEncoder(encoder) + enc.EncodeUVarint(uint64(TypeAccountWitnessProxy.Code())) + enc.Encode(op.Account) + enc.Encode(op.Proxy) + return enc.Err() +} diff --git a/types/operation_account_witness_vote.go b/types/operation_account_witness_vote.go new file mode 100644 index 0000000..9c0337b --- /dev/null +++ b/types/operation_account_witness_vote.go @@ -0,0 +1,32 @@ +package types + +import ( + "github.com/asuleymanov/steem-go/encoding/transaction" +) + +//AccountWitnessVoteOperation represents account_witness_vote operation data. +type AccountWitnessVoteOperation struct { + Account string `json:"account"` + Witness string `json:"witness"` + Approve bool `json:"approve"` +} + +//Type function that defines the type of operation AccountWitnessVoteOperation. +func (op *AccountWitnessVoteOperation) Type() OpType { + return TypeAccountWitnessVote +} + +//Data returns the operation data AccountWitnessVoteOperation. +func (op *AccountWitnessVoteOperation) Data() interface{} { + return op +} + +//MarshalTransaction is a function of converting type AccountWitnessVoteOperation to bytes. +func (op *AccountWitnessVoteOperation) MarshalTransaction(encoder *transaction.Encoder) error { + enc := transaction.NewRollingEncoder(encoder) + enc.EncodeUVarint(uint64(TypeAccountWitnessVote.Code())) + enc.Encode(op.Account) + enc.Encode(op.Witness) + enc.EncodeBool(op.Approve) + return enc.Err() +} diff --git a/types/operation_cancel_transfer_from_savings.go b/types/operation_cancel_transfer_from_savings.go new file mode 100644 index 0000000..fc68eb5 --- /dev/null +++ b/types/operation_cancel_transfer_from_savings.go @@ -0,0 +1,30 @@ +package types + +import ( + "github.com/asuleymanov/steem-go/encoding/transaction" +) + +//CancelTransferFromSavingsOperation represents cancel_transfer_from_savings operation data. +type CancelTransferFromSavingsOperation struct { + From string `json:"from"` + RequestID uint32 `json:"request_id"` +} + +//Type function that defines the type of operation CancelTransferFromSavingsOperation. +func (op *CancelTransferFromSavingsOperation) Type() OpType { + return TypeCancelTransferFromSavings +} + +//Data returns the operation data CancelTransferFromSavingsOperation. +func (op *CancelTransferFromSavingsOperation) Data() interface{} { + return op +} + +//MarshalTransaction is a function of converting type CancelTransferFromSavingsOperation to bytes. +func (op *CancelTransferFromSavingsOperation) MarshalTransaction(encoder *transaction.Encoder) error { + enc := transaction.NewRollingEncoder(encoder) + enc.EncodeUVarint(uint64(TypeCancelTransferFromSavings.Code())) + enc.Encode(op.From) + enc.Encode(op.RequestID) + return enc.Err() +} diff --git a/types/operation_change_recovery_account.go b/types/operation_change_recovery_account.go new file mode 100644 index 0000000..96177a8 --- /dev/null +++ b/types/operation_change_recovery_account.go @@ -0,0 +1,33 @@ +package types + +import ( + "github.com/asuleymanov/steem-go/encoding/transaction" +) + +//ChangeRecoveryAccountOperation represents change_recovery_account operation data. +type ChangeRecoveryAccountOperation struct { + AccountToRecover string `json:"account_to_recover"` + NewRecoveryAccount string `json:"new_recovery_account"` + Extensions []interface{} `json:"extensions"` +} + +//Type function that defines the type of operation ChangeRecoveryAccountOperation. +func (op *ChangeRecoveryAccountOperation) Type() OpType { + return TypeChangeRecoveryAccount +} + +//Data returns the operation data ChangeRecoveryAccountOperation. +func (op *ChangeRecoveryAccountOperation) Data() interface{} { + return op +} + +//MarshalTransaction is a function of converting type ChangeRecoveryAccountOperation to bytes. +func (op *ChangeRecoveryAccountOperation) MarshalTransaction(encoder *transaction.Encoder) error { + enc := transaction.NewRollingEncoder(encoder) + enc.EncodeUVarint(uint64(TypeChangeRecoveryAccount.Code())) + enc.Encode(op.AccountToRecover) + enc.Encode(op.NewRecoveryAccount) + //enc.Encode(op.Extensions) + enc.Encode(byte(0)) + return enc.Err() +} diff --git a/types/operation_claim_account.go b/types/operation_claim_account.go new file mode 100644 index 0000000..6a28dda --- /dev/null +++ b/types/operation_claim_account.go @@ -0,0 +1,33 @@ +package types + +import ( + "github.com/asuleymanov/steem-go/encoding/transaction" +) + +//ClaimAccountOperation represents claim_account operation data. +type ClaimAccountOperation struct { + Creator string `json:"creator"` + Fee *Asset `json:"fee"` + Extensions []interface{} `json:"extensions"` +} + +//Type function that defines the type of operation ClaimAccountOperation. +func (op *ClaimAccountOperation) Type() OpType { + return TypeClaimAccount +} + +//Data returns the operation data ClaimAccountOperation. +func (op *ClaimAccountOperation) Data() interface{} { + return op +} + +//MarshalTransaction is a function of converting type ClaimAccountOperation to bytes. +func (op *ClaimAccountOperation) MarshalTransaction(encoder *transaction.Encoder) error { + enc := transaction.NewRollingEncoder(encoder) + enc.EncodeUVarint(uint64(TypeClaimAccount.Code())) + enc.Encode(op.Creator) + enc.Encode(op.Fee) + //enc.Encode(op.Extensions) + enc.Encode(byte(0)) + return enc.Err() +} diff --git a/types/operation_claim_reward_balance.go b/types/operation_claim_reward_balance.go new file mode 100644 index 0000000..2a0e7b3 --- /dev/null +++ b/types/operation_claim_reward_balance.go @@ -0,0 +1,34 @@ +package types + +import ( + "github.com/asuleymanov/steem-go/encoding/transaction" +) + +//ClaimRewardBalanceOperation represents claim_reward_balance operation data. +type ClaimRewardBalanceOperation struct { + Account string `json:"account"` + RewardSteem *Asset `json:"reward_steem"` + RewardSbd *Asset `json:"reward_sbd"` + RewardVests *Asset `json:"reward_vests"` +} + +//Type function that defines the type of operation ClaimRewardBalanceOperation. +func (op *ClaimRewardBalanceOperation) Type() OpType { + return TypeClaimRewardBalance +} + +//Data returns the operation data ClaimRewardBalanceOperation. +func (op *ClaimRewardBalanceOperation) Data() interface{} { + return op +} + +//MarshalTransaction is a function of converting type ClaimRewardBalanceOperation to bytes. +func (op *ClaimRewardBalanceOperation) MarshalTransaction(encoder *transaction.Encoder) error { + enc := transaction.NewRollingEncoder(encoder) + enc.EncodeUVarint(uint64(TypeClaimRewardBalance.Code())) + enc.Encode(op.Account) + enc.Encode(op.RewardSteem) + enc.Encode(op.RewardSbd) + enc.Encode(op.RewardVests) + return enc.Err() +} diff --git a/types/operation_comment.go b/types/operation_comment.go new file mode 100644 index 0000000..48fed6e --- /dev/null +++ b/types/operation_comment.go @@ -0,0 +1,49 @@ +package types + +import ( + "github.com/asuleymanov/steem-go/encoding/transaction" +) + +//CommentOperation represents comment operation data. +type CommentOperation struct { + ParentAuthor string `json:"parent_author"` + ParentPermlink string `json:"parent_permlink"` + Author string `json:"author"` + Permlink string `json:"permlink"` + Title string `json:"title"` + Body string `json:"body"` + JSONMetadata string `json:"json_metadata"` +} + +//Type function that defines the type of operation CommentOperation. +func (op *CommentOperation) Type() OpType { + return TypeComment +} + +//Data returns the operation data CommentOperation. +func (op *CommentOperation) Data() interface{} { + return op +} + +//IsStory function specifies the type of publication. +func (op *CommentOperation) IsStory() bool { + return op.ParentAuthor == "" +} + +//MarshalTransaction is a function of converting type CommentOperation to bytes. +func (op *CommentOperation) MarshalTransaction(encoder *transaction.Encoder) error { + enc := transaction.NewRollingEncoder(encoder) + enc.EncodeUVarint(uint64(TypeComment.Code())) + if !op.IsStory() { + enc.Encode(op.ParentAuthor) + } else { + enc.Encode(byte(0)) + } + enc.Encode(op.ParentPermlink) + enc.Encode(op.Author) + enc.Encode(op.Permlink) + enc.Encode(op.Title) + enc.Encode(op.Body) + enc.Encode(op.JSONMetadata) + return enc.Err() +} diff --git a/types/operation_comment_options.go b/types/operation_comment_options.go new file mode 100644 index 0000000..4f454a5 --- /dev/null +++ b/types/operation_comment_options.go @@ -0,0 +1,60 @@ +package types + +import ( + "encoding/json" + + "github.com/asuleymanov/steem-go/encoding/transaction" +) + +//CommentOptionsOperation represents comment_options operation data. +/* +Extensions: +CommentPayoutBeneficiaries +AllowedVoteAssets +*/ +type CommentOptionsOperation struct { + Author string `json:"author"` + Permlink string `json:"permlink"` + MaxAcceptedPayout *Asset `json:"max_accepted_payout"` + PercentSteemDollars uint16 `json:"percent_steem_dollars"` + AllowVotes bool `json:"allow_votes"` + AllowCurationRewards bool `json:"allow_curation_rewards"` + Extensions []interface{} `json:"extensions"` +} + +//Type function that defines the type of operation CommentOptionsOperation. +func (op *CommentOptionsOperation) Type() OpType { + return TypeCommentOptions +} + +//Data returns the operation data CommentOptionsOperation. +func (op *CommentOptionsOperation) Data() interface{} { + return op +} + +//MarshalTransaction is a function of converting type CommentOptionsOperation to bytes. +func (op *CommentOptionsOperation) MarshalTransaction(encoder *transaction.Encoder) error { + enc := transaction.NewRollingEncoder(encoder) + enc.EncodeUVarint(uint64(TypeCommentOptions.Code())) + enc.Encode(op.Author) + enc.Encode(op.Permlink) + enc.Encode(op.MaxAcceptedPayout) + enc.Encode(op.PercentSteemDollars) + enc.EncodeBool(op.AllowVotes) + enc.EncodeBool(op.AllowCurationRewards) + if len(op.Extensions) > 0 { + //Parse Beneficiaries + z, _ := json.Marshal(op.Extensions[0]) + var l []interface{} + _ = json.Unmarshal(z, &l) + z1, _ := json.Marshal(l[1]) + var d CommentPayoutBeneficiaries + _ = json.Unmarshal(z1, &d) + + enc.Encode(byte(1)) + enc.Encode(d) + } else { + enc.Encode(byte(0)) + } + return enc.Err() +} diff --git a/types/operation_convert.go b/types/operation_convert.go new file mode 100644 index 0000000..f1e527b --- /dev/null +++ b/types/operation_convert.go @@ -0,0 +1,32 @@ +package types + +import ( + "github.com/asuleymanov/steem-go/encoding/transaction" +) + +//ConvertOperation represents convert operation data. +type ConvertOperation struct { + Owner string `json:"owner"` + RequestID uint32 `json:"requestid"` + Amount *Asset `json:"amount"` +} + +//Type function that defines the type of operation ConvertOperation. +func (op *ConvertOperation) Type() OpType { + return TypeConvert +} + +//Data returns the operation data ConvertOperation. +func (op *ConvertOperation) Data() interface{} { + return op +} + +//MarshalTransaction is a function of converting type ConvertOperation to bytes. +func (op *ConvertOperation) MarshalTransaction(encoder *transaction.Encoder) error { + enc := transaction.NewRollingEncoder(encoder) + enc.EncodeUVarint(uint64(TypeConvert.Code())) + enc.Encode(op.Owner) + enc.Encode(op.RequestID) + enc.Encode(op.Amount) + return enc.Err() +} diff --git a/types/operation_create_claimed_account.go b/types/operation_create_claimed_account.go new file mode 100644 index 0000000..0bd982a --- /dev/null +++ b/types/operation_create_claimed_account.go @@ -0,0 +1,43 @@ +package types + +import ( + "github.com/asuleymanov/steem-go/encoding/transaction" +) + +//CreateClaimedAccountOperation represents create_claimed_account operation data. +type CreateClaimedAccountOperation struct { + Creator string `json:"creator"` + NewAccountName string `json:"new_account_name"` + Owner *Authority `json:"owner"` + Active *Authority `json:"active"` + Posting *Authority `json:"posting"` + MemoKey string `json:"memo_key"` + JsonMetadata string `json:"json_metadata"` + Extensions []interface{} `json:"extensions"` +} + +//Type function that defines the type of operation CreateClaimedAccountOperation. +func (op *CreateClaimedAccountOperation) Type() OpType { + return TypeCreateClaimedAccount +} + +//Data returns the operation data CreateClaimedAccountOperation. +func (op *CreateClaimedAccountOperation) Data() interface{} { + return op +} + +//MarshalTransaction is a function of converting type CreateClaimedAccountOperation to bytes. +func (op *CreateClaimedAccountOperation) MarshalTransaction(encoder *transaction.Encoder) error { + enc := transaction.NewRollingEncoder(encoder) + enc.EncodeUVarint(uint64(TypeCreateClaimedAccount.Code())) + enc.Encode(op.Creator) + enc.Encode(op.NewAccountName) + enc.Encode(op.Owner) + enc.Encode(op.Active) + enc.Encode(op.Posting) + enc.Encode(op.MemoKey) + enc.Encode(op.JsonMetadata) + //enc.Encode(op.Extensions) + enc.Encode(byte(0)) + return enc.Err() +} diff --git a/types/operation_custom.go b/types/operation_custom.go new file mode 100644 index 0000000..54d26cd --- /dev/null +++ b/types/operation_custom.go @@ -0,0 +1,32 @@ +package types + +import ( + "github.com/asuleymanov/steem-go/encoding/transaction" +) + +//CustomOperation represents custom operation data. +type CustomOperation struct { + RequiredAuths []string `json:"required_auths"` + ID uint16 `json:"id"` + Datas []byte `json:"data"` +} + +//Type function that defines the type of operation CustomOperation. +func (op *CustomOperation) Type() OpType { + return TypeCustom +} + +//Data returns the operation data CustomOperation. +func (op *CustomOperation) Data() interface{} { + return op +} + +//MarshalTransaction is a function of converting type CustomOperation to bytes. +func (op *CustomOperation) MarshalTransaction(encoder *transaction.Encoder) error { + enc := transaction.NewRollingEncoder(encoder) + enc.EncodeUVarint(uint64(TypeCustom.Code())) + enc.Encode(op.RequiredAuths) + enc.Encode(op.ID) + enc.Encode(op.Datas) + return enc.Err() +} diff --git a/types/operation_custom_binary.go b/types/operation_custom_binary.go new file mode 100644 index 0000000..1235652 --- /dev/null +++ b/types/operation_custom_binary.go @@ -0,0 +1,21 @@ +package types + +//CustomBinaryOperation represents custom_binary operation data. +type CustomBinaryOperation struct { + RequiredOwnerAuths []string `json:"required_owner_auths"` + RequiredActiveAuths []string `json:"required_active_auths"` + RequiredPostingAuths []string `json:"required_posting_auths"` + RequiredAuths []Authority `json:"required_auths"` + ID string `json:"id"` + Datas []byte `json:"data"` +} + +//Type function that defines the type of operation CustomBinaryOperation. +func (op *CustomBinaryOperation) Type() OpType { + return TypeCustomBinary +} + +//Data returns the operation data CustomBinaryOperation. +func (op *CustomBinaryOperation) Data() interface{} { + return op +} diff --git a/types/operation_custom_json.go b/types/operation_custom_json.go index 43fb119..097d475 100644 --- a/types/operation_custom_json.go +++ b/types/operation_custom_json.go @@ -1,23 +1,28 @@ package types import ( - // Stdlib "bytes" "encoding/json" "io" "reflect" "strings" - // Vendor + "github.com/asuleymanov/steem-go/encoding/transaction" "github.com/pkg/errors" ) -const ( - TypeFollow = "follow" +var ( + TypeFollow = "follow" + TypeReblog = "reblog" + TypeLogin = "login" + TypePrivateMessage = "private_message" ) var customJSONDataObjects = map[string]interface{}{ - TypeFollow: &FollowOperation{}, + TypeFollow: &FollowOperation{}, + TypeReblog: &ReblogOperation{}, + TypeLogin: &LoginOperation{}, + TypePrivateMessage: &PrivateMessageOperation{}, } // FC_REFLECT( steemit::chain::custom_json_operation, @@ -26,7 +31,7 @@ var customJSONDataObjects = map[string]interface{}{ // (id) // (json) ) -// CustomJSONOperation represents custom_json operation data. +//CustomJSONOperation represents custom_json operation data. type CustomJSONOperation struct { RequiredAuths []string `json:"required_auths"` RequiredPostingAuths []string `json:"required_posting_auths"` @@ -34,14 +39,17 @@ type CustomJSONOperation struct { JSON string `json:"json"` } +//Type function that defines the type of operation. func (op *CustomJSONOperation) Type() OpType { return TypeCustomJSON } +//Data returns the operation data. func (op *CustomJSONOperation) Data() interface{} { return op } +//UnmarshalData unpacking the JSON parameter in the CustomJSONOperation type. func (op *CustomJSONOperation) UnmarshalData() (interface{}, error) { // Get the corresponding data object template. template, ok := customJSONDataObjects[op.ID] @@ -58,10 +66,9 @@ func (op *CustomJSONOperation) UnmarshalData() (interface{}, error) { if op.JSON[0] == '[' { rawTuple := make([]json.RawMessage, 2) if err := json.NewDecoder(strings.NewReader(op.JSON)).Decode(&rawTuple); err != nil { - return nil, errors.Wrapf(err, - "failed to unmarshal CustomJSONOperation.JSON: \n%v", op.JSON) + return nil, errors.Wrapf(err, "failed to unmarshal CustomJSONOperation.JSON: \n%v", op.JSON) } - if rawTuple[1] == nil { + if len(rawTuple) < 2 || rawTuple[1] == nil { return nil, errors.Errorf("invalid CustomJSONOperation.JSON: \n%v", op.JSON) } bodyReader = bytes.NewReader([]byte(rawTuple[1])) @@ -71,9 +78,77 @@ func (op *CustomJSONOperation) UnmarshalData() (interface{}, error) { // Unmarshal into the new object instance. if err := json.NewDecoder(bodyReader).Decode(opData); err != nil { - return nil, errors.Wrapf(err, - "failed to unmarshal CustomJSONOperation.JSON: \n%v", op.JSON) + return nil, errors.Wrapf(err, "failed to unmarshal CustomJSONOperation.JSON: \n%v", op.JSON) } return opData, nil } + +//MarshalTransaction is a function of converting type CustomJSONOperation to bytes. +func (op *CustomJSONOperation) MarshalTransaction(encoder *transaction.Encoder) error { + enc := transaction.NewRollingEncoder(encoder) + enc.EncodeUVarint(uint64(TypeCustomJSON.Code())) + enc.EncodeArrString(op.RequiredAuths) + enc.EncodeArrString(op.RequiredPostingAuths) + enc.Encode(op.ID) + enc.Encode(op.JSON) + return enc.Err() +} + +//FollowOperation the structure for the operation CustomJSONOperation. +type FollowOperation struct { + Follower string `json:"follower"` + Following string `json:"following"` + What []string `json:"what"` +} + +//ReblogOperation the structure for the operation CustomJSONOperation. +type ReblogOperation struct { + Account string `json:"account"` + Author string `json:"author"` + Permlink string `json:"permlink"` +} + +//LoginOperation the structure for the operation CustomJSONOperation. +type LoginOperation struct { + Account string `json:"account"` +} + +//PrivateMessageOperation the structure for the operation CustomJSONOperation. +type PrivateMessageOperation struct { + From string `json:"from"` + To string `json:"to"` + FromMemoKey string `json:"from_memo_key"` + ToMemoKey string `json:"to_memo_key"` + SentTime uint64 `json:"sent_time"` + Checksum uint32 `json:"checksum"` + EncryptedMessage string `json:"encrypted_message"` +} + +//MarshalCustomJSON generate a row from the structure fields. +func MarshalCustomJSON(v interface{}) (string, error) { + var tmp []interface{} + + typeInterface := reflect.TypeOf(v).Name() + switch typeInterface { + case "FollowOperation": + tmp = append(tmp, TypeFollow) + case "ReblogOperation": + tmp = append(tmp, TypeReblog) + case "LoginOperation": + tmp = append(tmp, TypeLogin) + case "PrivateMessageOperation": + tmp = append(tmp, TypePrivateMessage) + default: + return "", errors.New("Unknown type") + } + + tmp = append(tmp, v) + + b, err := json.Marshal(tmp) + if err != nil { + return "", err + } + + return string(b), nil //strings.Replace(string(b), "\"", "\\\"", -1), nil +} diff --git a/types/operation_custom_json_follow.go b/types/operation_custom_json_follow.go deleted file mode 100644 index 6a865d5..0000000 --- a/types/operation_custom_json_follow.go +++ /dev/null @@ -1,7 +0,0 @@ -package types - -type FollowOperation struct { - Follower string `json:"follower"` - Following string `json:"following"` - What []string `json:"what"` -} diff --git a/types/operation_decline_voting_rights.go b/types/operation_decline_voting_rights.go new file mode 100644 index 0000000..65e1637 --- /dev/null +++ b/types/operation_decline_voting_rights.go @@ -0,0 +1,30 @@ +package types + +import ( + "github.com/asuleymanov/steem-go/encoding/transaction" +) + +//DeclineVotingRightsOperation represents decline_voting_rights operation data. +type DeclineVotingRightsOperation struct { + Account string `json:"account"` + Decline bool `json:"decline"` +} + +//Type function that defines the type of operation DeclineVotingRightsOperation. +func (op *DeclineVotingRightsOperation) Type() OpType { + return TypeDeclineVotingRights +} + +//Data returns the operation data DeclineVotingRightsOperation. +func (op *DeclineVotingRightsOperation) Data() interface{} { + return op +} + +//MarshalTransaction is a function of converting type DeclineVotingRightsOperation to bytes. +func (op *DeclineVotingRightsOperation) MarshalTransaction(encoder *transaction.Encoder) error { + enc := transaction.NewRollingEncoder(encoder) + enc.EncodeUVarint(uint64(TypeDeclineVotingRights.Code())) + enc.Encode(op.Account) + enc.EncodeBool(op.Decline) + return enc.Err() +} diff --git a/types/operation_delegate_vesting_shares.go b/types/operation_delegate_vesting_shares.go new file mode 100644 index 0000000..d665ca1 --- /dev/null +++ b/types/operation_delegate_vesting_shares.go @@ -0,0 +1,32 @@ +package types + +import ( + "github.com/asuleymanov/steem-go/encoding/transaction" +) + +//DelegateVestingSharesOperation represents delegate_vesting_shares operation data. +type DelegateVestingSharesOperation struct { + Delegator string `json:"delegator"` + Delegatee string `json:"delegatee"` + VestingShares *Asset `json:"vesting_shares"` +} + +//Type function that defines the type of operation DelegateVestingSharesOperation. +func (op *DelegateVestingSharesOperation) Type() OpType { + return TypeDelegateVestingShares +} + +//Data returns the operation data DelegateVestingSharesOperation. +func (op *DelegateVestingSharesOperation) Data() interface{} { + return op +} + +//MarshalTransaction is a function of converting type DelegateVestingSharesOperation to bytes. +func (op *DelegateVestingSharesOperation) MarshalTransaction(encoder *transaction.Encoder) error { + enc := transaction.NewRollingEncoder(encoder) + enc.EncodeUVarint(uint64(TypeDelegateVestingShares.Code())) + enc.Encode(op.Delegator) + enc.Encode(op.Delegatee) + enc.Encode(op.VestingShares) + return enc.Err() +} diff --git a/types/operation_delete_comment.go b/types/operation_delete_comment.go new file mode 100644 index 0000000..6c3b925 --- /dev/null +++ b/types/operation_delete_comment.go @@ -0,0 +1,30 @@ +package types + +import ( + "github.com/asuleymanov/steem-go/encoding/transaction" +) + +//DeleteCommentOperation represents delete_comment operation data. +type DeleteCommentOperation struct { + Author string `json:"author"` + Permlink string `json:"permlink"` +} + +//Type function that defines the type of operation DeleteCommentOperation. +func (op *DeleteCommentOperation) Type() OpType { + return TypeDeleteComment +} + +//Data returns the operation data DeleteCommentOperation. +func (op *DeleteCommentOperation) Data() interface{} { + return op +} + +//MarshalTransaction is a function of converting type DeleteCommentOperation to bytes. +func (op *DeleteCommentOperation) MarshalTransaction(encoder *transaction.Encoder) error { + enc := transaction.NewRollingEncoder(encoder) + enc.EncodeUVarint(uint64(TypeDeleteComment.Code())) + enc.Encode(op.Author) + enc.Encode(op.Permlink) + return enc.Err() +} diff --git a/types/operation_escrow_approve.go b/types/operation_escrow_approve.go new file mode 100644 index 0000000..7e2e832 --- /dev/null +++ b/types/operation_escrow_approve.go @@ -0,0 +1,38 @@ +package types + +import ( + "github.com/asuleymanov/steem-go/encoding/transaction" +) + +//EscrowApproveOperation represents escrow_approve operation data. +type EscrowApproveOperation struct { + From string `json:"from"` + To string `json:"to"` + Agent string `json:"agent"` + Who string `json:"who"` + EscrowID uint32 `json:"escrow_id"` + Approve bool `json:"approve"` +} + +//Type function that defines the type of operation EscrowApproveOperation. +func (op *EscrowApproveOperation) Type() OpType { + return TypeEscrowApprove +} + +//Data returns the operation data EscrowApproveOperation. +func (op *EscrowApproveOperation) Data() interface{} { + return op +} + +//MarshalTransaction is a function of converting type EscrowApproveOperation to bytes. +func (op *EscrowApproveOperation) MarshalTransaction(encoder *transaction.Encoder) error { + enc := transaction.NewRollingEncoder(encoder) + enc.EncodeUVarint(uint64(TypeEscrowApprove.Code())) + enc.Encode(op.From) + enc.Encode(op.To) + enc.Encode(op.Agent) + enc.Encode(op.Who) + enc.Encode(op.EscrowID) + enc.EncodeBool(op.Approve) + return enc.Err() +} diff --git a/types/operation_escrow_dispute.go b/types/operation_escrow_dispute.go new file mode 100644 index 0000000..61db112 --- /dev/null +++ b/types/operation_escrow_dispute.go @@ -0,0 +1,36 @@ +package types + +import ( + "github.com/asuleymanov/steem-go/encoding/transaction" +) + +//EscrowDisputeOperation represents escrow_dispute operation data. +type EscrowDisputeOperation struct { + From string `json:"from"` + To string `json:"to"` + Agent string `json:"agent"` + Who string `json:"who"` + EscrowID uint32 `json:"escrow_id"` +} + +//Type function that defines the type of operation EscrowDisputeOperation. +func (op *EscrowDisputeOperation) Type() OpType { + return TypeEscrowDispute +} + +//Data returns the operation data EscrowDisputeOperation. +func (op *EscrowDisputeOperation) Data() interface{} { + return op +} + +//MarshalTransaction is a function of converting type EscrowDisputeOperation to bytes. +func (op *EscrowDisputeOperation) MarshalTransaction(encoder *transaction.Encoder) error { + enc := transaction.NewRollingEncoder(encoder) + enc.EncodeUVarint(uint64(TypeEscrowDispute.Code())) + enc.Encode(op.From) + enc.Encode(op.To) + enc.Encode(op.Agent) + enc.Encode(op.Who) + enc.Encode(op.EscrowID) + return enc.Err() +} diff --git a/types/operation_escrow_release.go b/types/operation_escrow_release.go new file mode 100644 index 0000000..a188fd2 --- /dev/null +++ b/types/operation_escrow_release.go @@ -0,0 +1,42 @@ +package types + +import ( + "github.com/asuleymanov/steem-go/encoding/transaction" +) + +//EscrowReleaseOperation represents escrow_release operation data. +type EscrowReleaseOperation struct { + From string `json:"from"` + To string `json:"to"` + Agent string `json:"agent"` + Who string `json:"who"` + Receiver string `json:"receiver"` + EscrowID uint32 `json:"escrow_id"` + SbdAmount *Asset `json:"sbd_amount"` + SteemAmount *Asset `json:"steem_amount"` +} + +//Type function that defines the type of operation EscrowReleaseOperation. +func (op *EscrowReleaseOperation) Type() OpType { + return TypeEscrowRelease +} + +//Data returns the operation data EscrowReleaseOperation. +func (op *EscrowReleaseOperation) Data() interface{} { + return op +} + +//MarshalTransaction is a function of converting type EscrowReleaseOperation to bytes. +func (op *EscrowReleaseOperation) MarshalTransaction(encoder *transaction.Encoder) error { + enc := transaction.NewRollingEncoder(encoder) + enc.EncodeUVarint(uint64(TypeEscrowRelease.Code())) + enc.Encode(op.From) + enc.Encode(op.To) + enc.Encode(op.Agent) + enc.Encode(op.Who) + enc.Encode(op.Receiver) + enc.Encode(op.EscrowID) + enc.Encode(op.SbdAmount) + enc.Encode(op.SteemAmount) + return enc.Err() +} diff --git a/types/operation_escrow_transfer.go b/types/operation_escrow_transfer.go new file mode 100644 index 0000000..981cd1a --- /dev/null +++ b/types/operation_escrow_transfer.go @@ -0,0 +1,46 @@ +package types + +import ( + "github.com/asuleymanov/steem-go/encoding/transaction" +) + +//EscrowTransferOperation represents escrow_transfer operation data. +type EscrowTransferOperation struct { + From string `json:"from"` + To string `json:"to"` + Agent string `json:"agent"` + EscrowID uint32 `json:"escrow_id"` + SbdAmount *Asset `json:"sbd_amount"` + SteemAmount *Asset `json:"steem_amount"` + Fee *Asset `json:"fee"` + RatificationDeadline *Time `json:"ratification_deadline"` + EscrowExpiration *Time `json:"escrow_expiration"` + JSONMeta string `json:"json_meta"` +} + +//Type function that defines the type of operation EscrowTransferOperation. +func (op *EscrowTransferOperation) Type() OpType { + return TypeEscrowTransfer +} + +//Data returns the operation data EscrowTransferOperation. +func (op *EscrowTransferOperation) Data() interface{} { + return op +} + +//MarshalTransaction is a function of converting type EscrowTransferOperation to bytes. +func (op *EscrowTransferOperation) MarshalTransaction(encoder *transaction.Encoder) error { + enc := transaction.NewRollingEncoder(encoder) + enc.EncodeUVarint(uint64(TypeEscrowTransfer.Code())) + enc.Encode(op.From) + enc.Encode(op.To) + enc.Encode(op.Agent) + enc.Encode(op.EscrowID) + enc.Encode(op.SbdAmount) + enc.Encode(op.SteemAmount) + enc.Encode(op.Fee) + enc.Encode(op.RatificationDeadline) + enc.Encode(op.EscrowExpiration) + enc.Encode(op.JSONMeta) + return enc.Err() +} diff --git a/types/operation_feed_publish.go b/types/operation_feed_publish.go new file mode 100644 index 0000000..311e76c --- /dev/null +++ b/types/operation_feed_publish.go @@ -0,0 +1,30 @@ +package types + +import ( + "github.com/asuleymanov/steem-go/encoding/transaction" +) + +//FeedPublishOperation represents feed_publish operation data. +type FeedPublishOperation struct { + Publisher string `json:"publisher"` + ExchangeRate *ExchRate `json:"exchange_rate"` +} + +//Type function that defines the type of operation FeedPublishOperation. +func (op *FeedPublishOperation) Type() OpType { + return TypeFeedPublish +} + +//Data returns the operation data FeedPublishOperation. +func (op *FeedPublishOperation) Data() interface{} { + return op +} + +//MarshalTransaction is a function of converting type FeedPublishOperation to bytes. +func (op *FeedPublishOperation) MarshalTransaction(encoder *transaction.Encoder) error { + enc := transaction.NewRollingEncoder(encoder) + enc.EncodeUVarint(uint64(TypeFeedPublish.Code())) + enc.Encode(op.Publisher) + enc.Encode(op.ExchangeRate) + return enc.Err() +} diff --git a/types/operation_limit_order_cancel.go b/types/operation_limit_order_cancel.go new file mode 100644 index 0000000..0666bd1 --- /dev/null +++ b/types/operation_limit_order_cancel.go @@ -0,0 +1,30 @@ +package types + +import ( + "github.com/asuleymanov/steem-go/encoding/transaction" +) + +//LimitOrderCancelOperation represents limit_order_cancel operation data. +type LimitOrderCancelOperation struct { + Owner string `json:"owner"` + OrderID uint32 `json:"orderid"` +} + +//Type function that defines the type of operation LimitOrderCancelOperation. +func (op *LimitOrderCancelOperation) Type() OpType { + return TypeLimitOrderCancel +} + +//Data returns the operation data LimitOrderCancelOperation. +func (op *LimitOrderCancelOperation) Data() interface{} { + return op +} + +//MarshalTransaction is a function of converting type LimitOrderCancelOperation to bytes. +func (op *LimitOrderCancelOperation) MarshalTransaction(encoder *transaction.Encoder) error { + enc := transaction.NewRollingEncoder(encoder) + enc.EncodeUVarint(uint64(TypeLimitOrderCancel.Code())) + enc.Encode(op.Owner) + enc.Encode(op.OrderID) + return enc.Err() +} diff --git a/types/operation_limit_order_create.go b/types/operation_limit_order_create.go new file mode 100644 index 0000000..c7f7a2f --- /dev/null +++ b/types/operation_limit_order_create.go @@ -0,0 +1,38 @@ +package types + +import ( + "github.com/asuleymanov/steem-go/encoding/transaction" +) + +//LimitOrderCreateOperation represents limit_order_create operation data. +type LimitOrderCreateOperation struct { + Owner string `json:"owner"` + OrderID uint32 `json:"orderid"` + AmountToSell *Asset `json:"amount_to_sell"` + MinToReceive *Asset `json:"min_to_receive"` + FillOrKill bool `json:"fill_or_kill"` + Expiration *Time `json:"expiration"` +} + +//Type function that defines the type of operation LimitOrderCreateOperation. +func (op *LimitOrderCreateOperation) Type() OpType { + return TypeLimitOrderCreate +} + +//Data returns the operation data LimitOrderCreateOperation. +func (op *LimitOrderCreateOperation) Data() interface{} { + return op +} + +//MarshalTransaction is a function of converting type LimitOrderCreateOperation to bytes. +func (op *LimitOrderCreateOperation) MarshalTransaction(encoder *transaction.Encoder) error { + enc := transaction.NewRollingEncoder(encoder) + enc.EncodeUVarint(uint64(TypeLimitOrderCreate.Code())) + enc.Encode(op.Owner) + enc.Encode(op.OrderID) + enc.Encode(op.AmountToSell) + enc.Encode(op.MinToReceive) + enc.EncodeBool(op.FillOrKill) + enc.Encode(op.Expiration) + return enc.Err() +} diff --git a/types/operation_limit_order_create2.go b/types/operation_limit_order_create2.go new file mode 100644 index 0000000..8a78be1 --- /dev/null +++ b/types/operation_limit_order_create2.go @@ -0,0 +1,38 @@ +package types + +import ( + "github.com/asuleymanov/steem-go/encoding/transaction" +) + +//LimitOrderCreate2Operation represents limit_order_create2 operation data. +type LimitOrderCreate2Operation struct { + Owner string `json:"owner"` + OrderID uint32 `json:"orderid"` + AmountToSell *Asset `json:"amount_to_sell"` + FillOrKill bool `json:"fill_or_kill"` + ExchangeRate *ExchRate `json:"exchange_rate"` + Expiration *Time `json:"expiration"` +} + +//Type function that defines the type of operation LimitOrderCreate2Operation. +func (op *LimitOrderCreate2Operation) Type() OpType { + return TypeLimitOrderCreate2 +} + +//Data returns the operation data LimitOrderCreate2Operation. +func (op *LimitOrderCreate2Operation) Data() interface{} { + return op +} + +//MarshalTransaction is a function of converting type LimitOrderCreate2Operation to bytes. +func (op *LimitOrderCreate2Operation) MarshalTransaction(encoder *transaction.Encoder) error { + enc := transaction.NewRollingEncoder(encoder) + enc.EncodeUVarint(uint64(TypeLimitOrderCreate2.Code())) + enc.Encode(op.Owner) + enc.Encode(op.OrderID) + enc.Encode(op.AmountToSell) + enc.EncodeBool(op.FillOrKill) + enc.Encode(op.ExchangeRate) + enc.Encode(op.Expiration) + return enc.Err() +} diff --git a/types/operation_object.go b/types/operation_object.go index d3cfc47..1a2becd 100644 --- a/types/operation_object.go +++ b/types/operation_object.go @@ -4,11 +4,13 @@ import ( "encoding/json" ) +//OperationObject type from parameter JSON type OperationObject struct { BlockNumber uint32 `json:"block"` TransactionID string `json:"trx_id"` TransactionInBlock uint32 `json:"trx_in_block"` Operation Operation `json:"op"` + OperationType OpType `json:"-"` OperationInTransaction uint16 `json:"op_in_trx"` VirtualOperation uint64 `json:"virtual_op"` Timestamp *Time `json:"timestamp"` @@ -24,6 +26,7 @@ type rawOperationObject struct { Timestamp *Time `json:"timestamp"` } +//UnmarshalJSON unpacking the JSON parameter in the OperationObject type. func (op *OperationObject) UnmarshalJSON(p []byte) error { var raw rawOperationObject if err := json.Unmarshal(p, &raw); err != nil { @@ -34,14 +37,16 @@ func (op *OperationObject) UnmarshalJSON(p []byte) error { op.TransactionID = raw.TransactionID op.TransactionInBlock = raw.TransactionInBlock op.Operation = raw.Operation.Data + op.OperationType = raw.Operation.Type op.OperationInTransaction = raw.OperationInTransaction op.VirtualOperation = raw.VirtualOperation op.Timestamp = raw.Timestamp return nil } +//MarshalJSON function for packing the OperationObject type in JSON. func (op *OperationObject) MarshalJSON() ([]byte, error) { - return json.Marshal(&rawOperationObject{ + return JSONMarshal(&rawOperationObject{ BlockNumber: op.BlockNumber, TransactionID: op.TransactionID, TransactionInBlock: op.TransactionInBlock, diff --git a/types/operation_pow.go b/types/operation_pow.go new file mode 100644 index 0000000..9b05d0a --- /dev/null +++ b/types/operation_pow.go @@ -0,0 +1,20 @@ +package types + +//POWOperation represents pow operation data. +type POWOperation struct { + WorkerAccount string `json:"worker_account"` + BlockID string `json:"block_id"` + Nonce *Int `json:"nonce"` + Work *POW `json:"work"` + Props *ChainProperties `json:"props"` +} + +//Type function that defines the type of operation POWOperation. +func (op *POWOperation) Type() OpType { + return TypePOW +} + +//Data returns the operation data POWOperation. +func (op *POWOperation) Data() interface{} { + return op +} diff --git a/types/operation_pow2.go b/types/operation_pow2.go new file mode 100644 index 0000000..5ced9c2 --- /dev/null +++ b/types/operation_pow2.go @@ -0,0 +1,17 @@ +package types + +//POW2Operation represents pow2 operation data. +type POW2Operation struct { + Input *POW2Input `json:"input"` + PowSummary uint32 `json:"pow_summary"` +} + +//Type function that defines the type of operation POW2Operation. +func (op *POW2Operation) Type() OpType { + return TypePOW2 +} + +//Data returns the operation data POW2Operation. +func (op *POW2Operation) Data() interface{} { + return op +} diff --git a/types/operation_recover_account.go b/types/operation_recover_account.go new file mode 100644 index 0000000..d0e8b05 --- /dev/null +++ b/types/operation_recover_account.go @@ -0,0 +1,35 @@ +package types + +import ( + "github.com/asuleymanov/steem-go/encoding/transaction" +) + +//RecoverAccountOperation represents recover_account operation data. +type RecoverAccountOperation struct { + AccountToRecover string `json:"account_to_recover"` + NewOwnerAuthority *Authority `json:"new_owner_authority"` + RecentOwnerAuthority *Authority `json:"recent_owner_authority"` + Extensions []interface{} `json:"extensions"` +} + +//Type function that defines the type of operation RecoverAccountOperation. +func (op *RecoverAccountOperation) Type() OpType { + return TypeRecoverAccount +} + +//Data returns the operation data RecoverAccountOperation. +func (op *RecoverAccountOperation) Data() interface{} { + return op +} + +//MarshalTransaction is a function of converting type RecoverAccountOperation to bytes. +func (op *RecoverAccountOperation) MarshalTransaction(encoder *transaction.Encoder) error { + enc := transaction.NewRollingEncoder(encoder) + enc.EncodeUVarint(uint64(TypeRecoverAccount.Code())) + enc.Encode(op.AccountToRecover) + enc.Encode(op.NewOwnerAuthority) + enc.Encode(op.RecentOwnerAuthority) + //enc.Encode(op.Extensions) + enc.Encode(byte(0)) + return enc.Err() +} diff --git a/types/operation_report_over_production.go b/types/operation_report_over_production.go new file mode 100644 index 0000000..e5c645c --- /dev/null +++ b/types/operation_report_over_production.go @@ -0,0 +1,16 @@ +package types + +//ReportOverProductionOperation represents report_over_production operation data. +type ReportOverProductionOperation struct { + Reporter string `json:"reporter"` +} + +//Type function that defines the type of operation ReportOverProductionOperation. +func (op *ReportOverProductionOperation) Type() OpType { + return TypeReportOverProduction +} + +//Data returns the operation data ReportOverProductionOperation. +func (op *ReportOverProductionOperation) Data() interface{} { + return op +} diff --git a/types/operation_request_account_recovery.go b/types/operation_request_account_recovery.go new file mode 100644 index 0000000..4b9cf76 --- /dev/null +++ b/types/operation_request_account_recovery.go @@ -0,0 +1,35 @@ +package types + +import ( + "github.com/asuleymanov/steem-go/encoding/transaction" +) + +//RequestAccountRecoveryOperation represents request_account_recovery operation data. +type RequestAccountRecoveryOperation struct { + RecoveryAccount string `json:"recovery_account"` + AccountToRecover string `json:"account_to_recover"` + NewOwnerAuthority *Authority `json:"new_owner_authority"` + Extensions []interface{} `json:"extensions"` +} + +//Type function that defines the type of operation RequestAccountRecoveryOperation. +func (op *RequestAccountRecoveryOperation) Type() OpType { + return TypeRequestAccountRecovery +} + +//Data returns the operation data RequestAccountRecoveryOperation. +func (op *RequestAccountRecoveryOperation) Data() interface{} { + return op +} + +//MarshalTransaction is a function of converting type RequestAccountRecoveryOperation to bytes. +func (op *RequestAccountRecoveryOperation) MarshalTransaction(encoder *transaction.Encoder) error { + enc := transaction.NewRollingEncoder(encoder) + enc.EncodeUVarint(uint64(TypeRequestAccountRecovery.Code())) + enc.Encode(op.RecoveryAccount) + enc.Encode(op.AccountToRecover) + enc.Encode(op.NewOwnerAuthority) + //enc.Encode(op.Extensions) + enc.Encode(byte(0)) + return enc.Err() +} diff --git a/types/operation_reset_account.go b/types/operation_reset_account.go new file mode 100644 index 0000000..fc1e580 --- /dev/null +++ b/types/operation_reset_account.go @@ -0,0 +1,32 @@ +package types + +import ( + "github.com/asuleymanov/steem-go/encoding/transaction" +) + +//ResetAccountOperation represents reset_account operation data. +type ResetAccountOperation struct { + ResetAccount string `json:"reset_account"` + AccountToReset string `json:"account_to_reset"` + NewOwnerAuthority *Authority `json:"new_owner_authority"` +} + +//Type function that defines the type of operation ResetAccountOperation. +func (op *ResetAccountOperation) Type() OpType { + return TypeResetAccount +} + +//Data returns the operation data ResetAccountOperation. +func (op *ResetAccountOperation) Data() interface{} { + return op +} + +//MarshalTransaction is a function of converting type ResetAccountOperation to bytes. +func (op *ResetAccountOperation) MarshalTransaction(encoder *transaction.Encoder) error { + enc := transaction.NewRollingEncoder(encoder) + enc.EncodeUVarint(uint64(TypeResetAccount.Code())) + enc.Encode(op.ResetAccount) + enc.Encode(op.AccountToReset) + enc.Encode(op.NewOwnerAuthority) + return enc.Err() +} diff --git a/types/operation_set_reset_account.go b/types/operation_set_reset_account.go new file mode 100644 index 0000000..0587d92 --- /dev/null +++ b/types/operation_set_reset_account.go @@ -0,0 +1,32 @@ +package types + +import ( + "github.com/asuleymanov/steem-go/encoding/transaction" +) + +//SetResetAccountOperation represents set_reset_account operation data. +type SetResetAccountOperation struct { + Account string `json:"account"` + CurrentResetAccount string `json:"current_reset_account"` + ResetAccount string `json:"reset_account"` +} + +//Type function that defines the type of operation SetResetAccountOperation. +func (op *SetResetAccountOperation) Type() OpType { + return TypeSetResetAccount +} + +//Data returns the operation data SetResetAccountOperation. +func (op *SetResetAccountOperation) Data() interface{} { + return op +} + +//MarshalTransaction is a function of converting type SetResetAccountOperation to bytes. +func (op *SetResetAccountOperation) MarshalTransaction(encoder *transaction.Encoder) error { + enc := transaction.NewRollingEncoder(encoder) + enc.EncodeUVarint(uint64(TypeSetResetAccount.Code())) + enc.Encode(op.Account) + enc.Encode(op.CurrentResetAccount) + enc.Encode(op.ResetAccount) + return enc.Err() +} diff --git a/types/operation_set_withdraw_vesting_route.go b/types/operation_set_withdraw_vesting_route.go new file mode 100644 index 0000000..f375ce7 --- /dev/null +++ b/types/operation_set_withdraw_vesting_route.go @@ -0,0 +1,34 @@ +package types + +import ( + "github.com/asuleymanov/steem-go/encoding/transaction" +) + +//SetWithdrawVestingRouteOperation represents set_withdraw_vesting_route operation data. +type SetWithdrawVestingRouteOperation struct { + FromAccount string `json:"from_account"` + ToAccount string `json:"to_account"` + Percent uint16 `json:"percent"` + AutoVest bool `json:"auto_vest"` +} + +//Type function that defines the type of operation SetWithdrawVestingRouteOperation. +func (op *SetWithdrawVestingRouteOperation) Type() OpType { + return TypeSetWithdrawVestingRoute +} + +//Data returns the operation data SetWithdrawVestingRouteOperation. +func (op *SetWithdrawVestingRouteOperation) Data() interface{} { + return op +} + +//MarshalTransaction is a function of converting type SetWithdrawVestingRouteOperation to bytes. +func (op *SetWithdrawVestingRouteOperation) MarshalTransaction(encoder *transaction.Encoder) error { + enc := transaction.NewRollingEncoder(encoder) + enc.EncodeUVarint(uint64(TypeSetWithdrawVestingRoute.Code())) + enc.Encode(op.FromAccount) + enc.Encode(op.ToAccount) + enc.Encode(op.Percent) + enc.EncodeBool(op.AutoVest) + return enc.Err() +} diff --git a/types/operation_transfer.go b/types/operation_transfer.go new file mode 100644 index 0000000..52fbe45 --- /dev/null +++ b/types/operation_transfer.go @@ -0,0 +1,34 @@ +package types + +import ( + "github.com/asuleymanov/steem-go/encoding/transaction" +) + +//TransferOperation represents transfer operation data. +type TransferOperation struct { + From string `json:"from"` + To string `json:"to"` + Amount *Asset `json:"amount"` + Memo string `json:"memo"` +} + +//Type function that defines the type of operation TransferOperation. +func (op *TransferOperation) Type() OpType { + return TypeTransfer +} + +//Data returns the operation data TransferOperation. +func (op *TransferOperation) Data() interface{} { + return op +} + +//MarshalTransaction is a function of converting type TransferOperation to bytes. +func (op *TransferOperation) MarshalTransaction(encoder *transaction.Encoder) error { + enc := transaction.NewRollingEncoder(encoder) + enc.EncodeUVarint(uint64(TypeTransfer.Code())) + enc.Encode(op.From) + enc.Encode(op.To) + enc.Encode(op.Amount) + enc.Encode(op.Memo) + return enc.Err() +} diff --git a/types/operation_transfer_from_savings.go b/types/operation_transfer_from_savings.go new file mode 100644 index 0000000..2a7ac45 --- /dev/null +++ b/types/operation_transfer_from_savings.go @@ -0,0 +1,36 @@ +package types + +import ( + "github.com/asuleymanov/steem-go/encoding/transaction" +) + +//TransferFromSavingsOperation represents transfer_from_savings operation data. +type TransferFromSavingsOperation struct { + From string `json:"from"` + RequestID uint32 `json:"request_id"` + To string `json:"to"` + Amount *Asset `json:"amount"` + Memo string `json:"memo"` +} + +//Type function that defines the type of operation TransferFromSavingsOperation. +func (op *TransferFromSavingsOperation) Type() OpType { + return TypeTransferFromSavings +} + +//Data returns the operation data TransferFromSavingsOperation. +func (op *TransferFromSavingsOperation) Data() interface{} { + return op +} + +//MarshalTransaction is a function of converting type TransferFromSavingsOperation to bytes. +func (op *TransferFromSavingsOperation) MarshalTransaction(encoder *transaction.Encoder) error { + enc := transaction.NewRollingEncoder(encoder) + enc.EncodeUVarint(uint64(TypeTransferFromSavings.Code())) + enc.Encode(op.From) + enc.Encode(op.RequestID) + enc.Encode(op.To) + enc.Encode(op.Amount) + enc.Encode(op.Memo) + return enc.Err() +} diff --git a/types/operation_transfer_to_savings.go b/types/operation_transfer_to_savings.go new file mode 100644 index 0000000..b4d82bd --- /dev/null +++ b/types/operation_transfer_to_savings.go @@ -0,0 +1,34 @@ +package types + +import ( + "github.com/asuleymanov/steem-go/encoding/transaction" +) + +//TransferToSavingsOperation represents transfer_to_savings operation data. +type TransferToSavingsOperation struct { + From string `json:"from"` + To string `json:"to"` + Amount *Asset `json:"amount"` + Memo string `json:"memo"` +} + +//Type function that defines the type of operation TransferToSavingsOperation. +func (op *TransferToSavingsOperation) Type() OpType { + return TypeTransferToSavings +} + +//Data returns the operation data TransferToSavingsOperation. +func (op *TransferToSavingsOperation) Data() interface{} { + return op +} + +//MarshalTransaction is a function of converting type TransferToSavingsOperation to bytes. +func (op *TransferToSavingsOperation) MarshalTransaction(encoder *transaction.Encoder) error { + enc := transaction.NewRollingEncoder(encoder) + enc.EncodeUVarint(uint64(TypeTransferToSavings.Code())) + enc.Encode(op.From) + enc.Encode(op.To) + enc.Encode(op.Amount) + enc.Encode(op.Memo) + return enc.Err() +} diff --git a/types/operation_transfer_to_vesting.go b/types/operation_transfer_to_vesting.go new file mode 100644 index 0000000..84c9129 --- /dev/null +++ b/types/operation_transfer_to_vesting.go @@ -0,0 +1,32 @@ +package types + +import ( + "github.com/asuleymanov/steem-go/encoding/transaction" +) + +//TransferToVestingOperation represents transfer_to_vesting operation data. +type TransferToVestingOperation struct { + From string `json:"from"` + To string `json:"to"` + Amount *Asset `json:"amount"` +} + +//Type function that defines the type of operation TransferToVestingOperation. +func (op *TransferToVestingOperation) Type() OpType { + return TypeTransferToVesting +} + +//Data returns the operation data TransferToVestingOperation. +func (op *TransferToVestingOperation) Data() interface{} { + return op +} + +//MarshalTransaction is a function of converting type TransferToVestingOperation to bytes. +func (op *TransferToVestingOperation) MarshalTransaction(encoder *transaction.Encoder) error { + enc := transaction.NewRollingEncoder(encoder) + enc.EncodeUVarint(uint64(TypeTransferToVesting.Code())) + enc.Encode(op.From) + enc.Encode(op.To) + enc.Encode(op.Amount) + return enc.Err() +} diff --git a/types/operation_vote.go b/types/operation_vote.go new file mode 100644 index 0000000..22fb7a7 --- /dev/null +++ b/types/operation_vote.go @@ -0,0 +1,34 @@ +package types + +import ( + "github.com/asuleymanov/steem-go/encoding/transaction" +) + +//VoteOperation represents vote operation data. +type VoteOperation struct { + Voter string `json:"voter"` + Author string `json:"author"` + Permlink string `json:"permlink"` + Weight int16 `json:"weight"` +} + +//Type function that defines the type of operation VoteOperation. +func (op *VoteOperation) Type() OpType { + return TypeVote +} + +//Data returns the operation data VoteOperation. +func (op *VoteOperation) Data() interface{} { + return op +} + +//MarshalTransaction is a function of converting type VoteOperation to bytes. +func (op *VoteOperation) MarshalTransaction(encoder *transaction.Encoder) error { + enc := transaction.NewRollingEncoder(encoder) + enc.EncodeUVarint(uint64(TypeVote.Code())) + enc.Encode(op.Voter) + enc.Encode(op.Author) + enc.Encode(op.Permlink) + enc.Encode(op.Weight) + return enc.Err() +} diff --git a/types/operation_withdraw_vesting.go b/types/operation_withdraw_vesting.go new file mode 100644 index 0000000..a0861b4 --- /dev/null +++ b/types/operation_withdraw_vesting.go @@ -0,0 +1,30 @@ +package types + +import ( + "github.com/asuleymanov/steem-go/encoding/transaction" +) + +//WithdrawVestingOperation represents withdraw_vesting operation data. +type WithdrawVestingOperation struct { + Account string `json:"account"` + VestingShares *Asset `json:"vesting_shares"` +} + +//Type function that defines the type of operation WithdrawVestingOperation. +func (op *WithdrawVestingOperation) Type() OpType { + return TypeWithdrawVesting +} + +//Data returns the operation data WithdrawVestingOperation. +func (op *WithdrawVestingOperation) Data() interface{} { + return op +} + +//MarshalTransaction is a function of converting type WithdrawVestingOperation to bytes. +func (op *WithdrawVestingOperation) MarshalTransaction(encoder *transaction.Encoder) error { + enc := transaction.NewRollingEncoder(encoder) + enc.EncodeUVarint(uint64(TypeWithdrawVesting.Code())) + enc.Encode(op.Account) + enc.Encode(op.VestingShares) + return enc.Err() +} diff --git a/types/operation_witness_update.go b/types/operation_witness_update.go new file mode 100644 index 0000000..48d12a7 --- /dev/null +++ b/types/operation_witness_update.go @@ -0,0 +1,36 @@ +package types + +import ( + "github.com/asuleymanov/steem-go/encoding/transaction" +) + +//WitnessUpdateOperation represents witness_update operation data. +type WitnessUpdateOperation struct { + Owner string `json:"owner"` + URL string `json:"url"` + BlockSigningKey string `json:"block_signing_key"` + Props *ChainProperties `json:"props"` + Fee *Asset `json:"fee"` +} + +//Type function that defines the type of operation WitnessUpdateOperation. +func (op *WitnessUpdateOperation) Type() OpType { + return TypeWitnessUpdate +} + +//Data returns the operation data WitnessUpdateOperation. +func (op *WitnessUpdateOperation) Data() interface{} { + return op +} + +//MarshalTransaction is a function of converting type WitnessUpdateOperation to bytes. +func (op *WitnessUpdateOperation) MarshalTransaction(encoder *transaction.Encoder) error { + enc := transaction.NewRollingEncoder(encoder) + enc.EncodeUVarint(uint64(TypeWitnessUpdate.Code())) + enc.Encode(op.Owner) + enc.Encode(op.URL) + enc.EncodePubKey(op.BlockSigningKey) + enc.Encode(op.Props) + enc.Encode(op.Fee) + return enc.Err() +} diff --git a/types/operations.go b/types/operations.go deleted file mode 100644 index 682509f..0000000 --- a/types/operations.go +++ /dev/null @@ -1,447 +0,0 @@ -package types - -import ( - // Stdlib - "encoding/json" - - // RPC - "github.com/go-steem/rpc/encoding/transaction" -) - -// FC_REFLECT( steemit::chain::report_over_production_operation, -// (reporter) -// (first_block) -// (second_block) ) - -type ReportOverProductionOperation struct { - Reporter string `json:"reporter"` -} - -func (op *ReportOverProductionOperation) Type() OpType { - return TypeReportOverProduction -} - -func (op *ReportOverProductionOperation) Data() interface{} { - return op -} - -// FC_REFLECT( steemit::chain::convert_operation, -// (owner) -// (requestid) -// (amount) ) - -type ConvertOperation struct { - Owner string `json:"owner"` - RequestID uint32 `json:"requestid"` - Amount string `json:"amount"` -} - -func (op *ConvertOperation) Type() OpType { - return TypeConvert -} - -func (op *ConvertOperation) Data() interface{} { - return op -} - -// FC_REFLECT( steemit::chain::feed_publish_operation, -// (publisher) -// (exchange_rate) ) - -type FeedPublishOperation struct { - Publisher string `json:"publisher"` - ExchangeRate struct { - Base string `json:"base"` - Quote string `json:"quote"` - } `json:"exchange_rate"` -} - -func (op *FeedPublishOperation) Type() OpType { - return TypeFeedPublish -} - -func (op *FeedPublishOperation) Data() interface{} { - return op -} - -// FC_REFLECT( steemit::chain::pow, -// (worker) -// (input) -// (signature) -// (work) ) - -type POW struct { - Worker string `json:"worker"` - Input string `json:"input"` - Signature string `json:"signature"` - Work string `json:"work"` -} - -// FC_REFLECT( steemit::chain::chain_properties, -// (account_creation_fee) -// (maximum_block_size) -// (sbd_interest_rate) ); - -type ChainProperties struct { - AccountCreationFee string `json:"account_creation_fee"` - MaximumBlockSize uint32 `json:"maximum_block_size"` - SBDInterestRate uint16 `json:"sbd_interest_rate"` -} - -// FC_REFLECT( steemit::chain::pow_operation, -// (worker_account) -// (block_id) -// (nonce) -// (work) -// (props) ) - -type POWOperation struct { - WorkerAccount string `json:"worker_account"` - BlockID string `json:"block_id"` - Nonce *Int `json:"nonce"` - Work *POW `json:"work"` - Props *ChainProperties `json:"props"` -} - -func (op *POWOperation) Type() OpType { - return TypePOW -} - -func (op *POWOperation) Data() interface{} { - return op -} - -// FC_REFLECT( steemit::chain::account_create_operation, -// (fee) -// (creator) -// (new_account_name) -// (owner) -// (active) -// (posting) -// (memo_key) -// (json_metadata) ) - -type AccountCreateOperation struct { - Fee string `json:"fee"` - Creator string `json:"creator"` - NewAccountName string `json:"new_account_name"` - Owner *Authority `json:"owner"` - Active *Authority `json:"active"` - Posting *Authority `json:"posting"` - MemoKey string `json:"memo_key"` - JsonMetadata string `json:"json_metadata"` -} - -func (op *AccountCreateOperation) Type() OpType { - return TypeAccountCreate -} - -func (op *AccountCreateOperation) Data() interface{} { - return op -} - -// FC_REFLECT( steemit::chain::account_update_operation, -// (account) -// (owner) -// (active) -// (posting) -// (memo_key) -// (json_metadata) ) - -type AccountUpdateOperation struct { - Account string `json:"account"` - Owner *Authority `json:"owner"` - Active *Authority `json:"active"` - Posting *Authority `json:"posting"` - MemoKey string `json:"memo_key"` - JsonMetadata string `json:"json_metadata"` -} - -func (op *AccountUpdateOperation) Type() OpType { - return TypeAccountUpdate -} - -func (op *AccountUpdateOperation) Data() interface{} { - return op -} - -// FC_REFLECT( steemit::chain::transfer_operation, -// (from) -// (to) -// (amount) -// (memo) ) - -type TransferOperation struct { - From string `json:"from"` - To string `json:"to"` - Amount string `json:"amount"` - Memo string `json:"memo"` -} - -func (op *TransferOperation) Type() OpType { - return TypeTransfer -} - -func (op *TransferOperation) Data() interface{} { - return op -} - -// FC_REFLECT( steemit::chain::transfer_to_vesting_operation, -// (from) -// (to) -// (amount) ) - -type TransferToVestingOperation struct { - From string `json:"from"` - To string `json:"to"` - Amount string `json:"amount"` -} - -func (op *TransferToVestingOperation) Type() OpType { - return TypeTransferToVesting -} - -func (op *TransferToVestingOperation) Data() interface{} { - return op -} - -// FC_REFLECT( steemit::chain::withdraw_vesting_operation, -// (account) -// (vesting_shares) ) - -type WithdrawVestingOperation struct { - Account string `json:"account"` - VestingShares string `json:"vesting_shares"` -} - -func (op *WithdrawVestingOperation) Type() OpType { - return TypeWithdrawVesting -} - -func (op *WithdrawVestingOperation) Data() interface{} { - return op -} - -// FC_REFLECT( steemit::chain::set_withdraw_vesting_route_operation, -// (from_account) -// (to_account) -// (percent) -// (auto_vest) ) - -// FC_REFLECT( steemit::chain::witness_update_operation, -// (owner) -// (url) -// (block_signing_key) -// (props) -// (fee) ) - -// FC_REFLECT( steemit::chain::account_witness_vote_operation, -// (account) -// (witness)(approve) ) - -type AccountWitnessVoteOperation struct { - Account string `json:"account"` - Witness string `json:"witness"` - Approve bool `json:"approve"` -} - -func (op *AccountWitnessVoteOperation) Type() OpType { - return TypeAccountWitnessVote -} - -func (op *AccountWitnessVoteOperation) Data() interface{} { - return op -} - -// FC_REFLECT( steemit::chain::account_witness_proxy_operation, -// (account) -// (proxy) ) - -type AccountWitnessProxyOperation struct { - Account string `json:"account"` - Proxy string `json:"proxy"` -} - -func (op *AccountWitnessProxyOperation) Type() OpType { - return TypeAccountWitnessProxy -} - -func (op *AccountWitnessProxyOperation) Data() interface{} { - return op -} - -// FC_REFLECT( steemit::chain::comment_operation, -// (parent_author) -// (parent_permlink) -// (author) -// (permlink) -// (title) -// (body) -// (json_metadata) ) - -// CommentOperation represents either a new post or a comment. -// -// In case Title is filled in and ParentAuthor is empty, it is a new post. -// The post category can be read from ParentPermlink. -type CommentOperation struct { - Author string `json:"author"` - Title string `json:"title"` - Permlink string `json:"permlink"` - ParentAuthor string `json:"parent_author"` - ParentPermlink string `json:"parent_permlink"` - Body string `json:"body"` - JsonMetadata string `json:"json_metadata"` -} - -func (op *CommentOperation) Type() OpType { - return TypeComment -} - -func (op *CommentOperation) Data() interface{} { - return op -} - -func (op *CommentOperation) IsStoryOperation() bool { - return op.ParentAuthor == "" -} - -// FC_REFLECT( steemit::chain::vote_operation, -// (voter) -// (author) -// (permlink) -// (weight) ) - -type VoteOperation struct { - Voter string `json:"voter"` - Author string `json:"author"` - Permlink string `json:"permlink"` - Weight Int16 `json:"weight"` -} - -func (op *VoteOperation) Type() OpType { - return TypeVote -} - -func (op *VoteOperation) Data() interface{} { - return op -} - -func (op *VoteOperation) MarshalTransaction(encoder *transaction.Encoder) error { - enc := transaction.NewRollingEncoder(encoder) - enc.EncodeUVarint(uint64(TypeVote.Code())) - enc.Encode(op.Voter) - enc.Encode(op.Author) - enc.Encode(op.Permlink) - enc.Encode(op.Weight) - return enc.Err() -} - -// FC_REFLECT( steemit::chain::custom_operation, -// (required_auths) -// (id) -// (data) ) - -// FC_REFLECT( steemit::chain::limit_order_create_operation, -// (owner) -// (orderid) -// (amount_to_sell) -// (min_to_receive) -// (fill_or_kill) -// (expiration) ) - -type LimitOrderCreateOperation struct { - Owner string `json:"owner"` - OrderID uint32 `json:"orderid"` - AmountToSell string `json:"amount_to_sell"` - MinToReceive string `json:"min_to_receive"` - FillOrKill bool `json:"fill_or_kill"` - Expiration *Time `json:"expiration"` -} - -func (op *LimitOrderCreateOperation) Type() OpType { - return TypeLimitOrderCreate -} - -func (op *LimitOrderCreateOperation) Data() interface{} { - return op -} - -// FC_REFLECT( steemit::chain::limit_order_cancel_operation, -// (owner) -// (orderid) ) - -type LimitOrderCancelOperation struct { - Owner string `json:"owner"` - OrderID uint32 `json:"orderid"` -} - -func (op *LimitOrderCancelOperation) Type() OpType { - return TypeLimitOrderCancel -} - -func (op *LimitOrderCancelOperation) Data() interface{} { - return op -} - -// FC_REFLECT( steemit::chain::delete_comment_operation, -// (author) -// (permlink) ) - -type DeleteCommentOperation struct { - Author string `json:"author"` - Permlink string `json:"permlink"` -} - -func (op *DeleteCommentOperation) Type() OpType { - return TypeDeleteComment -} - -func (op *DeleteCommentOperation) Data() interface{} { - return op -} - -// FC_REFLECT( steemit::chain::comment_options_operation, -// (author) -// (permlink) -// (max_accepted_payout) -// (percent_steem_dollars) -// (allow_votes) -// (allow_curation_rewards) -// (extensions) ) - -type CommentOptionsOperation struct { - Author string `json:"author"` - Permlink string `json:"permlink"` - MaxAcceptedPayout string `json:"max_accepted_payout"` - PercentSteemDollars uint16 `json:"percent_steem_dollars"` - AllowVotes bool `json:"allow_votes"` - AllowCurationRewards bool `json:"allow_curation_rewards"` - Extensions []interface{} `json:"extensions"` -} - -func (op *CommentOptionsOperation) Type() OpType { - return TypeCommentOptions -} - -func (op *CommentOptionsOperation) Data() interface{} { - return op -} - -type Authority struct { - AccountAuths StringInt64Map `json:"account_auths"` - KeyAuths StringInt64Map `json:"key_auths"` - WeightThreshold uint32 `json:"weight_threshold"` -} - -type UnknownOperation struct { - kind OpType - data *json.RawMessage -} - -func (op *UnknownOperation) Type() OpType { - return op.kind -} - -func (op *UnknownOperation) Data() interface{} { - return op.data -} diff --git a/types/operations_test.go b/types/operations_test.go deleted file mode 100644 index dcbebff..0000000 --- a/types/operations_test.go +++ /dev/null @@ -1,35 +0,0 @@ -package types - -import ( - // Stdlib - "bytes" - "encoding/hex" - "testing" - - // RPC - "github.com/go-steem/rpc/encoding/transaction" -) - -func TestVoteOperation_MarshalTransaction(t *testing.T) { - op := &VoteOperation{ - Voter: "xeroc", - Author: "xeroc", - Permlink: "piston", - Weight: 10000, - } - - expectedHex := "00057865726f63057865726f6306706973746f6e1027" - - var b bytes.Buffer - encoder := transaction.NewEncoder(&b) - - if err := encoder.Encode(op); err != nil { - t.Error(err) - } - - serializedHex := hex.EncodeToString(b.Bytes()) - - if serializedHex != expectedHex { - t.Errorf("expected %v, got %v", expectedHex, serializedHex) - } -} diff --git a/types/optype.go b/types/optype.go index a86c0ff..a608f86 100644 --- a/types/optype.go +++ b/types/optype.go @@ -1,6 +1,6 @@ package types -// OpType represents a Steem operation type, i.e. vote, comment, pow and so on. +// OpType represents a Golos operation type, i.e. vote, comment, pow and so on. type OpType string // Code returns the operation code associated with the given operation type. @@ -9,37 +9,62 @@ func (kind OpType) Code() uint16 { } const ( - TypeVote OpType = "vote" - TypeComment OpType = "comment" - TypeTransfer OpType = "transfer" - TypeTransferToVesting OpType = "transfer_to_vesting" - TypeWithdrawVesting OpType = "withdraw_vesting" - TypeLimitOrderCreate OpType = "limit_order_create" - TypeLimitOrderCancel OpType = "limit_order_cancel" - TypeFeedPublish OpType = "feed_publish" - TypeConvert OpType = "convert" - TypeAccountCreate OpType = "account_create" - TypeAccountUpdate OpType = "account_update" - TypeWitnessUpdate OpType = "witness_update" - TypeAccountWitnessVote OpType = "account_witness_vote" - TypeAccountWitnessProxy OpType = "account_witness_proxy" - TypePOW OpType = "pow" - TypeCustom OpType = "custom" - TypeReportOverProduction OpType = "report_over_production" - TypeDeleteComment OpType = "delete_comment" - TypeCustomJSON OpType = "custom_json" - TypeCommentOptions OpType = "comment_options" - TypeSetWithdrawVestingRoute OpType = "set_withdraw_vesting_route" - TypeLimitOrderCreate2 OpType = "limit_order_create2" - TypeChallengeAuthority OpType = "challenge_authority" - TypeProveAuthority OpType = "prove_authority" - TypeRequestAccountRecoverty OpType = "request_account_recovery" - TypeRecoverAccount OpType = "recover_account" - TypeChangeRecoveryAccount OpType = "change_recover_account" - TypeEscrowTransfer OpType = "escrow_transfer" - TypeEscrowDispute OpType = "escrow_dispute" - TypeEscrowRelease OpType = "escrow_release" - TypePOW2 OpType = "pow2" + TypeVote OpType = "vote" + TypeComment OpType = "comment" + TypeTransfer OpType = "transfer" + TypeTransferToVesting OpType = "transfer_to_vesting" + TypeWithdrawVesting OpType = "withdraw_vesting" + TypeLimitOrderCreate OpType = "limit_order_create" + TypeLimitOrderCancel OpType = "limit_order_cancel" + TypeFeedPublish OpType = "feed_publish" + TypeConvert OpType = "convert" + TypeAccountCreate OpType = "account_create" + TypeAccountUpdate OpType = "account_update" + TypeWitnessUpdate OpType = "witness_update" + TypeAccountWitnessVote OpType = "account_witness_vote" + TypeAccountWitnessProxy OpType = "account_witness_proxy" + TypePOW OpType = "pow" + TypeCustom OpType = "custom" + TypeReportOverProduction OpType = "report_over_production" + TypeDeleteComment OpType = "delete_comment" + TypeCustomJSON OpType = "custom_json" + TypeCommentOptions OpType = "comment_options" + TypeSetWithdrawVestingRoute OpType = "set_withdraw_vesting_route" + TypeLimitOrderCreate2 OpType = "limit_order_create2" + TypeClaimAccount OpType = "claim_account" + TypeCreateClaimedAccount OpType = "create_claimed_account" + TypeRequestAccountRecovery OpType = "request_account_recovery" + TypeRecoverAccount OpType = "recover_account" + TypeChangeRecoveryAccount OpType = "change_recovery_account" + TypeEscrowTransfer OpType = "escrow_transfer" + TypeEscrowDispute OpType = "escrow_dispute" + TypeEscrowRelease OpType = "escrow_release" + TypePOW2 OpType = "pow2" + TypeEscrowApprove OpType = "escrow_approve" + TypeTransferToSavings OpType = "transfer_to_savings" + TypeTransferFromSavings OpType = "transfer_from_savings" + TypeCancelTransferFromSavings OpType = "cancel_transfer_from_savings" + TypeCustomBinary OpType = "custom_binary" + TypeDeclineVotingRights OpType = "decline_voting_rights" + TypeResetAccount OpType = "reset_account" + TypeSetResetAccount OpType = "set_reset_account" + TypeClaimRewardBalance OpType = "claim_reward_balance" + TypeDelegateVestingShares OpType = "delegate_vesting_shares" + TypeAccountCreateWithDelegation OpType = "account_create_with_delegation" + TypeFillConvertRequest OpType = "fill_convert_request" //Virtual Operation + TypeAuthorReward OpType = "author_reward" //Virtual Operation + TypeCurationReward OpType = "curation_reward" //Virtual Operation + TypeCommentReward OpType = "comment_reward" //Virtual Operation + TypeLiquidityReward OpType = "liquidity_reward" //Virtual Operation + TypeInterest OpType = "interest" //Virtual Operation + TypeFillVestingWithdraw OpType = "fill_vesting_withdraw" //Virtual Operation + TypeFillOrder OpType = "fill_order" //Virtual Operation + TypeShutdownWitness OpType = "shutdown_witness" //Virtual Operation + TypeFillTransferFromSavings OpType = "fill_transfer_from_savings" //Virtual Operation + TypeHardfork OpType = "hardfork" //Virtual Operation + TypeCommentPayoutUpdate OpType = "comment_payout_update" //Virtual Operation + TypeReturnVestingDelegation OpType = "return_vesting_delegation" //Virtual Operation + TypeCommentBenefactorReward OpType = "comment_benefactor_reward" //Virtual Operation ) var opTypes = [...]OpType{ @@ -65,15 +90,40 @@ var opTypes = [...]OpType{ TypeCommentOptions, TypeSetWithdrawVestingRoute, TypeLimitOrderCreate2, - TypeChallengeAuthority, - TypeProveAuthority, - TypeRequestAccountRecoverty, + TypeClaimAccount, + TypeCreateClaimedAccount, + TypeRequestAccountRecovery, TypeRecoverAccount, TypeChangeRecoveryAccount, TypeEscrowTransfer, TypeEscrowDispute, TypeEscrowRelease, TypePOW2, + TypeEscrowApprove, + TypeTransferToSavings, + TypeTransferFromSavings, + TypeCancelTransferFromSavings, + TypeCustomBinary, + TypeDeclineVotingRights, + TypeResetAccount, + TypeSetResetAccount, + TypeClaimRewardBalance, + TypeDelegateVestingShares, + TypeAccountCreateWithDelegation, + TypeFillConvertRequest, //Virtual Operation + TypeAuthorReward, //Virtual Operation + TypeCurationReward, //Virtual Operation + TypeCommentReward, //Virtual Operation + TypeLiquidityReward, //Virtual Operation + TypeInterest, //Virtual Operation + TypeFillVestingWithdraw, //Virtual Operation + TypeFillOrder, //Virtual Operation + TypeShutdownWitness, //Virtual Operation + TypeFillTransferFromSavings, //Virtual Operation + TypeHardfork, //Virtual Operation + TypeCommentPayoutUpdate, //Virtual Operation + TypeReturnVestingDelegation, //Virtual Operation + TypeCommentBenefactorReward, //Virtual Operation } // opCodes keeps mapping operation type -> operation code. diff --git a/types/pow.go b/types/pow.go new file mode 100644 index 0000000..d54804c --- /dev/null +++ b/types/pow.go @@ -0,0 +1,18 @@ +package types + +// Add-on struct + +//POW is an additional structure used by other structures. +type POW struct { + Worker string `json:"worker"` + Input string `json:"input"` + Signature string `json:"signature"` + Work string `json:"work"` +} + +//POW2Input is an additional structure used by other structures. +type POW2Input struct { + WorkerAccount string `json:"worker_account"` + PrevBlock []byte `json:"prev_block"` + Nonce uint64 `json:"nonce"` +} diff --git a/types/rpc.go b/types/rpc.go new file mode 100644 index 0000000..9bc9f07 --- /dev/null +++ b/types/rpc.go @@ -0,0 +1,61 @@ +package types + +import ( + "encoding/json" + "fmt" +) + +type RPCRequest struct { + Method string `json:"method"` + Params interface{} `json:"params,omitempty"` + JSON string `json:"jsonrpc"` + ID uint64 `json:"id"` +} + +type RPCResponse struct { + Result *json.RawMessage `json:"result,omitempty"` + Error *RPCError `json:"error,omitempty"` + JSON string `json:"jsonrpc,omitempty"` + ID uint64 `json:"id"` +} + +type RPCError struct { + Code int `json:"code"` + Message string `json:"message"` + Data struct { + Code int `json:"code"` + Name string `json:"name"` + Message string `json:"message"` + Stack []struct { + Context struct { + Level string `json:"level"` + File string `json:"file"` + Line int `json:"line"` + Method string `json:"method"` + Hostname string `json:"hostname"` + ThreadName string `json:"thread_name"` + Timestamp string `json:"timestamp"` + } `json:"context"` + Format string `json:"format"` + Data interface{} `json:"data"` + } `json:"stack"` + } `json:"data"` +} + +type RPCIncoming struct { + ID uint64 `json:"id"` + JSON string `json:"jsonrpc"` + Result json.RawMessage `json:"result"` +} + +/* +Old Version +type RPCIncoming struct { + Method string `json:"method"` + Params []json.RawMessage `json:"params"` +} +*/ + +func (e *RPCError) Error() string { + return fmt.Sprintf("%d: %s\n %#v", e.Code, e.Message, e.Data) +} diff --git a/types/slice.go b/types/slice.go index 5d9e969..46e7123 100644 --- a/types/slice.go +++ b/types/slice.go @@ -7,8 +7,10 @@ import ( "github.com/pkg/errors" ) +//StringSlice type from parameter JSON type StringSlice []string +//UnmarshalJSON unpacking the JSON parameter in the StringSlice type. func (ss *StringSlice) UnmarshalJSON(data []byte) error { if len(data) == 0 { return nil diff --git a/types/map.go b/types/string_int64_map.go similarity index 76% rename from types/map.go rename to types/string_int64_map.go index 35721cf..c4f1e8e 100644 --- a/types/map.go +++ b/types/string_int64_map.go @@ -5,16 +5,19 @@ import ( "errors" ) +//StringInt64Map type from parameter JSON type StringInt64Map map[string]int64 +//MarshalJSON function for packing the StringInt64Map type in JSON. func (m StringInt64Map) MarshalJSON() ([]byte, error) { - xs := make([]interface{}, len(m)) + xs := make([]interface{}, 0, len(m)) for k, v := range m { xs = append(xs, []interface{}{k, v}) } - return json.Marshal(xs) + return JSONMarshal(xs) } +//UnmarshalJSON unpacking the JSON parameter in the StringInt64Map type. func (m *StringInt64Map) UnmarshalJSON(data []byte) error { var xs [][]interface{} if err := json.Unmarshal(data, &xs); err != nil { @@ -43,7 +46,6 @@ func (m *StringInt64Map) UnmarshalJSON(data []byte) error { v = t default: invalid = true - break } mp[k] = v diff --git a/types/time.go b/types/time.go index 0bceacc..886184d 100644 --- a/types/time.go +++ b/types/time.go @@ -1,25 +1,26 @@ package types import ( - // Stdlib "time" - // RPC - "github.com/go-steem/rpc/encoding/transaction" + "github.com/asuleymanov/steem-go/encoding/transaction" ) -const Layout = `"2006-01-02T15:04:05"` +const layout = `"2006-01-02T15:04:05"` +//Time type from parameter JSON type Time struct { *time.Time } +//MarshalJSON function for packing the Time type in JSON. func (t *Time) MarshalJSON() ([]byte, error) { - return []byte(t.Time.Format(Layout)), nil + return []byte(t.Time.Format(layout)), nil } +//UnmarshalJSON unpacking the JSON parameter in the Time type. func (t *Time) UnmarshalJSON(data []byte) error { - parsed, err := time.ParseInLocation(Layout, string(data), time.UTC) + parsed, err := time.ParseInLocation(layout, string(data), time.UTC) if err != nil { return err } @@ -27,6 +28,7 @@ func (t *Time) UnmarshalJSON(data []byte) error { return nil } +//MarshalTransaction is a function of converting type Time to bytes. func (t *Time) MarshalTransaction(encoder *transaction.Encoder) error { return encoder.Encode(uint32(t.Time.Unix())) } diff --git a/types/transaction.go b/types/transaction.go index 9b4a3bb..4ac5d60 100644 --- a/types/transaction.go +++ b/types/transaction.go @@ -1,11 +1,9 @@ package types import ( - // RPC - "github.com/go-steem/rpc/encoding/transaction" + "errors" - // Vendor - "github.com/pkg/errors" + "github.com/asuleymanov/steem-go/encoding/transaction" ) // Transaction represents a blockchain transaction. diff --git a/types/transaction_test.go b/types/transaction_test.go deleted file mode 100644 index 7749c97..0000000 --- a/types/transaction_test.go +++ /dev/null @@ -1,45 +0,0 @@ -package types - -import ( - // Stdlib - "bytes" - "encoding/hex" - "testing" - "time" - - // RPC - "github.com/go-steem/rpc/encoding/transaction" -) - -func TestTransaction_MarshalTransaction(t *testing.T) { - // The result we expect. - expected := "bd8c5fe26f45f179a8570100057865726f63057865726f6306706973746f6e102700" - - // Prepare the transaction. - expiration := time.Date(2016, 8, 8, 12, 24, 17, 0, time.UTC) - tx := Transaction{ - RefBlockNum: 36029, - RefBlockPrefix: 1164960351, - Expiration: &Time{&expiration}, - } - tx.PushOperation(&VoteOperation{ - Voter: "xeroc", - Author: "xeroc", - Permlink: "piston", - Weight: 10000, - }) - - // Marshal the transaction. - var b bytes.Buffer - encoder := transaction.NewEncoder(&b) - - if err := tx.MarshalTransaction(encoder); err != nil { - t.Error(err) - } - got := hex.EncodeToString(b.Bytes()) - - // Compare that we got with what we expect to get. - if got != expected { - t.Errorf("expected %v, got %v", expected, got) - } -} diff --git a/types/uint.go b/types/uint.go index f919f7c..013e034 100644 --- a/types/uint.go +++ b/types/uint.go @@ -1,14 +1,11 @@ package types import ( - // Stdlib "encoding/json" "strconv" - // RPC - "github.com/go-steem/rpc/encoding/transaction" + "github.com/asuleymanov/steem-go/encoding/transaction" - // Vendor "github.com/pkg/errors" ) @@ -31,8 +28,10 @@ func unmarshalUInt(data []byte) (uint64, error) { return i, errors.Wrapf(err, "types: failed to unmarshal unsigned integer: %v", data) } +//UInt type from parameter JSON type UInt uint +//UnmarshalJSON unpacking the JSON parameter in the UInt type. func (num *UInt) UnmarshalJSON(data []byte) error { v, err := unmarshalUInt(data) if err != nil { @@ -43,12 +42,15 @@ func (num *UInt) UnmarshalJSON(data []byte) error { return nil } +//MarshalTransaction is a function of converting type UInt to bytes. func (num UInt) MarshalTransaction(encoder *transaction.Encoder) error { return encoder.EncodeNumber(uint(num)) } +//UInt8 type from parameter JSON type UInt8 uint8 +//UnmarshalJSON unpacking the JSON parameter in the UInt8 type. func (num *UInt8) UnmarshalJSON(data []byte) error { v, err := unmarshalUInt(data) if err != nil { @@ -59,12 +61,15 @@ func (num *UInt8) UnmarshalJSON(data []byte) error { return nil } +//MarshalTransaction is a function of converting type UInt8 to bytes. func (num UInt8) MarshalTransaction(encoder *transaction.Encoder) error { return encoder.EncodeNumber(uint8(num)) } +//UInt16 type from parameter JSON type UInt16 uint16 +//UnmarshalJSON unpacking the JSON parameter in the UInt16 type. func (num *UInt16) UnmarshalJSON(data []byte) error { v, err := unmarshalUInt(data) if err != nil { @@ -75,12 +80,15 @@ func (num *UInt16) UnmarshalJSON(data []byte) error { return nil } +//MarshalTransaction is a function of converting type UInt16 to bytes. func (num UInt16) MarshalTransaction(encoder *transaction.Encoder) error { return encoder.EncodeNumber(uint16(num)) } +//UInt32 type from parameter JSON type UInt32 uint32 +//UnmarshalJSON unpacking the JSON parameter in the UInt32 type. func (num *UInt32) UnmarshalJSON(data []byte) error { v, err := unmarshalUInt(data) if err != nil { @@ -91,12 +99,15 @@ func (num *UInt32) UnmarshalJSON(data []byte) error { return nil } +//MarshalTransaction is a function of converting type UInt32 to bytes. func (num UInt32) MarshalTransaction(encoder *transaction.Encoder) error { return encoder.EncodeNumber(uint32(num)) } +//UInt64 type from parameter JSON type UInt64 uint64 +//UnmarshalJSON unpacking the JSON parameter in the UInt64 type. func (num *UInt64) UnmarshalJSON(data []byte) error { v, err := unmarshalUInt(data) if err != nil { @@ -107,6 +118,7 @@ func (num *UInt64) UnmarshalJSON(data []byte) error { return nil } +//MarshalTransaction is a function of converting type UInt64 to bytes. func (num UInt64) MarshalTransaction(encoder *transaction.Encoder) error { return encoder.EncodeNumber(uint64(num)) } diff --git a/types/unknown_operation.go b/types/unknown_operation.go new file mode 100644 index 0000000..85d9394 --- /dev/null +++ b/types/unknown_operation.go @@ -0,0 +1,21 @@ +package types + +import ( + "encoding/json" +) + +//UnknownOperation represents Unknown operation data. +type UnknownOperation struct { + kind OpType + data *json.RawMessage +} + +//Type function that defines the type of operation UnknownOperation. +func (op *UnknownOperation) Type() OpType { + return op.kind +} + +//Data returns the operation data UnknownOperation. +func (op *UnknownOperation) Data() interface{} { + return op.data +} diff --git a/types/voperation_author_reward.go b/types/voperation_author_reward.go new file mode 100644 index 0000000..771b5a0 --- /dev/null +++ b/types/voperation_author_reward.go @@ -0,0 +1,20 @@ +package types + +//AuthorRewardOperation represents author_reward operation data. +type AuthorRewardOperation struct { + Author string `json:"author"` + Permlink string `json:"permlink"` + SbdPayout *Asset `json:"sbd_payout"` + SteemPayout *Asset `json:"steem_payout"` + VestingPayout *Asset `json:"vesting_payout"` +} + +//Type function that defines the type of operation AuthorRewardOperation. +func (op *AuthorRewardOperation) Type() OpType { + return TypeAuthorReward +} + +//Data returns the operation data AuthorRewardOperation. +func (op *AuthorRewardOperation) Data() interface{} { + return op +} diff --git a/types/voperation_comment_benefactor_reward.go b/types/voperation_comment_benefactor_reward.go new file mode 100644 index 0000000..250a407 --- /dev/null +++ b/types/voperation_comment_benefactor_reward.go @@ -0,0 +1,21 @@ +package types + +//CommentBenefactorRewardOperation represents comment_benefactor_reward operation data. +type CommentBenefactorRewardOperation struct { + Benefactor string `json:"benefactor"` + Author string `json:"author"` + Permlink string `json:"permlink"` + SbdPayout *Asset `json:"sbd_payout"` + SteemPayout *Asset `json:"steem_payout"` + VestingPayout *Asset `json:"vesting_payout"` +} + +//Type function that defines the type of operation CommentBenefactorRewardOperation. +func (op *CommentBenefactorRewardOperation) Type() OpType { + return TypeCommentBenefactorReward +} + +//Data returns the operation data CommentBenefactorRewardOperation. +func (op *CommentBenefactorRewardOperation) Data() interface{} { + return op +} diff --git a/types/voperation_comment_payout_update.go b/types/voperation_comment_payout_update.go new file mode 100644 index 0000000..a350678 --- /dev/null +++ b/types/voperation_comment_payout_update.go @@ -0,0 +1,17 @@ +package types + +//CommentPayoutUpdateOperation represents comment_payout_update operation data. +type CommentPayoutUpdateOperation struct { + Author string `json:"author"` + Permlink string `json:"permlink"` +} + +//Type function that defines the type of operation CommentPayoutUpdateOperation. +func (op *CommentPayoutUpdateOperation) Type() OpType { + return TypeCommentPayoutUpdate +} + +//Data returns the operation data CommentPayoutUpdateOperation. +func (op *CommentPayoutUpdateOperation) Data() interface{} { + return op +} diff --git a/types/voperation_comment_reward.go b/types/voperation_comment_reward.go new file mode 100644 index 0000000..68b1bc8 --- /dev/null +++ b/types/voperation_comment_reward.go @@ -0,0 +1,18 @@ +package types + +//CommentRewardOperation represents comment_reward operation data. +type CommentRewardOperation struct { + Author string `json:"author"` + Permlink string `json:"permlink"` + Payout *Asset `json:"payout"` +} + +//Type function that defines the type of operation CommentRewardOperation. +func (op *CommentRewardOperation) Type() OpType { + return TypeCommentReward +} + +//Data returns the operation data CommentRewardOperation. +func (op *CommentRewardOperation) Data() interface{} { + return op +} diff --git a/types/voperation_curation_reward.go b/types/voperation_curation_reward.go new file mode 100644 index 0000000..f0e9c10 --- /dev/null +++ b/types/voperation_curation_reward.go @@ -0,0 +1,19 @@ +package types + +//CurationRewardOperation represents curation_reward operation data. +type CurationRewardOperation struct { + Curator string `json:"curator"` + Reward *Asset `json:"reward"` + CommentAuthor string `json:"comment_author"` + CommentPermlink string `json:"comment_permlink"` +} + +//Type function that defines the type of operation CurationRewardOperation. +func (op *CurationRewardOperation) Type() OpType { + return TypeCurationReward +} + +//Data returns the operation data CurationRewardOperation. +func (op *CurationRewardOperation) Data() interface{} { + return op +} diff --git a/types/voperation_fill_convert_request.go b/types/voperation_fill_convert_request.go new file mode 100644 index 0000000..32c2383 --- /dev/null +++ b/types/voperation_fill_convert_request.go @@ -0,0 +1,19 @@ +package types + +//FillConvertRequestOperation represents fill_convert_request operation data. +type FillConvertRequestOperation struct { + Owner string `json:"owner"` + Requestid uint32 `json:"requestid"` + AmountIn *Asset `json:"amount_in"` + AmountOut *Asset `json:"amount_out"` +} + +//Type function that defines the type of operation FillConvertRequestOperation. +func (op *FillConvertRequestOperation) Type() OpType { + return TypeFillConvertRequest +} + +//Data returns the operation data FillConvertRequestOperation. +func (op *FillConvertRequestOperation) Data() interface{} { + return op +} diff --git a/types/voperation_fill_order.go b/types/voperation_fill_order.go new file mode 100644 index 0000000..fc53f5c --- /dev/null +++ b/types/voperation_fill_order.go @@ -0,0 +1,21 @@ +package types + +//FillOrderOperation represents fill_order operation data. +type FillOrderOperation struct { + CurrentOwner string `json:"current_owner"` + CurrentOrderid uint32 `json:"current_orderid"` + CurrentPays *Asset `json:"current_pays"` + OpenOwner string `json:"open_owner"` + OpenOrderid uint32 `json:"open_orderid"` + OpenPays *Asset `json:"open_pays"` +} + +//Type function that defines the type of operation FillOrderOperation. +func (op *FillOrderOperation) Type() OpType { + return TypeFillOrder +} + +//Data returns the operation data FillOrderOperation. +func (op *FillOrderOperation) Data() interface{} { + return op +} diff --git a/types/voperation_fill_transfer_from_savings.go b/types/voperation_fill_transfer_from_savings.go new file mode 100644 index 0000000..6bf8c6f --- /dev/null +++ b/types/voperation_fill_transfer_from_savings.go @@ -0,0 +1,20 @@ +package types + +//FillTransferFromSavingsOperation represents fill_transfer_from_savings operation data. +type FillTransferFromSavingsOperation struct { + From string `json:"from"` + To string `json:"to"` + Amount *Asset `json:"amount"` + RequestID uint32 `json:"request_id"` + Memo string `json:"memo"` +} + +//Type function that defines the type of operation FillTransferFromSavingsOperation. +func (op *FillTransferFromSavingsOperation) Type() OpType { + return TypeFillTransferFromSavings +} + +//Data returns the operation data FillTransferFromSavingsOperation. +func (op *FillTransferFromSavingsOperation) Data() interface{} { + return op +} diff --git a/types/voperation_fill_vesting_withdraw.go b/types/voperation_fill_vesting_withdraw.go new file mode 100644 index 0000000..98708af --- /dev/null +++ b/types/voperation_fill_vesting_withdraw.go @@ -0,0 +1,19 @@ +package types + +//FillVestingWithdrawOperation represents fill_vesting_withdraw operation data. +type FillVestingWithdrawOperation struct { + FromAccount string `json:"from_account"` + ToAccount string `json:"to_account"` + Withdrawn *Asset `json:"withdrawn"` + Deposited *Asset `json:"deposited"` +} + +//Type function that defines the type of operation FillVestingWithdrawOperation. +func (op *FillVestingWithdrawOperation) Type() OpType { + return TypeFillVestingWithdraw +} + +//Data returns the operation data FillVestingWithdrawOperation. +func (op *FillVestingWithdrawOperation) Data() interface{} { + return op +} diff --git a/types/voperation_hardfork.go b/types/voperation_hardfork.go new file mode 100644 index 0000000..3be284c --- /dev/null +++ b/types/voperation_hardfork.go @@ -0,0 +1,16 @@ +package types + +//HardforkOperation represents hardfork operation data. +type HardforkOperation struct { + HardforkID uint32 `json:"hardfork_id"` +} + +//Type function that defines the type of operation HardforkOperation. +func (op *HardforkOperation) Type() OpType { + return TypeHardfork +} + +//Data returns the operation data HardforkOperation. +func (op *HardforkOperation) Data() interface{} { + return op +} diff --git a/types/voperation_interest.go b/types/voperation_interest.go new file mode 100644 index 0000000..efe3e9d --- /dev/null +++ b/types/voperation_interest.go @@ -0,0 +1,17 @@ +package types + +//InterestOperation represents interest operation data. +type InterestOperation struct { + Owner string `json:"owner"` + Interest *Asset `json:"interest"` +} + +//Type function that defines the type of operation InterestOperation. +func (op *InterestOperation) Type() OpType { + return TypeInterest +} + +//Data returns the operation data InterestOperation. +func (op *InterestOperation) Data() interface{} { + return op +} diff --git a/types/voperation_liquidity_reward.go b/types/voperation_liquidity_reward.go new file mode 100644 index 0000000..2b09dd4 --- /dev/null +++ b/types/voperation_liquidity_reward.go @@ -0,0 +1,17 @@ +package types + +//LiquidityRewardOperation represents liquidity_reward operation data. +type LiquidityRewardOperation struct { + Owner string `json:"owner"` + Payout *Asset `json:"payout"` +} + +//Type function that defines the type of operation LiquidityRewardOperation. +func (op *LiquidityRewardOperation) Type() OpType { + return TypeLiquidityReward +} + +//Data returns the operation data LiquidityRewardOperation. +func (op *LiquidityRewardOperation) Data() interface{} { + return op +} diff --git a/types/voperation_return_vesting_delegation.go b/types/voperation_return_vesting_delegation.go new file mode 100644 index 0000000..a40a97d --- /dev/null +++ b/types/voperation_return_vesting_delegation.go @@ -0,0 +1,17 @@ +package types + +//ReturnVestingDelegationOperation represents return_vesting_delegation operation data. +type ReturnVestingDelegationOperation struct { + Account string `json:"account"` + VestingShares *Asset `json:"vesting_shares"` +} + +//Type function that defines the type of operation ReturnVestingDelegationOperation. +func (op *ReturnVestingDelegationOperation) Type() OpType { + return TypeReturnVestingDelegation +} + +//Data returns the operation data ReturnVestingDelegationOperation. +func (op *ReturnVestingDelegationOperation) Data() interface{} { + return op +} diff --git a/types/voperation_shutdown_witness.go b/types/voperation_shutdown_witness.go new file mode 100644 index 0000000..b606682 --- /dev/null +++ b/types/voperation_shutdown_witness.go @@ -0,0 +1,16 @@ +package types + +//ShutdownWitnessOperation represents shutdown_witness operation data. +type ShutdownWitnessOperation struct { + Owner string `json:"owner"` +} + +//Type function that defines the type of operation ShutdownWitnessOperation. +func (op *ShutdownWitnessOperation) Type() OpType { + return TypeShutdownWitness +} + +//Data returns the operation data ShutdownWitnessOperation. +func (op *ShutdownWitnessOperation) Data() interface{} { + return op +}