From df4cdf2294ec146e4577fc013816b420e0afa572 Mon Sep 17 00:00:00 2001 From: Who Soup Date: Wed, 16 Oct 2019 11:58:16 +0200 Subject: [PATCH 01/14] make counter atomic to stop race conditions --- jsonrpc.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/jsonrpc.go b/jsonrpc.go index d38009c..4ac9cda 100644 --- a/jsonrpc.go +++ b/jsonrpc.go @@ -13,6 +13,7 @@ import ( "io/ioutil" "net/http" "strings" + "sync/atomic" "time" ) @@ -353,11 +354,10 @@ func walletRequest(req *JSON2Request) (*JSON2Response, error) { } // newCounter is used to generate the ID field for the JSON2Request -func newCounter() func() int { - count := 0 - return func() int { - count += 1 - return count +func newCounter() func() uint32 { + var count uint32 + return func() uint32 { + return atomic.AddUint32(&count, 1) } } From 8ea03766814b1988f4f773e666a83a34fc9ed9af Mon Sep 17 00:00:00 2001 From: WhoSoup <34172041+WhoSoup@users.noreply.github.com> Date: Sat, 15 Aug 2020 03:33:42 +0200 Subject: [PATCH 02/14] support pending entries (#140) Co-authored-by: Brian Deery --- entry.go | 25 ++++++++++++++++++++----- entry_test.go | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 53 insertions(+), 6 deletions(-) diff --git a/entry.go b/entry.go index 81bfe72..77ed2dc 100644 --- a/entry.go +++ b/entry.go @@ -18,6 +18,12 @@ type Entry struct { Content []byte `json:"content"` } +type PendingEntry struct { + ChainID string `json:"chainid,omitempty"` + EntryHash string `json:"entryhash"` + Status string `json:"status"` +} + // NewEntryFromBytes creates a new Factom Entry from byte data. func NewEntryFromBytes(chainid []byte, content []byte, extids ...[]byte) *Entry { entry := new(Entry) @@ -298,18 +304,27 @@ func GetEntry(hash string) (*Entry, error) { // GetPendingEntries requests a list of all Entries that are waiting to be // written into the next block on the Factom Blockchain. -func GetPendingEntries() (string, error) { +// Entry commits that are not yet revealed do not have a ChainID. +// The order of entries is: +// |VM1...|VM2...|VM3...|...|Unconfirmed...| +// Where entries in VM# are ordered inside and Unconfirmed are random. +// Unconfirmed entries are entry reveals with a status of NotConfirmed +// and only exist on the node, not the rest of the network. +func GetPendingEntries() ([]PendingEntry, error) { req := NewJSON2Request("pending-entries", APICounter(), nil) resp, err := factomdRequest(req) if err != nil { - return "", err + return nil, err } if resp.Error != nil { - return "", err + return nil, err } - rBytes := resp.JSONResult() + pending := make([]PendingEntry, 0) + if err := json.Unmarshal(resp.JSONResult(), &pending); err != nil { + return nil, err + } - return string(rBytes), nil + return pending, nil } diff --git a/entry_test.go b/entry_test.go index a02b7ae..7f1f357 100644 --- a/entry_test.go +++ b/entry_test.go @@ -305,6 +305,38 @@ hello world ` if expectedResponse != response.String() { - t.Errorf("expected:%s\nrecieved:%s", expectedResponse, response) + t.Errorf("expected:%s\nreceived:%s", expectedResponse, response) + } +} + +func TestGetPending(t *testing.T) { + factomdResponse := `{ + "jsonrpc":"2.0", + "id":0, + "result":[ + {"entryhash":"64ebbb592737e8dcad8bce5db209713a8af18ca306ea6542e86bb1d1c88f5529","chainid":"cffce0f409ebba4ed236d49d89c70e4bd1f1367d86402a3363366683265a242d","status":"TransactionACK"}, + {"entryhash":"fcfafbef8471d99cfda1cb8d77a15d71d39d96acaf85da836c1f5075a71bb7c3","chainid":"cffce0f409ebba4ed236d49d89c70e4bd1f1367d86402a3363366683265a242d","status":"TransactionACK"}, + {"entryhash":"5ecfd88edacbaeea8dde7ead5634dbfb000c37e9d6a07ffa14432f3a1d07549d","chainid":"6e4540d08d5ac6a1a394e982fb6a2ab8b516ee751c37420055141b94fe070bfe","status":"TransactionACK"}, + {"entryhash":"d72101ca339aa7206ae6d82594b61051ff901f78276c389ce8b885f34caa9d34","chainid":null,"status":"TransactionACK"}, + {"entryhash":"2eb17336caf62f1eb869a5380298ced3b2653b496bd90e99a0a2da58c8b3101b","chainid":"a642a8674f46696cc47fdb6b65f9c87b2a19c5ea8123b3d2f0c13b6f33a9d5ef","status":"TransactionACK"} + ]}` + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + fmt.Fprintln(w, factomdResponse) + })) + defer ts.Close() + + SetFactomdServer(ts.URL[7:]) + + response, err := GetPendingEntries() + if err != nil { + t.Error(err) + } + + received := fmt.Sprintf("%+v", response) + expected := "[{ChainID:cffce0f409ebba4ed236d49d89c70e4bd1f1367d86402a3363366683265a242d EntryHash:64ebbb592737e8dcad8bce5db209713a8af18ca306ea6542e86bb1d1c88f5529 Status:TransactionACK} {ChainID:cffce0f409ebba4ed236d49d89c70e4bd1f1367d86402a3363366683265a242d EntryHash:fcfafbef8471d99cfda1cb8d77a15d71d39d96acaf85da836c1f5075a71bb7c3 Status:TransactionACK} {ChainID:6e4540d08d5ac6a1a394e982fb6a2ab8b516ee751c37420055141b94fe070bfe EntryHash:5ecfd88edacbaeea8dde7ead5634dbfb000c37e9d6a07ffa14432f3a1d07549d Status:TransactionACK} {ChainID: EntryHash:d72101ca339aa7206ae6d82594b61051ff901f78276c389ce8b885f34caa9d34 Status:TransactionACK} {ChainID:a642a8674f46696cc47fdb6b65f9c87b2a19c5ea8123b3d2f0c13b6f33a9d5ef EntryHash:2eb17336caf62f1eb869a5380298ced3b2653b496bd90e99a0a2da58c8b3101b Status:TransactionACK}]" + + if received != expected { + t.Errorf("expected:%s\nreceived:%s", expected, received) } } From 77f8d427e53c3f5d56c6ac6c5603b62332bc456c Mon Sep 17 00:00:00 2001 From: WhoSoup <34172041+WhoSoup@users.noreply.github.com> Date: Sat, 15 Aug 2020 03:44:13 +0200 Subject: [PATCH 03/14] add optional cookiejar and open node integration (#139) Co-authored-by: Brian Deery --- jsonrpc.go | 19 +++++++++++++++++++ util.go | 3 +++ 2 files changed, 22 insertions(+) diff --git a/jsonrpc.go b/jsonrpc.go index 4ac9cda..9b7a1ce 100644 --- a/jsonrpc.go +++ b/jsonrpc.go @@ -12,9 +12,12 @@ import ( "fmt" "io/ioutil" "net/http" + "net/http/cookiejar" "strings" "sync/atomic" "time" + + "golang.org/x/net/publicsuffix" ) // RPCConfig is the configuration for the API handler @@ -190,6 +193,18 @@ func GetWalletEncryption() (bool, string) { return RpcConfig.WalletTLSEnable, RpcConfig.WalletTLSCertFile } +// SetOpenNode points the Factomd server to the open node API and enables cookies +func SetOpenNode() { + EnableCookies() + RpcConfig.FactomdServer = OpenNode +} + +// EnableCookies will accept and manage cookies from the API server +func EnableCookies() { + // cookiejar.New never returns an error + cookieJar, _ = cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List}) +} + // SetFactomdServer sets where to find the factomd server, and tells the server its public ip func SetFactomdServer(s string) { RpcConfig.FactomdServer = s @@ -250,6 +265,10 @@ func factomdRequest(req *JSON2Request) (*JSON2Response, error) { host = RpcConfig.FactomdServer } } + + // no effect if nil + client.Jar = cookieJar + re, err := http.NewRequest( "POST", fmt.Sprintf("%s://%s/v2", scheme, host), diff --git a/util.go b/util.go index 35a645d..16d8074 100644 --- a/util.go +++ b/util.go @@ -11,6 +11,7 @@ import ( "encoding/binary" "fmt" "math" + "net/http" "regexp" "strconv" "strings" @@ -20,6 +21,7 @@ import ( const ( // ZeroHash is the string of all 00s ZeroHash = "0000000000000000000000000000000000000000000000000000000000000000" + OpenNode = "https://api.factomd.net" ) var ( @@ -28,6 +30,7 @@ var ( FactomdServer: "localhost:8088", WalletServer: "localhost:8089", } + cookieJar http.CookieJar ) // EntryCost calculates the cost in Entry Credits of adding an Entry to a Chain From 1ddb222002035fed729d136a9d978a2e4cde8149 Mon Sep 17 00:00:00 2001 From: Matt York Date: Fri, 14 Aug 2020 20:48:10 -0500 Subject: [PATCH 04/14] expose func for creating commit message w/o json (#84) --- entry.go | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/entry.go b/entry.go index 77ed2dc..e9721e6 100644 --- a/entry.go +++ b/entry.go @@ -177,11 +177,8 @@ func (e *Entry) UnmarshalJSON(data []byte) error { return nil } -// ComposeEntryCommit creates a JSON2Request to commit a new Entry via the -// factomd web api. The request includes the marshaled MessageRequest with the -// Entry Credit Signature. -func ComposeEntryCommit(e *Entry, ec *ECAddress) (*JSON2Request, error) { - buf := new(bytes.Buffer) +func EntryCommitMessage(e *Entry, ec *ECAddress) (*bytes.Buffer, error) { + buf := new(bytes.Buffer) // 1 byte version buf.Write([]byte{0}) @@ -204,9 +201,19 @@ func ComposeEntryCommit(e *Entry, ec *ECAddress) (*JSON2Request, error) { buf.Write(ec.PubBytes()) buf.Write(sig[:]) - params := messageRequest{Message: hex.EncodeToString(buf.Bytes())} - req := NewJSON2Request("commit-entry", APICounter(), params) + return buf, nil +} +// ComposeEntryCommit creates a JSON2Request to commit a new Entry via the +// factomd web api. The request includes the marshaled MessageRequest with the +// Entry Credit Signature. +func ComposeEntryCommit(e *Entry, ec *ECAddress) (*JSON2Request, error) { + b, err := EntryCommitMessage(e, ec) + if err != nil { + return nil, err + } + params := messageRequest{Message: hex.EncodeToString(b.Bytes())} + req := NewJSON2Request("commit-entry", APICounter(), params) return req, nil } From f0b08c27fb0be4c1649360874055c3c7c5b3973b Mon Sep 17 00:00:00 2001 From: Paul Bernier Date: Mon, 17 Aug 2020 05:59:09 -0700 Subject: [PATCH 05/14] fix: GetECBalance method documentation (#134) Co-authored-by: Brian Deery --- balance.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/balance.go b/balance.go index 9c674aa..8918058 100644 --- a/balance.go +++ b/balance.go @@ -18,7 +18,7 @@ type MultiBalanceResponse struct { } `json:"balances"` } -// GetECBalance returns the balance in factoshi (factoid * 1e8) of a given Entry +// GetECBalance returns the Entry Credit balance of a given Entry // Credit Public Address. func GetECBalance(addr string) (int64, error) { type balanceResponse struct { From 096274b35e23c6959d4b2169a169b8fb0f1fb18a Mon Sep 17 00:00:00 2001 From: WhoSoup <34172041+WhoSoup@users.noreply.github.com> Date: Tue, 18 Aug 2020 05:40:46 +0200 Subject: [PATCH 06/14] library function for api call "anchors" (#130) * anchors support * allow request for height 0 * anchor unit test --- anchors.go | 132 ++++++++++++++++++++++++++++++++++++++++++++++++ anchors_test.go | 107 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 239 insertions(+) create mode 100644 anchors.go create mode 100644 anchors_test.go diff --git a/anchors.go b/anchors.go new file mode 100644 index 0000000..b2667ef --- /dev/null +++ b/anchors.go @@ -0,0 +1,132 @@ +package factom + +import ( + "bytes" + "encoding/json" + "fmt" + "strings" +) + +// Anchors is an anchors response from factomd. +// Note that Ethereum or Bitcoin can be nil +type Anchors struct { + Height uint32 `json:"directoryblockheight"` + KeyMR string `json:"directoryblockkeymr"` + Bitcoin *AnchorBitcoin `json:"bitcoin"` + Ethereum *AnchorEthereum `json:"ethereum"` +} + +// AnchorBitcoin is the bitcoin specific anchor +type AnchorBitcoin struct { + TransactionHash string `json:"transactionhash"` + BlockHash string `json:"blockhash"` +} + +// AnchorEthereum is the ethereum specific anchor +type AnchorEthereum struct { + RecordHeight int64 `json:"recordheight"` + DBHeightMax int64 `json:"dbheightmax"` + DBHeightMin int64 `json:"dbheightmin"` + WindowMR string `json:"windowmr"` + MerkleBranch []MerkleNode `json:"merklebranch"` + + ContractAddress string `json:"contractaddress"` + TxID string `json:"txid"` + BlockHash string `json:"blockhash"` + TxIndex int64 `json:"txindex"` +} + +// MerkleNode is part of the ethereum anchor +type MerkleNode struct { + Left string `json:"left,omitempty"` + Right string `json:"right,omitempty"` + Top string `json:"top,omitempty"` +} + +func (a *Anchors) String() string { + var sb strings.Builder + fmt.Fprintf(&sb, "Height: %d\n", a.Height) + fmt.Fprintf(&sb, "KeyMR: %s\n", a.KeyMR) + + if a.Bitcoin != nil { + fmt.Fprintf(&sb, "Bitcoin {\n") + fmt.Fprintf(&sb, " TransactionHash: %s\n", a.Bitcoin.TransactionHash) + fmt.Fprintf(&sb, " BlockHash: %s\n", a.Bitcoin.BlockHash) + fmt.Fprintf(&sb, "}\n") + } else { + fmt.Fprintf(&sb, "Bitcoin {}\n") + } + + if a.Ethereum != nil { + fmt.Fprintf(&sb, "Ethereum {\n") + fmt.Fprintf(&sb, " RecordHeight: %d\n", a.Ethereum.RecordHeight) + fmt.Fprintf(&sb, " DBHeightMax: %d\n", a.Ethereum.DBHeightMax) + fmt.Fprintf(&sb, " DBHeightMin: %d\n", a.Ethereum.DBHeightMin) + fmt.Fprintf(&sb, " WindowMR: %s\n", a.Ethereum.WindowMR) + fmt.Fprintf(&sb, " MerkleBranch {\n") + for _, branch := range a.Ethereum.MerkleBranch { + fmt.Fprintf(&sb, " Branch {\n") + fmt.Fprintf(&sb, " Left: %s\n", branch.Left) + fmt.Fprintf(&sb, " Right: %s\n", branch.Right) + fmt.Fprintf(&sb, " Top: %s\n", branch.Top) + fmt.Fprintf(&sb, " Branch }\n") + } + fmt.Fprintf(&sb, " }\n") + fmt.Fprintf(&sb, " ContractAddress: %s\n", a.Ethereum.ContractAddress) + fmt.Fprintf(&sb, " TxID: %s\n", a.Ethereum.TxID) + fmt.Fprintf(&sb, " BlockHash: %s\n", a.Ethereum.BlockHash) + fmt.Fprintf(&sb, " TxIndex: %d\n", a.Ethereum.TxIndex) + fmt.Fprintf(&sb, "}\n") + } else { + fmt.Fprintf(&sb, "Ethereum {}\n") + } + + return sb.String() +} + +// UnmarshalJSON is an unmarshaller that handles the variable response from factomd +func (a *Anchors) UnmarshalJSON(data []byte) error { + type tmp *Anchors // unmarshal into a new type to prevent infinite loop + // json can't unmarshal a bool into a struct, but it can recognize a null pointer + data = bytes.Replace(data, []byte("\"ethereum\":false"), []byte("\"ethereum\":null"), -1) + data = bytes.Replace(data, []byte("\"bitcoin\":false"), []byte("\"bitcoin\":null"), -1) + return json.Unmarshal(data, tmp(a)) +} + +func getAnchors(hash string, height int64) (*Anchors, error) { + var params interface{} + if hash != "" { + params = hashRequest{Hash: hash} + } else { + params = heightRequest{Height: height} + } + req := NewJSON2Request("anchors", APICounter(), params) + resp, err := factomdRequest(req) + if err != nil { + return nil, err + } + if resp.Error != nil { + return nil, resp.Error + } + var res Anchors + err = json.Unmarshal(resp.Result, &res) + if err != nil { + return nil, err + } + + return &res, nil +} + +// GetAnchors retrieves the bitcoin and ethereum anchors from factod. +// Hash can be entry hash, entry block keymr, factoid block keymr, +// admin block lookup hash, entry credit block header hash, or +// directory block keymr +func GetAnchors(hash string) (*Anchors, error) { + return getAnchors(hash, 0) +} + +// GetAnchorsByHeight retrieves the bitcoin and ethereum anchors for +// a specific height +func GetAnchorsByHeight(height int64) (*Anchors, error) { + return getAnchors("", height) +} diff --git a/anchors_test.go b/anchors_test.go new file mode 100644 index 0000000..8704770 --- /dev/null +++ b/anchors_test.go @@ -0,0 +1,107 @@ +package factom + +import ( + "fmt" + "net/http" + "net/http/httptest" + "testing" +) + +func TestGetAnchors(t *testing.T) { + factomdResponse := `{"jsonrpc":"2.0","id":0,"result":{"directoryblockheight":200000,"directoryblockkeymr":"ce86fc790dd1462aea255adaa64e2f21c871995df2c2c119352d869fa1d7269f","bitcoin":{"transactionhash":"6d2d1e506528ae3b476d70fb05517bbbb152a4698a23ff78b4d87249027f53ca","blockhash":"0000000000000000000234e270b3fa6de63caad8a319731db5643ddb16b80cdf"},"ethereum":{"recordheight":200001,"dbheightmax":200000,"dbheightmin":199001,"windowmr":"935480547a2545161438da05136ec4238d88e7f8e1075687d8292fcafc1d0b22","merklebranch":[{"left":"a92a4460a9555c8b57a282e7ad4514d0f5aa8a612963da79db0e7cede6299bcd","right":"ce86fc790dd1462aea255adaa64e2f21c871995df2c2c119352d869fa1d7269f","top":"bbbf494fff20d8fbada47980498a96e49fba920fd1ca4e0586af4961e4318354"},{"left":"b41ffe5f9b710edf6bcf1a6d3e92685155411111d640f2b2224ab15f04c46161","right":"bbbf494fff20d8fbada47980498a96e49fba920fd1ca4e0586af4961e4318354","top":"ca1e7d59dc295c48ebddf6c068826584fb5adb38798f5cac7f964509627825f5"},{"left":"c919c0fb94900f3016c75a7f9f992c79aa7db126db6c6c3b1af92bbee174bda6","right":"ca1e7d59dc295c48ebddf6c068826584fb5adb38798f5cac7f964509627825f5","top":"3c8e97e32b4dbc0e4d82f066217492ca72ca722b91dbc764c311c39a9196bdda"},{"left":"3c8e97e32b4dbc0e4d82f066217492ca72ca722b91dbc764c311c39a9196bdda","right":"3c8e97e32b4dbc0e4d82f066217492ca72ca722b91dbc764c311c39a9196bdda","top":"5713abb24c8a8480f5c04bfdb8668a93e8b0a24a0562afbc7f362d665938a89e"},{"left":"5713abb24c8a8480f5c04bfdb8668a93e8b0a24a0562afbc7f362d665938a89e","right":"5713abb24c8a8480f5c04bfdb8668a93e8b0a24a0562afbc7f362d665938a89e","top":"a7ab3a72f5e754f3e10f04b9f7f2e50f36c688776b521918a755e62fd6c2da34"},{"left":"5e42d2f5ba59338b317369bbc53fbb70c094364ae8c15c9a291dad65dec9d839","right":"a7ab3a72f5e754f3e10f04b9f7f2e50f36c688776b521918a755e62fd6c2da34","top":"bc5c336d7e53b6d9f2832038c69a78eb7300bd539b6002d44f450a4d2c979aa3"},{"left":"31041410eb7bd6a9fabe974a0f4eabe477ffbbf5806fdaee5da98faff63b8142","right":"bc5c336d7e53b6d9f2832038c69a78eb7300bd539b6002d44f450a4d2c979aa3","top":"f9b5967e2acadfac93bc2141ee9f85b0649f89452e44e4f8b1cbe877457a0da4"},{"left":"04aa1b2d35ea99becd92bcfccc7406aea37287a58425b83349d0fc8cc7bec443","right":"f9b5967e2acadfac93bc2141ee9f85b0649f89452e44e4f8b1cbe877457a0da4","top":"77eca087628e243bf2914162be6c4aefaf736ed56e8c125b955cef3c48a1ab4b"},{"left":"be8394e3f3a205ad37afb8af778695e2914f071b3d215e586a93288fd692eb00","right":"77eca087628e243bf2914162be6c4aefaf736ed56e8c125b955cef3c48a1ab4b","top":"12aa5f990bd209dfaa83d9ea050e96e3b44e54ffee02252028fe969da3ab690d"},{"left":"2bdead51afe10d07c5dc5b29351fedd4a13849261359a6f22100dcc7ff50543a","right":"12aa5f990bd209dfaa83d9ea050e96e3b44e54ffee02252028fe969da3ab690d","top":"935480547a2545161438da05136ec4238d88e7f8e1075687d8292fcafc1d0b22"}],"contractaddress":"0xfac701d9554a008e48b6307fb90457ba3959e8a8","txid":"0xd57492c9d505e3052c454acdfc3768bc3eb8859c91829654346503ef2dcb6a23","blockhash":"0x49e8f3c394079e6c3964fdf943ea9759475d4ca5aec90c9a695be07efdd88d32","txindex":31}}}` + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + fmt.Fprintln(w, factomdResponse) + })) + defer ts.Close() + + SetFactomdServer(ts.URL[7:]) + + response, err := GetAnchors("ce86fc790dd1462aea255adaa64e2f21c871995df2c2c119352d869fa1d7269f") // irrelevant, hardcoded above + if err != nil { + t.Error(err) + } + response2, err := GetAnchorsByHeight(200000) // irrelevant, hardcoded above + if err != nil { + t.Error(err) + } + + received1 := fmt.Sprintf("%+v", response) + received2 := fmt.Sprintf("%+v", response2) + expected := `Height: 200000 +KeyMR: ce86fc790dd1462aea255adaa64e2f21c871995df2c2c119352d869fa1d7269f +Bitcoin { + TransactionHash: 6d2d1e506528ae3b476d70fb05517bbbb152a4698a23ff78b4d87249027f53ca + BlockHash: 0000000000000000000234e270b3fa6de63caad8a319731db5643ddb16b80cdf +} +Ethereum { + RecordHeight: 200001 + DBHeightMax: 200000 + DBHeightMin: 199001 + WindowMR: 935480547a2545161438da05136ec4238d88e7f8e1075687d8292fcafc1d0b22 + MerkleBranch { + Branch { + Left: a92a4460a9555c8b57a282e7ad4514d0f5aa8a612963da79db0e7cede6299bcd + Right: ce86fc790dd1462aea255adaa64e2f21c871995df2c2c119352d869fa1d7269f + Top: bbbf494fff20d8fbada47980498a96e49fba920fd1ca4e0586af4961e4318354 + Branch } + Branch { + Left: b41ffe5f9b710edf6bcf1a6d3e92685155411111d640f2b2224ab15f04c46161 + Right: bbbf494fff20d8fbada47980498a96e49fba920fd1ca4e0586af4961e4318354 + Top: ca1e7d59dc295c48ebddf6c068826584fb5adb38798f5cac7f964509627825f5 + Branch } + Branch { + Left: c919c0fb94900f3016c75a7f9f992c79aa7db126db6c6c3b1af92bbee174bda6 + Right: ca1e7d59dc295c48ebddf6c068826584fb5adb38798f5cac7f964509627825f5 + Top: 3c8e97e32b4dbc0e4d82f066217492ca72ca722b91dbc764c311c39a9196bdda + Branch } + Branch { + Left: 3c8e97e32b4dbc0e4d82f066217492ca72ca722b91dbc764c311c39a9196bdda + Right: 3c8e97e32b4dbc0e4d82f066217492ca72ca722b91dbc764c311c39a9196bdda + Top: 5713abb24c8a8480f5c04bfdb8668a93e8b0a24a0562afbc7f362d665938a89e + Branch } + Branch { + Left: 5713abb24c8a8480f5c04bfdb8668a93e8b0a24a0562afbc7f362d665938a89e + Right: 5713abb24c8a8480f5c04bfdb8668a93e8b0a24a0562afbc7f362d665938a89e + Top: a7ab3a72f5e754f3e10f04b9f7f2e50f36c688776b521918a755e62fd6c2da34 + Branch } + Branch { + Left: 5e42d2f5ba59338b317369bbc53fbb70c094364ae8c15c9a291dad65dec9d839 + Right: a7ab3a72f5e754f3e10f04b9f7f2e50f36c688776b521918a755e62fd6c2da34 + Top: bc5c336d7e53b6d9f2832038c69a78eb7300bd539b6002d44f450a4d2c979aa3 + Branch } + Branch { + Left: 31041410eb7bd6a9fabe974a0f4eabe477ffbbf5806fdaee5da98faff63b8142 + Right: bc5c336d7e53b6d9f2832038c69a78eb7300bd539b6002d44f450a4d2c979aa3 + Top: f9b5967e2acadfac93bc2141ee9f85b0649f89452e44e4f8b1cbe877457a0da4 + Branch } + Branch { + Left: 04aa1b2d35ea99becd92bcfccc7406aea37287a58425b83349d0fc8cc7bec443 + Right: f9b5967e2acadfac93bc2141ee9f85b0649f89452e44e4f8b1cbe877457a0da4 + Top: 77eca087628e243bf2914162be6c4aefaf736ed56e8c125b955cef3c48a1ab4b + Branch } + Branch { + Left: be8394e3f3a205ad37afb8af778695e2914f071b3d215e586a93288fd692eb00 + Right: 77eca087628e243bf2914162be6c4aefaf736ed56e8c125b955cef3c48a1ab4b + Top: 12aa5f990bd209dfaa83d9ea050e96e3b44e54ffee02252028fe969da3ab690d + Branch } + Branch { + Left: 2bdead51afe10d07c5dc5b29351fedd4a13849261359a6f22100dcc7ff50543a + Right: 12aa5f990bd209dfaa83d9ea050e96e3b44e54ffee02252028fe969da3ab690d + Top: 935480547a2545161438da05136ec4238d88e7f8e1075687d8292fcafc1d0b22 + Branch } + } + ContractAddress: 0xfac701d9554a008e48b6307fb90457ba3959e8a8 + TxID: 0xd57492c9d505e3052c454acdfc3768bc3eb8859c91829654346503ef2dcb6a23 + BlockHash: 0x49e8f3c394079e6c3964fdf943ea9759475d4ca5aec90c9a695be07efdd88d32 + TxIndex: 31 +} +` + if received1 != expected { + t.Errorf("GetAnchors() expected:%s\nreceived:%s", expected, received1) + } + if received2 != expected { + t.Errorf("GetAnchorsByHeight() expected:%s\nreceived:%s", expected, received2) + } + +} From 70afea54f3695611fba5d743f467ccf86e9da2e5 Mon Sep 17 00:00:00 2001 From: WhoSoup <34172041+WhoSoup@users.noreply.github.com> Date: Wed, 19 Aug 2020 03:08:10 +0200 Subject: [PATCH 07/14] Make raw data optional in API Requests (#137) * add noraw option * use noraw param * add noraw option * use noraw param * remove raw data request * remove unnecessary comment Co-authored-by: Brian Deery --- ablock.go | 36 +++++++++++------------------------- ablock_test.go | 6 ++---- byHeight.go | 4 +--- dblock.go | 22 +++++++--------------- dblock_test.go | 3 +-- ecblock.go | 33 ++++++++++----------------------- ecblock_test.go | 6 ++---- fblock.go | 39 ++++++++++++--------------------------- fblock_test.go | 6 ++---- wallet/txdatabase.go | 22 ++++++++++++++++++---- wsapistructs.go | 2 ++ 11 files changed, 68 insertions(+), 111 deletions(-) diff --git a/ablock.go b/ablock.go index c135305..30ac639 100644 --- a/ablock.go +++ b/ablock.go @@ -5,7 +5,6 @@ package factom import ( - "encoding/hex" "encoding/json" "errors" "fmt" @@ -602,22 +601,21 @@ func (a *AdminAddAuthorityEfficiency) String() string { return s } -// GetABlock requests a specific ABlock from the factomd API. -func GetABlock(keymr string) (ablock *ABlock, raw []byte, err error) { - params := keyMRRequest{KeyMR: keymr} +// GetABlock requests a specific ABlock from the factomd API +func GetABlock(keymr string) (ablock *ABlock, err error) { + params := keyMRRequest{KeyMR: keymr, NoRaw: true} req := NewJSON2Request("admin-block", APICounter(), params) resp, err := factomdRequest(req) if err != nil { return } if resp.Error != nil { - return nil, nil, resp.Error + return nil, resp.Error } // create a wraper construct for the ECBlock API return wrap := new(struct { - ABlock *ABlock `json:"ablock"` - RawData string `json:"rawdata"` + ABlock *ABlock `json:"ablock"` }) err = json.Unmarshal(resp.JSONResult(), wrap) @@ -625,39 +623,27 @@ func GetABlock(keymr string) (ablock *ABlock, raw []byte, err error) { return } - raw, err = hex.DecodeString(wrap.RawData) - if err != nil { - return - } - - return wrap.ABlock, raw, nil + return wrap.ABlock, nil } // GetABlockByHeight requests an ABlock of a specific height from the factomd -// API. -func GetABlockByHeight(height int64) (ablock *ABlock, raw []byte, err error) { - params := heightRequest{Height: height} +func GetABlockByHeight(height int64) (ablock *ABlock, err error) { + params := heightRequest{Height: height, NoRaw: true} req := NewJSON2Request("ablock-by-height", APICounter(), params) resp, err := factomdRequest(req) if err != nil { return } if resp.Error != nil { - return nil, nil, resp.Error + return nil, resp.Error } wrap := new(struct { - ABlock *ABlock `json:"ablock"` - RawData string `json:"rawdata"` + ABlock *ABlock `json:"ablock"` }) if err = json.Unmarshal(resp.JSONResult(), wrap); err != nil { return } - raw, err = hex.DecodeString(wrap.RawData) - if err != nil { - return - } - - return wrap.ABlock, raw, nil + return wrap.ABlock, nil } diff --git a/ablock_test.go b/ablock_test.go index db73105..194b7c3 100644 --- a/ablock_test.go +++ b/ablock_test.go @@ -71,12 +71,11 @@ func TestGetABlock(t *testing.T) { SetFactomdServer(ts.URL[7:]) - ab, raw, err := GetABlock("e7eb4bda495dbe7657cae1525b6be78bd2fdbad952ebde506b6a97e1cf8f431e") + ab, err := GetABlock("e7eb4bda495dbe7657cae1525b6be78bd2fdbad952ebde506b6a97e1cf8f431e") if err != nil { t.Error(err) } t.Log("ABlock:", ab) - t.Log(fmt.Sprintf("Raw: %x\n", raw)) } func TestGetABlockByHeight(t *testing.T) { @@ -120,10 +119,9 @@ func TestGetABlockByHeight(t *testing.T) { SetFactomdServer(ts.URL[7:]) - ab, raw, err := GetABlockByHeight(20000) + ab, err := GetABlockByHeight(20000) if err != nil { t.Error(err) } t.Log("ABlock:", ab) - t.Log(fmt.Sprintf("Raw: %x\n", raw)) } diff --git a/byHeight.go b/byHeight.go index 626d12c..66c5b3b 100644 --- a/byHeight.go +++ b/byHeight.go @@ -9,8 +9,6 @@ import ( "fmt" ) - - type JStruct struct { data []byte } @@ -57,7 +55,7 @@ func (f *BlockByHeightRawResponse) String() string { // GetBlockByHeightRaw fetches the specified block type by height // Deprecated: use ablock, dblock, eblock, ecblock and fblock instead. func GetBlockByHeightRaw(blockType string, height int64) (*BlockByHeightRawResponse, error) { - params := heightRequest{Height: height} + params := heightRequest{Height: height, NoRaw: false} // include raw req := NewJSON2Request(fmt.Sprintf("%vblock-by-height", blockType), APICounter(), params) resp, err := factomdRequest(req) if err != nil { diff --git a/dblock.go b/dblock.go index ec126ad..62ebbf2 100644 --- a/dblock.go +++ b/dblock.go @@ -5,7 +5,6 @@ package factom import ( - "encoding/hex" "encoding/json" "fmt" ) @@ -67,8 +66,7 @@ func (db *DBlock) String() string { // return the propper information (it should match the dblock-by-height call) // GetDBlock requests a Directory Block by its Key Merkle Root from the factomd -// API. -func GetDBlock(keymr string) (dblock *DBlock, raw []byte, err error) { +func GetDBlock(keymr string) (dblock *DBlock, err error) { params := keyMRRequest{KeyMR: keymr} req := NewJSON2Request("directory-block", APICounter(), params) resp, err := factomdRequest(req) @@ -76,7 +74,7 @@ func GetDBlock(keymr string) (dblock *DBlock, raw []byte, err error) { return } if resp.Error != nil { - return nil, nil, resp.Error + return nil, resp.Error } db := new(struct { @@ -104,20 +102,19 @@ func GetDBlock(keymr string) (dblock *DBlock, raw []byte, err error) { // GetDBlockByHeight requests a Directory Block by its block height from the factomd // API. -func GetDBlockByHeight(height int64) (dblock *DBlock, raw []byte, err error) { - params := heightRequest{Height: height} +func GetDBlockByHeight(height int64) (dblock *DBlock, err error) { + params := heightRequest{Height: height, NoRaw: true} req := NewJSON2Request("dblock-by-height", APICounter(), params) resp, err := factomdRequest(req) if err != nil { return } if resp.Error != nil { - return nil, nil, resp.Error + return nil, resp.Error } wrap := new(struct { - DBlock *DBlock `json:"dblock"` - RawData string `json:"rawdata"` + DBlock *DBlock `json:"dblock"` }) err = json.Unmarshal(resp.JSONResult(), wrap) @@ -125,13 +122,8 @@ func GetDBlockByHeight(height int64) (dblock *DBlock, raw []byte, err error) { return } - raw, err = hex.DecodeString(wrap.RawData) - if err != nil { - return - } - wrap.DBlock.SequenceNumber = height - return wrap.DBlock, raw, nil + return wrap.DBlock, nil } // GetDBlockHead requests the most recent Directory Block Key Merkel Root diff --git a/dblock_test.go b/dblock_test.go index 7bf03fa..a25c4a0 100644 --- a/dblock_test.go +++ b/dblock_test.go @@ -61,12 +61,11 @@ func TestGetDBlockByHeight(t *testing.T) { SetFactomdServer(ts.URL[7:]) - d, raw, err := GetDBlockByHeight(100) + d, err := GetDBlockByHeight(100) if err != nil { t.Error(err) } t.Log("dblock:", d) - t.Log(fmt.Sprintf("raw: %x\n", raw)) } func TestGetDBlockHead(t *testing.T) { diff --git a/ecblock.go b/ecblock.go index 4e68663..e35b56d 100644 --- a/ecblock.go +++ b/ecblock.go @@ -350,61 +350,48 @@ func (e *ECBalanceIncrease) String() string { return s } -// GetECBlock requests a specified Entry Credit Block from the factomd API. -func GetECBlock(keymr string) (ecblock *ECBlock, raw []byte, err error) { - params := keyMRRequest{KeyMR: keymr} +// GetECBlock requests a specified Entry Credit Block from the factomd API +func GetECBlock(keymr string) (ecblock *ECBlock, err error) { + params := keyMRRequest{KeyMR: keymr, NoRaw: true} req := NewJSON2Request("entrycredit-block", APICounter(), params) resp, err := factomdRequest(req) if err != nil { return } if resp.Error != nil { - return nil, nil, resp.Error + return nil, resp.Error } // create a wraper construct for the ECBlock API return wrap := new(struct { ECBlock *ECBlock `json:"ecblock"` - RawData string `json:"rawdata"` }) err = json.Unmarshal(resp.JSONResult(), wrap) if err != nil { return } - raw, err = hex.DecodeString(wrap.RawData) - if err != nil { - return - } - - return wrap.ECBlock, raw, nil + return wrap.ECBlock, nil } -// GetECBlockByHeight request an Entry Credit Block of a given height from the -// factomd API. -func GetECBlockByHeight(height int64) (ecblock *ECBlock, raw []byte, err error) { - params := heightRequest{Height: height} +// GetECBlockByHeight request an Entry Credit Block of a given height +func GetECBlockByHeight(height int64) (ecblock *ECBlock, err error) { + params := heightRequest{Height: height, NoRaw: true} req := NewJSON2Request("ecblock-by-height", APICounter(), params) resp, err := factomdRequest(req) if err != nil { return } if resp.Error != nil { - return nil, nil, resp.Error + return nil, resp.Error } wrap := new(struct { ECBlock *ECBlock `json:"ecblock"` - RawData string `json:"rawdata"` }) if err = json.Unmarshal(resp.JSONResult(), wrap); err != nil { return } - raw, err = hex.DecodeString(wrap.RawData) - if err != nil { - return - } - - return wrap.ECBlock, raw, nil + return wrap.ECBlock, nil } diff --git a/ecblock_test.go b/ecblock_test.go index acea307..1eb7668 100644 --- a/ecblock_test.go +++ b/ecblock_test.go @@ -121,12 +121,11 @@ func TestGetECBlock(t *testing.T) { SetFactomdServer(ts.URL[7:]) - ecb, raw, err := GetECBlock("a7baaa24e477a0acef165461d70ec94ff3f33ad15562ecbe937967a761929a17") + ecb, err := GetECBlock("a7baaa24e477a0acef165461d70ec94ff3f33ad15562ecbe937967a761929a17") if err != nil { t.Error(err) } t.Log("ECBlock: ", ecb) - t.Log(fmt.Sprintf("raw: %x\n", raw)) } func TestGetECBlockByHeight(t *testing.T) { @@ -212,10 +211,9 @@ func TestGetECBlockByHeight(t *testing.T) { SetFactomdServer(ts.URL[7:]) - ecb, raw, err := GetECBlockByHeight(10199) + ecb, err := GetECBlockByHeight(10199) if err != nil { t.Error(err) } t.Log("ECBlock: ", ecb) - t.Log(fmt.Sprintf("raw: %x\n", raw)) } diff --git a/fblock.go b/fblock.go index c0a14cc..b7787a7 100644 --- a/fblock.go +++ b/fblock.go @@ -5,7 +5,6 @@ package factom import ( - "encoding/hex" "encoding/json" "fmt" ) @@ -44,62 +43,48 @@ func (f *FBlock) String() string { return s } -// GetFblock requests a specified Factoid Block from factomd. It returns the -// FBlock struct, the raw binary FBlock, and an error if present. -func GetFBlock(keymr string) (fblock *FBlock, raw []byte, err error) { - params := keyMRRequest{KeyMR: keymr} +// GetFBlock requests a specified Factoid Block from factomd by its keymr +func GetFBlock(keymr string) (fblock *FBlock, err error) { + params := keyMRRequest{KeyMR: keymr, NoRaw: true} req := NewJSON2Request("factoid-block", APICounter(), params) resp, err := factomdRequest(req) if err != nil { return } if resp.Error != nil { - return nil, nil, resp.Error + return nil, resp.Error } // Create temporary struct to unmarshal json object wrap := new(struct { - FBlock *FBlock `json:"fblock"` - RawData string `json:"rawdata"` + FBlock *FBlock `json:"fblock"` }) if err = json.Unmarshal(resp.JSONResult(), wrap); err != nil { return } - raw, err = hex.DecodeString(wrap.RawData) - if err != nil { - return - } - - return wrap.FBlock, raw, nil + return wrap.FBlock, nil } -// GetFBlockByHeight requests a specified Factoid Block from factomd, returning -// the FBlock struct, the raw binary FBlock, and an error if present. -func GetFBlockByHeight(height int64) (ablock *FBlock, raw []byte, err error) { - params := heightRequest{Height: height} +// GetFBlockByHeight requests a specified Factoid Block from factomd by its height +func GetFBlockByHeight(height int64) (fblock *FBlock, err error) { + params := heightRequest{Height: height, NoRaw: true} req := NewJSON2Request("fblock-by-height", APICounter(), params) resp, err := factomdRequest(req) if err != nil { return } if resp.Error != nil { - return nil, nil, resp.Error + return nil, resp.Error } wrap := new(struct { - FBlock *FBlock `json:"fblock"` - RawData string `json:"rawdata"` + FBlock *FBlock `json:"fblock"` }) if err = json.Unmarshal(resp.JSONResult(), wrap); err != nil { return } - raw, err = hex.DecodeString(wrap.RawData) - if err != nil { - return - } - - return wrap.FBlock, raw, nil + return wrap.FBlock, nil } diff --git a/fblock_test.go b/fblock_test.go index ec236be..42da6b3 100644 --- a/fblock_test.go +++ b/fblock_test.go @@ -99,12 +99,11 @@ func TestGetFBlock(t *testing.T) { SetFactomdServer(ts.URL[7:]) - fb, raw, err := GetFBlock("cfcac07b29ccfa413aeda646b5d386006468189939dfdfa6415b97cc35f2ea1a") + fb, err := GetFBlock("cfcac07b29ccfa413aeda646b5d386006468189939dfdfa6415b97cc35f2ea1a") if err != nil { t.Error(err) } t.Log(fb) - t.Log(fmt.Printf("%x\n", raw)) } func TestGetFBlockByHeight(t *testing.T) { @@ -175,10 +174,9 @@ func TestGetFBlockByHeight(t *testing.T) { SetFactomdServer(ts.URL[7:]) - ab, raw, err := GetFBlockByHeight(20000) + ab, err := GetFBlockByHeight(20000) if err != nil { t.Error(err) } t.Log("FBlock:", ab) - t.Log(fmt.Sprintf("Raw: %x\n", raw)) } diff --git a/wallet/txdatabase.go b/wallet/txdatabase.go index a6d4d06..903d53b 100644 --- a/wallet/txdatabase.go +++ b/wallet/txdatabase.go @@ -5,6 +5,7 @@ package wallet import ( + "encoding/hex" "fmt" "os" @@ -387,7 +388,7 @@ func fblockHead() (interfaces.IFBlock, error) { if err != nil { return nil, err } - dblock, _, err := factom.GetDBlock(dbhead) + dblock, err := factom.GetDBlock(dbhead) if err != nil { return nil, err } @@ -406,25 +407,38 @@ func fblockHead() (interfaces.IFBlock, error) { } func getfblock(keymr string) (interfaces.IFBlock, error) { - _, raw, err := factom.GetFBlock(keymr) + raw, err := factom.GetRaw(keymr) if err != nil { return nil, err } + return factoid.UnmarshalFBlock(raw) } func getfblockbyheight(height uint32) (interfaces.IFBlock, error) { - _, raw, err := factom.GetFBlockByHeight(int64(height)) + resp, err := factom.GetBlockByHeightRaw("fblock", int64(height)) + if err != nil { + return nil, err + } + + raw, err := hex.DecodeString(resp.RawData) if err != nil { return nil, err } + return factoid.UnmarshalFBlock(raw) } func getdblockbyheight(height uint32) (interfaces.IDirectoryBlock, error) { - _, raw, err := factom.GetDBlockByHeight(int64(height)) + resp, err := factom.GetBlockByHeightRaw("dblock", int64(height)) if err != nil { return nil, err } + + raw, err := hex.DecodeString(resp.RawData) + if err != nil { + return nil, err + } + return directoryBlock.UnmarshalDBlock(raw) } diff --git a/wsapistructs.go b/wsapistructs.go index c0bbf70..ef86292 100644 --- a/wsapistructs.go +++ b/wsapistructs.go @@ -8,6 +8,7 @@ package factom type heightRequest struct { Height int64 `json:"height"` + NoRaw bool `json:"noraw,omitempty"` } type replayRequest struct { @@ -59,6 +60,7 @@ type importRequest struct { type keyMRRequest struct { KeyMR string `json:"keymr"` + NoRaw bool `json:"noraw,omitempty"` } type messageRequest struct { From 205084f0a7f7108ef142d88e9d5088fb24577621 Mon Sep 17 00:00:00 2001 From: WhoSoup <34172041+WhoSoup@users.noreply.github.com> Date: Thu, 20 Aug 2020 04:21:43 +0200 Subject: [PATCH 08/14] remove wallet + add go mod (#144) --- glide.lock | 266 ----- glide.yaml | 32 - go.mod | 21 + go.sum | 50 + transaction_test.go | 42 - wallet/VERSION | 1 - wallet/database.go | 177 --- wallet/database_test.go | 352 ------ wallet/importexport.go | 70 -- wallet/importexport_test.go | 25 - wallet/importexportenc.go | 70 -- wallet/importexportenc_test.go | 5 - wallet/importexportldb.go | 70 -- wallet/sign.go | 28 - wallet/transaction.go | 427 -------- wallet/transaction_test.go | 245 ----- wallet/txdatabase.go | 444 -------- wallet/txdatabase_test.go | 105 -- wallet/util.go | 71 -- wallet/util_test.go | 19 - wallet/v1conversion.go | 154 --- wallet/v1conversion_test.go | 39 - wallet/walletDBO.go | 626 ----------- wallet/walletDBO_test.go | 109 -- wallet/wsapi/10000addressesTest.sh | 30 - wallet/wsapi/errors.go | 79 -- wallet/wsapi/structs.go | 221 ---- wallet/wsapi/wsapi.go | 1594 ---------------------------- wallet/wsapi/wsapi_test.gox | 144 --- wallet_test.go | 139 --- 30 files changed, 71 insertions(+), 5584 deletions(-) delete mode 100644 glide.lock delete mode 100644 glide.yaml create mode 100644 go.mod create mode 100644 go.sum delete mode 100644 wallet/VERSION delete mode 100644 wallet/database.go delete mode 100644 wallet/database_test.go delete mode 100644 wallet/importexport.go delete mode 100644 wallet/importexport_test.go delete mode 100644 wallet/importexportenc.go delete mode 100644 wallet/importexportenc_test.go delete mode 100644 wallet/importexportldb.go delete mode 100644 wallet/sign.go delete mode 100644 wallet/transaction.go delete mode 100644 wallet/transaction_test.go delete mode 100644 wallet/txdatabase.go delete mode 100644 wallet/txdatabase_test.go delete mode 100644 wallet/util.go delete mode 100644 wallet/util_test.go delete mode 100644 wallet/v1conversion.go delete mode 100644 wallet/v1conversion_test.go delete mode 100644 wallet/walletDBO.go delete mode 100644 wallet/walletDBO_test.go delete mode 100755 wallet/wsapi/10000addressesTest.sh delete mode 100644 wallet/wsapi/errors.go delete mode 100644 wallet/wsapi/structs.go delete mode 100644 wallet/wsapi/wsapi.go delete mode 100644 wallet/wsapi/wsapi_test.gox diff --git a/glide.lock b/glide.lock deleted file mode 100644 index 77c3a15..0000000 --- a/glide.lock +++ /dev/null @@ -1,266 +0,0 @@ -hash: 713151aa273eee2903d840e4b627c53d660de4d347a8a3e3a741bda0f5a0a193 -updated: 2019-07-08T12:30:27.753120923-05:00 -imports: -- name: github.com/beorn7/perks - version: 3a771d992973f24aa725d07868b467d1ddfceafb - subpackages: - - quantile -- name: github.com/btcsuitereleases/btcutil - version: f2b1058a82554c0c7c3b8809c5956c38374604d8 - subpackages: - - base58 -- name: github.com/FactomProject/basen - version: fe3947df716ebfda9847eb1b9a48f9592e06478c -- name: github.com/FactomProject/bolt - version: 952a1b4e9a55f458d536cf703bce25a4e9c99841 -- name: github.com/FactomProject/btcutil - version: 43986820ccd50b2b02b83e575cb3a39c87457482 - subpackages: - - base58 - - certs -- name: github.com/FactomProject/btcutilecc - version: d3a63a5752ecf3fbc06bd97365da752111c263df -- name: github.com/FactomProject/ed25519 - version: 38002c4fe7b609c2fa3144a3cc29b997b756bb4c - subpackages: - - edwards25519 -- name: github.com/FactomProject/factoid - version: 3ee9763f86849036723d1b059216e08a6d34b184 - subpackages: - - block - - database - - state - - state/stateinit - - wallet -- name: github.com/FactomProject/FactomCode - version: d7e03150a9d5d8672e52a0769a4c6376f5c12631 - subpackages: - - controlpanel -- name: github.com/FactomProject/factomd - version: 7fd7716f5af4f8962ef9390adb0f4e4416eb22da - subpackages: - - Utilities/CorrectChainHeads/correctChainHeads - - Utilities/tools - - activations - - anchor - - common/adminBlock - - common/constants - - common/directoryBlock - - common/directoryBlock/dbInfo - - common/entryBlock - - common/entryBlock/specialEntries - - common/entryCreditBlock - - common/factoid - - common/globals - - common/identity - - common/identityEntries - - common/interfaces - - common/messages - - common/messages/electionMsgs - - common/messages/msgbase - - common/messages/msgsupport - - common/primitives - - common/primitives/random - - controlPanel - - controlPanel/dataDumpFormatting - - controlPanel/files - - controlPanel/files/statics - - controlPanel/files/templates - - database/blockExtractor - - database/boltdb - - database/databaseOverlay - - database/hybridDB - - database/leveldb - - database/mapdb - - database/securedb - - elections - - electionsCore/election - - electionsCore/election/volunteercontrol - - electionsCore/errorhandling - - electionsCore/imessage - - electionsCore/messages - - electionsCore/primitives - - engine - - log - - p2p - - receipts - - state - - testHelper - - util - - util/atomic - - wsapi -- name: github.com/FactomProject/go-bip32 - version: 3b593af1c415abc1017648e4eb24a88c32fee0f3 -- name: github.com/FactomProject/go-bip39 - version: d1007fb78d9a7ec65314dad412973e63caf4c527 -- name: github.com/FactomProject/go-bip44 - version: b541a96d8da98567af7610ef96105a834e6ed46c -- name: github.com/FactomProject/go-simplejson - version: aabad6e819789e569bd6aabf444c935aa9ba1e44 -- name: github.com/FactomProject/go-spew - version: ddfaec9b42f58fc8172b0cbb03b21c6668dd0a46 - subpackages: - - spew -- name: github.com/FactomProject/goleveldb - version: e7800c6976c5d75f0a9c5e9e0e2ff8086940e58f - subpackages: - - leveldb - - leveldb/cache - - leveldb/comparer - - leveldb/errors - - leveldb/filter - - leveldb/iterator - - leveldb/journal - - leveldb/memdb - - leveldb/opt - - leveldb/storage - - leveldb/table - - leveldb/util -- name: github.com/FactomProject/logrustash - version: 9c7278ede46e1ccc03f0b17d6663730c340acb81 -- name: github.com/FactomProject/netki-go-partner-client - version: 426acb535e66cc2f9e6226ae254555e3572fd142 -- name: github.com/FactomProject/serveridentity - version: cf42d2aa8debbe9a690e8c12b7a9fcda127d3f2c - subpackages: - - identity - - utils -- name: github.com/FactomProject/snappy-go - version: f2f83b22c29e5abc60e3a95062ce1491d3b95371 -- name: github.com/FactomProject/web - version: 951cacf54656419dbaf444bc69b82799660ff521 -- name: github.com/golang/protobuf - version: 1918e1ff6ffd2be7bed0553df8650672c3bfe80d - subpackages: - - proto - - ptypes - - ptypes/any - - ptypes/duration - - ptypes/timestamp -- name: github.com/hashicorp/go-hclog - version: f1d61ad5398ffe4f2eb61eacb088340d44e99672 -- name: github.com/hashicorp/go-plugin - version: a1bc61569a26c0f65865932c0d55743b0567c494 - subpackages: - - internal/plugin -- name: github.com/hashicorp/yamux - version: 2f1d1f20f75d5404f53b9edf6b53ed5505508675 -- name: github.com/konsorten/go-windows-terminal-sequences - version: 5c8c8bd35d3832f5d134ae1e1e375b69a4d25242 -- name: github.com/matttproud/golang_protobuf_extensions - version: c12348ce28de40eed0136aa2b644d0ee0650e56c - subpackages: - - pbutil -- name: github.com/mitchellh/go-testing-interface - version: 6d0b8010fcc857872e42fc6c931227569016843c -- name: github.com/oklog/run - version: 6934b124db28979da51d3470dadfa34d73d72652 -- name: github.com/prometheus/client_golang - version: f30f428035633da15d00d3dfefb0128c5e569ef4 - subpackages: - - prometheus - - prometheus/internal -- name: github.com/prometheus/client_model - version: 5c3871d89910bfb32f5fcab2aa4b9ec68e65a99f - subpackages: - - go -- name: github.com/prometheus/common - version: 7e9e6cabbd393fc208072eedef99188d0ce788b6 - subpackages: - - expfmt - - internal/bitbucket.org/ww/goautoneg - - model -- name: github.com/prometheus/procfs - version: 185b4288413d2a0dd0806f78c90dde719829e5ae - subpackages: - - internal/util - - nfs - - xfs -- name: github.com/rs/cors - version: a3460e445dd310dbefee993fe449f2ff9c08ae71 -- name: github.com/sirupsen/logrus - version: 566a5f690849162ff53cf98f3c42135389d63f95 -- name: github.com/stretchr/testify - version: 34c6fa2dc70986bccbbffcc6130f6920a924b075 - subpackages: - - assert -- name: golang.org/x/crypto - version: 4d3f4d9ffa16a13f451c3b2999e9c49e9750bf06 - subpackages: - - pbkdf2 - - ripemd160 - - scrypt - - ssh/terminal -- name: golang.org/x/net - version: c44066c5c816ec500d459a2a324a753f78531ae0 - subpackages: - - context - - http/httpguts - - http2 - - http2/hpack - - idna - - internal/timeseries - - trace - - websocket -- name: golang.org/x/sys - version: 7e31e0c00fa05cb5fbf4347b585621d6709e19a4 - subpackages: - - unix - - windows -- name: golang.org/x/text - version: 342b2e1fbaa52c93f31447ad2c6abc048c63e475 - subpackages: - - secure/bidirule - - transform - - unicode/bidi - - unicode/norm -- name: google.golang.org/genproto - version: 32ee49c4dd805befd833990acba36cb75042378c - subpackages: - - googleapis/rpc/status -- name: google.golang.org/grpc - version: 684ef046099f3f1c4b1fe762e5826a2f7bc6e27f - subpackages: - - balancer - - balancer/base - - balancer/roundrobin - - binarylog/grpc_binarylog_v1 - - codes - - connectivity - - credentials - - credentials/internal - - encoding - - encoding/proto - - grpclog - - health - - health/grpc_health_v1 - - internal - - internal/backoff - - internal/balancerload - - internal/binarylog - - internal/channelz - - internal/envconfig - - internal/grpcrand - - internal/grpcsync - - internal/syscall - - internal/transport - - keepalive - - metadata - - naming - - peer - - resolver - - resolver/dns - - resolver/passthrough - - serviceconfig - - stats - - status - - tap -- name: gopkg.in/gcfg.v1 - version: 61b2c08bc8f6068f7c5ca684372f9a6cb1c45ebe - subpackages: - - scanner - - token - - types -- name: gopkg.in/warnings.v0 - version: ec4a0fea49c7b46c2aeb0b51aac55779c607e52b -testImports: [] diff --git a/glide.yaml b/glide.yaml deleted file mode 100644 index 72e6e85..0000000 --- a/glide.yaml +++ /dev/null @@ -1,32 +0,0 @@ -package: github.com/FactomProject/factom -import: -- package: github.com/FactomProject/btcutil - subpackages: - - base58 - - certs -- package: github.com/FactomProject/ed25519 -- package: github.com/FactomProject/factoid - subpackages: - - state/stateinit - - wallet -- package: github.com/FactomProject/factomd - subpackages: - - common/directoryBlock - - common/factoid - - common/interfaces - - common/primitives - - database/databaseOverlay - - database/hybridDB - - database/mapdb - - database/securedb -- package: github.com/FactomProject/go-bip32 -- package: github.com/FactomProject/go-bip39 -- package: github.com/FactomProject/go-bip44 -- package: github.com/FactomProject/goleveldb - subpackages: - - leveldb -- package: github.com/FactomProject/netki-go-partner-client -- package: github.com/FactomProject/web -- package: github.com/stretchr/testify - subpackages: - - assert \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..d828ebe --- /dev/null +++ b/go.mod @@ -0,0 +1,21 @@ +module github.com/FactomProject/factom + +go 1.14 + +require ( + github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e // indirect + github.com/FactomProject/btcutil v0.0.0-20160826074221-43986820ccd5 + github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec // indirect + github.com/FactomProject/ed25519 v0.0.0-20150814230546-38002c4fe7b6 + github.com/FactomProject/go-bip32 v0.3.5 + github.com/FactomProject/go-bip39 v0.3.5 + github.com/FactomProject/go-bip44 v0.0.0-20190306062959-b541a96d8da9 + github.com/FactomProject/go-simplejson v0.5.0 // indirect + github.com/FactomProject/netki-go-partner-client v0.0.0-20160324224126-426acb535e66 + github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect + github.com/cmars/basen v0.0.0-20150613233007-fe3947df716e // indirect + github.com/kr/pretty v0.2.1 // indirect + github.com/stretchr/testify v1.6.1 // indirect + golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc + launchpad.net/gocheck v0.0.0-20140225173054-000000000087 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..2607c34 --- /dev/null +++ b/go.sum @@ -0,0 +1,50 @@ +github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e h1:ahyvB3q25YnZWly5Gq1ekg6jcmWaGj/vG/MhF4aisoc= +github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e/go.mod h1:kGUqhHd//musdITWjFvNTHn90WG9bMLBEPQZ17Cmlpw= +github.com/FactomProject/btcutil v0.0.0-20160826074221-43986820ccd5 h1:cysv7OcQYt+P7IH1ZQAE1akzlwm+EXvxG6/0jYlJIE4= +github.com/FactomProject/btcutil v0.0.0-20160826074221-43986820ccd5/go.mod h1:vneMtVKjkLz6PtiXS/HB0UJXAF77wndeJwZiqaVrN84= +github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec h1:1Qb69mGp/UtRPn422BH4/Y4Q3SLUrD9KHuDkm8iodFc= +github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec/go.mod h1:CD8UlnlLDiqb36L110uqiP2iSflVjx9g/3U9hCI4q2U= +github.com/FactomProject/ed25519 v0.0.0-20150814230546-38002c4fe7b6 h1:e2tpL8IfImGBR8qODlnb/hnwPhS4wWzoOUKoFrA3RFQ= +github.com/FactomProject/ed25519 v0.0.0-20150814230546-38002c4fe7b6/go.mod h1:x54uxtF9AnHAvx2kRoIxKYLuUV+bRjUQ6u/3FhfmO5g= +github.com/FactomProject/go-bip32 v0.3.5 h1:etsJ4Y/wA7spZl/WG830ogTHQivGcV/8sCrrmkQICNQ= +github.com/FactomProject/go-bip32 v0.3.5/go.mod h1:efm/M7J/CGmQ5dPtGM0GWod5LuyShuFET6oY13168w4= +github.com/FactomProject/go-bip39 v0.3.5 h1:l9g92TeqCkC5NZhm72igTpf5yaYDp3Sy4CvnPYknp6U= +github.com/FactomProject/go-bip39 v0.3.5/go.mod h1:ygPVOtW424QxnJMze9XYDeh4wT19V3iVDOqVUl/USkE= +github.com/FactomProject/go-bip44 v0.0.0-20190306062959-b541a96d8da9 h1:Wprj9FTxqhjgl7e8ZHScGwF5Wc2WrHTbEDoQKciBdhw= +github.com/FactomProject/go-bip44 v0.0.0-20190306062959-b541a96d8da9/go.mod h1:4H/Y1yLgSJvRV4iqilsVhYlqXkPgf1QonpKyt6sdP+Q= +github.com/FactomProject/go-simplejson v0.5.0 h1:gO5Z2gV3+kqWcu8/TrjAbim0yFqUcFll7HzAhdxah7s= +github.com/FactomProject/go-simplejson v0.5.0/go.mod h1:P6XXHV5dI0hIZte+dQ4G8tzNIiyuF/UMVuInbQd036g= +github.com/FactomProject/netki-go-partner-client v0.0.0-20160324224126-426acb535e66 h1:H4nFZnW3EpbcefxbJAJxWCl7niO07QpKt8KeHtLwHoc= +github.com/FactomProject/netki-go-partner-client v0.0.0-20160324224126-426acb535e66/go.mod h1:L+Od0CCfb8IzUbPM4UxLaERB1I6Bw/lD2UtzRM90pIw= +github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= +github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= +github.com/cmars/basen v0.0.0-20150613233007-fe3947df716e h1:0XBUw73chJ1VYSsfvcPvVT7auykAJce9FpRr10L6Qhw= +github.com/cmars/basen v0.0.0-20150613233007-fe3947df716e/go.mod h1:P13beTBKr5Q18lJe1rIoLUqjM+CB1zYrRg44ZqGuQSA= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc h1:zK/HqS5bZxDptfPJNq8v7vJfXtkU7r9TLIoSr1bXaP4= +golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +launchpad.net/gocheck v0.0.0-20140225173054-000000000087 h1:Izowp2XBH6Ya6rv+hqbceQyw/gSGoXfH/UPoTGduL54= +launchpad.net/gocheck v0.0.0-20140225173054-000000000087/go.mod h1:hj7XX3B/0A+80Vse0e+BUHsHMTEhd0O4cpUHr/e/BUM= diff --git a/transaction_test.go b/transaction_test.go index 0690605..1d250ca 100644 --- a/transaction_test.go +++ b/transaction_test.go @@ -29,48 +29,6 @@ func TestJSONTransactions(t *testing.T) { t.Log("Unmarshaled:", tx2) } -func TestTransactions(t *testing.T) { - // start the test wallet - done, err := StartTestWallet() - if err != nil { - t.Error(err) - } - defer func() { done <- 1 }() - - // make sure the wallet is empty - if txs, err := ListTransactionsTmp(); err != nil { - t.Error(err) - } else if len(txs) > 0 { - t.Error("Unexpected transactions returned from the wallet:", txs) - } - - // create a new transaction - tx1, err := NewTransaction("tx1") - if err != nil { - t.Error(err) - } - if tx1 == nil { - t.Error("No transaction was returned") - } - - if tx, err := GetTmpTransaction("tx1"); err != nil { - t.Error(err) - } else if tx == nil { - t.Error("Temporary transaction was not saved in the wallet") - } - - // delete a transaction - if err := DeleteTransaction("tx1"); err != nil { - t.Error(err) - } - - if txs, err := ListTransactionsTmp(); err != nil { - t.Error(err) - } else if len(txs) > 0 { - t.Error("Unexpected transactions returned from the wallet:", txs) - } -} - // helper functions for testing func mkdummytx() *Transaction { diff --git a/wallet/VERSION b/wallet/VERSION deleted file mode 100644 index ed1fc35..0000000 --- a/wallet/VERSION +++ /dev/null @@ -1 +0,0 @@ -2.2.16 diff --git a/wallet/database.go b/wallet/database.go deleted file mode 100644 index 6add155..0000000 --- a/wallet/database.go +++ /dev/null @@ -1,177 +0,0 @@ -// Copyright 2016 Factom Foundation -// Use of this source code is governed by the MIT -// license that can be found in the LICENSE file. - -package wallet - -import ( - "fmt" - "sync" - - "github.com/FactomProject/factom" - "github.com/FactomProject/factomd/common/factoid" -) - -// Wallet is a connection to a Factom Wallet Database -type Wallet struct { - *WalletDatabaseOverlay - Encrypted bool - DBPath string - txlock sync.Mutex - transactions map[string]*factoid.Transaction - txdb *TXDatabaseOverlay -} - -func (w *Wallet) InitWallet() error { - dbSeed, err := w.GetOrCreateDBSeed() - if err != nil { - return err - } - if dbSeed == nil { - return fmt.Errorf("dbSeed not present in DB") - } - return nil -} - -func NewOrOpenLevelDBWallet(path string) (*Wallet, error) { - w := new(Wallet) - w.transactions = make(map[string]*factoid.Transaction) - - db, err := NewLevelDB(path) - if err != nil { - return nil, err - } - w.WalletDatabaseOverlay = db - - if err = w.InitWallet(); err != nil { - return nil, err - } - - return w, nil -} - -func NewOrOpenBoltDBWallet(path string) (*Wallet, error) { - w := new(Wallet) - w.transactions = make(map[string]*factoid.Transaction) - - db, err := NewBoltDB(path) - if err != nil { - return nil, err - } - w.WalletDatabaseOverlay = db - - if err = w.InitWallet(); err != nil { - return nil, err - } - - return w, nil -} - -func NewEncryptedBoltDBWallet(path, password string) (*Wallet, error) { - w := new(Wallet) - w.transactions = make(map[string]*factoid.Transaction) - - db, err := NewEncryptedBoltDB(path, password) - if err != nil { - return nil, err - } - w.WalletDatabaseOverlay = db - - if err = w.InitWallet(); err != nil { - return nil, err - } - - return w, nil -} - -func NewEncryptedBoltDBWalletAwaitingPassphrase(path string) (*Wallet, error) { - w := new(Wallet) - w.transactions = make(map[string]*factoid.Transaction) - w.Encrypted = true - w.DBPath = path - return w, nil -} - -func NewMapDBWallet() (*Wallet, error) { - w := new(Wallet) - w.transactions = make(map[string]*factoid.Transaction) - w.WalletDatabaseOverlay = NewMapDB() - - if err := w.InitWallet(); err != nil { - return nil, err - } - - return w, nil -} - -// Close closes a Factom Wallet Database -func (w *Wallet) Close() error { - if w.WalletDatabaseOverlay == nil { - return nil - } - return w.DBO.Close() -} - -// AddTXDB allows the wallet api to read from a local transaction cashe. -func (w *Wallet) AddTXDB(t *TXDatabaseOverlay) { - w.txdb = t -} - -// TXDB returns a handle for the Transaction Database. -func (w *Wallet) TXDB() *TXDatabaseOverlay { - return w.txdb -} - -// GenerateECAddress creates and stores a new Entry Credit Address in the -// Wallet. The address can be reproduced in the future using the Wallet Seed. -func (w *Wallet) GenerateECAddress() (*factom.ECAddress, error) { - return w.GetNextECAddress() -} - -// GenerateFCTAddress creates and stores a new Factoid Address in the Wallet. -// The address can be reproduced in the future using the Wallet Seed. -func (w *Wallet) GenerateFCTAddress() (*factom.FactoidAddress, error) { - return w.GetNextFCTAddress() -} - -// GenerateIdentityKey creates and stores a new Identity Key in the Wallet. -func (w *Wallet) GenerateIdentityKey() (*factom.IdentityKey, error) { - return w.GetNextIdentityKey() -} - -// GetAllAddresses retrieves all Entry Credit and Factoid Addresses from the -// Wallet Database. -func (w *Wallet) GetAllAddresses() ([]*factom.FactoidAddress, []*factom.ECAddress, error) { - fcs, err := w.GetAllFCTAddresses() - if err != nil { - return nil, nil, err - } - - ecs, err := w.GetAllECAddresses() - if err != nil { - return nil, nil, err - } - - return fcs, ecs, nil -} - -// GetSeed returns the string representaion of the Wallet Seed. The Wallet Seed -// can be used to regenerate the Factoid and Entry Credit Addresses previously -// generated by the wallet. Note that Addresses that are imported into the -// Wallet cannot be regenerated using the Wallet Seed. -func (w *Wallet) GetSeed() (string, error) { - seed, err := w.GetDBSeed() - if err != nil { - return "", err - } - - return seed.MnemonicSeed, nil -} - -func (w *Wallet) GetVersion() string { - return WalletVersion -} - -func (w *Wallet) GetApiVersion() string { - return ApiVersion -} diff --git a/wallet/database_test.go b/wallet/database_test.go deleted file mode 100644 index 0533c4f..0000000 --- a/wallet/database_test.go +++ /dev/null @@ -1,352 +0,0 @@ -// Copyright 2016 Factom Foundation -// Use of this source code is governed by the MIT -// license that can be found in the LICENSE file. - -package wallet_test - -import ( - "os" - "testing" - - "github.com/FactomProject/factom" - . "github.com/FactomProject/factom/wallet" -) - -func TestNewWallet(t *testing.T) { - // create a new database - w1, err := NewMapDBWallet() - if err != nil { - t.Error(err) - } - - // check that the seed got written - seed, err := w1.GetDBSeed() - if err != nil { - t.Error(err) - } - if len(seed.MnemonicSeed) == 0 { - t.Errorf("stored db seed is empty") - } - - if err := w1.Close(); err != nil { - t.Error(err) - } -} - -func TestOpenWallet(t *testing.T) { - dbpath := os.TempDir() + "/test_wallet-01" - - // create a new database - w1, err := NewOrOpenLevelDBWallet(dbpath) - if err != nil { - t.Error(err) - } - w1.Close() - - // make sure we can open the db - w2, err := NewOrOpenLevelDBWallet(dbpath) - if err != nil { - t.Error(err) - } - - // check that the seed is there - seed, err := w1.GetDBSeed() - if len(seed.MnemonicSeed) == 0 { - t.Errorf("stored db seed is empty") - } - - if err := w2.Close(); err != nil { - t.Error(err) - } - - // remove the testing db - if err := os.RemoveAll(dbpath); err != nil { - t.Error(err) - } -} - -func TestPutECAddress(t *testing.T) { - zSec := "Es2Rf7iM6PdsqfYCo3D1tnAR65SkLENyWJG1deUzpRMQmbh9F3eG" - - // create a new database - w, err := NewMapDBWallet() - if err != nil { - t.Error(err) - } - - // write a new ec address to the db - e, err := factom.GetECAddress(zSec) - if err != nil { - t.Error(err) - } - if err := w.InsertECAddress(e); err != nil { - t.Error(err) - } - - // Check that the address was written into the db - if _, err := w.GetECAddress(e.PubString()); err != nil { - t.Error(err) - } - - // close and remove the testing db - if err := w.Close(); err != nil { - t.Error(err) - } -} - -func TestPutFCTAddress(t *testing.T) { - zSec := "Fs1KWJrpLdfucvmYwN2nWrwepLn8ercpMbzXshd1g8zyhKXLVLWj" - - // create a new database - w, err := NewMapDBWallet() - if err != nil { - t.Error(err) - } - - // write a new fct address to the db - f, err := factom.GetFactoidAddress(zSec) - if err != nil { - t.Error(err) - } - if err := w.InsertFCTAddress(f); err != nil { - t.Error(err) - } - - // Check that the address was written into the db - if _, err := w.GetFCTAddress(f.String()); err != nil { - t.Error(err) - } - - // close and remove the testing db - if err := w.Close(); err != nil { - t.Error(err) - } -} - -func TestGenerateECAddress(t *testing.T) { - // create a new database - w, err := NewMapDBWallet() - if err != nil { - t.Error(err) - } - - // Generate a new ec address - e, err := w.GenerateECAddress() - if err != nil { - t.Error(err) - } - - // Check that the address was written into the db - if _, err := w.GetECAddress(e.PubString()); err != nil { - t.Error(err) - } - - // close and remove the testing db - if err := w.Close(); err != nil { - t.Error(err) - } -} - -func TestGenerateFCTAddress(t *testing.T) { - // create a new database - w, err := NewMapDBWallet() - if err != nil { - t.Error(err) - } - - // Generate a new fct address - f, err := w.GenerateFCTAddress() - if err != nil { - t.Error(err) - } - - // Check that the address was written into the db - if _, err := w.GetFCTAddress(f.String()); err != nil { - t.Error(err) - } - - // close and remove the testing db - if err := w.Close(); err != nil { - t.Error(err) - } -} - -func TestGetAllAddresses(t *testing.T) { - e1Sec := "Es2Rf7iM6PdsqfYCo3D1tnAR65SkLENyWJG1deUzpRMQmbh9F3eG" - f1Sec := "Fs1KWJrpLdfucvmYwN2nWrwepLn8ercpMbzXshd1g8zyhKXLVLWj" - e2Sec := "Es4NQHwo8F4Z4oMnVwndtjV1rzZN3t5pP5u5jtdgiR1RA6FH4Tmc" - f2Sec := "Fs3GFV6GNV6ar4b8eGcQWpGFbFtkNWKfEPdbywmha8ez5p7XMJyk" - correctLen := 2 - - // create a new database - w, err := NewMapDBWallet() - if err != nil { - t.Error(err) - } - - // write a new ec address to the db - e1, err := factom.GetECAddress(e1Sec) - if err != nil { - t.Error(err) - } - if err := w.InsertECAddress(e1); err != nil { - t.Error(err) - } - e2, err := factom.GetECAddress(e2Sec) - if err != nil { - t.Error(err) - } - if err := w.InsertECAddress(e2); err != nil { - t.Error(err) - } - - // write a new fct address to the db - f1, err := factom.GetFactoidAddress(f1Sec) - if err != nil { - t.Error(err) - } - if err := w.InsertFCTAddress(f1); err != nil { - t.Error(err) - } - f2, err := factom.GetFactoidAddress(f2Sec) - if err != nil { - t.Error(err) - } - if err := w.InsertFCTAddress(f2); err != nil { - t.Error(err) - } - - // get all addresses out of db - fs, es, err := w.GetAllAddresses() - if err != nil { - t.Error(err) - } else if fs == nil { - t.Errorf("No Factoid address was retrived") - } else if es == nil { - t.Errorf("No EC address was retrived") - } - // check that all the addresses are there - if len(fs) != correctLen { - t.Errorf("Wrong number of factoid addesses were retrived: %v", fs) - } - if len(es) != correctLen { - t.Errorf("Wrong number of ec addesses were retrived: %v", es) - } - - // print the addresses - for _, f := range fs { - t.Logf("%s %s", f, f.SecString()) - } - for _, e := range es { - t.Logf("%s %s", e, e.SecString()) - } - - // close and remove the testing db - if err := w.Close(); err != nil { - t.Error(err) - } -} - -func TestPutIdentityKey(t *testing.T) { - sec := "idsec2J3nNoqdiyboCBKDGauqN9Jb33dyFSqaJKZqTs6i5FmztsTn5f" - - // create a new database - w, err := NewMapDBWallet() - if err != nil { - t.Error(err) - } - - // write a new identity key to the db - k, err := factom.GetIdentityKey(sec) - if err != nil { - t.Error(err) - } - if err := w.InsertIdentityKey(k); err != nil { - t.Error(err) - } - - // Check that the key was written into the db - if _, err := w.GetIdentityKey(k.PubString()); err != nil { - t.Error(err) - } - - // close and remove the testing db - if err := w.Close(); err != nil { - t.Error(err) - } -} - -func TestGenerateIdentityKey(t *testing.T) { - // create a new database - w, err := NewMapDBWallet() - if err != nil { - t.Error(err) - } - - // Generate a new identity key - k, err := w.GenerateIdentityKey() - if err != nil { - t.Error(err) - } - - // Check that the key was written into the db - if _, err := w.GetIdentityKey(k.PubString()); err != nil { - t.Error(err) - } - - // close and remove the testing db - if err := w.Close(); err != nil { - t.Error(err) - } -} - -func TestGetAllIdentityKeys(t *testing.T) { - sec1 := "idsec2J3nNoqdiyboCBKDGauqN9Jb33dyFSqaJKZqTs6i5FmztsTn5f" - sec2 := "idsec1xuUyeCCrJhsojf2wLAZqRxPzPFR8Gidd9DRRid1yGy8ncAJG3" - - correctLen := 2 - - // create a new database - w, err := NewMapDBWallet() - if err != nil { - t.Error(err) - } - - // write a new identity keys to the db - k1, err := factom.GetIdentityKey(sec1) - if err != nil { - t.Error(err) - } - if err := w.InsertIdentityKey(k1); err != nil { - t.Error(err) - } - k2, err := factom.GetIdentityKey(sec2) - if err != nil { - t.Error(err) - } - if err := w.InsertIdentityKey(k2); err != nil { - t.Error(err) - } - - // get all identity keys out of db - ks, err := w.GetAllIdentityKeys() - if err != nil { - t.Error(err) - } else if ks == nil { - t.Errorf("No Identity key was retrived") - } - // check that all the keys are there - if len(ks) != correctLen { - t.Errorf("Wrong number of identity keys were retrived: %v", ks) - } - - // print the keys - for _, k := range ks { - t.Logf("%s %s", k, k.SecString()) - } - - // close and remove the testing db - if err := w.Close(); err != nil { - t.Error(err) - } -} diff --git a/wallet/importexport.go b/wallet/importexport.go deleted file mode 100644 index 81d1a9a..0000000 --- a/wallet/importexport.go +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2016 Factom Foundation -// Use of this source code is governed by the MIT -// license that can be found in the LICENSE file. - -package wallet - -import ( - "fmt" - "os" - - "github.com/FactomProject/factom" - "github.com/FactomProject/factomd/common/factoid" -) - -// ImportWalletFromMnemonic creates a new wallet with a provided Mnemonic seed -// defined in bip-0039. -func ImportWalletFromMnemonic(mnemonic, path string) (*Wallet, error) { - mnemonic, err := factom.ParseMnemonic(mnemonic) - if err != nil { - return nil, err - } - - // check if the file exists - _, err = os.Stat(path) - if err == nil { - return nil, fmt.Errorf("%s: file already exists", path) - } - - db, err := NewBoltDB(path) - if err != nil { - return nil, err - } - - seed := new(DBSeed) - seed.MnemonicSeed = mnemonic - if err := db.InsertDBSeed(seed); err != nil { - return nil, err - } - - w := new(Wallet) - w.transactions = make(map[string]*factoid.Transaction) - w.WalletDatabaseOverlay = db - - return w, nil -} - -// ExportWallet writes all the secret/publilc key pairs from a wallet and the -// wallet seed in a pritable format. -func ExportWallet(path string) (string, []*factom.FactoidAddress, []*factom.ECAddress, error) { - // check if the file exists - _, err := os.Stat(path) - if err != nil { - return "", nil, nil, err - } - - w, err := NewOrOpenBoltDBWallet(path) - if err != nil { - return "", nil, nil, err - } - - m, err := w.GetSeed() - if err != nil { - return "", nil, nil, err - } - fs, es, err := w.GetAllAddresses() - if err != nil { - return "", nil, nil, err - } - return m, fs, es, nil -} diff --git a/wallet/importexport_test.go b/wallet/importexport_test.go deleted file mode 100644 index 29b1723..0000000 --- a/wallet/importexport_test.go +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2016 Factom Foundation -// Use of this source code is governed by the MIT -// license that can be found in the LICENSE file. - -package wallet_test - -import ( - "testing" - - //"github.com/FactomProject/factom" - . "github.com/FactomProject/factom/wallet" -) - -func TestImportWithSpaces(t *testing.T) { - w, err := ImportWalletFromMnemonic("yellow yellow yellow yellow yellow yellow yellow yellow yellow yellow yellow yellow", "") - if err != nil { - t.Error(err) - t.FailNow() - } - _, err = w.GenerateFCTAddress() - if err != nil { - t.Error(err) - t.FailNow() - } -} diff --git a/wallet/importexportenc.go b/wallet/importexportenc.go deleted file mode 100644 index c66035f..0000000 --- a/wallet/importexportenc.go +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2016 Factom Foundation -// Use of this source code is governed by the MIT -// license that can be found in the LICENSE file. - -package wallet - -import ( - "fmt" - "os" - - "github.com/FactomProject/factom" - "github.com/FactomProject/factomd/common/factoid" -) - -// ImportEncryptedWalletFromMnemonic creates a new wallet with a provided Mnemonic seed -// defined in bip-0039. -func ImportEncryptedWalletFromMnemonic(mnemonic, path, password string) (*Wallet, error) { - mnemonic, err := factom.ParseMnemonic(mnemonic) - if err != nil { - return nil, err - } - - // check if the file exists - _, err = os.Stat(path) - if err == nil { - return nil, fmt.Errorf("%s: file already exists", path) - } - - db, err := NewEncryptedBoltDB(path, password) - if err != nil { - return nil, err - } - - seed := new(DBSeed) - seed.MnemonicSeed = mnemonic - if err := db.InsertDBSeed(seed); err != nil { - return nil, err - } - - w := new(Wallet) - w.transactions = make(map[string]*factoid.Transaction) - w.WalletDatabaseOverlay = db - - return w, nil -} - -// ExportEncryptedWallet writes all the secret/publilc key pairs from a wallet and the -// wallet seed in a pritable format. -func ExportEncryptedWallet(path, password string) (string, []*factom.FactoidAddress, []*factom.ECAddress, error) { - // check if the file exists - _, err := os.Stat(path) - if err != nil { - return "", nil, nil, err - } - - w, err := NewEncryptedBoltDBWallet(path, password) - if err != nil { - return "", nil, nil, err - } - - m, err := w.GetSeed() - if err != nil { - return "", nil, nil, err - } - fs, es, err := w.GetAllAddresses() - if err != nil { - return "", nil, nil, err - } - return m, fs, es, nil -} diff --git a/wallet/importexportenc_test.go b/wallet/importexportenc_test.go deleted file mode 100644 index 328d647..0000000 --- a/wallet/importexportenc_test.go +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2016 Factom Foundation -// Use of this source code is governed by the MIT -// license that can be found in the LICENSE file. - -package wallet_test diff --git a/wallet/importexportldb.go b/wallet/importexportldb.go deleted file mode 100644 index 3859fd0..0000000 --- a/wallet/importexportldb.go +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2016 Factom Foundation -// Use of this source code is governed by the MIT -// license that can be found in the LICENSE file. - -package wallet - -import ( - "fmt" - "os" - - "github.com/FactomProject/factom" - "github.com/FactomProject/factomd/common/factoid" -) - -// ImportWalletFromMnemonic creates a new wallet with a provided Mnemonic seed -// defined in bip-0039. -func ImportLDBWalletFromMnemonic(mnemonic, path string) (*Wallet, error) { - mnemonic, err := factom.ParseMnemonic(mnemonic) - if err != nil { - return nil, err - } - - // check if the file exists - _, err = os.Stat(path) - if err == nil { - return nil, fmt.Errorf("%s: file already exists", path) - } - - db, err := NewLevelDB(path) - if err != nil { - return nil, err - } - - seed := new(DBSeed) - seed.MnemonicSeed = mnemonic - if err := db.InsertDBSeed(seed); err != nil { - return nil, err - } - - w := new(Wallet) - w.transactions = make(map[string]*factoid.Transaction) - w.WalletDatabaseOverlay = db - - return w, nil -} - -// ExportLDBWallet writes all the secret/publilc key pairs from a wallet and the -// wallet seed in a pritable format. -func ExportLDBWallet(path string) (string, []*factom.FactoidAddress, []*factom.ECAddress, error) { - // check if the file exists - _, err := os.Stat(path) - if err != nil { - return "", nil, nil, err - } - - w, err := NewOrOpenLevelDBWallet(path) - if err != nil { - return "", nil, nil, err - } - - m, err := w.GetSeed() - if err != nil { - return "", nil, nil, err - } - fs, es, err := w.GetAllAddresses() - if err != nil { - return "", nil, nil, err - } - return m, fs, es, nil -} diff --git a/wallet/sign.go b/wallet/sign.go deleted file mode 100644 index cc5bb96..0000000 --- a/wallet/sign.go +++ /dev/null @@ -1,28 +0,0 @@ -package wallet - -import ( - "github.com/FactomProject/factomd/common/primitives" -) - -// SignData signs arbitrary data -func (w *Wallet) SignData(signer string, data []byte) ([]byte, []byte, error) { - - var priv []byte - var pub []byte - - if fa, err := w.GetFCTAddress(signer); err == nil { - priv = fa.SecBytes() - pub = fa.PubBytes() - } else if ec, err := w.GetECAddress(signer); err == nil { - priv = ec.SecBytes() - pub = ec.PubBytes() - } else if id, err := w.GetIdentityKey(signer); err == nil { - priv = id.SecBytes() - pub = id.PubBytes() - } else { - return nil, nil, ErrNoSuchAddress - } - - sig := primitives.Sign(priv, data) - return pub, sig, nil -} diff --git a/wallet/transaction.go b/wallet/transaction.go deleted file mode 100644 index 086c5f8..0000000 --- a/wallet/transaction.go +++ /dev/null @@ -1,427 +0,0 @@ -// Copyright 2016 Factom Foundation -// Use of this source code is governed by the MIT -// license that can be found in the LICENSE file. - -package wallet - -import ( - "encoding/hex" - "errors" - "fmt" - "regexp" - - "github.com/FactomProject/btcutil/base58" - "github.com/FactomProject/factom" - "github.com/FactomProject/factomd/common/factoid" - "github.com/FactomProject/factomd/common/primitives" - "github.com/FactomProject/goleveldb/leveldb" -) - -var ( - ErrFeeTooLow = errors.New("wallet: Insufficient Fee") - ErrNoSuchAddress = errors.New("wallet: No such address") - ErrNoSuchIdentityKey = errors.New("wallet: No such identity key") - ErrTXExists = errors.New("wallet: Transaction name already exists") - ErrTXNotExists = errors.New("wallet: Transaction name was not found") - ErrTXNoInputs = errors.New("wallet: Transaction has no inputs") - ErrTXInvalidName = errors.New("wallet: Transaction name is not valid") -) - -func (w *Wallet) NewTransaction(name string) error { - if w.TransactionExists(name) { - return ErrTXExists - } - - // check that the transaction name is valid - if name == "" { - return ErrTXInvalidName - } - if len(name) > 32 { - return ErrTXInvalidName - } - if match, err := regexp.MatchString("[^a-zA-Z0-9_-]", name); err != nil { - return err - } else if match { - return ErrTXInvalidName - } - - tx := new(factoid.Transaction) - tx.SetTimestamp(primitives.NewTimestampNow()) - - w.txlock.Lock() - defer w.txlock.Unlock() - - w.transactions[name] = tx - return nil -} - -func (w *Wallet) DeleteTransaction(name string) error { - if !w.TransactionExists(name) { - return ErrTXNotExists - } - - w.txlock.Lock() - defer w.txlock.Unlock() - delete(w.transactions, name) - return nil -} - -func (w *Wallet) AddInput(name, address string, amount uint64) error { - tx, err := w.GetTransaction(name) - if err != nil { - return err - } - - a, err := w.GetFCTAddress(address) - if err == leveldb.ErrNotFound { - return ErrNoSuchAddress - } else if err != nil { - return err - } - adr := factoid.NewAddress(a.RCDHash()) - - // First look if this is really an update - for _, input := range tx.GetInputs() { - if input.GetAddress().IsSameAs(adr) { - input.SetAmount(amount) - return nil - } - } - - // Add our new input - tx.AddInput(adr, amount) - tx.AddRCD(factoid.NewRCD_1(a.PubBytes())) - - return nil -} - -func (w *Wallet) AddOutput(name, address string, amount uint64) error { - tx, err := w.GetTransaction(name) - if err != nil { - return err - } - - // Make sure that this is a valid Factoid output - if factom.AddressStringType(address) != factom.FactoidPub { - return errors.New("Invalid Factoid Address") - } - - adr := factoid.NewAddress(base58.Decode(address)[2:34]) - - // First look if this is really an update - for _, output := range tx.GetOutputs() { - if output.GetAddress().IsSameAs(adr) { - output.SetAmount(amount) - return nil - } - } - - tx.AddOutput(adr, amount) - - return nil -} - -func (w *Wallet) AddECOutput(name, address string, amount uint64) error { - tx, err := w.GetTransaction(name) - if err != nil { - return err - } - - // Make sure that this is a valid Entry Credit output - if factom.AddressStringType(address) != factom.ECPub { - return errors.New("Invalid Entry Credit Address") - } - - adr := factoid.NewAddress(base58.Decode(address)[2:34]) - - // First look if this is really an update - for _, output := range tx.GetECOutputs() { - if output.GetAddress().IsSameAs(adr) { - output.SetAmount(amount) - return nil - } - } - - tx.AddECOutput(adr, amount) - - return nil -} - -func (w *Wallet) AddFee(name, address string, rate uint64) error { - tx, err := w.GetTransaction(name) - if err != nil { - return err - } - - { - ins, err := tx.TotalInputs() - if err != nil { - return err - } - outs, err := tx.TotalOutputs() - if err != nil { - return err - } - ecs, err := tx.TotalECs() - if err != nil { - return err - } - - if ins != outs+ecs { - return fmt.Errorf("Inputs and outputs don't add up") - } - } - - txfee, err := tx.CalculateFee(rate) - if err != nil { - return err - } - - a, err := w.GetFCTAddress(address) - if err != nil { - return err - } - adr := factoid.NewAddress(a.RCDHash()) - - for _, input := range tx.GetInputs() { - if input.GetAddress().IsSameAs(adr) { - amt, err := factoid.ValidateAmounts(input.GetAmount(), txfee) - if err != nil { - return err - } - input.SetAmount(amt) - return nil - } - } - return fmt.Errorf("%s is not an input to the transaction.", address) -} - -func (w *Wallet) SubFee(name, address string, rate uint64) error { - tx, err := w.GetTransaction(name) - if err != nil { - return err - } - - if !factom.IsValidAddress(address) { - return errors.New("Invalid Address") - } - - { - ins, err := tx.TotalInputs() - if err != nil { - return err - } - outs, err := tx.TotalOutputs() - if err != nil { - return err - } - ecs, err := tx.TotalECs() - if err != nil { - return err - } - - if ins != outs+ecs { - return fmt.Errorf("Inputs and outputs don't add up") - } - } - - txfee, err := tx.CalculateFee(rate) - if err != nil { - return err - } - - adr := factoid.NewAddress(base58.Decode(address)[2:34]) - - for _, output := range tx.GetOutputs() { - if output.GetAddress().IsSameAs(adr) { - output.SetAmount(output.GetAmount() - txfee) - return nil - } - } - return fmt.Errorf("%s is not an output to the transaction.", address) -} - -// SignTransaction signs a tmp transaction in the wallet with the appropriate -// keys from the wallet db -// force=true ignores the existing balance and fee overpayment checks. -func (w *Wallet) SignTransaction(name string, force bool) error { - tx, err := w.GetTransaction(name) - if err != nil { - return err - } - - if force == false { - // check that the address balances are sufficient for the transaction - if err := checkCovered(tx); err != nil { - return err - } - - // check that the fee is being paid (and not overpaid) - if err := checkFee(tx); err != nil { - return err - } - } - - data, err := tx.MarshalBinarySig() - if err != nil { - return err - } - - rcds := tx.GetRCDs() - if len(rcds) == 0 { - return ErrTXNoInputs - } - for i, rcd := range rcds { - a, err := rcd.GetAddress() - if err != nil { - return err - } - - f, err := w.GetFCTAddress(primitives.ConvertFctAddressToUserStr(a)) - if err != nil { - return err - } - sig := factoid.NewSingleSignatureBlock(f.SecBytes(), data) - tx.SetSignatureBlock(i, sig) - } - - return nil -} - -func (w *Wallet) GetTransaction(name string) (*factoid.Transaction, error) { - if !w.TransactionExists(name) { - return nil, ErrTXNotExists - } - - w.txlock.Lock() - defer w.txlock.Unlock() - - return w.transactions[name], nil -} - -func (w *Wallet) GetTransactions() map[string]*factoid.Transaction { - return w.transactions -} - -func (w *Wallet) TransactionExists(name string) bool { - w.txlock.Lock() - defer w.txlock.Unlock() - - if _, exists := w.transactions[name]; exists { - return true - } - return false -} - -func (w *Wallet) ComposeTransaction(name string) (*factom.JSON2Request, error) { - tx, err := w.GetTransaction(name) - if err != nil { - return nil, err - } - - type txreq struct { - Transaction string `json:"transaction"` - } - - param := new(txreq) - if p, err := tx.MarshalBinary(); err != nil { - return nil, err - } else { - param.Transaction = hex.EncodeToString(p) - } - - req := factom.NewJSON2Request("factoid-submit", APICounter(), param) - - return req, nil -} - -// Hexencoded transaction -func (w *Wallet) ImportComposedTransaction(name string, hexEncoded string) error { - tx := new(factoid.Transaction) - data, err := hex.DecodeString(hexEncoded) - if err != nil { - return err - } - - err = tx.UnmarshalBinary(data) - if err != nil { - return err - } - - w.txlock.Lock() - w.transactions[name] = tx - w.txlock.Unlock() - - return nil -} - -func checkCovered(tx *factoid.Transaction) error { - for _, in := range tx.GetInputs() { - balance, err := factom.GetFactoidBalance(in.GetUserAddress()) - if err != nil { - return err - } - if uint64(balance) < in.GetAmount() { - return fmt.Errorf( - "Address %s balance is too low. Available: %s Needed: %s", - in.GetUserAddress(), - factom.FactoshiToFactoid(uint64(balance)), - factom.FactoshiToFactoid(in.GetAmount()), - ) - } - } - return nil -} - -func checkFee(tx *factoid.Transaction) error { - ins, err := tx.TotalInputs() - if err != nil { - return err - } - outs, err := tx.TotalOutputs() - if err != nil { - return err - } - ecs, err := tx.TotalECs() - if err != nil { - return err - } - - // fee is the fee that will be paid - fee := int64(ins) - int64(outs) - int64(ecs) - - if fee <= 0 { - return ErrFeeTooLow - } - - rate, err := factom.GetECRate() - if err != nil { - return err - } - - // cfee is the fee calculated for the transaction - var cfee int64 - if c, err := tx.CalculateFee(rate); err != nil { - return err - } else if c == 0 { - return errors.New("wallet: Could not calculate fee") - } else { - cfee = int64(c) - } - - // fee is too low - if fee < cfee { - return ErrFeeTooLow - } - - // fee is too high (over 10x cfee) - if fee >= cfee*10 { - return fmt.Errorf( - "wallet: Overpaying fee by >10x. Paying: %v Requires: %v", - factom.FactoshiToFactoid(uint64(fee)), - factom.FactoshiToFactoid(uint64(cfee)), - ) - } - - return nil -} diff --git a/wallet/transaction_test.go b/wallet/transaction_test.go deleted file mode 100644 index ca729bf..0000000 --- a/wallet/transaction_test.go +++ /dev/null @@ -1,245 +0,0 @@ -// Copyright 2016 Factom Foundation -// Use of this source code is governed by the MIT -// license that can be found in the LICENSE file. - -package wallet_test - -import ( - "testing" - - "github.com/FactomProject/factom" - . "github.com/FactomProject/factom/wallet" - "github.com/FactomProject/factomd/common/primitives" -) - -func TestNewTransaction(t *testing.T) { - // create a new database - w1, err := NewMapDBWallet() - if err != nil { - t.Error(err) - } - - // create a new transaction - if err := w1.NewTransaction("test_tx-01"); err != nil { - t.Error(err) - } - if err := w1.NewTransaction("test_tx-02"); err != nil { - t.Error(err) - } - - if len(w1.GetTransactions()) != 2 { - t.Errorf("wrong number of transactions %v", w1.GetTransactions()) - } - - if err := w1.DeleteTransaction("test_tx-02"); err != nil { - t.Error(err) - } - - if len(w1.GetTransactions()) != 1 { - t.Errorf("wrong number of transactions %v", w1.GetTransactions()) - } - - // close and remove the testing db - if err := w1.Close(); err != nil { - t.Error(err) - } -} - -func TestAddInput(t *testing.T) { - zSec := "Fs1KWJrpLdfucvmYwN2nWrwepLn8ercpMbzXshd1g8zyhKXLVLWj" - - // create a new database - w1, err := NewMapDBWallet() - if err != nil { - t.Error(err) - } - - // write a new fct address to the db - f, err := factom.GetFactoidAddress(zSec) - if err != nil { - t.Error(err) - t.FailNow() - } - if err := w1.InsertFCTAddress(f); err != nil { - t.Error(err) - } - // Get the address back out of the db - adr, err := w1.GetFCTAddress(f.String()) - if err != nil { - t.Error(err) - } - - // create a new transaction - if err := w1.NewTransaction("tx-01"); err != nil { - t.Error(err) - } - - if err := w1.AddInput("tx-01", adr.String(), 5); err != nil { - t.Error(err) - } - if len(w1.GetTransactions()) != 1 { - t.Errorf("wrong number of transactions %v", w1.GetTransactions()) - } - t.Log(w1.GetTransactions()) - - // close and remove the testing db - if err := w1.Close(); err != nil { - t.Error(err) - } -} - -func TestComposeTrasnaction(t *testing.T) { - f1Sec := "Fs3E9gV6DXsYzf7Fqx1fVBQPQXV695eP3k5XbmHEZVRLkMdD9qCK" - // f1Sec := "Fs1KWJrpLdfucvmYwN2nWrwepLn8ercpMbzXshd1g8zyhKXLVLWj" - f2Sec := "Fs3GFV6GNV6ar4b8eGcQWpGFbFtkNWKfEPdbywmha8ez5p7XMJyk" - e1Sec := "Es2Rf7iM6PdsqfYCo3D1tnAR65SkLENyWJG1deUzpRMQmbh9F3eG" - - // create a new database - w1, err := NewMapDBWallet() - if err != nil { - t.Error(err) - } - - // write a new fct address to the db - if in, err := factom.GetFactoidAddress(f1Sec); err != nil { - t.Error(err) - } else { - if err := w1.InsertFCTAddress(in); err != nil { - t.Error(err) - } - } - - // Get the address back out of the db - f1 := factom.NewFactoidAddress() - if out, err := factom.GetFactoidAddress(f1Sec); err != nil { - t.Error(err) - } else { - if f, err := w1.GetFCTAddress(out.String()); err != nil { - t.Error(err) - } else { - f1 = f - } - } - - // setup a factoid address for receving - f2, err := factom.GetFactoidAddress(f2Sec) - if err != nil { - t.Error(err) - } - - // setup an ec address for receving - e1, err := factom.GetECAddress(e1Sec) - if err != nil { - t.FailNow() - t.Error(err) - } - - // create a new transaction - if err := w1.NewTransaction("tx-01"); err != nil { - t.Error(err) - } - if err := w1.AddInput("tx-01", f1.String(), 5e8); err != nil { - t.Error(err) - } - if err := w1.AddOutput("tx-01", f2.String(), 3e8); err != nil { - t.Error(err) - } - if err := w1.AddECOutput("tx-01", e1.PubString(), 2e8); err != nil { - t.Error(err) - } - if err := w1.AddFee("tx-01", f1.String(), 10000); err != nil { - t.Error(err) - } - if err := w1.SignTransaction("tx-01", true); err != nil { - t.Error(err) - } - - if j, err := w1.ComposeTransaction("tx-01"); err != nil { - t.Error(err) - } else { - t.Log(factom.EncodeJSONString(j)) - } - - // close and remove the testing db - if err := w1.Close(); err != nil { - t.Error(err) - } -} - -func TestImportComposedTransaction(t *testing.T) { - transSig := "020158fe8efb78010100afd89f60646f3e8750c550e4582eca5047546ffef89c13a175985e320232" + - "bacac81cc428afd7c20001ed0da7057f80dfeb596e6c72c4550c7c7694661dfee2c4a6ba1b903b6ec3e201718b" + - "5edd2914acc2e4677f336c1a32736e5e9bde13663e6413894f57ec272e28015183427204adbc50623d09ea5a76" + - "947b4c742e5b56d1483483d5d4336ac12872891be0afae50ce916639dd6db6200e3816d8bd73025b79b7af4de11fcd2105" - - transNoSig := "020158feae8aff010100afd89f60646f3e8750c550e4582eca5047546ffef89c13a175985e320232ba" + - "cac81cc428afd7c20001ed0da7057f80dfeb596e6c72c4550c7c7694661dfee2c4a6ba1b903b6ec3e201718b5e" + - "dd2914acc2e4677f336c1a32736e5e9bde13663e6413894f57ec272e2800000000000000000000000000000000" + - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - - _ = transSig - w1, err := NewMapDBWallet() - if err != nil { - t.Error(err) - } - - err = w1.ImportComposedTransaction("txImported", transNoSig) - if err != nil { - t.Error(err) - } - - trans := w1.GetTransactions()["txImported"] - if trans == nil { - t.Error("Transaction not found") - } - - ins := trans.GetInputs() - if len(ins) != 1 { - t.Error("Transaction only has 1 input") - } - - inAddr := primitives.ConvertFctAddressToUserStr(ins[0].GetAddress()) - if inAddr != "FA2jK2HcLnRdS94dEcU27rF3meoJfpUcZPSinpb7AwQvPRY6RL1Q" { - t.Error("Input does not match address") - } - - outs := trans.GetOutputs() - if len(outs) != 1 { - t.Error("Transaction only has 1 input") - } - - outAddr := primitives.ConvertFctAddressToUserStr(outs[0].GetAddress()) - if outAddr != "FA1yvkgzxMigVDU1WRnpHXhAE3e5zQpE6KKyf5EF76Y34TSg6m8X" { - t.Error("Ouput does not match address") - } - - sum, err := trans.TotalOutputs() - if err != nil { - t.Error(err) - } - - if sum != 1e8 { - t.Error("Output amount is incorrect") - } - - err = trans.ValidateSignatures() - if err == nil { - t.Error("Should be an error") - } - - // With sig - err = w1.ImportComposedTransaction("txImportedSig", transSig) - if err != nil { - t.Error(err) - } - - transStructSig := w1.GetTransactions()["txImportedSig"] - if transStructSig == nil { - t.Error("Transaction not found") - } - - err = transStructSig.ValidateSignatures() - if err != nil { - t.Error(err) - } -} diff --git a/wallet/txdatabase.go b/wallet/txdatabase.go deleted file mode 100644 index 903d53b..0000000 --- a/wallet/txdatabase.go +++ /dev/null @@ -1,444 +0,0 @@ -// Copyright 2016 Factom Foundation -// Use of this source code is governed by the MIT -// license that can be found in the LICENSE file. - -package wallet - -import ( - "encoding/hex" - "fmt" - "os" - - "github.com/FactomProject/factom" - "github.com/FactomProject/factomd/common/directoryBlock" - "github.com/FactomProject/factomd/common/factoid" - "github.com/FactomProject/factomd/common/interfaces" - "github.com/FactomProject/factomd/common/primitives" - "github.com/FactomProject/factomd/database/databaseOverlay" - "github.com/FactomProject/factomd/database/hybridDB" - "github.com/FactomProject/factomd/database/mapdb" -) - -// Database keys and key prefixes -var ( - fblockDBPrefix = []byte("FBlock") -) - -type TXDatabaseOverlay struct { - DBO databaseOverlay.Overlay - - // To indicate to sub processes to quit - quit bool -} - -func NewTXOverlay(db interfaces.IDatabase) *TXDatabaseOverlay { - answer := new(TXDatabaseOverlay) - answer.DBO.DB = db - return answer -} - -func NewTXMapDB() *TXDatabaseOverlay { - return NewTXOverlay(new(mapdb.MapDB)) -} - -func NewTXLevelDB(ldbpath string) (*TXDatabaseOverlay, error) { - db, err := hybridDB.NewLevelMapHybridDB(ldbpath, false) - if err != nil { - fmt.Printf("err opening transaction db: %v\n", err) - } - - if db == nil { - fmt.Println("Creating new transaction db ...") - db, err = hybridDB.NewLevelMapHybridDB(ldbpath, true) - - if err != nil { - return nil, err - } - } - fmt.Println("Transaction database started from: " + ldbpath) - return NewTXOverlay(db), nil -} - -func NewTXBoltDB(boltPath string) (*TXDatabaseOverlay, error) { - fileInfo, err := os.Stat(boltPath) - if err == nil { - if fileInfo.IsDir() { - return nil, fmt.Errorf("%s is not a Bolt databse file", boltPath) - } - } - if err != nil && !os.IsNotExist(err) { - fmt.Printf("database error %s\n", err) - return nil, err - } - - defer func() { - if r := recover(); r != nil { - fmt.Printf("Could not use wallet cache database file \"%s\"\n%v\n", boltPath, r) - os.Exit(1) - } - }() - db := hybridDB.NewBoltMapHybridDB(nil, boltPath) - - fmt.Println("Database started from: " + boltPath) - return NewTXOverlay(db), nil -} - -func (db *TXDatabaseOverlay) Close() error { - db.quit = true - return db.DBO.Close() -} - -// GetAllTXs returns a list of all transactions in the history of Factom. A -// local database is used to cache the factoid blocks. -func (db *TXDatabaseOverlay) GetAllTXs() ([]interfaces.ITransaction, error) { - // update the database and get the newest fblock - _, err := db.Update() - if err != nil { - return nil, err - } - fblock, err := db.DBO.FetchFBlockHead() - if err != nil { - return nil, err - } - if fblock == nil { - return nil, fmt.Errorf("FBlock Chain has not finished syncing") - } - txs := make([]interfaces.ITransaction, 0) - - for { - // get all of the txs from the block - height := fblock.GetDatabaseHeight() - for _, tx := range fblock.GetTransactions() { - ins, err := tx.TotalInputs() - if err != nil { - return nil, err - } - outs, err := tx.TotalOutputs() - if err != nil { - return nil, err - } - - if ins != 0 || outs != 0 { - tx.SetBlockHeight(height) - txs = append(txs, tx) - } - } - - if pre := fblock.GetPrevKeyMR().String(); pre != factom.ZeroHash { - // get the previous block - fblock, err = db.GetFBlock(pre) - if err != nil { - return nil, err - } else if fblock == nil { - return nil, fmt.Errorf("Missing fblock in database: %s", pre) - } - } else { - break - } - } - - return txs, nil -} - -// GetTX gets a transaction by the transaction id -func (db *TXDatabaseOverlay) GetTX(txid string) (interfaces.ITransaction, error) { - txs, err := db.GetAllTXs() - if err != nil { - return nil, err - } - - for _, tx := range txs { - if tx.GetSigHash().String() == txid { - return tx, nil - } - } - - return nil, fmt.Errorf("Transaction not found") -} - -// GetTXAddress returns a list of all transactions in the history of Factom that -// include a specific address. -func (db *TXDatabaseOverlay) GetTXAddress(adr string) ( - []interfaces.ITransaction, error) { - filtered := make([]interfaces.ITransaction, 0) - - txs, err := db.GetAllTXs() - if err != nil { - return nil, err - } - - if factom.AddressStringType(adr) == factom.FactoidPub { - for _, tx := range txs { - for _, in := range tx.GetInputs() { - if primitives.ConvertFctAddressToUserStr(in.GetAddress()) == adr { - filtered = append(filtered, tx) - } - } - for _, out := range tx.GetOutputs() { - if primitives.ConvertFctAddressToUserStr(out.GetAddress()) == adr { - filtered = append(filtered, tx) - } - } - } - } else if factom.AddressStringType(adr) == factom.ECPub { - for _, tx := range txs { - for _, out := range tx.GetECOutputs() { - if primitives.ConvertECAddressToUserStr(out.GetAddress()) == adr { - filtered = append(filtered, tx) - } - } - } - } else { - return nil, fmt.Errorf("not a valid address") - } - - return filtered, nil -} - -func (db *TXDatabaseOverlay) GetTXRange(start, end int) ( - []interfaces.ITransaction, error) { - if start < 0 || end < 0 || end < start { - return nil, fmt.Errorf("Range cannot have negative numbers") - } - - // update the database and get the newest fblock - _, err := db.Update() - if err != nil { - return nil, err - } - fblock, err := db.DBO.FetchFBlockHead() - if err != nil { - return nil, err - } - if fblock == nil { - return nil, fmt.Errorf("FBlock Chain has not finished syncing") - } - txs := make([]interfaces.ITransaction, 0) - - s, e := uint32(start), uint32(end) - - for { - // get all of the txs from the block - height := fblock.GetDatabaseHeight() - - if s <= height && height <= e { - for _, tx := range fblock.GetTransactions() { - ins, err := tx.TotalInputs() - if err != nil { - return nil, err - } - outs, err := tx.TotalOutputs() - if err != nil { - return nil, err - } - - if ins != 0 || outs != 0 { - tx.SetBlockHeight(height) - txs = append(txs, tx) - } - } - } - - precedessor := fblock.GetPrevKeyMR().String() - if height <= s || precedessor == factom.ZeroHash { - break - } - - // get the previous block - fblock, err = db.GetFBlock(precedessor) - if err != nil { - return nil, err - } else if fblock == nil { - return nil, fmt.Errorf("Missing fblock in database: %s", precedessor) - } - } - - return txs, nil -} - -// GetFBlock retrives a Factoid Block from Factom -func (db *TXDatabaseOverlay) GetFBlock(keymr string) (interfaces.IFBlock, error) { - h, err := primitives.NewShaHashFromStr(keymr) - if err != nil { - return nil, err - } - - fBlock, err := db.DBO.FetchFBlock(h) - if err != nil { - return nil, err - } - return fBlock, nil -} - -func (db *TXDatabaseOverlay) FetchNextFBlockHeight() (uint32, error) { - block, err := db.DBO.FetchFBlockHead() - if err != nil { - return 0, err - } - if block == nil { - return 0, nil - } - return block.GetDBHeight() + 1, nil -} - -func (db *TXDatabaseOverlay) InsertFBlockHead(fblock interfaces.IFBlock) error { - return db.DBO.SaveFactoidBlockHead(fblock) -} - -// Update gets all fblocks written since the database was last updated, and -// returns the most recent fblock keymr. -func (db *TXDatabaseOverlay) Update() (string, error) { - newestFBlock, err := fblockHead() - if err != nil { - return "", err - } - - start, err := db.FetchNextFBlockHeight() - if err != nil { - return "", err - } - - // Make sure we didn't switch networks - genesis, err := db.DBO.FetchFBlockByHeight(0) - if err != nil { - return "", err - } - if genesis != nil { - genesis2, err := getdblockbyheight(0) - if err != nil { - return "", err - } - - var gensisFBlockKeyMr interfaces.IHash - for _, e := range genesis2.GetDBEntries() { - if e.GetChainID().String() == "000000000000000000000000000000000000000000000000000000000000000f" { - gensisFBlockKeyMr = e.GetKeyMR() - break - } - } - - if gensisFBlockKeyMr == nil { - return "", fmt.Errorf("unable to fetch the genesis block via the api") - } - - if !gensisFBlockKeyMr.IsSameAs(genesis.GetKeyMR()) { - start = 0 - } - } - - newestHeight := newestFBlock.GetDatabaseHeight() - - // If the newest block in the tx cashe has a greater height than the newest - // fblock then clear the cashe and start from 0. - if start >= newestHeight { - db.DBO.Clear(databaseOverlay.FACTOIDBLOCK) - return newestFBlock.GetKeyMR().String(), nil - } - - // If the latest block from the database is not available from the blockchain - // then clear the cashe and start from 0. - if f, err := getfblockbyheight(start); err != nil { - db.DBO.Clear(databaseOverlay.FACTOIDBLOCK) - return f.GetKeyMR().String(), err - } - - db.DBO.StartMultiBatch() - for i := start; i <= newestHeight; i++ { - if i%1000 == 0 { - if newestHeight-start > 1000 { - fmt.Printf("Fetching block %v/%v\n", i, newestHeight) - } - } - fblock, err := getfblockbyheight(i) - if err != nil { - db.DBO.ExecuteMultiBatch() - return "", err - } - db.DBO.ProcessFBlockMultiBatch(fblock) - - // Save to DB every 500 blocks - if i%500 == 0 { - db.DBO.ExecuteMultiBatch() - db.DBO.StartMultiBatch() - } - - // If the wallet is stopped, this process becomes hard to kill. Have it exit - if db.quit { - break - } - } - - if !db.quit { - fmt.Printf("Fetching block %v/%v\n", newestHeight, newestHeight) - } - - // Save the remaining blocks - if err = db.DBO.ExecuteMultiBatch(); err != nil { - return "", err - } - - return newestFBlock.GetKeyMR().String(), nil -} - -// fblockHead gets the most recent fblock. -func fblockHead() (interfaces.IFBlock, error) { - fblockID := "000000000000000000000000000000000000000000000000000000000000000f" - - dbhead, err := factom.GetDBlockHead() - if err != nil { - return nil, err - } - dblock, err := factom.GetDBlock(dbhead) - if err != nil { - return nil, err - } - - var fblockmr string - for _, eblock := range dblock.DBEntries { - if eblock.ChainID == fblockID { - fblockmr = eblock.KeyMR - } - } - if fblockmr == "" { - return nil, err - } - - return getfblock(fblockmr) -} - -func getfblock(keymr string) (interfaces.IFBlock, error) { - raw, err := factom.GetRaw(keymr) - if err != nil { - return nil, err - } - - return factoid.UnmarshalFBlock(raw) -} - -func getfblockbyheight(height uint32) (interfaces.IFBlock, error) { - resp, err := factom.GetBlockByHeightRaw("fblock", int64(height)) - if err != nil { - return nil, err - } - - raw, err := hex.DecodeString(resp.RawData) - if err != nil { - return nil, err - } - - return factoid.UnmarshalFBlock(raw) -} - -func getdblockbyheight(height uint32) (interfaces.IDirectoryBlock, error) { - resp, err := factom.GetBlockByHeightRaw("dblock", int64(height)) - if err != nil { - return nil, err - } - - raw, err := hex.DecodeString(resp.RawData) - if err != nil { - return nil, err - } - - return directoryBlock.UnmarshalDBlock(raw) -} diff --git a/wallet/txdatabase_test.go b/wallet/txdatabase_test.go deleted file mode 100644 index 7692262..0000000 --- a/wallet/txdatabase_test.go +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2016 Factom Foundation -// Use of this source code is governed by the MIT -// license that can be found in the LICENSE file. - -package wallet_test - -import ( - "testing" - - . "github.com/FactomProject/factom/wallet" - "github.com/FactomProject/factomd/common/interfaces" - "github.com/FactomProject/factomd/testHelper" -) - -func TestTXDatabaseOverlay(t *testing.T) { - db1 := NewTXMapDB() - - fblock := fblockHead() - if err := db1.InsertFBlockHead(fblock); err != nil { - t.Error(err) - } - if f, err := db1.GetFBlock(fblock.GetKeyMR().String()); err != nil { - t.Error(err) - } else if f == nil { - t.Errorf("Fblock not found in db") - } -} - -/* -func TestGetAllTXs(t *testing.T) { - db1 := NewTXMapDB() - - txs, err := db1.GetAllTXs() - if err != nil { - t.Error(err) - } - t.Logf("got %d txs", len(txs)) -} - -func TestGetTXAddress(t *testing.T) { - db1 := NewTXMapDB() - - adr := "FA2jK2HcLnRdS94dEcU27rF3meoJfpUcZPSinpb7AwQvPRY6RL1Q" - txs, err := db1.GetTXAddress(adr) - if err != nil { - t.Error(err) - } - t.Logf("got %d txs", len(txs)) -} -*/ -//func TestGetAllTXs(t *testing.T) { -// dbpath := os.TempDir() + "/test_txdb-01" -// db1, err := NewTXLevelDB(dbpath) -// if err != nil { -// t.Error(err) -// } -// defer db1.Close() -// -// txs := make(chan interfaces.ITransaction, 500) -// errs := make(chan error) -// output := make(chan string) -// -// go db1.GetAllTXs(txs, errs) -// -// go func() { -// for tx := range txs { -// output <- fmt.Sprint("Got TX:", tx) -// } -// output <- fmt.Sprint("end of txs") -// }() -// -// go func() { -// for err := range errs { -// output <- fmt.Sprintln("Got error:", err) -// } -// output <- fmt.Sprint("end of errs") -// }() -// -// for { -// fmt.Println(<-output) -// } -// -//// for { -//// select { -//// case tx, ok := <-txs: -//// fmt.Println("Got TX:", tx) -//// if !ok { -//// txs = nil -//// } -//// case err, ok := <-errs: -//// if !ok { -//// errs = nil -//// } -//// } -//// -//// if txs == nil && errs == nil { -//// break -//// } -//// } -//} - -// fblockHead gets the most recent fblock. -func fblockHead() interfaces.IFBlock { - return testHelper.CreateTestFactoidBlock(nil) -} diff --git a/wallet/util.go b/wallet/util.go deleted file mode 100644 index e50a98a..0000000 --- a/wallet/util.go +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2016 Factom Foundation -// Use of this source code is governed by the MIT -// license that can be found in the LICENSE file. - -package wallet - -import ( - "bytes" - "crypto/sha256" - - "github.com/FactomProject/btcutil/base58" -) - -const ( - SeedLength = 64 - ApiVersion = "2.0" -) - -// WalletVersion sets the semantic version number of the build -// if using the standard vendor directory build process for factom-walletd in the factom-walletd repo is -// $ go install -ldflags "-X github.com/FactomProject/factom/wallet.WalletVersion=`cat ./vendor/github.com/FactomProject/factom/wallet/VERSION`" -v - -//if doing development and modifying the factom repo, in factom-walletd run -// $ go install -ldflags "-X github.com/FactomProject/factom/wallet.WalletVersion=`cat $GOPATH/src/github.com/FactomProject/factom/wallet/VERSION`" -v - -// It also seems to need to have the previous binary deleted if recompiling to have this message show up if no code has changed. - -var WalletVersion string = "BuiltWithoutVersion" - -// seed address prefix -var seedPrefix = []byte{0x13, 0xdd} - -// SeedString returnes the string representation of a raw Wallet Seed or Next -// Wallet Seed. -func SeedString(seed []byte) string { - if len(seed) != SeedLength { - return "" - } - - buf := new(bytes.Buffer) - - // 2 byte Seed Address Prefix - buf.Write(seedPrefix) - - // 64 byte Seed - buf.Write(seed) - - // 4 byte Checksum - check := shad(buf.Bytes())[:4] - buf.Write(check) - - return base58.Encode(buf.Bytes()) -} - -// shad Double Sha256 Hash; sha256(sha256(data)) -func shad(data []byte) []byte { - h1 := sha256.Sum256(data) - h2 := sha256.Sum256(h1[:]) - return h2[:] -} - -// newCounter is used to generate the ID field for the JSON2Request -func newCounter() func() int { - count := 0 - return func() int { - count += 1 - return count - } -} - -var APICounter = newCounter() diff --git a/wallet/util_test.go b/wallet/util_test.go deleted file mode 100644 index dd8f459..0000000 --- a/wallet/util_test.go +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2016 Factom Foundation -// Use of this source code is governed by the MIT -// license that can be found in the LICENSE file. - -package wallet_test - -import ( - . "github.com/FactomProject/factom/wallet" - "testing" -) - -func TestSeedString(t *testing.T) { - zSeedStr := "sdLGjhUDxGpiBEPRhTwysRYmxNQD6V48Aa84oVzfHvy6suim6qB6m3MCp8aHu1k1CNVLJdB8N9HtGR4NZTtFfp3mj591eA3" - - seed := make([]byte, 64) - if SeedString(seed) != zSeedStr { - t.Errorf("seed string does not match") - } -} diff --git a/wallet/v1conversion.go b/wallet/v1conversion.go deleted file mode 100644 index 017df78..0000000 --- a/wallet/v1conversion.go +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright 2016 Factom Foundation -// Use of this source code is governed by the MIT -// license that can be found in the LICENSE file. - -package wallet - -import ( - "fmt" - - "github.com/FactomProject/factoid" - "github.com/FactomProject/factoid/state/stateinit" - "github.com/FactomProject/factoid/wallet" - "github.com/FactomProject/factom" -) - -// This file is a dirty hack to to get the keys out of a version 1 wallet. - -// ImportV1Wallet takes a version 1 wallet bolt.db file and imports all of its -// addresses into a factom wallet. -func ImportV1Wallet(v1path, v2path string) (*Wallet, error) { - w, err := NewOrOpenBoltDBWallet(v2path) - if err != nil { - return nil, err - } - - fstate := stateinit.NewFactoidState(v1path) - - _, values := fstate.GetDB().GetKeysValues([]byte(factoid.W_NAME)) - for _, v := range values { - we, ok := v.(wallet.IWalletEntry) - if !ok { - w.Close() - return nil, fmt.Errorf("Cannot retrieve addresses from version 1 database") - } - - switch we.GetType() { - case "fct": - f, err := factom.MakeFactoidAddress(we.GetPrivKey(0)[:32]) - if err != nil { - w.Close() - return nil, err - } - if err := w.InsertFCTAddress(f); err != nil { - w.Close() - return nil, err - } - case "ec": - e, err := factom.MakeECAddress(we.GetPrivKey(0)[:32]) - if err != nil { - w.Close() - return nil, err - } - if err := w.InsertECAddress(e); err != nil { - w.Close() - return nil, err - } - default: - return nil, fmt.Errorf("version 1 database returned unknown address type %s %#v", we.GetType(), we) - } - } - return w, err -} - -// ImportV1WalletToLDB takes a version 1 wallet bolt.db file and imports all of its -// addresses into a factom wallet. -func ImportV1WalletToLDB(v1path, v2path string) (*Wallet, error) { - w, err := NewOrOpenBoltDBWallet(v2path) - if err != nil { - return nil, err - } - - fstate := stateinit.NewFactoidState(v1path) - - _, values := fstate.GetDB().GetKeysValues([]byte(factoid.W_NAME)) - for _, v := range values { - we, ok := v.(wallet.IWalletEntry) - if !ok { - w.Close() - return nil, fmt.Errorf("Cannot retrieve addresses from version 1 database") - } - - switch we.GetType() { - case "fct": - f, err := factom.MakeFactoidAddress(we.GetPrivKey(0)[:32]) - if err != nil { - w.Close() - return nil, err - } - if err := w.InsertFCTAddress(f); err != nil { - w.Close() - return nil, err - } - case "ec": - e, err := factom.MakeECAddress(we.GetPrivKey(0)[:32]) - if err != nil { - w.Close() - return nil, err - } - if err := w.InsertECAddress(e); err != nil { - w.Close() - return nil, err - } - default: - return nil, fmt.Errorf("version 1 database returned unknown address type %s %#v", we.GetType(), we) - } - } - return w, err -} - -// ImportV1WalletToEncryptedDB takes a version 1 wallet bolt.db file and imports all of its -// addresses into a factom wallet. -func ImportV1WalletToEncryptedDB(v1path, v2path, password string) (*Wallet, error) { - w, err := NewEncryptedBoltDBWallet(v2path, password) - if err != nil { - return nil, err - } - - fstate := stateinit.NewFactoidState(v1path) - - _, values := fstate.GetDB().GetKeysValues([]byte(factoid.W_NAME)) - for _, v := range values { - we, ok := v.(wallet.IWalletEntry) - if !ok { - w.Close() - return nil, fmt.Errorf("Cannot retrieve addresses from version 1 database") - } - - switch we.GetType() { - case "fct": - f, err := factom.MakeFactoidAddress(we.GetPrivKey(0)[:32]) - if err != nil { - w.Close() - return nil, err - } - if err := w.InsertFCTAddress(f); err != nil { - w.Close() - return nil, err - } - case "ec": - e, err := factom.MakeECAddress(we.GetPrivKey(0)[:32]) - if err != nil { - w.Close() - return nil, err - } - if err := w.InsertECAddress(e); err != nil { - w.Close() - return nil, err - } - default: - return nil, fmt.Errorf("version 1 database returned unknown address type %s %#v", we.GetType(), we) - } - } - return w, err -} diff --git a/wallet/v1conversion_test.go b/wallet/v1conversion_test.go deleted file mode 100644 index 2b1054d..0000000 --- a/wallet/v1conversion_test.go +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2016 Factom Foundation -// Use of this source code is governed by the MIT -// license that can be found in the LICENSE file. - -package wallet_test - -import ( - "os" - "testing" - - . "github.com/FactomProject/factom/wallet" -) - -func TestImportV1Wallet(t *testing.T) { - v1path := os.TempDir() + "/factoid_wallet_bolt.db" - v2path := os.TempDir() + "/test_wallet-01" - - w, err := ImportV1Wallet(v1path, v2path) - if err != nil { - t.Error(err) - } - - fs, es, err := w.GetAllAddresses() - // print the addresses - for _, f := range fs { - t.Logf("%s %s", f, f.SecString()) - } - for _, e := range es { - t.Logf("%s %s", e, e.SecString()) - } - - // close and remove the testing db - if err := w.Close(); err != nil { - t.Error(err) - } - if err := os.RemoveAll(v2path); err != nil { - t.Error(err) - } -} diff --git a/wallet/walletDBO.go b/wallet/walletDBO.go deleted file mode 100644 index e5d354c..0000000 --- a/wallet/walletDBO.go +++ /dev/null @@ -1,626 +0,0 @@ -package wallet - -import ( - "bytes" - "crypto/rand" - "encoding/gob" - "fmt" - "os" - "path/filepath" - "sort" - "strings" - - "github.com/FactomProject/factom" - "github.com/FactomProject/factomd/common/interfaces" - "github.com/FactomProject/factomd/common/primitives" - "github.com/FactomProject/factomd/database/databaseOverlay" - "github.com/FactomProject/factomd/database/hybridDB" - "github.com/FactomProject/factomd/database/mapdb" - "github.com/FactomProject/factomd/database/securedb" - "github.com/FactomProject/go-bip32" - "github.com/FactomProject/go-bip39" -) - -// Database keys and key prefixes -var ( - fcDBPrefix = []byte("Factoids") - ecDBPrefix = []byte("Entry Credits") - seedDBKey = []byte("DB Seed") - identityDBPrefix = []byte("Identities") -) - -type WalletDatabaseOverlay struct { - DBO databaseOverlay.Overlay -} - -func NewWalletOverlay(db interfaces.IDatabase) *WalletDatabaseOverlay { - answer := new(WalletDatabaseOverlay) - answer.DBO.DB = db - return answer -} - -func NewMapDB() *WalletDatabaseOverlay { - return NewWalletOverlay(new(mapdb.MapDB)) -} - -func NewLevelDB(ldbpath string) (*WalletDatabaseOverlay, error) { - db, err := hybridDB.NewLevelMapHybridDB(ldbpath, false) - if err != nil { - fmt.Printf("err opening db: %v\n", err) - } - - if db == nil { - fmt.Println("Creating new db ...") - db, err = hybridDB.NewLevelMapHybridDB(ldbpath, true) - - if err != nil { - return nil, err - } - } - fmt.Println("Database started from: " + ldbpath) - return NewWalletOverlay(db), nil -} - -func NewBoltDB(boltPath string) (*WalletDatabaseOverlay, error) { - // setup panic recovery - defer func() { - if r := recover(); r != nil { - fmt.Printf("Could not use wallet file \"%s\"\n%v\n", boltPath, r) - os.Exit(1) - } - }() - - // check if the file exists or if it is a directory - fileInfo, err := os.Stat(boltPath) - if err == nil { - if fileInfo.IsDir() { - return nil, fmt.Errorf("The path %s is a directory. Please specify a file name.", boltPath) - } - } - - // create the wallet directory if it doesn't already exist - if os.IsNotExist(err) { - if err := os.MkdirAll(filepath.Dir(boltPath), 0700); err != nil { - fmt.Printf("database error %s\n", err) - } - } - - // check for other errors besides the file not existing - if err != nil && !os.IsNotExist(err) { - fmt.Printf("database error %s\n", err) - return nil, err - } - - db := hybridDB.NewBoltMapHybridDB(nil, boltPath) - fmt.Println("Database started from: " + boltPath) - return NewWalletOverlay(db), nil -} - -func NewEncryptedBoltDB(boltPath, password string) (*WalletDatabaseOverlay, error) { - err := CreateEncryptedBoltDB(boltPath) - if err != nil { - return nil, err - } - wOverlay, err := OpenEncryptedBoltDB(boltPath, password) - if err != nil { - return nil, err - } - return wOverlay, nil -} - -func CreateEncryptedBoltDB(boltPath string) error { - // check if the file exists or if it is a directory - fileInfo, err := os.Stat(boltPath) - if err == nil { - if fileInfo.IsDir() { - return fmt.Errorf("The path %s is a directory. Please specify a file name.", boltPath) - } - } - - // create the wallet directory if it doesn't already exist - if os.IsNotExist(err) { - if err := os.MkdirAll(filepath.Dir(boltPath), 0700); err != nil { - fmt.Printf("database error %s\n", err) - } - } - - if err != nil && !os.IsNotExist(err) { //some other error, besides the file not existing - fmt.Printf("database error %s\n", err) - return err - } - return nil -} - -func OpenEncryptedBoltDB(boltPath, password string) (*WalletDatabaseOverlay, error) { - defer func() { - if r := recover(); r != nil { - fmt.Printf("Could not use wallet file \"%s\"\n%v\n", boltPath, r) - os.Exit(1) - } - }() - db, err := securedb.NewEncryptedDB(boltPath, "Bolt", password) - if err != nil { - return nil, err - } - - fmt.Println("Encrypted Database started from: " + boltPath) - return NewWalletOverlay(db), nil -} - -type DBSeedBase struct { - MnemonicSeed string - NextFactoidAddressIndex uint32 - NextECAddressIndex uint32 - NextIdentityKeyIndex uint32 -} - -type DBSeed struct { - DBSeedBase -} - -var _ interfaces.BinaryMarshallable = (*DBSeed)(nil) - -func (e *DBSeed) MarshalBinary() ([]byte, error) { - var data primitives.Buffer - - enc := gob.NewEncoder(&data) - - err := enc.Encode(e.DBSeedBase) - if err != nil { - return nil, err - } - return data.DeepCopyBytes(), nil -} - -func (e *DBSeed) UnmarshalBinaryData(data []byte) (newData []byte, err error) { - dec := gob.NewDecoder(primitives.NewBuffer(data)) - dbsb := DBSeedBase{} - err = dec.Decode(&dbsb) - if err != nil { - return nil, err - } - e.DBSeedBase = dbsb - return nil, nil -} - -func (e *DBSeed) UnmarshalBinary(data []byte) (err error) { - _, err = e.UnmarshalBinaryData(data) - return -} - -func (e *DBSeed) JSONByte() ([]byte, error) { - return primitives.EncodeJSON(e) -} - -func (e *DBSeed) JSONString() (string, error) { - return primitives.EncodeJSONString(e) -} - -func (e *DBSeed) JSONBuffer(b *bytes.Buffer) error { - return primitives.EncodeJSONToBuffer(e, b) -} - -func (e *DBSeed) String() string { - str, _ := e.JSONString() - return str -} - -func (e *DBSeed) NextFCTAddress() (*factom.FactoidAddress, error) { - add, err := factom.MakeBIP44FactoidAddress( - e.MnemonicSeed, - bip32.FirstHardenedChild, - 0, - e.NextFactoidAddressIndex, - ) - if err != nil { - return nil, err - } - e.NextFactoidAddressIndex++ - return add, nil -} - -func (e *DBSeed) NextECAddress() (*factom.ECAddress, error) { - add, err := factom.MakeBIP44ECAddress( - e.MnemonicSeed, - bip32.FirstHardenedChild, - 0, - e.NextECAddressIndex, - ) - if err != nil { - return nil, err - } - e.NextECAddressIndex++ - return add, nil -} - -func (e *DBSeed) NextIdentityKey() (*factom.IdentityKey, error) { - add, err := factom.MakeBIP44IdentityKey( - e.MnemonicSeed, - bip32.FirstHardenedChild, - 0, - e.NextIdentityKeyIndex, - ) - if err != nil { - return nil, err - } - e.NextIdentityKeyIndex++ - return add, nil -} - -func NewRandomSeed() (*DBSeed, error) { - seed := make([]byte, 16) - if n, err := rand.Read(seed); err != nil { - panic(err) - return nil, err - } else if n != 16 { - return nil, fmt.Errorf("Wrong number of bytes read: %d", n) - } - - mnemonic, err := bip39.NewMnemonic(seed) - if err != nil { - panic(err) - return nil, err - } - - dbSeed := new(DBSeed) - dbSeed.MnemonicSeed = mnemonic - - return dbSeed, nil -} - -func (db *WalletDatabaseOverlay) InsertDBSeed(seed *DBSeed) error { - if seed == nil { - return nil - } - - batch := []interfaces.Record{} - batch = append(batch, interfaces.Record{seedDBKey, seedDBKey, seed}) - - return db.DBO.PutInBatch(batch) -} - -func (db *WalletDatabaseOverlay) GetDBSeed() (*DBSeed, error) { - data, err := db.DBO.Get(seedDBKey, seedDBKey, new(DBSeed)) - if err != nil { - return nil, err - } - if data == nil { - return nil, nil - } - return data.(*DBSeed), nil -} - -func (db *WalletDatabaseOverlay) GetOrCreateDBSeed() (*DBSeed, error) { - data, err := db.DBO.Get(seedDBKey, seedDBKey, new(DBSeed)) - if err != nil { - return nil, err - } - if data == nil { - seed, err := NewRandomSeed() - if err != nil { - return nil, err - } - err = db.InsertDBSeed(seed) - if err != nil { - return nil, err - } - return seed, nil - } - return data.(*DBSeed), nil -} - -func (db *WalletDatabaseOverlay) GetNextECAddress() (*factom.ECAddress, error) { - seed, err := db.GetOrCreateDBSeed() - if err != nil { - return nil, err - } - add, err := seed.NextECAddress() - if err != nil { - return nil, err - } - err = db.InsertDBSeed(seed) - if err != nil { - return nil, err - } - err = db.InsertECAddress(add) - if err != nil { - return nil, err - } - return add, nil -} - -func (db *WalletDatabaseOverlay) GetNextFCTAddress() (*factom.FactoidAddress, error) { - seed, err := db.GetOrCreateDBSeed() - if err != nil { - return nil, err - } - add, err := seed.NextFCTAddress() - if err != nil { - return nil, err - } - err = db.InsertDBSeed(seed) - if err != nil { - return nil, err - } - err = db.InsertFCTAddress(add) - if err != nil { - return nil, err - } - return add, nil -} - -func (db *WalletDatabaseOverlay) InsertECAddress(e *factom.ECAddress) error { - if e == nil { - return nil - } - - batch := []interfaces.Record{} - batch = append(batch, interfaces.Record{ecDBPrefix, []byte(e.PubString()), e}) - - return db.DBO.PutInBatch(batch) -} - -func (db *WalletDatabaseOverlay) GetECAddress(pubString string) (*factom.ECAddress, error) { - data, err := db.DBO.Get(ecDBPrefix, []byte(pubString), new(factom.ECAddress)) - if err != nil { - return nil, err - } - if data == nil { - return nil, ErrNoSuchAddress - } - return data.(*factom.ECAddress), nil -} - -func (db *WalletDatabaseOverlay) GetAllECAddresses() ([]*factom.ECAddress, error) { - list, err := db.DBO.FetchAllBlocksFromBucket(ecDBPrefix, new(ECA)) - if err != nil { - return nil, err - } - return toECList(list), nil -} - -func toECList(source []interfaces.BinaryMarshallableAndCopyable) []*factom.ECAddress { - answer := make([]*factom.ECAddress, len(source)) - for i, v := range source { - answer[i] = v.(*ECA).ECAddress - } - sort.Sort(byECName(answer)) - return answer -} - -type byECName []*factom.ECAddress - -func (f byECName) Len() int { - return len(f) -} -func (f byECName) Less(i, j int) bool { - a := strings.Compare(f[i].PubString(), f[j].PubString()) - return a < 0 -} -func (f byECName) Swap(i, j int) { - f[i], f[j] = f[j], f[i] -} - -func (db *WalletDatabaseOverlay) InsertFCTAddress(e *factom.FactoidAddress) error { - if e == nil { - return nil - } - - batch := []interfaces.Record{} - batch = append(batch, interfaces.Record{fcDBPrefix, []byte(e.String()), e}) - - return db.DBO.PutInBatch(batch) -} - -func (db *WalletDatabaseOverlay) GetFCTAddress(str string) (*factom.FactoidAddress, error) { - data, err := db.DBO.Get(fcDBPrefix, []byte(str), new(factom.FactoidAddress)) - if err != nil { - return nil, err - } - if data == nil { - return nil, ErrNoSuchAddress - } - return data.(*factom.FactoidAddress), nil -} - -func (db *WalletDatabaseOverlay) GetAllFCTAddresses() ([]*factom.FactoidAddress, error) { - list, err := db.DBO.FetchAllBlocksFromBucket(fcDBPrefix, new(FA)) - if err != nil { - return nil, err - } - return toFList(list), nil -} - -func toFList(source []interfaces.BinaryMarshallableAndCopyable) []*factom.FactoidAddress { - answer := make([]*factom.FactoidAddress, len(source)) - for i, v := range source { - answer[i] = v.(*FA).FactoidAddress - } - sort.Sort(byFName(answer)) - return answer -} - -func (db *WalletDatabaseOverlay) RemoveAddress(pubString string) error { - if len(pubString) == 0 { - return nil - } - if pubString[:1] == "F" { - data, err := db.DBO.Get(fcDBPrefix, []byte(pubString), new(factom.FactoidAddress)) - if err != nil { - return err - } - if data == nil { - return ErrNoSuchAddress - } - err = db.DBO.Delete(fcDBPrefix, []byte(pubString)) - if err == nil { - err := db.DBO.Delete(fcDBPrefix, []byte(pubString)) //delete twice to flush the db file - return err - } else { - return err - } - } else if pubString[:1] == "E" { - data, err := db.DBO.Get(ecDBPrefix, []byte(pubString), new(factom.ECAddress)) - if err != nil { - return err - } - if data == nil { - return ErrNoSuchAddress - } - err = db.DBO.Delete(ecDBPrefix, []byte(pubString)) - if err == nil { - err := db.DBO.Delete(ecDBPrefix, []byte(pubString)) //delete twice to flush the db file - return err - } else { - return err - } - } else { - return fmt.Errorf("Unknown address type") - } - - return nil -} - -type byFName []*factom.FactoidAddress - -func (f byFName) Len() int { - return len(f) -} -func (f byFName) Less(i, j int) bool { - a := strings.Compare(f[i].String(), f[j].String()) - return a < 0 -} -func (f byFName) Swap(i, j int) { - f[i], f[j] = f[j], f[i] -} - -type ECA struct { - *factom.ECAddress -} - -var _ interfaces.BinaryMarshallableAndCopyable = (*ECA)(nil) - -func (t *ECA) New() interfaces.BinaryMarshallableAndCopyable { - e := new(ECA) - e.ECAddress = factom.NewECAddress() - return e -} - -type FA struct { - *factom.FactoidAddress -} - -var _ interfaces.BinaryMarshallableAndCopyable = (*FA)(nil) - -func (t *FA) New() interfaces.BinaryMarshallableAndCopyable { - e := new(FA) - e.FactoidAddress = factom.NewFactoidAddress() - return e -} - -func (db *WalletDatabaseOverlay) GetNextIdentityKey() (*factom.IdentityKey, error) { - seed, err := db.GetOrCreateDBSeed() - if err != nil { - return nil, err - } - add, err := seed.NextIdentityKey() - if err != nil { - return nil, err - } - err = db.InsertDBSeed(seed) - if err != nil { - return nil, err - } - err = db.InsertIdentityKey(add) - if err != nil { - return nil, err - } - return add, nil -} - -func (db *WalletDatabaseOverlay) InsertIdentityKey(e *factom.IdentityKey) error { - if e == nil { - return nil - } - - batch := []interfaces.Record{} - batch = append(batch, interfaces.Record{identityDBPrefix, []byte(e.String()), e}) - - return db.DBO.PutInBatch(batch) -} - -func (db *WalletDatabaseOverlay) GetIdentityKey(str string) (*factom.IdentityKey, error) { - data, err := db.DBO.Get(identityDBPrefix, []byte(str), new(factom.IdentityKey)) - if err != nil { - return nil, err - } - if data == nil { - return nil, ErrNoSuchIdentityKey - } - return data.(*factom.IdentityKey), nil -} - -func (db *WalletDatabaseOverlay) GetAllIdentityKeys() ([]*factom.IdentityKey, error) { - list, err := db.DBO.FetchAllBlocksFromBucket(identityDBPrefix, new(ID)) - if err != nil { - return nil, err - } - return toIdentityKeyList(list), nil -} - -func toIdentityKeyList(source []interfaces.BinaryMarshallableAndCopyable) []*factom.IdentityKey { - answer := make([]*factom.IdentityKey, len(source)) - for i, v := range source { - answer[i] = v.(*ID).IdentityKey - } - sort.Sort(byKeyName(answer)) - return answer -} - -func (db *WalletDatabaseOverlay) RemoveIdentityKey(pubString string) error { - if len(pubString) == 0 { - return nil - } - - data, err := db.DBO.Get(identityDBPrefix, []byte(pubString), new(factom.IdentityKey)) - if err != nil { - return err - } - if data == nil { - return ErrNoSuchIdentityKey - } - err = db.DBO.Delete(identityDBPrefix, []byte(pubString)) - if err == nil { - err := db.DBO.Delete(identityDBPrefix, []byte(pubString)) //delete twice to flush the db file - return err - } else { - return err - } - - return nil -} - -type byKeyName []*factom.IdentityKey - -func (f byKeyName) Len() int { - return len(f) -} -func (f byKeyName) Less(i, j int) bool { - a := strings.Compare(f[i].String(), f[j].String()) - return a < 0 -} -func (f byKeyName) Swap(i, j int) { - f[i], f[j] = f[j], f[i] -} - -type ID struct { - *factom.IdentityKey -} - -var _ interfaces.BinaryMarshallableAndCopyable = (*ID)(nil) - -func (t *ID) New() interfaces.BinaryMarshallableAndCopyable { - e := new(ID) - e.IdentityKey = factom.NewIdentityKey() - return e -} diff --git a/wallet/walletDBO_test.go b/wallet/walletDBO_test.go deleted file mode 100644 index 9e42dd8..0000000 --- a/wallet/walletDBO_test.go +++ /dev/null @@ -1,109 +0,0 @@ -package wallet_test - -import ( - "strings" - "testing" - - . "github.com/FactomProject/factom/wallet" -) - -func TestWalletDBO(t *testing.T) { - db := NewMapDB() - seed, err := db.GetOrCreateDBSeed() - if err != nil { - t.Errorf("%v", err) - } - - seed.MnemonicSeed = "yellow yellow yellow yellow yellow yellow yellow yellow yellow yellow yellow yellow" - - err = db.InsertDBSeed(seed) - if err != nil { - t.Errorf("%v", err) - } - - ec, err := db.GetNextECAddress() - if err != nil { - t.Errorf("%v", err) - } - if ec.String() != "EC2KnJQN86MYq4pQyeSGTHSiVdkhRCPXS3udzD4im6BXRBjZFMmR" { - t.Errorf("%v", ec.String()) - } - ec, err = db.GetNextECAddress() - if err != nil { - t.Errorf("%v", err) - } - if ec.String() != "EC2UNG5LztGN3BNiVMEgkBP8ra8ud3HjjWWXKjrQozJ98rTvXKYy" { - t.Errorf("%v", ec.String()) - } - - f, err := db.GetNextFCTAddress() - if err != nil { - t.Errorf("%v", err) - } - if f.String() != "FA22de5NSG2FA2HmMaD4h8qSAZAJyztmmnwgLPghCQKoSekwYYct" { - t.Errorf("%v", f.String()) - } - f, err = db.GetNextFCTAddress() - if err != nil { - t.Errorf("%v", err) - } - if f.String() != "FA3heCmxKCk1tCCfiAMDmX8Ctg6XTQjRRaJrF5Jagc9rbo7wqQLV" { - t.Errorf("%v", f.String()) - } -} - -func TestDBSeed(t *testing.T) { - seed, err := NewRandomSeed() - if err != nil { - t.Errorf("%v", err) - } - if seed.MnemonicSeed == "" { - t.Errorf("Empty mnemonic seed returned") - } - l := len(strings.Fields(seed.MnemonicSeed)) - if l < 12 { - t.Errorf("Not enough words in mnemonic. Expecitng 12, found %d", l) - } - if l > 12 { - t.Errorf("Too many words in mnemonic. Expecitng 12, found %d", l) - } - - seed.MnemonicSeed = "yellow yellow yellow yellow yellow yellow yellow yellow yellow yellow yellow yellow" - - ec, err := seed.NextECAddress() - if err != nil { - t.Errorf("%v", err) - } - if ec.String() != "EC2KnJQN86MYq4pQyeSGTHSiVdkhRCPXS3udzD4im6BXRBjZFMmR" { - t.Errorf("%v", ec.String()) - } - ec, err = seed.NextECAddress() - if err != nil { - t.Errorf("%v", err) - } - if ec.String() != "EC2UNG5LztGN3BNiVMEgkBP8ra8ud3HjjWWXKjrQozJ98rTvXKYy" { - t.Errorf("%v", ec.String()) - } - - f, err := seed.NextFCTAddress() - if err != nil { - t.Errorf("%v", err) - } - if f.String() != "FA22de5NSG2FA2HmMaD4h8qSAZAJyztmmnwgLPghCQKoSekwYYct" { - t.Errorf("%v", f.String()) - } - f, err = seed.NextFCTAddress() - if err != nil { - t.Errorf("%v", err) - } - if f.String() != "FA3heCmxKCk1tCCfiAMDmX8Ctg6XTQjRRaJrF5Jagc9rbo7wqQLV" { - t.Errorf("%v", f.String()) - } - - if seed.NextFactoidAddressIndex != 2 { - t.Errorf("Wrong NextFactoidAddressIndex") - } - if seed.NextECAddressIndex != 2 { - t.Errorf("Wrong NextECAddressIndex") - } -} diff --git a/wallet/wsapi/10000addressesTest.sh b/wallet/wsapi/10000addressesTest.sh deleted file mode 100755 index a3da8da..0000000 --- a/wallet/wsapi/10000addressesTest.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash -#set -x - -# This is to test the the load that wallet-balances can handle. - -FCTADDWFACTOIDS="FA3EPZYqodgyEGXNMbiZKE5TS2x2J9wF8J9MvPZb52iGR78xMgCb" - -# This takes makes 10,000 factoid addresses and funds all of them with a random factoshi balance - -COUNTFCT=10000 -OUTPUTFCT="" - -while [ $COUNT -gt 0 ]; do - OUTPUTFCT="$(echo `factom-cli newfctaddress`)" - AMOUNTFCT="1.$((RANDOM % 10000))" - factom-cli sendfct FA3EPZYqodgyEGXNMbiZKE5TS2x2J9wF8J9MvPZb52iGR78xMgCb "$OUTPUTFCT" "$AMOUNTFCT" >> factomcli.log & - let COUNTFCT=COUNTFCT-1 -done - -# This creates 1000 entry credit addesses and funds them - -COUNTEC=1000 -OUTPUTEC="" - -while [ $COUNTEC -gt 0 ]; do - OUTPUTEC="$(echo `factom-cli newecaddress`)" - AMOUNTEC="$((1 + RANDOM % 15))" - factom-cli buyec "$FCTADDWFACTOIDS" "$OUTPUTEC" "$AMOUNTEC" >> factomcliec.log & - let COUNTEC=COUNTEC-1 -done diff --git a/wallet/wsapi/errors.go b/wallet/wsapi/errors.go deleted file mode 100644 index 6404e2a..0000000 --- a/wallet/wsapi/errors.go +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2016 Factom Foundation -// Use of this source code is governed by the MIT -// license that can be found in the LICENSE file. - -package wsapi - -import ( - "github.com/FactomProject/factom" - "github.com/FactomProject/web" -) - -const httpBad = 200 - -// handleV2Error handles the error responses to RPC calls -func handleV2Error(ctx *web.Context, j *factom.JSON2Request, err *factom.JSONError) { - resp := factom.NewJSON2Response() - if j != nil { - resp.ID = j.ID - } else { - resp.ID = nil - } - resp.Error = err - - ctx.WriteHeader(httpBad) - ctx.Write([]byte(resp.String())) -} - -/* -The error codes from and including -32768 to -32000 are reserved for pre-defined errors. Any code within this range, but not defined explicitly below is reserved for future use. The error codes are nearly the same as those suggested for XML-RPC at the following url: http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php - -code message meaning --32700 Parse error Invalid JSON was received by the server. - An error occurred on the server while parsing the JSON text. --32600 Invalid Request The JSON sent is not a valid Request object. --32601 Method not found The method does not exist / is not available. --32602 Invalid params Invalid method parameter(s). --32603 Internal error Internal JSON-RPC error. --32000 to -32099 Server error Reserved for implementation-defined server-errors. -*/ - -// RPC Errors - -func newParseError() *factom.JSONError { - return factom.NewJSONError(-32700, "Parse error", nil) -} - -func newInvalidRequestError() *factom.JSONError { - return factom.NewJSONError(-32600, "Invalid Request", nil) -} - -func newMethodNotFoundError() *factom.JSONError { - return factom.NewJSONError(-32601, "Method not found", nil) -} - -func newInvalidParamsError() *factom.JSONError { - return factom.NewJSONError(-32602, "Invalid params", nil) -} - -func newInternalError() *factom.JSONError { - return factom.NewJSONError(-32603, "Internal error", nil) -} - -func newWalletIsLockedError() *factom.JSONError { - return factom.NewJSONError(-32001, "Wallet is locked", nil) -} - -func newIncorrectPassphraseError() *factom.JSONError { - return factom.NewJSONError(-32003, "Incorrect passphrase", nil) -} - -// Custom Errors - -func newCustomInternalError(data interface{}) *factom.JSONError { - return factom.NewJSONError(-32603, "Internal error", data) -} - -func newCustomInvalidParamsError(data interface{}) *factom.JSONError { - return factom.NewJSONError(-32602, "Invalid params", data) -} diff --git a/wallet/wsapi/structs.go b/wallet/wsapi/structs.go deleted file mode 100644 index a0de86c..0000000 --- a/wallet/wsapi/structs.go +++ /dev/null @@ -1,221 +0,0 @@ -// Copyright 2016 Factom Foundation -// Use of this source code is governed by the MIT -// license that can be found in the LICENSE file. - -package wsapi - -import ( - "github.com/FactomProject/factom" -) - -type TLSConfig struct { - TLSEnable bool - TLSKeyFile string - TLSCertFile string -} - -// requests - -type passphraseRequest struct { - Password string `json:"passphrase"` - Timeout int64 `json:"timeout"` -} - -type addressRequest struct { - Address string `json:"address"` -} - -type addressesRequest struct { - Addresses []string `json:"addresses"` -} - -type importRequest struct { - Addresses []struct { - Secret string `json:"secret"` - } `json:"addresses"` -} - -type importKoinifyRequest struct { - Words string `json:"words"` -} - -type transactionRequest struct { - Name string `json:"tx-name"` - Force bool `json:"force"` -} - -type signDataRequest struct { - Signer string `json:"signer"` - Data []byte `json:"data"` -} - -type transactionValueRequest struct { - Name string `json:"tx-name"` - Address string `json:"address"` - Amount uint64 `json:"amount"` -} - -type transactionAddressRequest struct { - Name string `json:"tx-name"` - Address string `json:"address"` -} - -type txdbRequest struct { - TxID string `json:"txid,omitempty"` - Address string `json:"address,omitempty"` - Range struct { - Start int `json:"start"` - End int `json:"end"` - } `json:"range,omitempty"` -} - -type entryRequest struct { - Entry factom.Entry `json:"entry"` - ECPub string `json:"ecpub"` - Force bool `json:"force"` -} - -type chainRequest struct { - Chain factom.Chain `json:"chain"` - ECPub string `json:"ecpub"` - Force bool `json:"force"` -} - -type identityKeyRequest struct { - Public string `json:"public"` -} - -type importIdentityKeysRequest struct { - Keys []struct { - Secret string `json:"secret"` - } `json:keys` -} - -type activeIdentityKeysRequest struct { - ChainID string `json:"chainid"` - Height *int64 `json:"height"` -} - -type identityChainRequest struct { - Name []string `json:"name"` - PubKeys []string `json:"pubkeys"` - ECPub string `json:"ecpub"` - Force bool `json:"force"` -} - -type identityKeyReplacementRequest struct { - ChainID string `json:"chainid"` - OldKey string `json:"oldkey"` - NewKey string `json:"newkey"` - SignerKey string `json:"signerkey"` - ECPub string `json:"ecpub"` - Force bool `json:"force"` -} - -type identityAttributeRequest struct { - ReceiverChainID string `json:"receiver-chainid"` - DestinationChainID string `json:"destination-chainid"` - Attributes []factom.IdentityAttribute `json:"attributes"` - SignerKey string `json:"signerkey"` - SignerChainID string `json:"signer-chainid"` - ECPub string `json:"ecpub"` - Force bool `json:"force"` -} - -type identityAttributeEndorsementRequest struct { - DestinationChainID string `json:"destination-chainid"` - EntryHash string `json:"entry-hash"` - SignerKey string `json:"signerkey"` - SignerChainID string `json:"signer-chainid"` - ECPub string `json:"ecpub"` - Force bool `json:"force"` -} - -// responses - -type addressResponse struct { - Public string `json:"public"` - Secret string `json:"secret"` -} - -type multiAddressResponse struct { - Addresses []*addressResponse `json:"addresses"` -} - -type balanceResponse struct { - CurrentHeight uint32 `json:"current-height"` - LastSavedHeight uint `json:"last-saved-height"` - Balances []interface{} `json:"balances"` -} - -type multiBalanceResponse struct { - FactoidAccountBalances struct { - Ack int64 `json:"ack"` - Saved int64 `json:"saved"` - } `json:"fctaccountbalances"` - EntryCreditAccountBalances struct { - Ack int64 `json:"ack"` - Saved int64 `json:"saved"` - } `json:"ecaccountbalances"` -} - -type walletBackupResponse struct { - Seed string `json:"wallet-seed"` - Addresses []*addressResponse `json:"addresses"` - IdentityKeys []*identityKeyResponse `json:"identity-keys"` -} - -type multiTransactionResponse struct { - Transactions []*factom.Transaction `json:"transactions"` -} - -type propertiesResponse struct { - WalletVersion string `json:"walletversion"` - WalletApiVersion string `json:"walletapiversion"` -} - -type simpleResponse struct { - Success bool `json:"success"` -} - -type unlockResponse struct { - Success bool `json:"success"` - UnlockedUntil int64 `json:"unlockeduntil"` -} - -type entryResponse struct { - Commit *factom.JSON2Request `json:"commit"` - Reveal *factom.JSON2Request `json:"reveal"` -} - -type heightResponse struct { - Height int64 `json:"height"` -} - -type identityKeyResponse struct { - Public string `json:"public"` - Secret string `json:"secret,omitempty"` -} - -type multiIdentityKeyResponse struct { - Keys []*identityKeyResponse `json:"keys"` -} - -type activeIdentityKeysResponse struct { - ChainID string `json:"chainid"` - Height int64 `json:"height"` - Keys []string `json:"keys"` -} - -type signDataResponse struct { - PubKey []byte `json:"pubkey"` - Signature []byte `json:"signature"` -} - -// Helper structs - -type UnmarBody struct { - Jsonrpc string `json:"jsonrps"` - Id int `json:"id"` - Result balanceResponse `json:"result"` -} diff --git a/wallet/wsapi/wsapi.go b/wallet/wsapi/wsapi.go deleted file mode 100644 index bb4adbe..0000000 --- a/wallet/wsapi/wsapi.go +++ /dev/null @@ -1,1594 +0,0 @@ -// Copyright 2016 Factom Foundation -// Use of this source code is governed by the MIT -// license that can be found in the LICENSE file. - -package wsapi - -import ( - "bytes" - "crypto/sha256" - "crypto/subtle" - "crypto/tls" - "encoding/base64" - "encoding/hex" - "encoding/json" - "errors" - "fmt" - "io/ioutil" - "log" - "net/http" - "os" - "reflect" - "strings" - "time" - - "github.com/FactomProject/btcutil/certs" - "github.com/FactomProject/factom" - "github.com/FactomProject/factom/wallet" - "github.com/FactomProject/factomd/common/factoid" - "github.com/FactomProject/factomd/common/interfaces" - "github.com/FactomProject/factomd/common/primitives" - "github.com/FactomProject/factomd/database/securedb" - "github.com/FactomProject/web" -) - -const APIVersion string = "2.0" - -var ( - webServer *web.Server - fctWallet *wallet.Wallet - rpcUser string - rpcPass string - authsha []byte -) - -// httpBasicAuth returns the UTF-8 bytes of the HTTP Basic authentication -// string: -// -// "Basic " + base64(username + ":" + password) -func httpBasicAuth(username, password string) []byte { - const header = "Basic " - base64 := base64.StdEncoding - - b64InputLen := len(username) + len(":") + len(password) - b64Input := make([]byte, 0, b64InputLen) - b64Input = append(b64Input, username...) - b64Input = append(b64Input, ':') - b64Input = append(b64Input, password...) - - output := make([]byte, len(header)+base64.EncodedLen(b64InputLen)) - copy(output, header) - base64.Encode(output[len(header):], b64Input) - return output -} - -func genCertPair(certFile string, keyFile string, extraAddress string) error { - fmt.Println("Generating TLS certificates...") - - org := "factom autogenerated cert" - validUntil := time.Now().Add(10 * 365 * 24 * time.Hour) - - var externalAddresses []string - if extraAddress != "" { - externalAddresses = strings.Split(extraAddress, ",") - for _, i := range externalAddresses { - fmt.Printf("adding %s to certificate\n", i) - } - } - - cert, key, err := certs.NewTLSCertPair(org, validUntil, externalAddresses) - if err != nil { - return err - } - - // Write cert and key files. - if err = ioutil.WriteFile(certFile, cert, 0666); err != nil { - return err - } - if err = ioutil.WriteFile(keyFile, key, 0600); err != nil { - os.Remove(certFile) - return err - } - - fmt.Println("Done generating TLS certificates") - return nil -} - -// filesExists reports whether the named file or directory exists. -func fileExists(name string) bool { - if _, err := os.Stat(name); err != nil { - if os.IsNotExist(err) { - return false - } - } - return true -} - -func Start(w *wallet.Wallet, net string, c factom.RPCConfig) { - webServer = web.NewServer() - fctWallet = w - - if len(c.WalletCORSDomains) > 0 { - domains := strings.Split(c.WalletCORSDomains, ",") - var cors []string - for _, domain := range domains { - cors = append(cors, strings.Trim(domain, " ")) - } - webServer.Config.CorsDomains = cors - } - - rpcUser = c.WalletRPCUser - rpcPass = c.WalletRPCPassword - - h := sha256.New() - h.Write(httpBasicAuth(rpcUser, rpcPass)) - authsha = h.Sum(nil) //set this in the beginning to prevent timing attacks - - webServer.Post("/v2", handleV2) - webServer.Get("/v2", handleV2) - - if c.WalletTLSEnable == false { - webServer.Run(net) - } else { - if !fileExists(c.WalletTLSKeyFile) && !fileExists(c.WalletTLSCertFile) { - err := genCertPair(c.WalletTLSCertFile, c.WalletTLSKeyFile, c.WalletServer) - if err != nil { - log.Fatal(err) - } - } - keypair, err := tls.LoadX509KeyPair(c.WalletTLSCertFile, c.WalletTLSKeyFile) - if err != nil { - log.Fatal(err) - } - tlsConfig := &tls.Config{ - Certificates: []tls.Certificate{keypair}, - MinVersion: tls.VersionTLS12, - } - webServer.RunTLS(net, tlsConfig) - } -} - -func Stop() { - fctWallet.Close() - webServer.Close() -} - -func checkAuthHeader(r *http.Request) error { - // Don't bother to check the autorization if the rpc user/pass is not - // specified. - if rpcUser == "" { - return nil - } - - authhdr := r.Header["Authorization"] - if len(authhdr) == 0 { - fmt.Println("Username and Password expected, but none were received") - return errors.New("no auth") - } - - h := sha256.New() - h.Write([]byte(authhdr[0])) - presentedPassHash := h.Sum(nil) - cmp := subtle.ConstantTimeCompare(presentedPassHash, authsha) //compare hashes because ConstantTimeCompare takes a constant time based on the slice size. hashing gives a constant slice size. - if cmp != 1 { - fmt.Println("Incorrect Username and/or Password were received") - return errors.New("bad auth") - } - return nil -} - -func handleV2(ctx *web.Context) { - if err := checkAuthHeader(ctx.Request); err != nil { - remoteIP := "" - remoteIP += strings.Split(ctx.Request.RemoteAddr, ":")[0] - fmt.Printf("Unauthorized API client connection attempt from %s\n", remoteIP) - ctx.ResponseWriter.Header().Add("WWW-Authenticate", `Basic realm="factomd RPC"`) - http.Error(ctx.ResponseWriter, "401 Unauthorized.", http.StatusUnauthorized) - return - } - body, err := ioutil.ReadAll(ctx.Request.Body) - if err != nil { - handleV2Error(ctx, nil, newInvalidRequestError()) - return - } - - j, err := factom.ParseJSON2Request(string(body)) - if err != nil { - handleV2Error(ctx, nil, newInvalidRequestError()) - return - } - - jsonResp, jsonError := handleV2Request(j) - - if jsonError != nil { - handleV2Error(ctx, j, jsonError) - return - } - - ctx.Write([]byte(jsonResp.String())) -} - -func handleV2Request(j *factom.JSON2Request) (*factom.JSON2Response, *factom.JSONError) { - var resp interface{} - var jsonError *factom.JSONError - params := []byte(j.Params) - - // Only expose a subset of endpoints if the wallet is still waiting to be unlocked - if fctWallet.Encrypted && (fctWallet.WalletDatabaseOverlay == nil || fctWallet.DBO.DB.(*securedb.EncryptedDB).UnlockedUntil.Unix() < time.Now().Unix()) { - switch j.Method { - case "get-height": - resp, jsonError = handleGetHeight(params) - case "properties": - resp, jsonError = handleProperties(params) - case "transactions": - resp, jsonError = handleAllTransactions(params) - case "unlock-wallet": - resp, jsonError = handleWalletPassphrase(params) - default: - jsonError = newWalletIsLockedError() - } - } else { - switch j.Method { - case "address": - resp, jsonError = handleAddress(params) - case "all-addresses": - resp, jsonError = handleAllAddresses(params) - case "generate-ec-address": - resp, jsonError = handleGenerateECAddress(params) - case "generate-factoid-address": - resp, jsonError = handleGenerateFactoidAddress(params) - case "import-addresses": - resp, jsonError = handleImportAddresses(params) - case "import-koinify": - resp, jsonError = handleImportKoinify(params) - case "wallet-backup": - resp, jsonError = handleWalletBackup(params) - case "transactions": - resp, jsonError = handleAllTransactions(params) - case "new-transaction": - resp, jsonError = handleNewTransaction(params) - case "delete-transaction": - resp, jsonError = handleDeleteTransaction(params) - case "tmp-transactions": - resp, jsonError = handleTmpTransactions(params) - case "transaction-hash": - resp, jsonError = handleTransactionHash(params) - case "add-input": - resp, jsonError = handleAddInput(params) - case "add-output": - resp, jsonError = handleAddOutput(params) - case "add-ec-output": - resp, jsonError = handleAddECOutput(params) - case "add-fee": - resp, jsonError = handleAddFee(params) - case "sub-fee": - resp, jsonError = handleSubFee(params) - case "sign-transaction": - resp, jsonError = handleSignTransaction(params) - case "sign-data": - resp, jsonError = handleSignData(params) - case "compose-transaction": - resp, jsonError = handleComposeTransaction(params) - case "remove-address": - resp, jsonError = handleRemoveAddress(params) - case "properties": - resp, jsonError = handleProperties(params) - case "compose-chain": - resp, jsonError = handleComposeChain(params) - case "compose-entry": - resp, jsonError = handleComposeEntry(params) - case "get-height": - resp, jsonError = handleGetHeight(params) - case "wallet-balances": - resp, jsonError = handleWalletBalances(params) - case "identity-key": - resp, jsonError = handleIdentityKey(params) - case "all-identity-keys": - resp, jsonError = handleAllIdentityKeys(params) - case "import-identity-keys": - resp, jsonError = handleImportIdentityKeys(params) - case "generate-identity-key": - resp, jsonError = handleGenerateIdentityKey(params) - case "remove-identity-key": - resp, jsonError = handleRemoveIdentityKey(params) - case "active-identity-keys": - resp, jsonError = handleActiveIdentityKeys(params) - case "compose-identity-chain": - resp, jsonError = handleComposeIdentityChain(params) - case "compose-identity-key-replacement": - resp, jsonError = handleComposeIdentityKeyReplacement(params) - case "compose-identity-attribute": - resp, jsonError = handleComposeIdentityAttribute(params) - case "compose-identity-attribute-endorsement": - resp, jsonError = handleComposeIdentityAttributeEndorsement(params) - case "unlock-wallet": - resp, jsonError = handleWalletPassphrase(params) - default: - jsonError = newMethodNotFoundError() - } - } - - if jsonError != nil { - return nil, jsonError - } - - // don't print password attempts or private keys to output - switch j.Method { - case "import-addresses", "import-koinify", "unlock-wallet": - fmt.Printf("API V2 method: <%v>\n", j.Method) - default: - fmt.Printf("API V2 method: <%v> parameters: %s\n", j.Method, params) - } - - jsonResp := factom.NewJSON2Response() - jsonResp.ID = j.ID - if b, err := json.Marshal(resp); err != nil { - return nil, newCustomInternalError(err.Error()) - } else { - jsonResp.Result = b - } - - return jsonResp, nil -} - -func handleWalletBalances(params []byte) (interface{}, *factom.JSONError) { - //Get all of the addresses in the wallet - fs, es, err := fctWallet.GetAllAddresses() - if err != nil { - return nil, newCustomInternalError(err.Error() + " Wallet empty") - } - - fctAccounts := make([]string, 0) - ecAccounts := make([]string, 0) - - for _, f := range fs { - if len(mkAddressResponse(f).Secret) > 0 { - fctAccounts = append(fctAccounts, mkAddressResponse(f).Public) - } - } - for _, e := range es { - if len(mkAddressResponse(e).Secret) > 0 { - ecAccounts = append(ecAccounts, mkAddressResponse(e).Public) - } - } - - var stringOfAccountsEC string - if len(ecAccounts) != 0 { - stringOfAccountsEC = strings.Join(ecAccounts, `", "`) - } - - url := "http://" + factom.FactomdServer() + "/v2" - if url == "http:///v2" { - url = "http://localhost:8088/v2" - } - // Get Entry Credit balances from multiple-ec-balances API in factomd - jsonStrEC := []byte(`{"jsonrpc": "2.0", "id": 0, "method": "multiple-ec-balances", "params":{"addresses":["` + stringOfAccountsEC + `"]}} `) - reqEC, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStrEC)) - reqEC.Header.Set("content-type", "text/plain;") - reqEC.SetBasicAuth(factom.GetFactomdRpcConfig()) - - clientEC := &http.Client{} - callRespEC, err := clientEC.Do(reqEC) - if err != nil { - panic(err) - } - - defer callRespEC.Body.Close() - bodyEC, _ := ioutil.ReadAll(callRespEC.Body) - - respEC := new(UnmarBody) - errEC := json.Unmarshal([]byte(bodyEC), &respEC) - if errEC != nil { - return nil, newInvalidParamsError() - } - - //Total up the balances - var ( - ackBalTotalEC int64 - savedBalTotalEC int64 - badErrorEC string - ) - - var floatType = reflect.TypeOf(int64(0)) - - for i := range respEC.Result.Balances { - x, ok := respEC.Result.Balances[i].(map[string]interface{}) - if ok != true { - fmt.Println(x) - } - v := reflect.ValueOf(x["ack"]) - covneredAck := v.Convert(floatType) - ackBalTotalEC = ackBalTotalEC + covneredAck.Int() - - rawr := reflect.ValueOf(x["saved"]) - convertedSaved := rawr.Convert(floatType) - savedBalTotalEC = savedBalTotalEC + convertedSaved.Int() - - errors := x["err"] - if errors == "Not fully booted" { - badErrorEC = "Not fully booted" - } else if errors == "Error decoding address" { - badErrorEC = "Error decoding address" - } - } - - stringOfAccountsFCT := "" - if len(fctAccounts) != 0 { - stringOfAccountsFCT = strings.Join(fctAccounts, `", "`) - } - - // Get Factoid balances from multiple-fct-balances API in factomd - jsonStrFCT := []byte(`{"jsonrpc": "2.0", "id": 0, "method": "multiple-fct-balances", "params":{"addresses":["` + stringOfAccountsFCT + `"]}} `) - reqFCT, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStrFCT)) - reqFCT.Header.Set("content-type", "text/plain;") - reqFCT.SetBasicAuth(factom.GetFactomdRpcConfig()) - - clientFCT := &http.Client{} - callRespFCT, errFCT := clientFCT.Do(reqFCT) - if errFCT != nil { - panic(errFCT) - } - - defer callRespFCT.Body.Close() - bodyFCT, _ := ioutil.ReadAll(callRespFCT.Body) - - respFCT := new(UnmarBody) - errFCT2 := json.Unmarshal([]byte(bodyFCT), &respFCT) - if errFCT2 != nil { - return nil, newInvalidParamsError() - } - - // Total up the balances - var ( - ackBalTotalFCT int64 - savedBalTotalFCT int64 - badErrorFCT string - ) - - for i := range respFCT.Result.Balances { - x, ok := respFCT.Result.Balances[i].(map[string]interface{}) - if ok != true { - fmt.Println(x) - } - v := reflect.ValueOf(x["ack"]) - covneredAck := v.Convert(floatType) - ackBalTotalFCT = ackBalTotalFCT + covneredAck.Int() - - rawr := reflect.ValueOf(x["saved"]) - convertedSaved := rawr.Convert(floatType) - savedBalTotalFCT = savedBalTotalFCT + convertedSaved.Int() - - errors := x["err"] - if errors == "Not fully booted" { - badErrorFCT = "Not fully booted" - } else if errors == "Error decoding address" { - badErrorFCT = "Error decoding address" - } - - } - - if badErrorFCT == "Not fully booted" || badErrorEC == "Not fully booted" { - type nfb struct { - NotFullyBooted string `json:"Factomd Error"` - } - nfbstatus := new(nfb) - nfbstatus.NotFullyBooted = "Factomd is not fully booted, please wait and try again." - return nfbstatus, nil - } else if badErrorFCT == "Error decoding address" || badErrorEC == "Error decoding address" { - type errorDecoding struct { - NotFullyBooted string `json:"Factomd Error"` - } - errDecReturn := new(errorDecoding) - errDecReturn.NotFullyBooted = "There was an error decoding an address" - return errDecReturn, nil - } - - resp := new(multiBalanceResponse) - resp.FactoidAccountBalances.Ack = ackBalTotalFCT - resp.FactoidAccountBalances.Saved = savedBalTotalFCT - resp.EntryCreditAccountBalances.Ack = ackBalTotalEC - resp.EntryCreditAccountBalances.Saved = savedBalTotalEC - - return resp, nil -} - -func handleRemoveAddress(params []byte) (interface{}, *factom.JSONError) { - req := new(addressRequest) - if err := json.Unmarshal(params, req); err != nil { - return nil, newInvalidParamsError() - } - - err := fctWallet.WalletDatabaseOverlay.RemoveAddress(req.Address) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - - resp := new(simpleResponse) - resp.Success = true - - return resp, nil -} - -func handleAddress(params []byte) (interface{}, *factom.JSONError) { - req := new(addressRequest) - if err := json.Unmarshal(params, req); err != nil { - return nil, newInvalidParamsError() - } - - resp := new(addressResponse) - switch factom.AddressStringType(req.Address) { - case factom.ECPub: - e, err := fctWallet.GetECAddress(req.Address) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - if e == nil { - return nil, newCustomInternalError("Wallet: address not found") - } - resp = mkAddressResponse(e) - case factom.FactoidPub: - f, err := fctWallet.GetFCTAddress(req.Address) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - resp = mkAddressResponse(f) - default: - return nil, newCustomInternalError("Invalid address type") - } - - return resp, nil -} - -func handleAllAddresses(params []byte) (interface{}, *factom.JSONError) { - resp := new(multiAddressResponse) - - fs, es, err := fctWallet.GetAllAddresses() - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - for _, f := range fs { - resp.Addresses = append(resp.Addresses, mkAddressResponse(f)) - } - for _, e := range es { - resp.Addresses = append(resp.Addresses, mkAddressResponse(e)) - } - - return resp, nil -} - -func handleGenerateFactoidAddress(params []byte) (interface{}, *factom.JSONError) { - a, err := fctWallet.GenerateFCTAddress() - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - - resp := mkAddressResponse(a) - - return resp, nil -} - -func handleGenerateECAddress(params []byte) (interface{}, *factom.JSONError) { - a, err := fctWallet.GenerateECAddress() - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - - resp := mkAddressResponse(a) - - return resp, nil -} - -func handleImportAddresses(params []byte) (interface{}, *factom.JSONError) { - req := new(importRequest) - if err := json.Unmarshal(params, req); err != nil { - return nil, newInvalidParamsError() - } - - resp := new(multiAddressResponse) - for _, v := range req.Addresses { - switch factom.AddressStringType(v.Secret) { - case factom.FactoidSec: - f, err := factom.GetFactoidAddress(v.Secret) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - if err := fctWallet.InsertFCTAddress(f); err != nil { - return nil, newCustomInternalError(err.Error()) - } - a := mkAddressResponse(f) - resp.Addresses = append(resp.Addresses, a) - case factom.ECSec: - e, err := factom.GetECAddress(v.Secret) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - if err := fctWallet.InsertECAddress(e); err != nil { - return nil, newCustomInternalError(err.Error()) - } - a := mkAddressResponse(e) - resp.Addresses = append(resp.Addresses, a) - default: - return nil, newCustomInternalError("address could not be imported") - } - } - return resp, nil -} - -func handleImportKoinify(params []byte) (interface{}, *factom.JSONError) { - req := new(importKoinifyRequest) - if err := json.Unmarshal(params, req); err != nil { - return nil, newInvalidParamsError() - } - - f, err := factom.MakeFactoidAddressFromKoinify(req.Words) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - if err := fctWallet.InsertFCTAddress(f); err != nil { - return nil, newCustomInternalError(err.Error()) - } - - return mkAddressResponse(f), nil -} - -func handleWalletBackup(params []byte) (interface{}, *factom.JSONError) { - resp := new(walletBackupResponse) - - if seed, err := fctWallet.GetSeed(); err != nil { - return nil, newCustomInternalError(err.Error()) - } else { - resp.Seed = seed - } - - fs, es, err := fctWallet.GetAllAddresses() - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - for _, f := range fs { - a := mkAddressResponse(f) - resp.Addresses = append(resp.Addresses, a) - } - for _, e := range es { - a := mkAddressResponse(e) - resp.Addresses = append(resp.Addresses, a) - } - - idKeys, err := fctWallet.GetAllIdentityKeys() - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - for _, k := range idKeys { - keyResp := new(identityKeyResponse) - keyResp.Public = k.PubString() - keyResp.Secret = k.SecString() - resp.IdentityKeys = append(resp.IdentityKeys, keyResp) - } - - return resp, nil -} - -func handleAllTransactions(params []byte) (interface{}, *factom.JSONError) { - if fctWallet.TXDB() == nil { - return nil, newCustomInternalError( - "Wallet does not have a transaction database") - } - req := new(txdbRequest) - if params != nil { - err := json.Unmarshal(params, req) - if err != nil { - return nil, newInvalidParamsError() - } - } - - resp := new(multiTransactionResponse) - - switch { - case req == nil: - txs, err := fctWallet.TXDB().GetAllTXs() - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - for _, tx := range txs { - r, err := factoidTxToTransaction(tx) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - resp.Transactions = append(resp.Transactions, r) - } - case req.TxID != "": - p, err := factom.GetRaw(req.TxID) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - tx := new(factoid.Transaction) - if err := tx.UnmarshalBinary(p); err != nil { - return nil, newCustomInternalError(err.Error()) - } - - r, err := factoidTxToTransaction(tx) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - resp.Transactions = append(resp.Transactions, r) - case req.Address != "": - txs, err := fctWallet.TXDB().GetTXAddress(req.Address) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - for _, tx := range txs { - r, err := factoidTxToTransaction(tx) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - resp.Transactions = append(resp.Transactions, r) - } - case req.Range.End != 0: - txs, err := fctWallet.TXDB().GetTXRange(req.Range.Start, req.Range.End) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - for _, tx := range txs { - r, err := factoidTxToTransaction(tx) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - resp.Transactions = append(resp.Transactions, r) - } - default: - txs, err := fctWallet.TXDB().GetAllTXs() - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - for _, tx := range txs { - r, err := factoidTxToTransaction(tx) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - resp.Transactions = append(resp.Transactions, r) - } - } - - return resp, nil -} - -// transaction handlers - -func handleNewTransaction(params []byte) (interface{}, *factom.JSONError) { - req := new(transactionRequest) - if err := json.Unmarshal(params, req); err != nil { - return nil, newInvalidParamsError() - } - - if err := fctWallet.NewTransaction(req.Name); err != nil { - return nil, newCustomInternalError(err.Error()) - } - - tx := fctWallet.GetTransactions()[req.Name] - resp, err := factoidTxToTransaction(tx) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - resp.Name = req.Name - resp.FeesRequired = feesRequired(tx) - - return resp, nil -} - -func handleDeleteTransaction(params []byte) (interface{}, *factom.JSONError) { - req := new(transactionRequest) - if err := json.Unmarshal(params, req); err != nil { - return nil, newInvalidParamsError() - } - - if err := fctWallet.DeleteTransaction(req.Name); err != nil { - return nil, newCustomInternalError(err.Error()) - } - resp := &factom.Transaction{Name: req.Name} - return resp, nil -} - -func handleTmpTransactions(params []byte) (interface{}, *factom.JSONError) { - resp := new(multiTransactionResponse) - txs := fctWallet.GetTransactions() - - for name, tx := range txs { - r, err := factoidTxToTransaction(tx) - if err != nil { - continue - } - r.Name = name - r.FeesRequired = feesRequired(tx) - resp.Transactions = append(resp.Transactions, r) - } - - return resp, nil -} - -func handleTransactionHash(params []byte) (interface{}, *factom.JSONError) { - req := new(transactionRequest) - if err := json.Unmarshal(params, req); err != nil { - return nil, newInvalidParamsError() - } - - resp := new(factom.Transaction) - txs := fctWallet.GetTransactions() - - for name, tx := range txs { - if name == req.Name { - resp.Name = name - resp.TxID = tx.GetSigHash().String() - return resp, nil - } - } - - return nil, newCustomInternalError("Transaction not found") -} - -func handleAddInput(params []byte) (interface{}, *factom.JSONError) { - req := new(transactionValueRequest) - if err := json.Unmarshal(params, req); err != nil { - return nil, newInvalidParamsError() - } - - if err := fctWallet.AddInput(req.Name, req.Address, req.Amount); err != nil { - return nil, newCustomInternalError(err.Error()) - } - tx := fctWallet.GetTransactions()[req.Name] - resp, err := factoidTxToTransaction(tx) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - resp.Name = req.Name - resp.FeesRequired = feesRequired(tx) - - return resp, nil -} - -func handleAddOutput(params []byte) (interface{}, *factom.JSONError) { - req := new(transactionValueRequest) - if err := json.Unmarshal(params, req); err != nil { - return nil, newInvalidParamsError() - } - - if err := fctWallet.AddOutput(req.Name, req.Address, req.Amount); err != nil { - return nil, newCustomInternalError(err.Error()) - } - tx := fctWallet.GetTransactions()[req.Name] - resp, err := factoidTxToTransaction(tx) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - resp.Name = req.Name - resp.FeesRequired = feesRequired(tx) - - return resp, nil -} - -func handleAddECOutput(params []byte) (interface{}, *factom.JSONError) { - req := new(transactionValueRequest) - if err := json.Unmarshal(params, req); err != nil { - return nil, newInvalidParamsError() - } - - if err := fctWallet.AddECOutput(req.Name, req.Address, req.Amount); err != nil { - return nil, newCustomInternalError(err.Error()) - } - tx := fctWallet.GetTransactions()[req.Name] - resp, err := factoidTxToTransaction(tx) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - resp.Name = req.Name - resp.FeesRequired = feesRequired(tx) - - return resp, nil -} - -func handleAddFee(params []byte) (interface{}, *factom.JSONError) { - req := new(transactionAddressRequest) - if err := json.Unmarshal(params, req); err != nil { - return nil, newInvalidParamsError() - } - - rate, err := factom.GetECRate() - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - if err := fctWallet.AddFee(req.Name, req.Address, rate); err != nil { - return nil, newCustomInternalError(err.Error()) - } - tx := fctWallet.GetTransactions()[req.Name] - resp, err := factoidTxToTransaction(tx) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - resp.Name = req.Name - resp.FeesRequired = feesRequired(tx) - - return resp, nil -} - -func handleSubFee(params []byte) (interface{}, *factom.JSONError) { - req := new(transactionAddressRequest) - if err := json.Unmarshal(params, req); err != nil { - return nil, newInvalidParamsError() - } - - rate, err := factom.GetECRate() - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - if err := fctWallet.SubFee(req.Name, req.Address, rate); err != nil { - return nil, newCustomInternalError(err.Error()) - } - tx := fctWallet.GetTransactions()[req.Name] - resp, err := factoidTxToTransaction(tx) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - resp.Name = req.Name - resp.FeesRequired = feesRequired(tx) - - return resp, nil -} - -func handleSignTransaction(params []byte) (interface{}, *factom.JSONError) { - req := new(transactionRequest) - if err := json.Unmarshal(params, req); err != nil { - return nil, newInvalidParamsError() - } - - force := req.Force - - if err := fctWallet.SignTransaction(req.Name, force); err != nil { - return nil, newCustomInternalError(err.Error()) - } - tx := fctWallet.GetTransactions()[req.Name] - resp, err := factoidTxToTransaction(tx) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - resp.Name = req.Name - resp.FeesRequired = feesRequired(tx) - - return resp, nil -} - -func handleSignData(params []byte) (interface{}, *factom.JSONError) { - req := new(signDataRequest) - if err := json.Unmarshal(params, req); err != nil { - return nil, newInvalidParamsError() - } - - pub, sig, err := fctWallet.SignData(req.Signer, req.Data) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - - return signDataResponse{PubKey: pub, Signature: sig}, nil -} - -func handleComposeTransaction(params []byte) (interface{}, *factom.JSONError) { - req := new(transactionRequest) - if err := json.Unmarshal(params, req); err != nil { - return nil, newInvalidParamsError() - } - - t, err := fctWallet.ComposeTransaction(req.Name) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - return t, nil -} - -func handleComposeChain(params []byte) (interface{}, *factom.JSONError) { - req := new(chainRequest) - if err := json.Unmarshal(params, req); err != nil { - return nil, newInvalidParamsError() - } - - c := factom.NewChain(req.Chain.FirstEntry) - ecpub := req.ECPub - force := req.Force - - ec, err := fctWallet.GetECAddress(ecpub) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - if ec == nil { - return nil, newCustomInternalError("Wallet: address not found") - } - - if !force { - // check ec address balance - balance, err := factom.GetECBalance(ecpub) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - - if cost, err := factom.EntryCost(c.FirstEntry); err != nil { - return nil, newCustomInternalError(err.Error()) - } else if balance < int64(cost)+10 { - return nil, newCustomInternalError("Not enough Entry Credits") - } - - if factom.ChainExists(c.ChainID) { - return nil, newCustomInvalidParamsError("Chain " + c.ChainID + " already exists") - } - } - - commit, err := factom.ComposeChainCommit(c, ec) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - - reveal, err := factom.ComposeChainReveal(c) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - - resp := new(entryResponse) - resp.Commit = commit - resp.Reveal = reveal - return resp, nil -} - -func handleComposeEntry(params []byte) (interface{}, *factom.JSONError) { - req := new(entryRequest) - if err := json.Unmarshal(params, req); err != nil { - return nil, newInvalidParamsError() - } - - e := req.Entry - ecpub := req.ECPub - force := req.Force - - ec, err := fctWallet.GetECAddress(ecpub) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - if ec == nil { - return nil, newCustomInternalError("Wallet: address not found") - } - if !force { - // check ec address balance - balance, err := factom.GetECBalance(ecpub) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - - if cost, err := factom.EntryCost(&e); err != nil { - newCustomInternalError(err.Error()) - } else if balance < int64(cost) { - newCustomInternalError("Not enough Entry Credits") - } - - if !factom.ChainExists(e.ChainID) { - return nil, newCustomInvalidParamsError("Chain " + e.ChainID + " was not found") - } - } - - commit, err := factom.ComposeEntryCommit(&e, ec) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - - reveal, err := factom.ComposeEntryReveal(&e) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - - resp := new(entryResponse) - resp.Commit = commit - resp.Reveal = reveal - return resp, nil -} - -func handleProperties(params []byte) (interface{}, *factom.JSONError) { - props := new(propertiesResponse) - props.WalletVersion = fctWallet.GetVersion() - props.WalletApiVersion = fctWallet.GetApiVersion() - return props, nil -} - -func handleGetHeight(params []byte) (interface{}, *factom.JSONError) { - resp := new(heightResponse) - - block, err := fctWallet.TXDB().DBO.FetchFBlockHead() - - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - if block == nil { - resp.Height = 0 - return resp, nil - } - - resp.Height = int64(block.GetDBHeight()) - return resp, nil -} - -// Identity handlers - -func handleIdentityKey(params []byte) (interface{}, *factom.JSONError) { - req := new(identityKeyRequest) - if err := json.Unmarshal(params, req); err != nil { - return nil, newInvalidParamsError() - } - - e, err := fctWallet.GetIdentityKey(req.Public) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - if e == nil { - return nil, newCustomInternalError("Wallet: identity key not found") - } - resp := new(identityKeyResponse) - resp.Public = e.PubString() - resp.Secret = e.SecString() - return resp, nil -} - -func handleAllIdentityKeys(params []byte) (interface{}, *factom.JSONError) { - resp := new(multiIdentityKeyResponse) - - keys, err := fctWallet.GetAllIdentityKeys() - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - for _, v := range keys { - key := new(identityKeyResponse) - key.Public = v.PubString() - key.Secret = v.SecString() - resp.Keys = append(resp.Keys, key) - } - - return resp, nil -} - -func handleImportIdentityKeys(params []byte) (interface{}, *factom.JSONError) { - req := new(importIdentityKeysRequest) - if err := json.Unmarshal(params, req); err != nil { - return nil, newInvalidParamsError() - } - - resp := new(multiIdentityKeyResponse) - for _, v := range req.Keys { - key, err := factom.GetIdentityKey(v.Secret) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - if err := fctWallet.InsertIdentityKey(key); err != nil { - return nil, newCustomInternalError(err.Error()) - } - keyResp := new(identityKeyResponse) - keyResp.Public = key.PubString() - keyResp.Secret = v.Secret - resp.Keys = append(resp.Keys, keyResp) - } - return resp, nil -} - -func handleGenerateIdentityKey(params []byte) (interface{}, *factom.JSONError) { - k, err := fctWallet.GenerateIdentityKey() - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - resp := identityKeyResponse{} - resp.Public = k.PubString() - resp.Secret = k.SecString() - return resp, nil -} - -func handleRemoveIdentityKey(params []byte) (interface{}, *factom.JSONError) { - req := new(identityKeyRequest) - if err := json.Unmarshal(params, req); err != nil { - return nil, newInvalidParamsError() - } - - err := fctWallet.WalletDatabaseOverlay.RemoveIdentityKey(req.Public) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - - resp := new(simpleResponse) - resp.Success = true - - return resp, nil -} - -func handleActiveIdentityKeys(params []byte) (interface{}, *factom.JSONError) { - req := new(activeIdentityKeysRequest) - if err := json.Unmarshal(params, req); err != nil { - return nil, newInvalidParamsError() - } - - resp := new(activeIdentityKeysResponse) - resp.ChainID = req.ChainID - - if req.Height == nil { - keys, currentHeight, err := factom.GetActiveIdentityKeys(req.ChainID) - if err != nil { - return nil, newCustomInternalError(fmt.Sprintf("ActiveIdentityKeys: %s", err.Error())) - } - resp.Keys = keys - resp.Height = currentHeight - return resp, nil - } - - keys, err := factom.GetActiveIdentityKeysAtHeight(req.ChainID, *req.Height) - if err != nil { - return nil, newCustomInternalError(fmt.Sprintf("ActiveIdentityKeys: %s", err.Error())) - } - resp.Keys = keys - resp.Height = *req.Height - return resp, nil -} - -func handleComposeIdentityChain(params []byte) (interface{}, *factom.JSONError) { - req := new(identityChainRequest) - if err := json.Unmarshal(params, req); err != nil { - return nil, newInvalidParamsError() - } - - ecpub := req.ECPub - ec, err := fctWallet.GetECAddress(ecpub) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - if ec == nil { - return nil, newCustomInternalError("Wallet: entry credit address not found") - } - - c, err := factom.NewIdentityChain(req.Name, req.PubKeys) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - if !req.Force { - // check ec address balance - balance, err := factom.GetECBalance(ecpub) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - - if cost, err := factom.EntryCost(c.FirstEntry); err != nil { - return nil, newCustomInternalError(err.Error()) - } else if balance < int64(cost)+10 { - return nil, newCustomInternalError("Not enough Entry Credits") - } - - if factom.ChainExists(c.ChainID) { - return nil, newCustomInvalidParamsError("Chain " + c.ChainID + " already exists") - } - } - - commit, err := factom.ComposeChainCommit(c, ec) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - - reveal, err := factom.ComposeChainReveal(c) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - - resp := new(entryResponse) - resp.Commit = commit - resp.Reveal = reveal - return resp, nil -} - -func handleComposeIdentityKeyReplacement(params []byte) (interface{}, *factom.JSONError) { - req := new(identityKeyReplacementRequest) - if err := json.Unmarshal(params, req); err != nil { - return nil, newInvalidParamsError() - } - - signerKey, err := fctWallet.GetIdentityKey(req.SignerKey) - if err != nil || signerKey == nil { - return nil, newCustomInternalError("Wallet: failed to fetch signerkey from given identity public key") - } - - ecpub := req.ECPub - ec, err := fctWallet.GetECAddress(ecpub) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - if ec == nil { - return nil, newCustomInternalError("Wallet: entry credit address not found") - } - - e, err := factom.NewIdentityKeyReplacementEntry(req.ChainID, req.OldKey, req.NewKey, signerKey) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - if !req.Force { - // check ec address balance - balance, err := factom.GetECBalance(ecpub) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - - if cost, err := factom.EntryCost(e); err != nil { - newCustomInternalError(err.Error()) - } else if balance < int64(cost) { - newCustomInternalError("Not enough Entry Credits") - } - - if !factom.ChainExists(e.ChainID) { - return nil, newCustomInvalidParamsError("Chain " + e.ChainID + " was not found") - } - } - - commit, err := factom.ComposeEntryCommit(e, ec) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - - reveal, err := factom.ComposeEntryReveal(e) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - - resp := new(entryResponse) - resp.Commit = commit - resp.Reveal = reveal - return resp, nil -} - -func handleComposeIdentityAttribute(params []byte) (interface{}, *factom.JSONError) { - req := new(identityAttributeRequest) - if err := json.Unmarshal(params, req); err != nil { - return nil, newInvalidParamsError() - } - - signerKey, err := fctWallet.GetIdentityKey(req.SignerKey) - if err != nil || signerKey == nil { - return nil, newCustomInternalError("Wallet: failed to fetch signerkey from given identity public key") - } - - for _, attribute := range req.Attributes { - if attribute.Key == nil { - return nil, newCustomInternalError("Attribute key must not be nil") - } - if attribute.Value == nil { - return nil, newCustomInternalError("Attribute value must not be nil") - } - } - attributesJSON, err := json.Marshal(req.Attributes) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - - e := factom.NewIdentityAttributeEntry(req.ReceiverChainID, req.DestinationChainID, string(attributesJSON), signerKey, req.SignerChainID) - ecpub := req.ECPub - force := req.Force - - ec, err := fctWallet.GetECAddress(ecpub) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - if ec == nil { - return nil, newCustomInternalError("Wallet: address not found") - } - if !force { - // check ec address balance - balance, err := factom.GetECBalance(ecpub) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - - if cost, err := factom.EntryCost(e); err != nil { - newCustomInternalError(err.Error()) - } else if balance < int64(cost) { - newCustomInternalError("Not enough Entry Credits") - } - - if !factom.ChainExists(e.ChainID) { - return nil, newCustomInvalidParamsError("Chain " + e.ChainID + " was not found") - } - } - - commit, err := factom.ComposeEntryCommit(e, ec) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - - reveal, err := factom.ComposeEntryReveal(e) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - - resp := new(entryResponse) - resp.Commit = commit - resp.Reveal = reveal - return resp, nil -} - -func handleComposeIdentityAttributeEndorsement(params []byte) (interface{}, *factom.JSONError) { - req := new(identityAttributeEndorsementRequest) - if err := json.Unmarshal(params, req); err != nil { - return nil, newInvalidParamsError() - } - - signerKey, err := fctWallet.GetIdentityKey(req.SignerKey) - if err != nil || signerKey == nil { - return nil, newCustomInternalError("Wallet: failed to fetch signerkey from given identity public key") - } - - e := factom.NewIdentityAttributeEndorsementEntry(req.DestinationChainID, req.EntryHash, signerKey, req.SignerChainID) - ecpub := req.ECPub - force := req.Force - - ec, err := fctWallet.GetECAddress(ecpub) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - if ec == nil { - return nil, newCustomInternalError("Wallet: address not found") - } - if !force { - // check ec address balance - balance, err := factom.GetECBalance(ecpub) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - - if cost, err := factom.EntryCost(e); err != nil { - newCustomInternalError(err.Error()) - } else if balance < int64(cost) { - newCustomInternalError("Not enough Entry Credits") - } - - if !factom.ChainExists(e.ChainID) { - return nil, newCustomInvalidParamsError("Chain " + e.ChainID + " was not found") - } - } - - commit, err := factom.ComposeEntryCommit(e, ec) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - - reveal, err := factom.ComposeEntryReveal(e) - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - - resp := new(entryResponse) - resp.Commit = commit - resp.Reveal = reveal - return resp, nil -} - -func handleWalletPassphrase(params []byte) (interface{}, *factom.JSONError) { - req := new(passphraseRequest) - if err := json.Unmarshal(params, req); err != nil { - return nil, newInvalidParamsError() - } - - // If timeout is > 2^30, set to 2^30 - if req.Timeout > 1073741824 { - req.Timeout = 1073741824 - } - - // If this isn't the first time booting an encrypted wallet, we postpone creating the database until now - if fctWallet.WalletDatabaseOverlay == nil { - db, err := wallet.NewEncryptedBoltDB(fctWallet.DBPath, req.Password) - if err != nil { - return nil, newIncorrectPassphraseError() - } - fctWallet.WalletDatabaseOverlay = db - - err = fctWallet.InitWallet() - if err != nil { - return nil, newCustomInternalError(err.Error()) - } - fctWallet.DBO.DB.(*securedb.EncryptedDB).Lock() - } - - encdb, ok := fctWallet.DBO.DB.(*securedb.EncryptedDB) - if !ok { - return nil, newCustomInternalError("Cannot unlock non-encrypted wallet. This database is always unlocked") - } - - err := encdb.UnlockFor(req.Password, time.Second*time.Duration(req.Timeout)) - if err != nil { - return nil, newIncorrectPassphraseError() - } - - return &unlockResponse{Success: true, UnlockedUntil: encdb.UnlockedUntil.Unix()}, nil -} - -// utility functions - -type addressResponder interface { - String() string - SecString() string -} - -func mkAddressResponse(a addressResponder) *addressResponse { - r := new(addressResponse) - r.Public = a.String() - r.Secret = a.SecString() - return r -} - -func factoidTxToTransaction(t interfaces.ITransaction) ( - *factom.Transaction, - error, -) { - r := new(factom.Transaction) - r.TxID = hex.EncodeToString(t.GetSigHash().Bytes()) - - r.BlockHeight = t.GetBlockHeight() - r.Timestamp = t.GetTimestamp().GetTime() - - if len(t.GetSignatureBlocks()) > 0 { - if err := t.ValidateSignatures(); err == nil { - r.IsSigned = true - } - } - - if i, err := t.TotalInputs(); err != nil { - return nil, err - } else { - r.TotalInputs = i - } - - if i, err := t.TotalOutputs(); err != nil { - return nil, err - } else { - r.TotalOutputs = i - } - - if i, err := t.TotalECs(); err != nil { - return nil, err - } else { - r.TotalECOutputs = i - } - - for _, v := range t.GetInputs() { - tmp := new(factom.TransAddress) - tmp.Address = primitives.ConvertFctAddressToUserStr(v.GetAddress()) - tmp.Amount = v.GetAmount() - r.Inputs = append(r.Inputs, tmp) - } - - for _, v := range t.GetOutputs() { - tmp := new(factom.TransAddress) - tmp.Address = primitives.ConvertFctAddressToUserStr(v.GetAddress()) - tmp.Amount = v.GetAmount() - r.Outputs = append(r.Outputs, tmp) - } - - for _, v := range t.GetECOutputs() { - tmp := new(factom.TransAddress) - tmp.Address = primitives.ConvertECAddressToUserStr(v.GetAddress()) - tmp.Amount = v.GetAmount() - r.ECOutputs = append(r.ECOutputs, tmp) - } - - if r.TotalInputs <= r.TotalOutputs+r.TotalECOutputs { - r.FeesPaid = 0 - r.FeesRequired = r.FeesRequired - } else { - r.FeesPaid = r.TotalInputs - (r.TotalOutputs + r.TotalECOutputs) - } - - return r, nil -} - -func feesRequired(t interfaces.ITransaction) uint64 { - rate, err := factom.GetECRate() - if err != nil { - rate = 0 - } - - fee, err := t.CalculateFee(rate) - if err != nil { - return 0 - } - - return fee -} diff --git a/wallet/wsapi/wsapi_test.gox b/wallet/wsapi/wsapi_test.gox deleted file mode 100644 index 2b8e7ed..0000000 --- a/wallet/wsapi/wsapi_test.gox +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright 2016 Factom Foundation -// Use of this source code is governed by the MIT -// license that can be found in the LICENSE file. - -// run ``factom-walletd -w /tmp/test_wallet-01 -p 8889`` and ``factomd`` to test the wsapi calls. - -package wsapi_test - -import ( - "bytes" - "io/ioutil" - "net/http" - "testing" -) - -var testnet = "localhost:8889" - -func TesthhandleWalletBalances(t *testing.T) { - // Replace the accounts will accounts in your wallet - req := `{"jsonrpc": "2.0", "id": 0, "method": "wallet-balances"}` - - resp, err := apiCall(req) - if err != nil { - t.Error(err) - } - t.Log(resp) -} - -func TestAllAddresses(t *testing.T) { - req := `{"jsonrpc":"2.0","id":0,"method":"all-addresses"}` - - resp, err := apiCall(req) - if err != nil { - t.Error(err) - } - t.Log(resp) -} - -func TestGenerateECAddress(t *testing.T) { - req := `{"jsonrpc":"2.0","id":0,"method":"generate-ec-address"}` - - resp, err := apiCall(req) - if err != nil { - t.Error(err) - } - t.Log(resp) -} - -func TestGenerateFCTAddress(t *testing.T) { - req := `{"jsonrpc":"2.0","id":0,"method":"generate-factoid-address"}` - - resp, err := apiCall(req) - if err != nil { - t.Error(err) - } - t.Log(resp) -} - -func TestImportAddresses(t *testing.T) { - req := `{"jsonrpc":"2.0","id":0,"method":"import-addresses","params":{"addresses":[{"secret":"Fs3E9gV6DXsYzf7Fqx1fVBQPQXV695eP3k5XbmHEZVRLkMdD9qCK"}]}}` - - resp, err := apiCall(req) - if err != nil { - t.Error(err) - } - t.Log(resp) -} - -func TestAddress(t *testing.T) { - req := `{"jsonrpc":"2.0","id":0,"method":"address","params":{"address":"FA2jK2HcLnRdS94dEcU27rF3meoJfpUcZPSinpb7AwQvPRY6RL1Q"}}` - - resp, err := apiCall(req) - if err != nil { - t.Error(err) - } - t.Log(resp) -} - -func TestTransaction(t *testing.T) { - new := `{"jsonrpc":"2.0","id":0,"method":"new-transaction","params":{"tx-name":"a"}}` - - if resp, err := apiCall(new); err != nil { - t.Error(err) - } else { - t.Log(resp) - } - - addin := `{"jsonrpc":"2.0","id":0,"method":"add-input","params":{"tx-name":"a","address":"FA2jK2HcLnRdS94dEcU27rF3meoJfpUcZPSinpb7AwQvPRY6RL1Q","amount":1000000000}}` - if resp, err := apiCall(addin); err != nil { - t.Error(err) - } else { - t.Log(resp) - } - - addout := `{"jsonrpc":"2.0","id":0,"method":"add-output","params":{"tx-name":"a","address":"FA2xWmGckzbACp7LR43bfnViCyN4uNhugBxiaYPtWGVZVSn1y2m6","amount":1000000000}}` - if resp, err := apiCall(addout); err != nil { - t.Error(err) - } else { - t.Log(resp) - } - - addfee := `{"jsonrpc":"2.0","id":0,"method":"add-fee","params":{"tx-name":"a","address":"FA2jK2HcLnRdS94dEcU27rF3meoJfpUcZPSinpb7AwQvPRY6RL1Q"}}` - if resp, err := apiCall(addfee); err != nil { - t.Error(err) - } else { - t.Log(resp) - } - - sign := `{"jsonrpc":"2.0","id":0,"method":"sign-transaction","params":{"tx-name":"a"}}` - if resp, err := apiCall(sign); err != nil { - t.Error(err) - } else { - t.Log(resp) - } - - compose := `{"jsonrpc":"2.0","id":0,"method":"compose-transaction","params":{"tx-name":"a"}}` - if resp, err := apiCall(compose); err != nil { - t.Error(err) - } else { - t.Log(resp) - } -} - -func apiCall(req string) (string, error) { - client := &http.Client{} - buf := bytes.NewBuffer([]byte(req)) - re, err := http.NewRequest("POST", "http://"+testnet+"/v2", buf) - if err != nil { - return "", err - } - re.SetBasicAuth(RpcUser, RpcPass) - resp, err := client.Do(re) - if err != nil { - return "", err - } - defer resp.Body.Close() - - p, err := ioutil.ReadAll(resp.Body) - if err != nil { - return "", err - } - - return string(p), nil -} diff --git a/wallet_test.go b/wallet_test.go index 34d7c77..4d36f44 100644 --- a/wallet_test.go +++ b/wallet_test.go @@ -10,66 +10,12 @@ import ( "fmt" "io/ioutil" "net/http" - "os" . "github.com/FactomProject/factom" - "github.com/FactomProject/factom/wallet" - "github.com/FactomProject/factom/wallet/wsapi" "testing" ) -func TestImportAddress(t *testing.T) { - var ( - fs1 = "Fs2TCa7Mo4XGy9FQSoZS8JPnDfv7SjwUSGqrjMWvc1RJ9sKbJeXA" - fa1 = "FA3T1gTkuKGG2MWpAkskSoTnfjxZDKVaAYwziNTC1pAYH5B9A1rh" - es1 = "Es4KmwK65t9HCsibYzVDFrijvkgTFZKdEaEAgfMtYTPSVtM3NDSx" - ec1 = "EC2CyGKaNddLFxrjkFgiaRZnk77b8iQia3Zj6h5fxFReAcDwCo3i" - - bads = []string{ - "Fs2TCa7Mo4XGy9FQSoZS8JPnDfv7SjwUSGqrjMWvc1RJ9sKbJeX", //short - "Fs2TCa7Mo4XGy9FQSoZS8JPnDfv7SjwUSGqrjMWvc1RJ9sKbJeeA", //check - "", //empty - "Fc2TCa7Mo4XGy9FQSoZS8JPnDfv7SjwUSGqrjMWvc1RJ9sKbJeXA", //prefix - } - ) - - // start the test wallet - done, err := StartTestWallet() - if err != nil { - t.Error(err) - } - defer func() { done <- 1 }() - - // import the good addresses - if _, _, err := ImportAddresses(fs1, es1); err != nil { - t.Error(err) - } - - if f, err := FetchFactoidAddress(fa1); err != nil { - t.Error(err) - } else if f == nil { - t.Error("Wallet returned nil factoid address") - } else if f.SecString() != fs1 { - t.Error("Wallet returned incorrect address", fs1, f.SecString()) - } - - if e, err := FetchECAddress(ec1); err != nil { - t.Error(err) - } else if e == nil { - t.Error("Wallet returned nil ec address") - } else if e.SecString() != es1 { - t.Error("Wallet returned incorrect address", es1, e.SecString()) - } - - // try to import the bad addresses - for _, bad := range bads { - if _, _, err := ImportAddresses(bad); err == nil { - t.Error("Bad address was imported without error", bad) - } - } -} - // // TODO: revisit this test and try to fix the problem // @@ -149,44 +95,6 @@ func helper(t *testing.T, addr []string) (*walletcall, string) { return respEC, "" } -func TestImportKoinify(t *testing.T) { - var ( - good_mnemonic = "yellow yellow yellow yellow yellow yellow yellow" + - " yellow yellow yellow yellow yellow" // good - koinifyexpect = "FA3cih2o2tjEUsnnFR4jX1tQXPpSXFwsp3rhVp6odL5PNCHWvZV1" - - bad_mnemonic = []string{ - "", // bad empty - "yellow yellow yellow yellow yellow yellow yellow yellow yellow" + - " yellow yellow", // bad short - "yellow yellow yellow yellow yellow yellow yellow yellow yellow" + - " yellow yellow asdfasdf", // bad word - } - ) - - // start the test wallet - done, err := StartTestWallet() - if err != nil { - t.Error(err) - } - defer func() { done <- 1 }() - - // check the import for koinify names - fa, err := ImportKoinify(good_mnemonic) - if err != nil { - t.Error(err) - } - if fa.String() != koinifyexpect { - t.Error("Incorrect address from Koinify mnemonic", fa, koinifyexpect) - } - - for _, m := range bad_mnemonic { - if _, err := ImportKoinify(m); err == nil { - t.Error("No error for bad address:", m) - } - } -} - // helper functions for testing func populateTestWallet() error { @@ -214,50 +122,3 @@ func populateTestWallet() error { return nil } - -// StartTestWallet runs a test wallet and serves the wallet api. The caller -// must write an int to the chan when compleate to stop the wallet api and -// remove the test db. -func StartTestWallet() (chan int, error) { - var ( - walletdbfile = os.TempDir() + "/testingwallet.bolt" - txdbfile = os.TempDir() + "/testingtxdb.bolt" - ) - - // make a chan to signal when the test is finished with the wallet - done := make(chan int, 1) - - // setup a testing wallet - fctWallet, err := wallet.NewOrOpenBoltDBWallet(walletdbfile) - if err != nil { - return nil, err - } - defer os.Remove(walletdbfile) - - txdb, err := wallet.NewTXBoltDB(txdbfile) - if err != nil { - return nil, err - } else { - fctWallet.AddTXDB(txdb) - } - defer os.Remove(txdbfile) - - RpcConfig = &RPCConfig{ - WalletTLSEnable: false, - WalletTLSKeyFile: "", - WalletTLSCertFile: "", - WalletRPCUser: "", - WalletRPCPassword: "", - WalletServer: "localhost:8089", - } - - go wsapi.Start(fctWallet, ":8089", *RpcConfig) - go func() { - <-done - wsapi.Stop() - fctWallet.Close() - txdb.Close() - }() - - return done, nil -} From f3aedaad4d73b93f17dba6b11b6777f209ac3349 Mon Sep 17 00:00:00 2001 From: WhoSoup <34172041+WhoSoup@users.noreply.github.com> Date: Thu, 20 Aug 2020 04:28:33 +0200 Subject: [PATCH 09/14] Add support for transactions (#141) * return useful results from pending transactions * expand fblock transactions * add support for dbheight * capitalization, unit test, time conversion Co-authored-by: Brian Deery --- fblock.go | 145 ++++++++++++++++++++++++++++++++++-- fblock_test.go | 25 ++++++- transaction.go | 21 ------ transaction_pending.go | 40 ++++++++++ transaction_pending_test.go | 35 +++++++++ 5 files changed, 235 insertions(+), 31 deletions(-) create mode 100644 transaction_pending.go create mode 100644 transaction_pending_test.go diff --git a/fblock.go b/fblock.go index b7787a7..a417508 100644 --- a/fblock.go +++ b/fblock.go @@ -7,24 +7,155 @@ package factom import ( "encoding/json" "fmt" + "time" ) // FBlock represents a Factoid Block returned from factomd. // Note: the FBlock api return does not use a "Header" field like the other // block types do for some reason. type FBlock struct { - BodyMR string `json:"bodymr"` // Merkle root of the Factoid transactions which accompany this block. - PrevKeyMR string `json:"prevkeymr"` // Key Merkle root of previous block. - PrevLedgerKeyMR string `json:"prevledgerkeymr"` // Sha3 of the previous Factoid Block - ExchRate int64 `json:"exchrate"` // Factoshis per Entry Credit - DBHeight int64 `json:"dbheight"` // Directory Block height - Transactions []json.RawMessage `json:"transactions"` + BodyMR string `json:"bodymr"` // Merkle root of the Factoid transactions which accompany this block. + PrevKeyMR string `json:"prevkeymr"` // Key Merkle root of previous block. + PrevLedgerKeyMR string `json:"prevledgerkeymr"` // Sha3 of the previous Factoid Block + ExchRate int64 `json:"exchrate"` // Factoshis per Entry Credit + DBHeight int64 `json:"dbheight"` // Directory Block height + Transactions []*FBTransaction `json:"transactions"` // The transactions inside the block ChainID string `json:"chainid,omitempty"` KeyMR string `json:"keymr,omitempty"` LedgerKeyMR string `json:"ledgerkeymr,omitempty"` } +// FBTransactions represents a single and valid transaction contained inside +// of an FBlock. +// The data has been rearranged from the raw json response to make it easier to work with. +type FBTransaction struct { + TxID string `json:"txid"` // hex string + BlockHeight int64 `json:"blockheight"` + Timestamp time.Time `json:"timestamp"` + Inputs []SignedTransactionAddress `json:"inputs"` + Outputs []TransactionAddress `json:"outputs"` + OutECs []TransactionAddress `json:"outecs"` +} + +// TransactionAddress holds the relevant data for either an input or an output. +// The amount is in either Factoshi (Input and Output) or EC (OutECs). +// The RCDHash is the SHA256 hash of the RCD. +// The address is the human readable address calculated from the RCDHash and type. +type TransactionAddress struct { + Amount uint64 `json:"amount"` + RCDHash string `json:"address"` // hex string + Address string `json:"useraddress"` +} + +// SignedTransactionAddress contains a TransactionAddress along with the RCD and +// cryptographic signatures specified by the RCD. +type SignedTransactionAddress struct { + TransactionAddress + RCD string `json:"rcd"` // hex string + Signatures []string `json:"signatures"` // slice of hex strings +} + +// these two types are just used internally as intermediary holding structs +// to transform the json response to the appropriate struct and back +type rawFBTransaction struct { + TxID string `json:"txid"` + BlockHeight int64 `json:"blockheight"` + MilliTimestamp int64 `json:"millitimestamp"` + Inputs []TransactionAddress `json:"inputs"` + Outputs []TransactionAddress `json:"outputs"` + OutECs []TransactionAddress `json:"outecs"` + RCDs []string `json:"rcds"` + SigBlocks []rawSigBlock `json:"sigblocks"` +} +type rawSigBlock struct { + Signatures []string `json:"signatures"` +} + +func (t *FBTransaction) MarshalJSON() ([]byte, error) { + txReq := &rawFBTransaction{ + TxID: t.TxID, + BlockHeight: t.BlockHeight, + MilliTimestamp: t.Timestamp.UnixNano()/1e6 + (t.Timestamp.UnixNano()/1e3)%1e3, + Outputs: t.Outputs, + OutECs: t.OutECs, + Inputs: make([]TransactionAddress, 0, len(t.Inputs)), + RCDs: make([]string, 0, len(t.Inputs)), + SigBlocks: make([]rawSigBlock, 0, len(t.Inputs)), + } + + for _, in := range t.Inputs { + txReq.Inputs = append(txReq.Inputs, TransactionAddress{Amount: in.Amount, RCDHash: in.RCDHash, Address: in.Address}) + txReq.RCDs = append(txReq.RCDs, in.RCD) + txReq.SigBlocks = append(txReq.SigBlocks, rawSigBlock{in.Signatures}) + } + + return json.Marshal(txReq) +} + +func (t *FBTransaction) UnmarshalJSON(data []byte) error { + txResp := new(rawFBTransaction) + + if err := json.Unmarshal(data, txResp); err != nil { + return err + } + + t.BlockHeight = txResp.BlockHeight + // the bug in the nanosecond conversion is intentional to stay consistent with factomd + t.Timestamp = time.Unix(txResp.MilliTimestamp/1e3, (txResp.MilliTimestamp%1e3)*1e3) + t.Outputs = txResp.Outputs + t.OutECs = txResp.OutECs + t.TxID = txResp.TxID + + // catch decoding errors or malicious data + if len(txResp.Inputs) != len(txResp.RCDs) || len(txResp.Inputs) != len(txResp.SigBlocks) { + return fmt.Errorf("invalid signature counts") + } + + for i := range txResp.Inputs { + var sta SignedTransactionAddress + sta.Amount = txResp.Inputs[i].Amount + sta.RCDHash = txResp.Inputs[i].RCDHash + sta.Address = txResp.Inputs[i].Address + sta.RCD = txResp.RCDs[i] + sta.Signatures = txResp.SigBlocks[i].Signatures + + t.Inputs = append(t.Inputs, sta) + } + + return nil +} + +func (t FBTransaction) String() string { + var s string + s += fmt.Sprintln("TxID:", t.TxID) + s += fmt.Sprintln("BlockHeight:", t.BlockHeight) + s += fmt.Sprintln("Timestamp:", t.Timestamp) + + if len(t.Inputs) > 0 { + s += fmt.Sprintln("Inputs:") + for _, in := range t.Inputs { + s += fmt.Sprintln(" ", in.Address, in.Amount) + } + } + + if len(t.Outputs) > 0 { + s += fmt.Sprintln("Outputs:") + for _, out := range t.Outputs { + s += fmt.Sprintln(" ", out.Address, out.Amount) + } + } + + if len(t.OutECs) > 0 { + s += fmt.Sprintln("OutECs:") + for _, ec := range t.OutECs { + s += fmt.Sprintln(" ", ec.Address, ec.Amount) + } + } + + return s +} + func (f *FBlock) String() string { var s string @@ -36,7 +167,7 @@ func (f *FBlock) String() string { s += fmt.Sprintln("Transactions {") for _, t := range f.Transactions { - s += fmt.Sprintln(string(t)) + s += fmt.Sprintln(t.String()) } s += fmt.Sprintln("}") diff --git a/fblock_test.go b/fblock_test.go index 42da6b3..62ae573 100644 --- a/fblock_test.go +++ b/fblock_test.go @@ -5,13 +5,13 @@ package factom_test import ( + "bytes" + "encoding/json" + "fmt" "net/http" "net/http/httptest" "testing" - "encoding/json" - "fmt" - . "github.com/FactomProject/factom" ) @@ -180,3 +180,22 @@ func TestGetFBlockByHeight(t *testing.T) { } t.Log("FBlock:", ab) } + +func TestFBTransaction_MarshalJSON(t *testing.T) { + original := []byte(`[{"txid":"fab98df81a80b1177c5226ff307be7ecc77c30666c63f06623a606424d41fe72","blockheight":0,"millitimestamp":1453149000985,"inputs":[],"outputs":[],"outecs":[],"rcds":[],"sigblocks":[]},{"txid":"1ec91421e01d95267f3deb9b9d5f29d3438387a0280a5ffa5e9a60f235212ae8","blockheight":0,"millitimestamp":1453149058599,"inputs":[{"amount":26268275436,"address":"3d956f129c08ac413025be3f6e47e3fb26461df35c9ccaf2fe4d53373e52536b","useraddress":"FA2SCdYb8iBYmMcmeUjHB8NhKx6DqH3wDovkumgbKt4oNkD3TJMg"}],"outputs":[{"amount":26267184636,"address":"ccf82cf94557f08a6859d8bf4a9b3ce361d0abae1e3bf5136b24638b74d32bc6","useraddress":"FA3XME5vdcjG8jPT188UFkum9BeAJJLgwyCkGB12QLsDA2qQaBET"}],"outecs":[],"rcds":["016664074524dd6a58e6593780717233b56d381a6798e5ee5ba75564bde589a6bf"],"sigblocks":[{"signatures":["efdab088b50d56ea2dfd4f600d5727a06cd7e9f3c353288e6898723ea32f4f044d27a80a199cfefec06cf53e18ea863b05b1075001d592b913e7f32c3d3f2204"]}]}]`) + + var tr []*FBTransaction + + if err := json.Unmarshal(original, &tr); err != nil { + t.Errorf("error unmarshalling transactions: %v", err) + } + + rem, err := json.Marshal(tr) + if err != nil { + t.Errorf("error re-marshalling transactions: %v", err) + } + + if !bytes.Equal(rem, original) { + t.Errorf("re-marshall doesn't match original. got = %v, want = %v", string(rem), string(original)) + } +} diff --git a/transaction.go b/transaction.go index a0324b9..5300c7c 100644 --- a/transaction.go +++ b/transaction.go @@ -735,27 +735,6 @@ func GetTransaction(txID string) (*TransactionResponse, error) { return txResp, nil } -// TODO: GetPendingTransactions() should return something more useful than a -// json string. - -// GetPendingTransactions requests a list of transactions that have been -// submitted to the Factom Network, but have not yet been included in a Factoid -// Block. -func GetPendingTransactions() (string, error) { - req := NewJSON2Request("pending-transactions", APICounter(), nil) - resp, err := factomdRequest(req) - - if err != nil { - return "", err - } - if resp.Error != nil { - return "", err - } - - transList := resp.JSONResult() - return string(transList), nil -} - // GetTmpTransaction requests a temporary transaction from the wallet. func GetTmpTransaction(name string) (*Transaction, error) { txs, err := ListTransactionsTmp() diff --git a/transaction_pending.go b/transaction_pending.go new file mode 100644 index 0000000..3309d0a --- /dev/null +++ b/transaction_pending.go @@ -0,0 +1,40 @@ +package factom + +import ( + "encoding/json" +) + +// PendingTransaction is a single transaction returned by the pending-transaction API call +type PendingTransaction struct { + TxID string `json:"transactionid"` + DBHeight uint32 `json:"dbheight"` + Status string `json:"status"` + Fees uint64 `json:"fees"` + Inputs []TransactionAddress + Outputs []TransactionAddress + ECOutputs []TransactionAddress +} + +// GetPendingTransactions requests a list of transactions that have been +// submitted to the Factom Network, but have not yet been included in a Factoid +// Block. +func GetPendingTransactions() ([]PendingTransaction, error) { + req := NewJSON2Request("pending-transactions", APICounter(), nil) + resp, err := factomdRequest(req) + + if err != nil { + return nil, err + } + if resp.Error != nil { + return nil, resp.Error + } + + transList := resp.JSONResult() + + var res []PendingTransaction + if err := json.Unmarshal(transList, &res); err != nil { + return nil, err + } + + return res, nil +} diff --git a/transaction_pending_test.go b/transaction_pending_test.go new file mode 100644 index 0000000..a7ad18a --- /dev/null +++ b/transaction_pending_test.go @@ -0,0 +1,35 @@ +package factom + +import ( + "fmt" + "net/http" + "net/http/httptest" + "testing" +) + +func TestGetPendingTransactions(t *testing.T) { + factomdResponse := `{ + "jsonrpc":"2.0", + "id":0, + "result":[{"transactionid":"5d87f2ba00ebaee1b0aac86b97ab5a8d4ab5f07a948966856f5723b15c57a5ab","status":"TransactionACK","inputs":null,"outputs":[{"amount":320000000,"address":"d06f8b5d84395afbec25d3aa3f3f2d5a6573ebd30e4d02e797d77107395c2b4a","useraddress":"FA3YsmVnBs2LwrthnFzEJYFTw2HzhYMmjnD2GtG6W2r2S2SvonCv"},{"amount":576000000,"address":"e8e41a1c7ca7c29dc928e509cae82cae1a4711eb12fddc8bde25bf1733069efc","useraddress":"FA3jeSpDq6JSNmizDS8TWwdxKS9EiKFeRCXyZNYrfH6eqX1K9o8u"},{"amount":320000000,"address":"6a437b1475d9455e8447f7b2a7f32044a28f5f16486d2f239eca5bdba02030c8","useraddress":"FA2msvDAoVgzRx8BTmxFAYDLbg2VQXqhi2HzKQHmsb4mVrKjY4B6"},{"amount":320000000,"address":"2b20273033acf00b40e88848a853a56dd0543570f4f28d30b050634d1e28bd6e","useraddress":"FA2J59Eb8xyizJLachFn8LuRXVsSNkoW2uPKrna8X4UrtyVwztgY"},{"amount":320000000,"address":"53c597f02efd7ef3caad48182969236a70883bada570aa316bc9b20efedcc73f","useraddress":"FA2byPxs4XYmra4Z6t3UcDwGaNP62LM631QarrqvVbrbsy9XCGoX"},{"amount":320000000,"address":"8f3064f2d1b8fac099719de1651e81c0fc3f0f29ef1a95a3b46c90e628b51ccc","useraddress":"FA3498SdD2tDAi2485czk6shCfU5Q3PC5Wsui6sNzxi1dTpwxpFj"},{"amount":320000000,"address":"d06f8b5d84395afbec25d3aa3f3f2d5a6573ebd30e4d02e797d77107395c2b4a","useraddress":"FA3YsmVnBs2LwrthnFzEJYFTw2HzhYMmjnD2GtG6W2r2S2SvonCv"},{"amount":320000000,"address":"6a437b1475d9455e8447f7b2a7f32044a28f5f16486d2f239eca5bdba02030c8","useraddress":"FA2msvDAoVgzRx8BTmxFAYDLbg2VQXqhi2HzKQHmsb4mVrKjY4B6"},{"amount":320000000,"address":"7e73ae9e7c31c632697e9c9207075a66f099f82f5d1da2aec157146d48c75533","useraddress":"FA2vmboi6PbPHdGjqG5PfRhCG1hcpkXqNkVigAmmjq9atUAA2NpZ"},{"amount":192000000,"address":"edef61cc4d07e34b64376c8ff5785274f84f191ae22224e90c8119d73727f3ce","useraddress":"FA3msHkoAH78cAVvdJE4Za6uSatPQR2aTgvSViRFN3vLBsixvkZQ"},{"amount":480000000,"address":"665650f5cfd74ec95bac1bc823dc7b7ab5b388a3b0ba31663cb678955e8146aa","useraddress":"FA2k9d61ujVHRRMkajk1pKteRSaqGUKf3iJyCBEbU6HeWe2sC5nx"},{"amount":320000000,"address":"cf681312cc74d91691e6c1be0df58c638145d877c6f1647c6ca8d88d2ffbfd17","useraddress":"FA3YRUjaGJjpjvNZPW7CCD7Ba7bJne25HdrTainPYotNw4rUoh46"},{"amount":640000000,"address":"cc26b2335e9b7c60bfd0e0e98d07ec7f58e67d54fb5717b86bbb868584ca1243","useraddress":"FA3WzKn5ygD8E2uGocAYhaooeQyehSjq1HC3hSb2EaJ4ZGfjku63"},{"amount":320000000,"address":"53c597f02efd7ef3caad48182969236a70883bada570aa316bc9b20efedcc73f","useraddress":"FA2byPxs4XYmra4Z6t3UcDwGaNP62LM631QarrqvVbrbsy9XCGoX"}],"ecoutputs":null,"fees":0},{"transactionid":"3a1e8dc38ef4112bc3110ae47a02c417ba465a5478bf5b97a9b4fc842a9ce67a","status":"TransactionACK","inputs":[{"amount":212000,"address":"d2e2860610e282b6d8d9fed05e2a8dce5d8211d86ca14e2edf05023f0759be97","useraddress":"FA3ZxKyN3HHoJftdGbFp5PRvi12jPvQ3SgGNTDQya2D5jwFrtLqZ"}],"outputs":[],"ecoutputs":[{"amount":200000,"address":"3b6a27bcceb6a42d62a3a8d02a6f0d73653215771de243a63ac048a18b59da29","useraddress":"EC2DKSYyRcNWf7RS963VFYgMExoHRYLHVeCfQ9PGPmNzwrcmgm2r"}],"fees":12000},{"transactionid":"0b3f7feadcc849c60bdad4ccac4b831039c6321cecb6667a8d39a8c14850e5cc","status":"TransactionACK","inputs":[{"amount":500012000,"address":"d2e2860610e282b6d8d9fed05e2a8dce5d8211d86ca14e2edf05023f0759be97","useraddress":"FA3ZxKyN3HHoJftdGbFp5PRvi12jPvQ3SgGNTDQya2D5jwFrtLqZ"}],"outputs":[{"amount":500000000,"address":"646f3e8750c550e4582eca5047546ffef89c13a175985e320232bacac81cc428","useraddress":"FA2jK2HcLnRdS94dEcU27rF3meoJfpUcZPSinpb7AwQvPRY6RL1Q"}],"ecoutputs":[],"fees":12000}] + }` + + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + fmt.Fprintln(w, factomdResponse) + })) + defer ts.Close() + + SetFactomdServer(ts.URL[7:]) + + response, err := GetPendingTransactions() + if err != nil { + t.Error(err) + } + + got := fmt.Sprintf("%+v", response) + want := `[{TxID:5d87f2ba00ebaee1b0aac86b97ab5a8d4ab5f07a948966856f5723b15c57a5ab Status:TransactionACK Fees:0 Inputs:[] Outputs:[{Amount:320000000 RCDHash:d06f8b5d84395afbec25d3aa3f3f2d5a6573ebd30e4d02e797d77107395c2b4a Address:FA3YsmVnBs2LwrthnFzEJYFTw2HzhYMmjnD2GtG6W2r2S2SvonCv} {Amount:576000000 RCDHash:e8e41a1c7ca7c29dc928e509cae82cae1a4711eb12fddc8bde25bf1733069efc Address:FA3jeSpDq6JSNmizDS8TWwdxKS9EiKFeRCXyZNYrfH6eqX1K9o8u} {Amount:320000000 RCDHash:6a437b1475d9455e8447f7b2a7f32044a28f5f16486d2f239eca5bdba02030c8 Address:FA2msvDAoVgzRx8BTmxFAYDLbg2VQXqhi2HzKQHmsb4mVrKjY4B6} {Amount:320000000 RCDHash:2b20273033acf00b40e88848a853a56dd0543570f4f28d30b050634d1e28bd6e Address:FA2J59Eb8xyizJLachFn8LuRXVsSNkoW2uPKrna8X4UrtyVwztgY} {Amount:320000000 RCDHash:53c597f02efd7ef3caad48182969236a70883bada570aa316bc9b20efedcc73f Address:FA2byPxs4XYmra4Z6t3UcDwGaNP62LM631QarrqvVbrbsy9XCGoX} {Amount:320000000 RCDHash:8f3064f2d1b8fac099719de1651e81c0fc3f0f29ef1a95a3b46c90e628b51ccc Address:FA3498SdD2tDAi2485czk6shCfU5Q3PC5Wsui6sNzxi1dTpwxpFj} {Amount:320000000 RCDHash:d06f8b5d84395afbec25d3aa3f3f2d5a6573ebd30e4d02e797d77107395c2b4a Address:FA3YsmVnBs2LwrthnFzEJYFTw2HzhYMmjnD2GtG6W2r2S2SvonCv} {Amount:320000000 RCDHash:6a437b1475d9455e8447f7b2a7f32044a28f5f16486d2f239eca5bdba02030c8 Address:FA2msvDAoVgzRx8BTmxFAYDLbg2VQXqhi2HzKQHmsb4mVrKjY4B6} {Amount:320000000 RCDHash:7e73ae9e7c31c632697e9c9207075a66f099f82f5d1da2aec157146d48c75533 Address:FA2vmboi6PbPHdGjqG5PfRhCG1hcpkXqNkVigAmmjq9atUAA2NpZ} {Amount:192000000 RCDHash:edef61cc4d07e34b64376c8ff5785274f84f191ae22224e90c8119d73727f3ce Address:FA3msHkoAH78cAVvdJE4Za6uSatPQR2aTgvSViRFN3vLBsixvkZQ} {Amount:480000000 RCDHash:665650f5cfd74ec95bac1bc823dc7b7ab5b388a3b0ba31663cb678955e8146aa Address:FA2k9d61ujVHRRMkajk1pKteRSaqGUKf3iJyCBEbU6HeWe2sC5nx} {Amount:320000000 RCDHash:cf681312cc74d91691e6c1be0df58c638145d877c6f1647c6ca8d88d2ffbfd17 Address:FA3YRUjaGJjpjvNZPW7CCD7Ba7bJne25HdrTainPYotNw4rUoh46} {Amount:640000000 RCDHash:cc26b2335e9b7c60bfd0e0e98d07ec7f58e67d54fb5717b86bbb868584ca1243 Address:FA3WzKn5ygD8E2uGocAYhaooeQyehSjq1HC3hSb2EaJ4ZGfjku63} {Amount:320000000 RCDHash:53c597f02efd7ef3caad48182969236a70883bada570aa316bc9b20efedcc73f Address:FA2byPxs4XYmra4Z6t3UcDwGaNP62LM631QarrqvVbrbsy9XCGoX}] ECOutputs:[]} {TxID:3a1e8dc38ef4112bc3110ae47a02c417ba465a5478bf5b97a9b4fc842a9ce67a Status:TransactionACK Fees:12000 Inputs:[{Amount:212000 RCDHash:d2e2860610e282b6d8d9fed05e2a8dce5d8211d86ca14e2edf05023f0759be97 Address:FA3ZxKyN3HHoJftdGbFp5PRvi12jPvQ3SgGNTDQya2D5jwFrtLqZ}] Outputs:[] ECOutputs:[{Amount:200000 RCDHash:3b6a27bcceb6a42d62a3a8d02a6f0d73653215771de243a63ac048a18b59da29 Address:EC2DKSYyRcNWf7RS963VFYgMExoHRYLHVeCfQ9PGPmNzwrcmgm2r}]} {TxID:0b3f7feadcc849c60bdad4ccac4b831039c6321cecb6667a8d39a8c14850e5cc Status:TransactionACK Fees:12000 Inputs:[{Amount:500012000 RCDHash:d2e2860610e282b6d8d9fed05e2a8dce5d8211d86ca14e2edf05023f0759be97 Address:FA3ZxKyN3HHoJftdGbFp5PRvi12jPvQ3SgGNTDQya2D5jwFrtLqZ}] Outputs:[{Amount:500000000 RCDHash:646f3e8750c550e4582eca5047546ffef89c13a175985e320232bacac81cc428 Address:FA2jK2HcLnRdS94dEcU27rF3meoJfpUcZPSinpb7AwQvPRY6RL1Q}] ECOutputs:[]}]` + if got != want { + t.Errorf("unexpected response. got = %s, want = %s", got, want) + } +} From aa37ac40884af02a1ae76b99de19def5bd6e255b Mon Sep 17 00:00:00 2001 From: WhoSoup <34172041+WhoSoup@users.noreply.github.com> Date: Fri, 21 Aug 2020 02:58:45 +0200 Subject: [PATCH 10/14] add dbheight to unit test (#146) --- transaction_pending_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/transaction_pending_test.go b/transaction_pending_test.go index a7ad18a..df48029 100644 --- a/transaction_pending_test.go +++ b/transaction_pending_test.go @@ -11,7 +11,7 @@ func TestGetPendingTransactions(t *testing.T) { factomdResponse := `{ "jsonrpc":"2.0", "id":0, - "result":[{"transactionid":"5d87f2ba00ebaee1b0aac86b97ab5a8d4ab5f07a948966856f5723b15c57a5ab","status":"TransactionACK","inputs":null,"outputs":[{"amount":320000000,"address":"d06f8b5d84395afbec25d3aa3f3f2d5a6573ebd30e4d02e797d77107395c2b4a","useraddress":"FA3YsmVnBs2LwrthnFzEJYFTw2HzhYMmjnD2GtG6W2r2S2SvonCv"},{"amount":576000000,"address":"e8e41a1c7ca7c29dc928e509cae82cae1a4711eb12fddc8bde25bf1733069efc","useraddress":"FA3jeSpDq6JSNmizDS8TWwdxKS9EiKFeRCXyZNYrfH6eqX1K9o8u"},{"amount":320000000,"address":"6a437b1475d9455e8447f7b2a7f32044a28f5f16486d2f239eca5bdba02030c8","useraddress":"FA2msvDAoVgzRx8BTmxFAYDLbg2VQXqhi2HzKQHmsb4mVrKjY4B6"},{"amount":320000000,"address":"2b20273033acf00b40e88848a853a56dd0543570f4f28d30b050634d1e28bd6e","useraddress":"FA2J59Eb8xyizJLachFn8LuRXVsSNkoW2uPKrna8X4UrtyVwztgY"},{"amount":320000000,"address":"53c597f02efd7ef3caad48182969236a70883bada570aa316bc9b20efedcc73f","useraddress":"FA2byPxs4XYmra4Z6t3UcDwGaNP62LM631QarrqvVbrbsy9XCGoX"},{"amount":320000000,"address":"8f3064f2d1b8fac099719de1651e81c0fc3f0f29ef1a95a3b46c90e628b51ccc","useraddress":"FA3498SdD2tDAi2485czk6shCfU5Q3PC5Wsui6sNzxi1dTpwxpFj"},{"amount":320000000,"address":"d06f8b5d84395afbec25d3aa3f3f2d5a6573ebd30e4d02e797d77107395c2b4a","useraddress":"FA3YsmVnBs2LwrthnFzEJYFTw2HzhYMmjnD2GtG6W2r2S2SvonCv"},{"amount":320000000,"address":"6a437b1475d9455e8447f7b2a7f32044a28f5f16486d2f239eca5bdba02030c8","useraddress":"FA2msvDAoVgzRx8BTmxFAYDLbg2VQXqhi2HzKQHmsb4mVrKjY4B6"},{"amount":320000000,"address":"7e73ae9e7c31c632697e9c9207075a66f099f82f5d1da2aec157146d48c75533","useraddress":"FA2vmboi6PbPHdGjqG5PfRhCG1hcpkXqNkVigAmmjq9atUAA2NpZ"},{"amount":192000000,"address":"edef61cc4d07e34b64376c8ff5785274f84f191ae22224e90c8119d73727f3ce","useraddress":"FA3msHkoAH78cAVvdJE4Za6uSatPQR2aTgvSViRFN3vLBsixvkZQ"},{"amount":480000000,"address":"665650f5cfd74ec95bac1bc823dc7b7ab5b388a3b0ba31663cb678955e8146aa","useraddress":"FA2k9d61ujVHRRMkajk1pKteRSaqGUKf3iJyCBEbU6HeWe2sC5nx"},{"amount":320000000,"address":"cf681312cc74d91691e6c1be0df58c638145d877c6f1647c6ca8d88d2ffbfd17","useraddress":"FA3YRUjaGJjpjvNZPW7CCD7Ba7bJne25HdrTainPYotNw4rUoh46"},{"amount":640000000,"address":"cc26b2335e9b7c60bfd0e0e98d07ec7f58e67d54fb5717b86bbb868584ca1243","useraddress":"FA3WzKn5ygD8E2uGocAYhaooeQyehSjq1HC3hSb2EaJ4ZGfjku63"},{"amount":320000000,"address":"53c597f02efd7ef3caad48182969236a70883bada570aa316bc9b20efedcc73f","useraddress":"FA2byPxs4XYmra4Z6t3UcDwGaNP62LM631QarrqvVbrbsy9XCGoX"}],"ecoutputs":null,"fees":0},{"transactionid":"3a1e8dc38ef4112bc3110ae47a02c417ba465a5478bf5b97a9b4fc842a9ce67a","status":"TransactionACK","inputs":[{"amount":212000,"address":"d2e2860610e282b6d8d9fed05e2a8dce5d8211d86ca14e2edf05023f0759be97","useraddress":"FA3ZxKyN3HHoJftdGbFp5PRvi12jPvQ3SgGNTDQya2D5jwFrtLqZ"}],"outputs":[],"ecoutputs":[{"amount":200000,"address":"3b6a27bcceb6a42d62a3a8d02a6f0d73653215771de243a63ac048a18b59da29","useraddress":"EC2DKSYyRcNWf7RS963VFYgMExoHRYLHVeCfQ9PGPmNzwrcmgm2r"}],"fees":12000},{"transactionid":"0b3f7feadcc849c60bdad4ccac4b831039c6321cecb6667a8d39a8c14850e5cc","status":"TransactionACK","inputs":[{"amount":500012000,"address":"d2e2860610e282b6d8d9fed05e2a8dce5d8211d86ca14e2edf05023f0759be97","useraddress":"FA3ZxKyN3HHoJftdGbFp5PRvi12jPvQ3SgGNTDQya2D5jwFrtLqZ"}],"outputs":[{"amount":500000000,"address":"646f3e8750c550e4582eca5047546ffef89c13a175985e320232bacac81cc428","useraddress":"FA2jK2HcLnRdS94dEcU27rF3meoJfpUcZPSinpb7AwQvPRY6RL1Q"}],"ecoutputs":[],"fees":12000}] + "result":[{"transactionid":"5d87f2ba00ebaee1b0aac86b97ab5a8d4ab5f07a948966856f5723b15c57a5ab","dbheight":12345,"status":"TransactionACK","inputs":null,"outputs":[{"amount":320000000,"address":"d06f8b5d84395afbec25d3aa3f3f2d5a6573ebd30e4d02e797d77107395c2b4a","useraddress":"FA3YsmVnBs2LwrthnFzEJYFTw2HzhYMmjnD2GtG6W2r2S2SvonCv"},{"amount":576000000,"address":"e8e41a1c7ca7c29dc928e509cae82cae1a4711eb12fddc8bde25bf1733069efc","useraddress":"FA3jeSpDq6JSNmizDS8TWwdxKS9EiKFeRCXyZNYrfH6eqX1K9o8u"},{"amount":320000000,"address":"6a437b1475d9455e8447f7b2a7f32044a28f5f16486d2f239eca5bdba02030c8","useraddress":"FA2msvDAoVgzRx8BTmxFAYDLbg2VQXqhi2HzKQHmsb4mVrKjY4B6"},{"amount":320000000,"address":"2b20273033acf00b40e88848a853a56dd0543570f4f28d30b050634d1e28bd6e","useraddress":"FA2J59Eb8xyizJLachFn8LuRXVsSNkoW2uPKrna8X4UrtyVwztgY"},{"amount":320000000,"address":"53c597f02efd7ef3caad48182969236a70883bada570aa316bc9b20efedcc73f","useraddress":"FA2byPxs4XYmra4Z6t3UcDwGaNP62LM631QarrqvVbrbsy9XCGoX"},{"amount":320000000,"address":"8f3064f2d1b8fac099719de1651e81c0fc3f0f29ef1a95a3b46c90e628b51ccc","useraddress":"FA3498SdD2tDAi2485czk6shCfU5Q3PC5Wsui6sNzxi1dTpwxpFj"},{"amount":320000000,"address":"d06f8b5d84395afbec25d3aa3f3f2d5a6573ebd30e4d02e797d77107395c2b4a","useraddress":"FA3YsmVnBs2LwrthnFzEJYFTw2HzhYMmjnD2GtG6W2r2S2SvonCv"},{"amount":320000000,"address":"6a437b1475d9455e8447f7b2a7f32044a28f5f16486d2f239eca5bdba02030c8","useraddress":"FA2msvDAoVgzRx8BTmxFAYDLbg2VQXqhi2HzKQHmsb4mVrKjY4B6"},{"amount":320000000,"address":"7e73ae9e7c31c632697e9c9207075a66f099f82f5d1da2aec157146d48c75533","useraddress":"FA2vmboi6PbPHdGjqG5PfRhCG1hcpkXqNkVigAmmjq9atUAA2NpZ"},{"amount":192000000,"address":"edef61cc4d07e34b64376c8ff5785274f84f191ae22224e90c8119d73727f3ce","useraddress":"FA3msHkoAH78cAVvdJE4Za6uSatPQR2aTgvSViRFN3vLBsixvkZQ"},{"amount":480000000,"address":"665650f5cfd74ec95bac1bc823dc7b7ab5b388a3b0ba31663cb678955e8146aa","useraddress":"FA2k9d61ujVHRRMkajk1pKteRSaqGUKf3iJyCBEbU6HeWe2sC5nx"},{"amount":320000000,"address":"cf681312cc74d91691e6c1be0df58c638145d877c6f1647c6ca8d88d2ffbfd17","useraddress":"FA3YRUjaGJjpjvNZPW7CCD7Ba7bJne25HdrTainPYotNw4rUoh46"},{"amount":640000000,"address":"cc26b2335e9b7c60bfd0e0e98d07ec7f58e67d54fb5717b86bbb868584ca1243","useraddress":"FA3WzKn5ygD8E2uGocAYhaooeQyehSjq1HC3hSb2EaJ4ZGfjku63"},{"amount":320000000,"address":"53c597f02efd7ef3caad48182969236a70883bada570aa316bc9b20efedcc73f","useraddress":"FA2byPxs4XYmra4Z6t3UcDwGaNP62LM631QarrqvVbrbsy9XCGoX"}],"ecoutputs":null,"fees":0},{"transactionid":"3a1e8dc38ef4112bc3110ae47a02c417ba465a5478bf5b97a9b4fc842a9ce67a","dbheight":11111,"status":"TransactionACK","inputs":[{"amount":212000,"address":"d2e2860610e282b6d8d9fed05e2a8dce5d8211d86ca14e2edf05023f0759be97","useraddress":"FA3ZxKyN3HHoJftdGbFp5PRvi12jPvQ3SgGNTDQya2D5jwFrtLqZ"}],"outputs":[],"ecoutputs":[{"amount":200000,"address":"3b6a27bcceb6a42d62a3a8d02a6f0d73653215771de243a63ac048a18b59da29","useraddress":"EC2DKSYyRcNWf7RS963VFYgMExoHRYLHVeCfQ9PGPmNzwrcmgm2r"}],"fees":12000},{"transactionid":"0b3f7feadcc849c60bdad4ccac4b831039c6321cecb6667a8d39a8c14850e5cc","dbheight":54321,"status":"TransactionACK","inputs":[{"amount":500012000,"address":"d2e2860610e282b6d8d9fed05e2a8dce5d8211d86ca14e2edf05023f0759be97","useraddress":"FA3ZxKyN3HHoJftdGbFp5PRvi12jPvQ3SgGNTDQya2D5jwFrtLqZ"}],"outputs":[{"amount":500000000,"address":"646f3e8750c550e4582eca5047546ffef89c13a175985e320232bacac81cc428","useraddress":"FA2jK2HcLnRdS94dEcU27rF3meoJfpUcZPSinpb7AwQvPRY6RL1Q"}],"ecoutputs":[],"fees":12000}] }` ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -28,7 +28,7 @@ func TestGetPendingTransactions(t *testing.T) { } got := fmt.Sprintf("%+v", response) - want := `[{TxID:5d87f2ba00ebaee1b0aac86b97ab5a8d4ab5f07a948966856f5723b15c57a5ab Status:TransactionACK Fees:0 Inputs:[] Outputs:[{Amount:320000000 RCDHash:d06f8b5d84395afbec25d3aa3f3f2d5a6573ebd30e4d02e797d77107395c2b4a Address:FA3YsmVnBs2LwrthnFzEJYFTw2HzhYMmjnD2GtG6W2r2S2SvonCv} {Amount:576000000 RCDHash:e8e41a1c7ca7c29dc928e509cae82cae1a4711eb12fddc8bde25bf1733069efc Address:FA3jeSpDq6JSNmizDS8TWwdxKS9EiKFeRCXyZNYrfH6eqX1K9o8u} {Amount:320000000 RCDHash:6a437b1475d9455e8447f7b2a7f32044a28f5f16486d2f239eca5bdba02030c8 Address:FA2msvDAoVgzRx8BTmxFAYDLbg2VQXqhi2HzKQHmsb4mVrKjY4B6} {Amount:320000000 RCDHash:2b20273033acf00b40e88848a853a56dd0543570f4f28d30b050634d1e28bd6e Address:FA2J59Eb8xyizJLachFn8LuRXVsSNkoW2uPKrna8X4UrtyVwztgY} {Amount:320000000 RCDHash:53c597f02efd7ef3caad48182969236a70883bada570aa316bc9b20efedcc73f Address:FA2byPxs4XYmra4Z6t3UcDwGaNP62LM631QarrqvVbrbsy9XCGoX} {Amount:320000000 RCDHash:8f3064f2d1b8fac099719de1651e81c0fc3f0f29ef1a95a3b46c90e628b51ccc Address:FA3498SdD2tDAi2485czk6shCfU5Q3PC5Wsui6sNzxi1dTpwxpFj} {Amount:320000000 RCDHash:d06f8b5d84395afbec25d3aa3f3f2d5a6573ebd30e4d02e797d77107395c2b4a Address:FA3YsmVnBs2LwrthnFzEJYFTw2HzhYMmjnD2GtG6W2r2S2SvonCv} {Amount:320000000 RCDHash:6a437b1475d9455e8447f7b2a7f32044a28f5f16486d2f239eca5bdba02030c8 Address:FA2msvDAoVgzRx8BTmxFAYDLbg2VQXqhi2HzKQHmsb4mVrKjY4B6} {Amount:320000000 RCDHash:7e73ae9e7c31c632697e9c9207075a66f099f82f5d1da2aec157146d48c75533 Address:FA2vmboi6PbPHdGjqG5PfRhCG1hcpkXqNkVigAmmjq9atUAA2NpZ} {Amount:192000000 RCDHash:edef61cc4d07e34b64376c8ff5785274f84f191ae22224e90c8119d73727f3ce Address:FA3msHkoAH78cAVvdJE4Za6uSatPQR2aTgvSViRFN3vLBsixvkZQ} {Amount:480000000 RCDHash:665650f5cfd74ec95bac1bc823dc7b7ab5b388a3b0ba31663cb678955e8146aa Address:FA2k9d61ujVHRRMkajk1pKteRSaqGUKf3iJyCBEbU6HeWe2sC5nx} {Amount:320000000 RCDHash:cf681312cc74d91691e6c1be0df58c638145d877c6f1647c6ca8d88d2ffbfd17 Address:FA3YRUjaGJjpjvNZPW7CCD7Ba7bJne25HdrTainPYotNw4rUoh46} {Amount:640000000 RCDHash:cc26b2335e9b7c60bfd0e0e98d07ec7f58e67d54fb5717b86bbb868584ca1243 Address:FA3WzKn5ygD8E2uGocAYhaooeQyehSjq1HC3hSb2EaJ4ZGfjku63} {Amount:320000000 RCDHash:53c597f02efd7ef3caad48182969236a70883bada570aa316bc9b20efedcc73f Address:FA2byPxs4XYmra4Z6t3UcDwGaNP62LM631QarrqvVbrbsy9XCGoX}] ECOutputs:[]} {TxID:3a1e8dc38ef4112bc3110ae47a02c417ba465a5478bf5b97a9b4fc842a9ce67a Status:TransactionACK Fees:12000 Inputs:[{Amount:212000 RCDHash:d2e2860610e282b6d8d9fed05e2a8dce5d8211d86ca14e2edf05023f0759be97 Address:FA3ZxKyN3HHoJftdGbFp5PRvi12jPvQ3SgGNTDQya2D5jwFrtLqZ}] Outputs:[] ECOutputs:[{Amount:200000 RCDHash:3b6a27bcceb6a42d62a3a8d02a6f0d73653215771de243a63ac048a18b59da29 Address:EC2DKSYyRcNWf7RS963VFYgMExoHRYLHVeCfQ9PGPmNzwrcmgm2r}]} {TxID:0b3f7feadcc849c60bdad4ccac4b831039c6321cecb6667a8d39a8c14850e5cc Status:TransactionACK Fees:12000 Inputs:[{Amount:500012000 RCDHash:d2e2860610e282b6d8d9fed05e2a8dce5d8211d86ca14e2edf05023f0759be97 Address:FA3ZxKyN3HHoJftdGbFp5PRvi12jPvQ3SgGNTDQya2D5jwFrtLqZ}] Outputs:[{Amount:500000000 RCDHash:646f3e8750c550e4582eca5047546ffef89c13a175985e320232bacac81cc428 Address:FA2jK2HcLnRdS94dEcU27rF3meoJfpUcZPSinpb7AwQvPRY6RL1Q}] ECOutputs:[]}]` + want := `[{TxID:5d87f2ba00ebaee1b0aac86b97ab5a8d4ab5f07a948966856f5723b15c57a5ab DBHeight:12345 Status:TransactionACK Fees:0 Inputs:[] Outputs:[{Amount:320000000 RCDHash:d06f8b5d84395afbec25d3aa3f3f2d5a6573ebd30e4d02e797d77107395c2b4a Address:FA3YsmVnBs2LwrthnFzEJYFTw2HzhYMmjnD2GtG6W2r2S2SvonCv} {Amount:576000000 RCDHash:e8e41a1c7ca7c29dc928e509cae82cae1a4711eb12fddc8bde25bf1733069efc Address:FA3jeSpDq6JSNmizDS8TWwdxKS9EiKFeRCXyZNYrfH6eqX1K9o8u} {Amount:320000000 RCDHash:6a437b1475d9455e8447f7b2a7f32044a28f5f16486d2f239eca5bdba02030c8 Address:FA2msvDAoVgzRx8BTmxFAYDLbg2VQXqhi2HzKQHmsb4mVrKjY4B6} {Amount:320000000 RCDHash:2b20273033acf00b40e88848a853a56dd0543570f4f28d30b050634d1e28bd6e Address:FA2J59Eb8xyizJLachFn8LuRXVsSNkoW2uPKrna8X4UrtyVwztgY} {Amount:320000000 RCDHash:53c597f02efd7ef3caad48182969236a70883bada570aa316bc9b20efedcc73f Address:FA2byPxs4XYmra4Z6t3UcDwGaNP62LM631QarrqvVbrbsy9XCGoX} {Amount:320000000 RCDHash:8f3064f2d1b8fac099719de1651e81c0fc3f0f29ef1a95a3b46c90e628b51ccc Address:FA3498SdD2tDAi2485czk6shCfU5Q3PC5Wsui6sNzxi1dTpwxpFj} {Amount:320000000 RCDHash:d06f8b5d84395afbec25d3aa3f3f2d5a6573ebd30e4d02e797d77107395c2b4a Address:FA3YsmVnBs2LwrthnFzEJYFTw2HzhYMmjnD2GtG6W2r2S2SvonCv} {Amount:320000000 RCDHash:6a437b1475d9455e8447f7b2a7f32044a28f5f16486d2f239eca5bdba02030c8 Address:FA2msvDAoVgzRx8BTmxFAYDLbg2VQXqhi2HzKQHmsb4mVrKjY4B6} {Amount:320000000 RCDHash:7e73ae9e7c31c632697e9c9207075a66f099f82f5d1da2aec157146d48c75533 Address:FA2vmboi6PbPHdGjqG5PfRhCG1hcpkXqNkVigAmmjq9atUAA2NpZ} {Amount:192000000 RCDHash:edef61cc4d07e34b64376c8ff5785274f84f191ae22224e90c8119d73727f3ce Address:FA3msHkoAH78cAVvdJE4Za6uSatPQR2aTgvSViRFN3vLBsixvkZQ} {Amount:480000000 RCDHash:665650f5cfd74ec95bac1bc823dc7b7ab5b388a3b0ba31663cb678955e8146aa Address:FA2k9d61ujVHRRMkajk1pKteRSaqGUKf3iJyCBEbU6HeWe2sC5nx} {Amount:320000000 RCDHash:cf681312cc74d91691e6c1be0df58c638145d877c6f1647c6ca8d88d2ffbfd17 Address:FA3YRUjaGJjpjvNZPW7CCD7Ba7bJne25HdrTainPYotNw4rUoh46} {Amount:640000000 RCDHash:cc26b2335e9b7c60bfd0e0e98d07ec7f58e67d54fb5717b86bbb868584ca1243 Address:FA3WzKn5ygD8E2uGocAYhaooeQyehSjq1HC3hSb2EaJ4ZGfjku63} {Amount:320000000 RCDHash:53c597f02efd7ef3caad48182969236a70883bada570aa316bc9b20efedcc73f Address:FA2byPxs4XYmra4Z6t3UcDwGaNP62LM631QarrqvVbrbsy9XCGoX}] ECOutputs:[]} {TxID:3a1e8dc38ef4112bc3110ae47a02c417ba465a5478bf5b97a9b4fc842a9ce67a DBHeight:11111 Status:TransactionACK Fees:12000 Inputs:[{Amount:212000 RCDHash:d2e2860610e282b6d8d9fed05e2a8dce5d8211d86ca14e2edf05023f0759be97 Address:FA3ZxKyN3HHoJftdGbFp5PRvi12jPvQ3SgGNTDQya2D5jwFrtLqZ}] Outputs:[] ECOutputs:[{Amount:200000 RCDHash:3b6a27bcceb6a42d62a3a8d02a6f0d73653215771de243a63ac048a18b59da29 Address:EC2DKSYyRcNWf7RS963VFYgMExoHRYLHVeCfQ9PGPmNzwrcmgm2r}]} {TxID:0b3f7feadcc849c60bdad4ccac4b831039c6321cecb6667a8d39a8c14850e5cc DBHeight:54321 Status:TransactionACK Fees:12000 Inputs:[{Amount:500012000 RCDHash:d2e2860610e282b6d8d9fed05e2a8dce5d8211d86ca14e2edf05023f0759be97 Address:FA3ZxKyN3HHoJftdGbFp5PRvi12jPvQ3SgGNTDQya2D5jwFrtLqZ}] Outputs:[{Amount:500000000 RCDHash:646f3e8750c550e4582eca5047546ffef89c13a175985e320232bacac81cc428 Address:FA2jK2HcLnRdS94dEcU27rF3meoJfpUcZPSinpb7AwQvPRY6RL1Q}] ECOutputs:[]}]` if got != want { t.Errorf("unexpected response. got = %s, want = %s", got, want) } From 7b8f2b8eb473881c1153e6cdcabad6dd885adf43 Mon Sep 17 00:00:00 2001 From: Paul Bernier Date: Mon, 31 Aug 2020 05:05:23 -0700 Subject: [PATCH 11/14] fix circle ci and improve integration (test results...) (#147) --- .circleci/config.yml | 90 ++++++++++++++------------------------------ 1 file changed, 28 insertions(+), 62 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 56244f2..6e3610f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,78 +1,44 @@ -# Golang CircleCI 2.0 configuration file -# -# Check https://circleci.com/docs/2.0/language-go/ for more details -version: 2 +version: 2 jobs: build: - docker: - # specify the version - - image: circleci/golang:1.12 + docker: + # CircleCI Go images available at: https://hub.docker.com/r/circleci/golang/ + - image: circleci/golang:1.14 - # Specify service dependencies here if necessary - # CircleCI maintains a library of pre-built images - # documented at https://circleci.com/docs/2.0/circleci-images/ - # - image: circleci/postgres:9.4 + environment: + TEST_RESULTS: /tmp/test-results - working_directory: /go/src/github.com/FactomProject/factom steps: - checkout - - run: - name: Get Glide - command: | - go get -v github.com/Masterminds/glide - cd $GOPATH/src/github.com/Masterminds/glide - git checkout tags/v0.13.1 - go install -# Potentially enable coveralls in the future -# - run: -# name: Get goveralls -# command: | -# go get github.com/mattn/goveralls - - run: - name: Get the dependencies - command: | - glide install - - run: - name: Build and install to verify it builds - command: go install -v + - run: mkdir -p $TEST_RESULTS - # Move gopath to tmp so we have test files - - run: - name: Move GOPATH to persist - command: cp -r $GOPATH/ /tmp + - restore_cache: + keys: + - go-mod-v4-{{ checksum "go.sum" }} + # Make sure it builds first + - run: go build - - persist_to_workspace: - root: /tmp - paths: go - - test: - working_directory: /tmp # All the binaries are saved here - docker: - - image: circleci/golang:1.12 - - steps: - - attach_workspace: - at: /tmp - run: name: Run unit tests command: | - export PATH="/tmp/go/bin:$PATH" - export GOPATH=/tmp/go - cd /tmp/go/src/github.com/FactomProject/factom - go test -v ./... + PACKAGE_NAMES=$(go list ./... | circleci tests split --split-by=timings --timings-type=classname) + gotestsum --junitfile ${TEST_RESULTS}/gotestsum-report.xml -- $PACKAGE_NAMES + + - save_cache: + key: go-mod-v4-{{ checksum "go.sum" }} + paths: + - "/go/pkg/mod" + + - store_artifacts: + path: /tmp/test-results + destination: raw-test-output + + - store_test_results: + path: /tmp/test-results workflows: version: 2 - commit-workflow: + build-workflow: jobs: - - build: - filters: - tags: - only: /.*/ - - test: - filters: - tags: - only: /.*/ - requires: - - build \ No newline at end of file + - build From afafc3a8957f254a59a7b199552b2f1e87cd78a1 Mon Sep 17 00:00:00 2001 From: WhoSoup <34172041+WhoSoup@users.noreply.github.com> Date: Thu, 3 Sep 2020 04:21:33 +0200 Subject: [PATCH 12/14] rename outecs to ecoutputs (#151) --- fblock.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/fblock.go b/fblock.go index a417508..8b3357a 100644 --- a/fblock.go +++ b/fblock.go @@ -35,11 +35,11 @@ type FBTransaction struct { Timestamp time.Time `json:"timestamp"` Inputs []SignedTransactionAddress `json:"inputs"` Outputs []TransactionAddress `json:"outputs"` - OutECs []TransactionAddress `json:"outecs"` + ECOutputs []TransactionAddress `json:"outecs"` } // TransactionAddress holds the relevant data for either an input or an output. -// The amount is in either Factoshi (Input and Output) or EC (OutECs). +// The amount is in either Factoshi (Input and Output) or EC (ECOutputs). // The RCDHash is the SHA256 hash of the RCD. // The address is the human readable address calculated from the RCDHash and type. type TransactionAddress struct { @@ -64,7 +64,7 @@ type rawFBTransaction struct { MilliTimestamp int64 `json:"millitimestamp"` Inputs []TransactionAddress `json:"inputs"` Outputs []TransactionAddress `json:"outputs"` - OutECs []TransactionAddress `json:"outecs"` + ECOutputs []TransactionAddress `json:"outecs"` RCDs []string `json:"rcds"` SigBlocks []rawSigBlock `json:"sigblocks"` } @@ -78,7 +78,7 @@ func (t *FBTransaction) MarshalJSON() ([]byte, error) { BlockHeight: t.BlockHeight, MilliTimestamp: t.Timestamp.UnixNano()/1e6 + (t.Timestamp.UnixNano()/1e3)%1e3, Outputs: t.Outputs, - OutECs: t.OutECs, + ECOutputs: t.ECOutputs, Inputs: make([]TransactionAddress, 0, len(t.Inputs)), RCDs: make([]string, 0, len(t.Inputs)), SigBlocks: make([]rawSigBlock, 0, len(t.Inputs)), @@ -104,7 +104,7 @@ func (t *FBTransaction) UnmarshalJSON(data []byte) error { // the bug in the nanosecond conversion is intentional to stay consistent with factomd t.Timestamp = time.Unix(txResp.MilliTimestamp/1e3, (txResp.MilliTimestamp%1e3)*1e3) t.Outputs = txResp.Outputs - t.OutECs = txResp.OutECs + t.ECOutputs = txResp.ECOutputs t.TxID = txResp.TxID // catch decoding errors or malicious data @@ -146,9 +146,9 @@ func (t FBTransaction) String() string { } } - if len(t.OutECs) > 0 { - s += fmt.Sprintln("OutECs:") - for _, ec := range t.OutECs { + if len(t.ECOutputs) > 0 { + s += fmt.Sprintln("ECOutputs:") + for _, ec := range t.ECOutputs { s += fmt.Sprintln(" ", ec.Address, ec.Amount) } } From 4724045d7aee180bc6a3574f82eed86f633da387 Mon Sep 17 00:00:00 2001 From: Paul Bernier Date: Mon, 19 Oct 2020 00:27:06 -0700 Subject: [PATCH 13/14] Improve CI (#153) --- .circleci/config.yml | 24 ++++++++++++++++++++++-- entry.go | 4 ++-- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 6e3610f..34dff80 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,6 @@ version: 2 jobs: build: docker: - # CircleCI Go images available at: https://hub.docker.com/r/circleci/golang/ - image: circleci/golang:1.14 environment: @@ -16,7 +15,6 @@ jobs: keys: - go-mod-v4-{{ checksum "go.sum" }} - # Make sure it builds first - run: go build - run: @@ -37,8 +35,30 @@ jobs: - store_test_results: path: /tmp/test-results + gofmt: + docker: + - image: circleci/golang:1.14 + + steps: + - checkout + - run: + name: "Enforce Go Formatted Code" + command: test $(gofmt -l . | wc -l) -eq 0 + + govet: + docker: + - image: circleci/golang:1.14 + + steps: + - checkout + - run: + name: "Go vet" + command: go vet ./... + workflows: version: 2 build-workflow: jobs: - build + - gofmt + - govet diff --git a/entry.go b/entry.go index e9721e6..e549123 100644 --- a/entry.go +++ b/entry.go @@ -178,7 +178,7 @@ func (e *Entry) UnmarshalJSON(data []byte) error { } func EntryCommitMessage(e *Entry, ec *ECAddress) (*bytes.Buffer, error) { - buf := new(bytes.Buffer) + buf := new(bytes.Buffer) // 1 byte version buf.Write([]byte{0}) @@ -209,7 +209,7 @@ func EntryCommitMessage(e *Entry, ec *ECAddress) (*bytes.Buffer, error) { // Entry Credit Signature. func ComposeEntryCommit(e *Entry, ec *ECAddress) (*JSON2Request, error) { b, err := EntryCommitMessage(e, ec) - if err != nil { + if err != nil { return nil, err } params := messageRequest{Message: hex.EncodeToString(b.Bytes())} From c93c3bbc654926f553b06d4ea64386e23b014a1d Mon Sep 17 00:00:00 2001 From: ilzheev Date: Fri, 23 Oct 2020 16:31:02 +0200 Subject: [PATCH 14/14] FCT anchors specification --- anchors.go | 79 ++++++++++++++++++++++++++++++++++++++++++++++++- anchors_test.go | 37 +++++++++++++++++++++++ 2 files changed, 115 insertions(+), 1 deletion(-) diff --git a/anchors.go b/anchors.go index b2667ef..8deebdc 100644 --- a/anchors.go +++ b/anchors.go @@ -43,6 +43,20 @@ type MerkleNode struct { Top string `json:"top,omitempty"` } +// AnchorsFactom is an anchors response from factomd in custom (private) networks, e.g. testnet +// Note that Ethereum or Bitcoin can be nil +type AnchorsFactom struct { + Height uint32 `json:"directoryblockheight"` + KeyMR string `json:"directoryblockkeymr"` + Factom *AnchorFactom `json:"factom"` +} + +// AnchorFactom is the factom mainnet specific anchor, used into custom (private) networks, e.g. testnet +type AnchorFactom struct { + EntryHash string `json:"entryhash"` + BlockHash string `json:"blockhash"` +} + func (a *Anchors) String() string { var sb strings.Builder fmt.Fprintf(&sb, "Height: %d\n", a.Height) @@ -84,6 +98,23 @@ func (a *Anchors) String() string { return sb.String() } +func (a *AnchorsFactom) String() string { + var sb strings.Builder + fmt.Fprintf(&sb, "Height: %d\n", a.Height) + fmt.Fprintf(&sb, "KeyMR: %s\n", a.KeyMR) + + if a.Factom != nil { + fmt.Fprintf(&sb, "Factom {\n") + fmt.Fprintf(&sb, " EntryHash: %s\n", a.Factom.EntryHash) + fmt.Fprintf(&sb, " BlockHash: %s\n", a.Factom.BlockHash) + fmt.Fprintf(&sb, "}\n") + } else { + fmt.Fprintf(&sb, "Factom {}\n") + } + + return sb.String() +} + // UnmarshalJSON is an unmarshaller that handles the variable response from factomd func (a *Anchors) UnmarshalJSON(data []byte) error { type tmp *Anchors // unmarshal into a new type to prevent infinite loop @@ -93,6 +124,14 @@ func (a *Anchors) UnmarshalJSON(data []byte) error { return json.Unmarshal(data, tmp(a)) } +// UnmarshalJSON is an unmarshaller that handles the variable response from factomd +func (a *AnchorsFactom) UnmarshalJSON(data []byte) error { + type tmp *AnchorsFactom // unmarshal into a new type to prevent infinite loop + // json can't unmarshal a bool into a struct, but it can recognize a null pointer + data = bytes.Replace(data, []byte("\"factom\":false"), []byte("\"factom\":null"), -1) + return json.Unmarshal(data, tmp(a)) +} + func getAnchors(hash string, height int64) (*Anchors, error) { var params interface{} if hash != "" { @@ -117,7 +156,31 @@ func getAnchors(hash string, height int64) (*Anchors, error) { return &res, nil } -// GetAnchors retrieves the bitcoin and ethereum anchors from factod. +func getAnchorsFactom(hash string, height int64) (*AnchorsFactom, error) { + var params interface{} + if hash != "" { + params = hashRequest{Hash: hash} + } else { + params = heightRequest{Height: height} + } + req := NewJSON2Request("anchors", APICounter(), params) + resp, err := factomdRequest(req) + if err != nil { + return nil, err + } + if resp.Error != nil { + return nil, resp.Error + } + var res AnchorsFactom + err = json.Unmarshal(resp.Result, &res) + if err != nil { + return nil, err + } + + return &res, nil +} + +// GetAnchors retrieves the bitcoin and ethereum anchors from factomd. // Hash can be entry hash, entry block keymr, factoid block keymr, // admin block lookup hash, entry credit block header hash, or // directory block keymr @@ -125,8 +188,22 @@ func GetAnchors(hash string) (*Anchors, error) { return getAnchors(hash, 0) } +// GetAnchorsFactom retrieves factom mainnet anchors from factomd. +// Hash can be entry hash, entry block keymr, factoid block keymr, +// admin block lookup hash, entry credit block header hash, or +// directory block keymr +func GetAnchorsFactom(hash string) (*AnchorsFactom, error) { + return getAnchorsFactom(hash, 0) +} + // GetAnchorsByHeight retrieves the bitcoin and ethereum anchors for // a specific height func GetAnchorsByHeight(height int64) (*Anchors, error) { return getAnchors("", height) } + +// GetAnchorsFactomByHeight retrieves factom mainnet anchors for +// a specific height +func GetAnchorsFactomByHeight(height int64) (*AnchorsFactom, error) { + return getAnchorsFactom("", height) +} diff --git a/anchors_test.go b/anchors_test.go index 8704770..e36c2ed 100644 --- a/anchors_test.go +++ b/anchors_test.go @@ -105,3 +105,40 @@ Ethereum { } } + +func TestGetAnchorsFactom(t *testing.T) { + factomdResponse := `{"jsonrpc":"2.0","id":0,"result":{"directoryblockheight":200000,"directoryblockkeymr":"ce86fc790dd1462aea255adaa64e2f21c871995df2c2c119352d869fa1d7269f","factom":{"entryhash":"2e1be21115e4fb2b24f25941448c620b08bd1ab01971931900944fee9136674f","blockhash":"3c9d3b90d00ecc62fd354d0bd6eb02b7f67c3ea05bc3d2d38853dcd3cadfe71a"}}}` + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + fmt.Fprintln(w, factomdResponse) + })) + defer ts.Close() + + SetFactomdServer(ts.URL[7:]) + + response, err := GetAnchorsFactom("ce86fc790dd1462aea255adaa64e2f21c871995df2c2c119352d869fa1d7269f") // irrelevant, hardcoded above + if err != nil { + t.Error(err) + } + response2, err := GetAnchorsFactomByHeight(200000) // irrelevant, hardcoded above + if err != nil { + t.Error(err) + } + + received1 := fmt.Sprintf("%+v", response) + received2 := fmt.Sprintf("%+v", response2) + expected := `Height: 200000 +KeyMR: ce86fc790dd1462aea255adaa64e2f21c871995df2c2c119352d869fa1d7269f +Factom { + EntryHash: 2e1be21115e4fb2b24f25941448c620b08bd1ab01971931900944fee9136674f + BlockHash: 3c9d3b90d00ecc62fd354d0bd6eb02b7f67c3ea05bc3d2d38853dcd3cadfe71a +} +` + if received1 != expected { + t.Errorf("GetAnchorsFactom() expected:%s\nreceived:%s", expected, received1) + } + if received2 != expected { + t.Errorf("GetAnchorsFactomByHeight() expected:%s\nreceived:%s", expected, received2) + } + +}