diff --git a/api/api.go b/api/api.go index 268db6da0..57b350d76 100644 --- a/api/api.go +++ b/api/api.go @@ -4,6 +4,12 @@ import ( "fmt" "net" + "github.com/zoobc/zoobc-core/common/chaintype" + core_service "github.com/zoobc/zoobc-core/core/service" + + "github.com/zoobc/zoobc-core/common/crypto" + "github.com/zoobc/zoobc-core/common/transaction" + "github.com/sirupsen/logrus" "github.com/zoobc/zoobc-core/api/handler" "github.com/zoobc/zoobc-core/api/service" @@ -25,10 +31,19 @@ func init() { } } -func startGrpcServer(port int, queryExecutor *query.Executor, p2pHostService contract.P2PType) { +func startGrpcServer(port int, queryExecutor query.ExecutorInterface, p2pHostService contract.P2PType) { grpcServer := grpc.NewServer( grpc.UnaryInterceptor(util.NewServerInterceptor(apiLogger)), ) + actionTypeSwitcher := &transaction.TypeSwitcher{ + Executor: queryExecutor, + } + mempoolService := core_service.NewMempoolService( + &chaintype.MainChain{}, + queryExecutor, + query.NewMempoolQuery(&chaintype.MainChain{}), + actionTypeSwitcher, + query.NewAccountBalanceQuery()) serv, err := net.Listen("tcp", fmt.Sprintf(":%d", port)) if err != nil { @@ -43,7 +58,13 @@ func startGrpcServer(port int, queryExecutor *query.Executor, p2pHostService con // Set GRPC handler for Transactions requests rpc_service.RegisterTransactionServiceServer(grpcServer, &handler.TransactionHandler{ - Service: service.NewTransactionService(queryExecutor), + Service: service.NewTransactionService( + queryExecutor, + &crypto.Signature{}, + actionTypeSwitcher, + mempoolService, + apiLogger, + ), }) // Set GRPC handler for Transactions requests @@ -62,6 +83,6 @@ func startGrpcServer(port int, queryExecutor *query.Executor, p2pHostService con } // Start starts api servers in the given port and passing query executor -func Start(grpcPort, restPort int, queryExecutor *query.Executor, p2pHostService contract.P2PType) { +func Start(grpcPort, restPort int, queryExecutor query.ExecutorInterface, p2pHostService contract.P2PType) { startGrpcServer(grpcPort, queryExecutor, p2pHostService) } diff --git a/api/client/PostTransaction/client.go b/api/client/PostTransaction/client.go new file mode 100644 index 000000000..4dca4aedd --- /dev/null +++ b/api/client/PostTransaction/client.go @@ -0,0 +1,47 @@ +package main + +import ( + "context" + + log "github.com/sirupsen/logrus" + rpc_model "github.com/zoobc/zoobc-core/common/model" + rpc_service "github.com/zoobc/zoobc-core/common/service" + "google.golang.org/grpc" +) + +func main() { + conn, err := grpc.Dial(":3001", grpc.WithInsecure()) + if err != nil { + log.Fatalf("did not connect: %s", err) + } + defer conn.Close() + + c := rpc_service.NewTransactionServiceClient(conn) + + response, err := c.PostTransaction(context.Background(), &rpc_model.PostTransactionRequest{ + // TransactionBytes: []byte{ // keep this to test multiple transaction in single block. + // 1, 0, 1, 82, 108, 58, 93, 0, 0, 0, 0, 0, 0, 66, 67, 90, 110, 83, 102, 113, 112, 80, 53, 116, 113, 70, 81, 108, 77, 84, 89, + // 107, 68, 101, 66, 86, 70, 87, 110, 98, 121, 86, 75, 55, 118, 76, 114, 53, 79, 82, 70, 112, 84, 106, 103, 116, 78, 0, 0, 66, + // 67, 90, 75, 76, 118, 103, 85, 89, 90, 49, 75, 75, 120, 45, 106, 116, 70, 57, 75, 111, 74, 115, 107, 106, 86, 80, 118, 66, 57, + // 106, 112, 73, 106, 102, 122, 122, 73, 54, 122, 68, 87, 48, 74, 1, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 16, 39, 0, 0, 0, 0, 0, 0, 140, + // 134, 217, 86, 232, 251, 81, 174, 86, 44, 221, 44, 226, 73, 245, 19, 170, 94, 47, 160, 53, 20, 225, 192, 19, 200, 196, 217, 96, 64, + // 66, 6, 146, 16, 61, 104, 106, 112, 122, 96, 233, 224, 208, 119, 245, 148, 60, 9, 131, 211, 110, 68, 167, 115, 243, 251, 90, 64, 234, + // 66, 108, 30, 116, 9, + // }, + TransactionBytes: []byte{ + 1, 0, 1, 53, 119, 58, 93, 0, 0, 0, 0, 0, 0, 66, 67, 90, 110, 83, 102, 113, 112, 80, 53, 116, 113, 70, 81, 108, 77, 84, 89, 107, 68, + 101, 66, 86, 70, 87, 110, 98, 121, 86, 75, 55, 118, 76, 114, 53, 79, 82, 70, 112, 84, 106, 103, 116, 78, 0, 0, 66, 67, 90, 75, 76, + 118, 103, 85, 89, 90, 49, 75, 75, 120, 45, 106, 116, 70, 57, 75, 111, 74, 115, 107, 106, 86, 80, 118, 66, 57, 106, 112, 73, 106, + 102, 122, 122, 73, 54, 122, 68, 87, 48, 74, 1, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 16, 39, 0, 0, 0, 0, 0, 0, 32, 85, 34, 198, 89, 78, + 166, 142, 59, 148, 243, 133, 69, 66, 123, 219, 2, 3, 229, 172, 221, 35, 185, 208, 43, 44, 172, 96, 166, 116, 205, 93, 78, 194, 153, + 95, 243, 145, 108, 96, 42, 6, 186, 128, 59, 117, 83, 196, 26, 9, 15, 157, 215, 108, 180, 35, 195, 100, 7, 142, 47, 96, 108, 10, + }, + }) + + if err != nil { + log.Fatalf("error calling rpc_service.PostTransaction: %s", err) + } + + log.Printf("response from remote rpc_service.PostTransaction(): %s", response) + +} diff --git a/api/handler/transactionHandler.go b/api/handler/transactionHandler.go index 586c05cd6..9331b53eb 100644 --- a/api/handler/transactionHandler.go +++ b/api/handler/transactionHandler.go @@ -39,3 +39,16 @@ func (th *TransactionHandler) GetTransactions(ctx context.Context, return response, nil } + +// PostTransaction handle transaction submitted by client +func (th *TransactionHandler) PostTransaction(ctx context.Context, + req *model.PostTransactionRequest) (*model.PostTransactionResponse, error) { + chainType := chaintype.GetChainType(0) + transaction, err := th.Service.PostTransaction(chainType, req) + if err != nil { + return nil, err + } + return &model.PostTransactionResponse{ + Transaction: transaction, + }, nil +} diff --git a/api/service/transactionApiService.go b/api/service/transactionApiService.go index 8f3dee22f..a49cdb155 100644 --- a/api/service/transactionApiService.go +++ b/api/service/transactionApiService.go @@ -3,7 +3,16 @@ package service import ( "database/sql" "errors" - "fmt" + "time" + + "github.com/sirupsen/logrus" + + "github.com/zoobc/zoobc-core/common/transaction" + "github.com/zoobc/zoobc-core/core/service" + + "github.com/zoobc/zoobc-core/common/crypto" + + "github.com/zoobc/zoobc-core/common/util" "github.com/zoobc/zoobc-core/common/contract" "github.com/zoobc/zoobc-core/common/model" @@ -15,20 +24,33 @@ type ( TransactionServiceInterface interface { GetTransaction(contract.ChainType, *model.GetTransactionRequest) (*model.Transaction, error) GetTransactions(contract.ChainType, *model.GetTransactionsRequest) (*model.GetTransactionsResponse, error) + PostTransaction(contract.ChainType, *model.PostTransactionRequest) (*model.Transaction, error) } // TransactionService represents struct of TransactionService TransactionService struct { - Query *query.Executor + Query query.ExecutorInterface + Signature crypto.SignatureInterface + ActionTypeSwitcher transaction.TypeActionSwitcher + MempoolService service.MempoolServiceInterface + Log *logrus.Logger } ) var transactionServiceInstance *TransactionService // NewTransactionService creates a singleton instance of TransactionService -func NewTransactionService(queryExecutor *query.Executor) *TransactionService { +func NewTransactionService(queryExecutor query.ExecutorInterface, signature crypto.SignatureInterface, + txTypeSwitcher transaction.TypeActionSwitcher, mempoolService service.MempoolServiceInterface, + log *logrus.Logger) *TransactionService { if transactionServiceInstance == nil { - transactionServiceInstance = &TransactionService{Query: queryExecutor} + transactionServiceInstance = &TransactionService{ + Query: queryExecutor, + Signature: signature, + ActionTypeSwitcher: txTypeSwitcher, + MempoolService: mempoolService, + Log: log, + } } return transactionServiceInstance } @@ -44,7 +66,6 @@ func (ts *TransactionService) GetTransaction(chainType contract.ChainType, txQuery := query.NewTransactionQuery(chainType) rows, err = ts.Query.ExecuteSelect(txQuery.GetTransaction(params.ID)) if err != nil { - fmt.Printf("GetTransaction fails %v\n", err) return nil, err } defer rows.Close() @@ -70,7 +91,6 @@ func (ts *TransactionService) GetTransactions(chainType contract.ChainType, selectQuery := txQuery.GetTransactions(params.Limit, params.Offset) rows, err = ts.Query.ExecuteSelect(selectQuery) if err != nil { - fmt.Printf("GetTransactions fails %v\n", err) return nil, err } defer rows.Close() @@ -78,7 +98,6 @@ func (ts *TransactionService) GetTransactions(chainType contract.ChainType, rows2, err = ts.Query.ExecuteSelect(query.GetTotalRecordOfSelect(selectQuery)) if err != nil { - fmt.Printf("GetTransactions total records fails %v\n", err) return nil, err } defer rows2.Close() @@ -100,3 +119,61 @@ func (ts *TransactionService) GetTransactions(chainType contract.ChainType, Transactions: txs, }, nil } + +func (ts *TransactionService) PostTransaction(chaintype contract.ChainType, req *model.PostTransactionRequest) (*model.Transaction, + error) { + txBytes := req.TransactionBytes + // get unsigned bytes + tx, err := util.ParseTransactionBytes(txBytes, true) + if err != nil { + return nil, err + } + // Validate Tx + txType := ts.ActionTypeSwitcher.GetTransactionType(tx) + + // Save to mempool + mpTx := &model.MempoolTransaction{ + FeePerByte: 0, + ID: tx.ID, + TransactionBytes: txBytes, + ArrivalTimestamp: time.Now().Unix(), + } + if err := ts.MempoolService.ValidateMempoolTransaction(mpTx); err != nil { + ts.Log.Warnf("Invalid transaction submitted: %v", err) + return nil, err + } + // Apply Unconfirmed + err = ts.Query.BeginTx() + if err != nil { + ts.Log.Warnf("error opening db transaction %v", err) + return nil, err + } + err = txType.ApplyUnconfirmed() + if err != nil { + ts.Log.Warnf("fail ApplyUnconfirmed tx: %v", err) + errRollback := ts.Query.RollbackTx() + if errRollback != nil { + ts.Log.Warnf("error rolling back db transaction %v", errRollback) + return nil, errRollback + } + return nil, err + } + err = ts.MempoolService.AddMempoolTransaction(mpTx) + if err != nil { + ts.Log.Warnf("error AddMempoolTransaction: %v", err) + errRollback := ts.Query.RollbackTx() + if errRollback != nil { + ts.Log.Warnf("error rolling back db transaction %v", errRollback) + return nil, err + } + return nil, err + } + err = ts.Query.CommitTx() + if err != nil { + ts.Log.Warnf("error committing db transaction: %v", err) + return nil, err + } + + // return parsed transaction + return tx, nil +} diff --git a/api/service/transactionApiService_test.go b/api/service/transactionApiService_test.go index 908f448c4..476aded10 100644 --- a/api/service/transactionApiService_test.go +++ b/api/service/transactionApiService_test.go @@ -3,13 +3,21 @@ package service import ( + "database/sql" + "errors" "reflect" "testing" + "github.com/sirupsen/logrus" + "github.com/DATA-DOG/go-sqlmock" "github.com/zoobc/zoobc-core/common/chaintype" + "github.com/zoobc/zoobc-core/common/contract" + "github.com/zoobc/zoobc-core/common/crypto" "github.com/zoobc/zoobc-core/common/model" "github.com/zoobc/zoobc-core/common/query" + "github.com/zoobc/zoobc-core/common/transaction" + "github.com/zoobc/zoobc-core/core/service" ) // resetTransactionService resets the singleton back to nil, used in test case teardown @@ -17,6 +25,246 @@ func resetTransactionService() { transactionServiceInstance = nil } +type ( + mockTypeSwitcherValidateFail struct { + transaction.TypeSwitcher + } + mockTxTypeValidateFail struct { + transaction.TXEmpty + } + mockTypeSwitcherApplyUnconfirmedFail struct { + transaction.TypeSwitcher + } + mockTxTypeApplyUnconfirmedFail struct { + transaction.TXEmpty + } + mockTypeSwitcherSuccess struct { + transaction.TypeSwitcher + } + mockTxTypeSuccess struct { + transaction.TXEmpty + } + mockMempoolServiceFailAdd struct { + service.MempoolService + } + mockMempoolServiceFailValidate struct { + service.MempoolService + } + mockMempoolServiceSuccess struct { + service.MempoolService + } + mockGetTransactionExecutorTxsFail struct { + query.Executor + } + mockGetTransactionExecutorTotalFail struct { + query.Executor + } + mockGetTransactionExecutorTotalFailRow struct { + query.Executor + } + mockGetTransactionExecutorSuccess struct { + query.Executor + } + mockGetTransactionExecutorTxNoRow struct { + query.Executor + } + mockGetTransactionExecutorTxSuccess struct { + query.Executor + } + mockTransactionExecutorFailBeginTx struct { + query.Executor + } + mockTransactionExecutorSuccess struct { + query.Executor + } + mockTransactionExecutorRollbackFail struct { + mockTransactionExecutorSuccess + } + mockTransactionExecutorCommitFail struct { + mockTransactionExecutorSuccess + } +) + +var mockLog = logrus.New() + +func (*mockTypeSwitcherValidateFail) GetTransactionType(tx *model.Transaction) transaction.TypeAction { + return &mockTxTypeValidateFail{} +} + +func (*mockTypeSwitcherApplyUnconfirmedFail) GetTransactionType(tx *model.Transaction) transaction.TypeAction { + return &mockTxTypeApplyUnconfirmedFail{} +} + +func (*mockTypeSwitcherSuccess) GetTransactionType(tx *model.Transaction) transaction.TypeAction { + return &mockTxTypeSuccess{} +} + +func (*mockTxTypeValidateFail) Validate() error { + return errors.New("mockError:validateFail") +} + +func (*mockTxTypeApplyUnconfirmedFail) Validate() error { + return nil +} + +func (*mockTxTypeSuccess) Validate() error { + return nil +} + +func (*mockTxTypeApplyUnconfirmedFail) ApplyUnconfirmed() error { + return errors.New("mockError:ApplyUnconfirmedFail") +} + +func (*mockTxTypeSuccess) ApplyUnconfirmed() error { + return nil +} + +func (*mockMempoolServiceFailAdd) AddMempoolTransaction(mpTx *model.MempoolTransaction) error { + return errors.New("mockError:addTxFail") +} + +func (*mockMempoolServiceFailAdd) ValidateMempoolTransaction(mpTx *model.MempoolTransaction) error { + return nil +} + +func (*mockMempoolServiceFailValidate) ValidateMempoolTransaction(mpTx *model.MempoolTransaction) error { + return errors.New("mockedError") +} + +func (*mockMempoolServiceSuccess) AddMempoolTransaction(mpTx *model.MempoolTransaction) error { + return nil +} + +func (*mockMempoolServiceSuccess) ValidateMempoolTransaction(mpTx *model.MempoolTransaction) error { + return nil +} + +func (*mockGetTransactionExecutorTxsFail) ExecuteSelect(qe string, args ...interface{}) (*sql.Rows, error) { + return nil, errors.New("mockError:getTxsFail") +} + +func (*mockGetTransactionExecutorTotalFail) ExecuteSelect(qe string, args ...interface{}) (*sql.Rows, error) { + db, mock, _ := sqlmock.New() + defer db.Close() + switch qe { + case "SELECT id, block_id, block_height, sender_account_type, sender_account_address, recipient_account_type, " + + "recipient_account_address, transaction_type, fee, timestamp, transaction_hash, transaction_body_length, " + + "transaction_body_bytes, signature, version from \"transaction\" ORDER BY block_height, timestamp LIMIT 0,2": + mock.ExpectQuery(qe).WillReturnRows(sqlmock.NewRows([]string{ + "ID", "BlockID", "Height", "SenderAccountType", "SenderAccountAddress", "RecipientAccountType", "RecipientAccountAddress", + "TransactionType", "Fee", "Timestamp", "TransactionHash", "TransactionBodyLength", "TransactionBodyBytes", "Signature", + "Version", + }).AddRow(4545420970999433273, 1, 1, 0, "senderA", 0, "recipientA", 1, 1, 10000, []byte{1, 1}, 8, []byte{1, 2, 3, 4, 5, 6, 7, 8}, + []byte{0, 0, 0, 0, 0, 0, 0}, 1, + ).AddRow( + 4545420970999433274, 1, 1, 0, "senderA", 0, "recipientA", 1, 1, 10000, []byte{1, 1}, 8, []byte{1, 2, 3, 4, 5, 6, 7, 8}, + []byte{0, 0, 0, 0, 0, 0, 0}, 1)) + default: + return nil, errors.New("mockError:totalFail") + } + return db.Query(qe) +} + +func (*mockGetTransactionExecutorTxNoRow) ExecuteSelect(qe string, args ...interface{}) (*sql.Rows, error) { + db, mock, _ := sqlmock.New() + defer db.Close() + mock.ExpectQuery(qe).WillReturnRows(sqlmock.NewRows([]string{ + "ID", "BlockID", "Height", "SenderAccountType", "SenderAccountAddress", "RecipientAccountType", "RecipientAccountAddress", + "TransactionType", "Fee", "Timestamp", "TransactionHash", "TransactionBodyLength", "TransactionBodyBytes", "Signature", + "Version"})) + return db.Query(qe) +} + +func (*mockGetTransactionExecutorTxSuccess) ExecuteSelect(qe string, args ...interface{}) (*sql.Rows, error) { + db, mock, _ := sqlmock.New() + defer db.Close() + mock.ExpectQuery(qe).WillReturnRows(sqlmock.NewRows([]string{ + "ID", "BlockID", "Height", "SenderAccountType", "SenderAccountAddress", "RecipientAccountType", "RecipientAccountAddress", + "TransactionType", "Fee", "Timestamp", "TransactionHash", "TransactionBodyLength", "TransactionBodyBytes", "Signature", + "Version", + }).AddRow(4545420970999433273, 1, 1, 0, "senderA", 0, "recipientA", 1, 1, 10000, []byte{1, 1}, 8, []byte{1, 2, 3, 4, 5, 6, 7, 8}, + []byte{0, 0, 0, 0, 0, 0, 0}, 1, + )) + return db.Query(qe) +} + +func (*mockGetTransactionExecutorTotalFailRow) ExecuteSelect(qe string, args ...interface{}) (*sql.Rows, error) { + db, mock, _ := sqlmock.New() + defer db.Close() + switch qe { + case "SELECT id, block_id, block_height, sender_account_type, sender_account_address, recipient_account_type, " + + "recipient_account_address, transaction_type, fee, timestamp, transaction_hash, transaction_body_length, " + + "transaction_body_bytes, signature, version from \"transaction\" ORDER BY block_height, timestamp LIMIT 0,2": + mock.ExpectQuery(qe).WillReturnRows(sqlmock.NewRows([]string{ + "ID", "BlockID", "Height", "SenderAccountType", "SenderAccountAddress", "RecipientAccountType", "RecipientAccountAddress", + "TransactionType", "Fee", "Timestamp", "TransactionHash", "TransactionBodyLength", "TransactionBodyBytes", "Signature", "Version", + }).AddRow(4545420970999433273, 1, 1, 0, "senderA", 0, "recipientA", 1, 1, 10000, []byte{1, 1}, 8, []byte{1, 2, 3, 4, 5, 6, 7, 8}, + []byte{0, 0, 0, 0, 0, 0, 0}, 1, + ).AddRow( + 4545420970999433274, 1, 1, 0, "senderA", 0, "recipientA", 1, 1, 10000, []byte{1, 1}, 8, []byte{1, 2, 3, 4, 5, 6, 7, 8}, + []byte{0, 0, 0, 0, 0, 0, 0}, 1)) + default: + mock.ExpectQuery("wrongRow").WillReturnRows(sqlmock.NewRows([]string{ + "total_record", + }).AddRow("abc")) + return db.Query("wrongRow") + } + return db.Query(qe) +} + +func (*mockGetTransactionExecutorSuccess) ExecuteSelect(qe string, args ...interface{}) (*sql.Rows, error) { + db, mock, _ := sqlmock.New() + defer db.Close() + switch qe { + case "SELECT id, block_id, block_height, sender_account_type, sender_account_address, recipient_account_type, " + + "recipient_account_address, transaction_type, fee, timestamp, transaction_hash, transaction_body_length, " + + "transaction_body_bytes, signature, version from \"transaction\" ORDER BY block_height, timestamp LIMIT 0,2": + mock.ExpectQuery(qe).WillReturnRows(sqlmock.NewRows([]string{ + "ID", "BlockID", "Height", "SenderAccountType", "SenderAccountAddress", "RecipientAccountType", "RecipientAccountAddress", + "TransactionType", "Fee", "Timestamp", "TransactionHash", "TransactionBodyLength", "TransactionBodyBytes", "Signature", + "Version", + }).AddRow(4545420970999433273, 1, 1, 0, "senderA", 0, "recipientA", 1, 1, 10000, []byte{1, 1}, 8, []byte{1, 2, 3, 4, 5, 6, 7, 8}, + []byte{0, 0, 0, 0, 0, 0, 0}, 1, + ).AddRow( + 4545420970999433274, 1, 1, 0, "senderA", 0, "recipientA", 1, 1, 10000, []byte{1, 1}, 8, []byte{1, 2, 3, 4, 5, 6, 7, 8}, + []byte{0, 0, 0, 0, 0, 0, 0}, 1)) + default: + mock.ExpectQuery("total-success").WillReturnRows(sqlmock.NewRows([]string{ + "total_record", + }).AddRow(2)) + return db.Query("total-success") + } + return db.Query(qe) +} + +func (*mockTransactionExecutorFailBeginTx) BeginTx() error { + return errors.New("mockedError") +} + +func (*mockTransactionExecutorSuccess) BeginTx() error { + return nil +} + +func (*mockTransactionExecutorSuccess) CommitTx() error { + return nil +} + +func (*mockTransactionExecutorSuccess) RollbackTx() error { + return nil +} + +func (*mockTransactionExecutorSuccess) ExecuteTransaction(qStr string, args ...interface{}) error { + return nil +} + +func (*mockTransactionExecutorRollbackFail) RollbackTx() error { + return errors.New("mockedError") +} + +func (*mockTransactionExecutorCommitFail) CommitTx() error { + return errors.New("mockedError") +} + func TestNewTransactionervice(t *testing.T) { db, _, err := sqlmock.New() if err != nil { @@ -35,7 +283,8 @@ func TestNewTransactionervice(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := NewTransactionService(query.NewQueryExecutor(db)); !reflect.DeepEqual(got, tt.want) { + if got := NewTransactionService(query.NewQueryExecutor(db), + nil, nil, nil, nil); !reflect.DeepEqual(got, tt.want) { t.Errorf("NewTransactionService() = %v, want %v", got, tt.want) } defer resetTransactionService() @@ -43,112 +292,412 @@ func TestNewTransactionervice(t *testing.T) { } } -func Test_TransactionService_GetTransactions(t *testing.T) { - params := &model.GetTransactionsRequest{ - Limit: 2, - Offset: 0, +func TestTransactionService_PostTransaction(t *testing.T) { + type fields struct { + Query query.ExecutorInterface + Signature crypto.SignatureInterface + ActionTypeSwitcher transaction.TypeActionSwitcher + MempoolService service.MempoolServiceInterface + Log *logrus.Logger } - - expectedData := struct { - Total uint64 - Count uint32 - Transactions []*model.Transaction + type args struct { + chaintype contract.ChainType + req *model.PostTransactionRequest + } + tests := []struct { + name string + fields fields + args args + want *model.Transaction + wantErr bool }{ - Total: 2, - Count: 2, - Transactions: []*model.Transaction{ - { - ID: 1, - BlockID: 1, - Height: 1, + { + name: "PostTransaction:txBytesInvalid", + fields: fields{ + Query: nil, + }, + args: args{ + chaintype: &chaintype.MainChain{}, + req: &model.PostTransactionRequest{ + TransactionBytes: []byte{ + 1, 0, 1, 53, 119, 58, 93, 0, 0, 0, 0, 0, 0, 66, 67, 90, 110, 83, 102, 113, 112, 80, 53, 116, 113, 70, 81, 108, 77, + 84, 89, 107, 68, 101, 66, 86, 70, 87, 110, 98, 121, 86, 75, 55, 118, 76, 114, 53, 79, 82, 70, 112, 84, 106, 103, 116, + 78, 0, 0, 66, 67, 90, 75, 76, 118, 103, 85, 89, 90, 49, 75, 75, 120, 45, 106, 116, 70, 57, 75, 111, 74, 115, 107, + 106, 86, 80, 118, 66, 57, 106, 112, 73, 106, 102, 122, 122, 73, 54, 122, 68, 87, 48, 74, 1, 0, 0, 0, 0, 0, 0, 0, 8, 0, + 0, 0, 16, 39, 0, 0, 0, 0, 0, 0, 32, 85, 34, 198, 89, 78, 166, 142, 59, 148, 243, 133, 69, 66, 123, 219, 2, 3, 229, 172, + 221, 35, 185, 208, 43, 44, 172, 96, 166, 116, 205, 93, 78, 194, + }, + }, + }, + wantErr: true, + want: nil, + }, + { + name: "PostTransaction:txType.ValidateFail", + fields: fields{ + Query: nil, + ActionTypeSwitcher: &mockTypeSwitcherValidateFail{}, + MempoolService: &mockMempoolServiceFailValidate{}, + Log: mockLog, + }, + args: args{ + chaintype: &chaintype.MainChain{}, + req: &model.PostTransactionRequest{ + TransactionBytes: []byte{ + 1, 0, 1, 53, 119, 58, 93, 0, 0, 0, 0, 0, 0, 66, 67, 90, 110, 83, 102, 113, 112, 80, 53, 116, 113, 70, 81, 108, 77, + 84, 89, 107, 68, 101, 66, 86, 70, 87, 110, 98, 121, 86, 75, 55, 118, 76, 114, 53, 79, 82, 70, 112, 84, 106, 103, 116, + 78, 0, 0, 66, 67, 90, 75, 76, 118, 103, 85, 89, 90, 49, 75, 75, 120, 45, 106, 116, 70, 57, 75, 111, 74, 115, 107, + 106, 86, 80, 118, 66, 57, 106, 112, 73, 106, 102, 122, 122, 73, 54, 122, 68, 87, 48, 74, 1, 0, 0, 0, 0, 0, 0, 0, 8, 0, + 0, 0, 16, 39, 0, 0, 0, 0, 0, 0, 32, 85, 34, 198, 89, 78, 166, 142, 59, 148, 243, 133, 69, 66, 123, 219, 2, 3, 229, 172, + 221, 35, 185, 208, 43, 44, 172, 96, 166, 116, 205, 93, 78, 194, 153, 95, 243, 145, 108, 96, 42, 6, 186, 128, 59, 117, + 83, 196, 26, 9, 15, 157, 215, 108, 180, 35, 195, 100, 7, 142, 47, 96, 108, 10, + }, + }, + }, + wantErr: true, + want: nil, + }, + { + name: "PostTransaction:beginTxFail", + fields: fields{ + Query: &mockTransactionExecutorFailBeginTx{}, + ActionTypeSwitcher: &mockTypeSwitcherApplyUnconfirmedFail{}, + Log: mockLog, + MempoolService: &mockMempoolServiceSuccess{}, + }, + args: args{ + chaintype: &chaintype.MainChain{}, + req: &model.PostTransactionRequest{ + TransactionBytes: []byte{ + 1, 0, 1, 53, 119, 58, 93, 0, 0, 0, 0, 0, 0, 66, 67, 90, 110, 83, 102, 113, 112, 80, 53, 116, 113, 70, 81, 108, 77, + 84, 89, 107, 68, 101, 66, 86, 70, 87, 110, 98, 121, 86, 75, 55, 118, 76, 114, 53, 79, 82, 70, 112, 84, 106, 103, 116, + 78, 0, 0, 66, 67, 90, 75, 76, 118, 103, 85, 89, 90, 49, 75, 75, 120, 45, 106, 116, 70, 57, 75, 111, 74, 115, 107, + 106, 86, 80, 118, 66, 57, 106, 112, 73, 106, 102, 122, 122, 73, 54, 122, 68, 87, 48, 74, 1, 0, 0, 0, 0, 0, 0, 0, 8, 0, + 0, 0, 16, 39, 0, 0, 0, 0, 0, 0, 32, 85, 34, 198, 89, 78, 166, 142, 59, 148, 243, 133, 69, 66, 123, 219, 2, 3, 229, 172, + 221, 35, 185, 208, 43, 44, 172, 96, 166, 116, 205, 93, 78, 194, 153, 95, 243, 145, 108, 96, 42, 6, 186, 128, 59, 117, + 83, 196, 26, 9, 15, 157, 215, 108, 180, 35, 195, 100, 7, 142, 47, 96, 108, 10, + }, + }, + }, + wantErr: true, + want: nil, + }, + { + name: "PostTransaction:txType.ApplyUnconfirmedFail", + fields: fields{ + Query: &mockTransactionExecutorSuccess{}, + ActionTypeSwitcher: &mockTypeSwitcherApplyUnconfirmedFail{}, + Log: mockLog, + MempoolService: &mockMempoolServiceSuccess{}, + }, + args: args{ + chaintype: &chaintype.MainChain{}, + req: &model.PostTransactionRequest{ + TransactionBytes: []byte{ + 1, 0, 1, 53, 119, 58, 93, 0, 0, 0, 0, 0, 0, 66, 67, 90, 110, 83, 102, 113, 112, 80, 53, 116, 113, 70, 81, 108, 77, + 84, 89, 107, 68, 101, 66, 86, 70, 87, 110, 98, 121, 86, 75, 55, 118, 76, 114, 53, 79, 82, 70, 112, 84, 106, 103, 116, + 78, 0, 0, 66, 67, 90, 75, 76, 118, 103, 85, 89, 90, 49, 75, 75, 120, 45, 106, 116, 70, 57, 75, 111, 74, 115, 107, + 106, 86, 80, 118, 66, 57, 106, 112, 73, 106, 102, 122, 122, 73, 54, 122, 68, 87, 48, 74, 1, 0, 0, 0, 0, 0, 0, 0, 8, 0, + 0, 0, 16, 39, 0, 0, 0, 0, 0, 0, 32, 85, 34, 198, 89, 78, 166, 142, 59, 148, 243, 133, 69, 66, 123, 219, 2, 3, 229, 172, + 221, 35, 185, 208, 43, 44, 172, 96, 166, 116, 205, 93, 78, 194, 153, 95, 243, 145, 108, 96, 42, 6, 186, 128, 59, 117, + 83, 196, 26, 9, 15, 157, 215, 108, 180, 35, 195, 100, 7, 142, 47, 96, 108, 10, + }, + }, + }, + wantErr: true, + want: nil, + }, + { + name: "PostTransaction:txType.ApplyUnconfirmedFail-RollbackFail", + fields: fields{ + Query: &mockTransactionExecutorRollbackFail{}, + ActionTypeSwitcher: &mockTypeSwitcherApplyUnconfirmedFail{}, + Log: mockLog, + MempoolService: &mockMempoolServiceSuccess{}, + }, + args: args{ + chaintype: &chaintype.MainChain{}, + req: &model.PostTransactionRequest{ + TransactionBytes: []byte{ + 1, 0, 1, 53, 119, 58, 93, 0, 0, 0, 0, 0, 0, 66, 67, 90, 110, 83, 102, 113, 112, 80, 53, 116, 113, 70, 81, 108, 77, + 84, 89, 107, 68, 101, 66, 86, 70, 87, 110, 98, 121, 86, 75, 55, 118, 76, 114, 53, 79, 82, 70, 112, 84, 106, 103, 116, + 78, 0, 0, 66, 67, 90, 75, 76, 118, 103, 85, 89, 90, 49, 75, 75, 120, 45, 106, 116, 70, 57, 75, 111, 74, 115, 107, + 106, 86, 80, 118, 66, 57, 106, 112, 73, 106, 102, 122, 122, 73, 54, 122, 68, 87, 48, 74, 1, 0, 0, 0, 0, 0, 0, 0, 8, 0, + 0, 0, 16, 39, 0, 0, 0, 0, 0, 0, 32, 85, 34, 198, 89, 78, 166, 142, 59, 148, 243, 133, 69, 66, 123, 219, 2, 3, 229, 172, + 221, 35, 185, 208, 43, 44, 172, 96, 166, 116, 205, 93, 78, 194, 153, 95, 243, 145, 108, 96, 42, 6, 186, 128, 59, 117, + 83, 196, 26, 9, 15, 157, 215, 108, 180, 35, 195, 100, 7, 142, 47, 96, 108, 10, + }, + }, + }, + wantErr: true, + want: nil, + }, + { + name: "PostTransaction:txType.AddMempoolTransactionFail", + fields: fields{ + Query: &mockTransactionExecutorSuccess{}, + ActionTypeSwitcher: &mockTypeSwitcherSuccess{}, + MempoolService: &mockMempoolServiceFailAdd{}, + Log: mockLog, + }, + args: args{ + chaintype: &chaintype.MainChain{}, + req: &model.PostTransactionRequest{ + TransactionBytes: []byte{ + 1, 0, 1, 53, 119, 58, 93, 0, 0, 0, 0, 0, 0, 66, 67, 90, 110, 83, 102, 113, 112, 80, 53, 116, 113, 70, 81, 108, 77, + 84, 89, 107, 68, 101, 66, 86, 70, 87, 110, 98, 121, 86, 75, 55, 118, 76, 114, 53, 79, 82, 70, 112, 84, 106, 103, 116, + 78, 0, 0, 66, 67, 90, 75, 76, 118, 103, 85, 89, 90, 49, 75, 75, 120, 45, 106, 116, 70, 57, 75, 111, 74, 115, 107, + 106, 86, 80, 118, 66, 57, 106, 112, 73, 106, 102, 122, 122, 73, 54, 122, 68, 87, 48, 74, 1, 0, 0, 0, 0, 0, 0, 0, 8, 0, + 0, 0, 16, 39, 0, 0, 0, 0, 0, 0, 32, 85, 34, 198, 89, 78, 166, 142, 59, 148, 243, 133, 69, 66, 123, 219, 2, 3, 229, 172, + 221, 35, 185, 208, 43, 44, 172, 96, 166, 116, 205, 93, 78, 194, 153, 95, 243, 145, 108, 96, 42, 6, 186, 128, 59, 117, + 83, 196, 26, 9, 15, 157, 215, 108, 180, 35, 195, 100, 7, 142, 47, 96, 108, 10, + }, + }, + }, + wantErr: true, + want: nil, + }, + { + name: "PostTransaction:txType.AddMempoolTransactionFail-RollbackFail", + fields: fields{ + Query: &mockTransactionExecutorRollbackFail{}, + ActionTypeSwitcher: &mockTypeSwitcherSuccess{}, + MempoolService: &mockMempoolServiceFailAdd{}, + Log: mockLog, + }, + args: args{ + chaintype: &chaintype.MainChain{}, + req: &model.PostTransactionRequest{ + TransactionBytes: []byte{ + 1, 0, 1, 53, 119, 58, 93, 0, 0, 0, 0, 0, 0, 66, 67, 90, 110, 83, 102, 113, 112, 80, 53, 116, 113, 70, 81, 108, 77, + 84, 89, 107, 68, 101, 66, 86, 70, 87, 110, 98, 121, 86, 75, 55, 118, 76, 114, 53, 79, 82, 70, 112, 84, 106, 103, 116, + 78, 0, 0, 66, 67, 90, 75, 76, 118, 103, 85, 89, 90, 49, 75, 75, 120, 45, 106, 116, 70, 57, 75, 111, 74, 115, 107, + 106, 86, 80, 118, 66, 57, 106, 112, 73, 106, 102, 122, 122, 73, 54, 122, 68, 87, 48, 74, 1, 0, 0, 0, 0, 0, 0, 0, 8, 0, + 0, 0, 16, 39, 0, 0, 0, 0, 0, 0, 32, 85, 34, 198, 89, 78, 166, 142, 59, 148, 243, 133, 69, 66, 123, 219, 2, 3, 229, 172, + 221, 35, 185, 208, 43, 44, 172, 96, 166, 116, 205, 93, 78, 194, 153, 95, 243, 145, 108, 96, 42, 6, 186, 128, 59, 117, + 83, 196, 26, 9, 15, 157, 215, 108, 180, 35, 195, 100, 7, 142, 47, 96, 108, 10, + }, + }, + }, + wantErr: true, + want: nil, + }, + { + name: "PostTransaction:txType.AddMempoolTransactionFail-RollbackFail", + fields: fields{ + Query: &mockTransactionExecutorCommitFail{}, + ActionTypeSwitcher: &mockTypeSwitcherSuccess{}, + MempoolService: &mockMempoolServiceSuccess{}, + Log: mockLog, + }, + args: args{ + chaintype: &chaintype.MainChain{}, + req: &model.PostTransactionRequest{ + TransactionBytes: []byte{ + 1, 0, 1, 53, 119, 58, 93, 0, 0, 0, 0, 0, 0, 66, 67, 90, 110, 83, 102, 113, 112, 80, 53, 116, 113, 70, 81, 108, 77, + 84, 89, 107, 68, 101, 66, 86, 70, 87, 110, 98, 121, 86, 75, 55, 118, 76, 114, 53, 79, 82, 70, 112, 84, 106, 103, 116, + 78, 0, 0, 66, 67, 90, 75, 76, 118, 103, 85, 89, 90, 49, 75, 75, 120, 45, 106, 116, 70, 57, 75, 111, 74, 115, 107, + 106, 86, 80, 118, 66, 57, 106, 112, 73, 106, 102, 122, 122, 73, 54, 122, 68, 87, 48, 74, 1, 0, 0, 0, 0, 0, 0, 0, 8, 0, + 0, 0, 16, 39, 0, 0, 0, 0, 0, 0, 32, 85, 34, 198, 89, 78, 166, 142, 59, 148, 243, 133, 69, 66, 123, 219, 2, 3, 229, 172, + 221, 35, 185, 208, 43, 44, 172, 96, 166, 116, 205, 93, 78, 194, 153, 95, 243, 145, 108, 96, 42, 6, 186, 128, 59, 117, + 83, 196, 26, 9, 15, 157, 215, 108, 180, 35, 195, 100, 7, 142, 47, 96, 108, 10, + }, + }, + }, + wantErr: true, + want: nil, + }, + { + name: "PostTransaction:txType.Success", + fields: fields{ + Query: &mockTransactionExecutorSuccess{}, + ActionTypeSwitcher: &mockTypeSwitcherSuccess{}, + MempoolService: &mockMempoolServiceSuccess{}, + Log: mockLog, + }, + args: args{ + chaintype: &chaintype.MainChain{}, + req: &model.PostTransactionRequest{ + TransactionBytes: []byte{ + 1, 0, 1, 53, 119, 58, 93, 0, 0, 0, 0, 0, 0, 66, 67, 90, 110, 83, 102, 113, 112, 80, 53, 116, 113, 70, 81, 108, 77, + 84, 89, 107, 68, 101, 66, 86, 70, 87, 110, 98, 121, 86, 75, 55, 118, 76, 114, 53, 79, 82, 70, 112, 84, 106, 103, 116, + 78, 0, 0, 66, 67, 90, 75, 76, 118, 103, 85, 89, 90, 49, 75, 75, 120, 45, 106, 116, 70, 57, 75, 111, 74, 115, 107, + 106, 86, 80, 118, 66, 57, 106, 112, 73, 106, 102, 122, 122, 73, 54, 122, 68, 87, 48, 74, 1, 0, 0, 0, 0, 0, 0, 0, 8, 0, + 0, 0, 16, 39, 0, 0, 0, 0, 0, 0, 32, 85, 34, 198, 89, 78, 166, 142, 59, 148, 243, 133, 69, 66, 123, 219, 2, 3, 229, 172, + 221, 35, 185, 208, 43, 44, 172, 96, 166, 116, 205, 93, 78, 194, 153, 95, 243, 145, 108, 96, 42, 6, 186, 128, 59, 117, + 83, 196, 26, 9, 15, 157, 215, 108, 180, 35, 195, 100, 7, 142, 47, 96, 108, 10, + }, + }, + }, + wantErr: false, + want: &model.Transaction{ + ID: -4430102867797816008, + Version: 1, + TransactionType: 1, + Timestamp: 1564112693, SenderAccountType: 0, - SenderAccountAddress: "abc", + SenderAccountAddress: "BCZnSfqpP5tqFQlMTYkDeBVFWnbyVK7vLr5ORFpTjgtN", RecipientAccountType: 0, - RecipientAccountAddress: "abc", - TransactionType: 1, + RecipientAccountAddress: "BCZKLvgUYZ1KKx-jtF9KoJskjVPvB9jpIjfzzI6zDW0J", Fee: 1, - Timestamp: 11000, - TransactionHash: []byte{}, - TransactionBodyLength: 1, - TransactionBodyBytes: []byte{}, - Signature: []byte{}, - Version: 1, - }, - { - ID: 2, - BlockID: 2, - Height: 2, - SenderAccountType: 1, - SenderAccountAddress: "bcd", - RecipientAccountType: 1, - RecipientAccountAddress: "bcd", - TransactionType: 2, - Fee: 2, - Timestamp: 21000, - TransactionHash: []byte{}, - TransactionBodyLength: 2, - TransactionBodyBytes: []byte{}, - Signature: []byte{}, - Version: 1, + TransactionBodyLength: 8, + TransactionHash: []byte{56, 205, 120, 214, 233, 28, 133, 194, 224, 240, 192, 247, 227, 35, 183, 63, 118, 111, 147, + 55, 104, 54, 76, 108, 224, 194, 232, 88, 36, 93, 104, 76}, + Signature: []byte{32, 85, 34, 198, 89, 78, 166, 142, 59, 148, 243, 133, 69, 66, 123, 219, 2, 3, 229, 172, 221, 35, 185, + 208, 43, 44, 172, 96, 166, 116, 205, 93, 78, 194, 153, 95, 243, 145, 108, 96, 42, 6, 186, 128, 59, 117, 83, 196, 26, + 9, 15, 157, 215, 108, 180, 35, 195, 100, 7, 142, 47, 96, 108, 10}, + TransactionBodyBytes: []byte{16, 39, 0, 0, 0, 0, 0, 0}, }, }, } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ts := &TransactionService{ + Query: tt.fields.Query, + Signature: tt.fields.Signature, + ActionTypeSwitcher: tt.fields.ActionTypeSwitcher, + MempoolService: tt.fields.MempoolService, + Log: tt.fields.Log, + } + got, err := ts.PostTransaction(tt.args.chaintype, tt.args.req) + if (err != nil) != tt.wantErr { + t.Errorf("TransactionService.PostTransaction() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("TransactionService.PostTransaction() = %v, want %v", got, tt.want) + } + }) + } +} - db, mock, err := sqlmock.New() - if err != nil { - t.Fatalf("error while opening database connection") +func TestTransactionService_GetTransactions(t *testing.T) { + type fields struct { + Query query.ExecutorInterface + Signature crypto.SignatureInterface + ActionTypeSwitcher transaction.TypeActionSwitcher + MempoolService service.MempoolServiceInterface + } + type args struct { + chainType contract.ChainType + params *model.GetTransactionsRequest } - defer db.Close() - instance := NewTransactionService(query.NewQueryExecutor(db)) - defer resetTransactionService() tests := []struct { name string + fields fields + args args want *model.GetTransactionsResponse wantErr bool }{ { - name: "GetTransactions:success", + name: "GetTransactions:executeSelectGetTxsFail", + fields: fields{ + Query: &mockGetTransactionExecutorTxsFail{}, + }, + args: args{ + chainType: &chaintype.MainChain{}, + params: &model.GetTransactionsRequest{ + Limit: 2, + Offset: 0, + }, + }, + want: nil, + wantErr: true, + }, + { + name: "GetTransactions:executeSelectGetTotalFail", + fields: fields{ + Query: &mockGetTransactionExecutorTotalFail{}, + }, + args: args{ + chainType: &chaintype.MainChain{}, + params: &model.GetTransactionsRequest{ + Limit: 2, + Offset: 0, + }, + }, + want: nil, + wantErr: true, + }, + { + name: "GetTransactions:executeSelectGetTotalFailRow", + fields: fields{ + Query: &mockGetTransactionExecutorTotalFailRow{}, + }, + args: args{ + chainType: &chaintype.MainChain{}, + params: &model.GetTransactionsRequest{ + Limit: 2, + Offset: 0, + }, + }, + want: &model.GetTransactionsResponse{}, + wantErr: true, + }, + { + name: "GetTransactions:executeSelectGetTotalFailRow", + fields: fields{ + Query: &mockGetTransactionExecutorSuccess{}, + }, + args: args{ + chainType: &chaintype.MainChain{}, + params: &model.GetTransactionsRequest{ + Limit: 2, + Offset: 0, + }, + }, want: &model.GetTransactionsResponse{ - Transactions: expectedData.Transactions, - Total: expectedData.Total, - Count: expectedData.Count, + Count: 2, + Total: 2, + Transactions: []*model.Transaction{ + { + ID: 4545420970999433273, + BlockID: 1, + Height: 1, + SenderAccountType: 0, + SenderAccountAddress: "senderA", + RecipientAccountType: 0, + RecipientAccountAddress: "recipientA", + TransactionType: 1, + Fee: 1, + Timestamp: 10000, + TransactionHash: []byte{1, 1}, + TransactionBodyLength: 8, + TransactionBodyBytes: []byte{1, 2, 3, 4, 5, 6, 7, 8}, + Signature: []byte{0, 0, 0, 0, 0, 0, 0}, + Version: 1, + }, + { + ID: 4545420970999433274, + BlockID: 1, + Height: 1, + SenderAccountType: 0, + SenderAccountAddress: "senderA", + RecipientAccountType: 0, + RecipientAccountAddress: "recipientA", + TransactionType: 1, + Fee: 1, + Timestamp: 10000, + TransactionHash: []byte{1, 1}, + TransactionBodyLength: 8, + TransactionBodyBytes: []byte{1, 2, 3, 4, 5, 6, 7, 8}, + Signature: []byte{0, 0, 0, 0, 0, 0, 0}, + Version: 1, + }, + }, }, wantErr: false, }, } - - chainType := chaintype.GetChainType(0) - transactionQuery := query.NewTransactionQuery(chainType) - queryStr := transactionQuery.GetTransactions(params.Limit, params.Offset) - - mock.ExpectQuery(queryStr).WillReturnRows(sqlmock.NewRows([]string{ - "id", - "block_id", - "block_height", - "sender_account_type", - "sender_account_address", - "recipient_account_type", - "recipient_account_address", - "transaction_type", - "fee", - "timestamp", - "transaction_hash", - "transaction_body_length", - "transaction_body_bytes", - "signature", - "version", - }).AddRow( - 1, 1, 1, 0, "abc", 0, "abc", 1, 1, 11000, []byte{}, 1, []byte{}, []byte{}, 1, - ).AddRow( - 2, 2, 2, 1, "bcd", 1, "bcd", 2, 2, 21000, []byte{}, 2, []byte{}, []byte{}, 1, - )) - - // mocking record count query - mock.ExpectQuery("SELECT count").WillReturnRows(sqlmock.NewRows([]string{"total_record"}).AddRow(2)) - for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := instance.GetTransactions(chainType, params) + ts := &TransactionService{ + Query: tt.fields.Query, + Signature: tt.fields.Signature, + ActionTypeSwitcher: tt.fields.ActionTypeSwitcher, + MempoolService: tt.fields.MempoolService, + } + got, err := ts.GetTransactions(tt.args.chainType, tt.args.params) if (err != nil) != tt.wantErr { t.Errorf("TransactionService.GetTransactions() error = %v, wantErr %v", err, tt.wantErr) return @@ -156,80 +705,96 @@ func Test_TransactionService_GetTransactions(t *testing.T) { if !reflect.DeepEqual(got, tt.want) { t.Errorf("TransactionService.GetTransactions() = %v, want %v", got, tt.want) } - - if err := mock.ExpectationsWereMet(); err != nil { - t.Errorf("there were unfulfilled expectations: %s", err) - } }) } } -func Test_TransactionService_GetTransaction(t *testing.T) { - params := &model.GetTransactionRequest{ - ID: 1, +func TestTransactionService_GetTransaction(t *testing.T) { + type fields struct { + Query query.ExecutorInterface + Signature crypto.SignatureInterface + ActionTypeSwitcher transaction.TypeActionSwitcher + MempoolService service.MempoolServiceInterface } - - db, mock, err := sqlmock.New() - if err != nil { - t.Fatalf("error while opening database connection") + type args struct { + chainType contract.ChainType + params *model.GetTransactionRequest } - defer db.Close() - instance := NewTransactionService(query.NewQueryExecutor(db)) - defer resetTransactionService() - tests := []struct { name string + fields fields + args args want *model.Transaction wantErr bool }{ + { + name: "GetTransaction:failExecuteSelect", + fields: fields{ + Query: &mockGetTransactionExecutorTxsFail{}, + }, + args: args{ + chainType: &chaintype.MainChain{}, + params: &model.GetTransactionRequest{ + ID: 1, + }, + }, + wantErr: true, + want: nil, + }, + { + name: "GetTransaction:noRowExecuteSelect", + fields: fields{ + Query: &mockGetTransactionExecutorTxNoRow{}, + }, + args: args{ + chainType: &chaintype.MainChain{}, + params: &model.GetTransactionRequest{ + ID: 1, + }, + }, + wantErr: true, + want: nil, + }, { name: "GetTransaction:success", + fields: fields{ + Query: &mockGetTransactionExecutorTxSuccess{}, + }, + args: args{ + chainType: &chaintype.MainChain{}, + params: &model.GetTransactionRequest{ + ID: 1, + }, + }, + wantErr: false, want: &model.Transaction{ - ID: 1, + ID: 4545420970999433273, BlockID: 1, Height: 1, SenderAccountType: 0, - SenderAccountAddress: "abc", + SenderAccountAddress: "senderA", RecipientAccountType: 0, - RecipientAccountAddress: "abc", + RecipientAccountAddress: "recipientA", TransactionType: 1, Fee: 1, - Timestamp: 11000, - TransactionHash: []byte{}, - TransactionBodyLength: 1, - TransactionBodyBytes: []byte{}, - Signature: []byte{}, + Timestamp: 10000, + TransactionHash: []byte{1, 1}, + TransactionBodyLength: 8, + TransactionBodyBytes: []byte{1, 2, 3, 4, 5, 6, 7, 8}, + Signature: []byte{0, 0, 0, 0, 0, 0, 0}, Version: 1, }, - wantErr: false, }, } - - chainType := chaintype.GetChainType(0) - transactionQuery := query.NewTransactionQuery(chainType) - queryStr := transactionQuery.GetTransaction(params.ID) - - mock.ExpectQuery(queryStr).WillReturnRows(sqlmock.NewRows([]string{ - "id", - "block_id", - "block_height", - "sender_account_type", - "sender_account_address", - "recipient_account_type", - "recipient_account_address", - "transaction_type", - "fee", - "timestamp", - "transaction_hash", - "transaction_body_length", - "transaction_body_bytes", - "signature", - "version", - }).AddRow(1, 1, 1, 0, "abc", 0, "abc", 1, 1, 11000, []byte{}, 1, []byte{}, []byte{}, 1)) - for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := instance.GetTransaction(chainType, params) + ts := &TransactionService{ + Query: tt.fields.Query, + Signature: tt.fields.Signature, + ActionTypeSwitcher: tt.fields.ActionTypeSwitcher, + MempoolService: tt.fields.MempoolService, + } + got, err := ts.GetTransaction(tt.args.chainType, tt.args.params) if (err != nil) != tt.wantErr { t.Errorf("TransactionService.GetTransaction() error = %v, wantErr %v", err, tt.wantErr) return @@ -237,10 +802,6 @@ func Test_TransactionService_GetTransaction(t *testing.T) { if !reflect.DeepEqual(got, tt.want) { t.Errorf("TransactionService.GetTransaction() = %v, want %v", got, tt.want) } - - if err := mock.ExpectationsWereMet(); err != nil { - t.Errorf("there were unfulfilled expectations: %s", err) - } }) } } diff --git a/cmd/main.go b/cmd/main.go index 0323133b7..9a03a6e40 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -3,12 +3,15 @@ package main import ( "github.com/spf13/cobra" "github.com/zoobc/zoobc-core/cmd/account" + "github.com/zoobc/zoobc-core/cmd/transaction" + "github.com/zoobc/zoobc-core/common/crypto" "github.com/zoobc/zoobc-core/common/util" ) func main() { var rootCmd = &cobra.Command{Use: "zoobc"} - logger, _ := util.InitLogger(".log/", "debug.log") + logger, _ := util.InitLogger(".log/", "cmd.debug.log") rootCmd.AddCommand(account.GenerateAccount(logger)) + rootCmd.AddCommand(transaction.GenerateTransactionBytes(logger, &crypto.Signature{})) _ = rootCmd.Execute() } diff --git a/cmd/transaction/transactionSendMoney.go b/cmd/transaction/transactionSendMoney.go new file mode 100644 index 000000000..bb053bf61 --- /dev/null +++ b/cmd/transaction/transactionSendMoney.go @@ -0,0 +1,66 @@ +package transaction + +import ( + "encoding/hex" + "fmt" + "time" + + "github.com/zoobc/zoobc-core/common/crypto" + + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "github.com/zoobc/zoobc-core/common/model" + "github.com/zoobc/zoobc-core/common/util" +) + +func GenerateTransactionBytes(logger *logrus.Logger, + signature crypto.SignatureInterface) *cobra.Command { + var txCmd = &cobra.Command{ + Use: "tx", + Short: "tx command used to generate transaction.", + Long: `tx command generate signed transaction bytes in form of hex or []bytes + `, + Args: cobra.MinimumNArgs(1), + Run: func(cmd *cobra.Command, args []string) { + if args[0] == "generate" { + amount := int64(10000) + seed := "prune filth cleaver removable earthworm tricky sulfur citation hesitate stout snort guy" + + tx := &model.Transaction{ + Version: 1, + TransactionType: util.ConvertBytesToUint32([]byte{1, 0, 0, 0}), + Timestamp: time.Now().Unix(), + SenderAccountType: 0, + SenderAccountAddress: "BCZnSfqpP5tqFQlMTYkDeBVFWnbyVK7vLr5ORFpTjgtN", + RecipientAccountType: 0, + RecipientAccountAddress: "BCZKLvgUYZ1KKx-jtF9KoJskjVPvB9jpIjfzzI6zDW0J", + Fee: 1, + TransactionBodyLength: 8, + TransactionBody: &model.Transaction_SendMoneyTransactionBody{ + SendMoneyTransactionBody: &model.SendMoneyTransactionBody{ + Amount: amount, + }, + }, + TransactionBodyBytes: util.ConvertUint64ToBytes(uint64(amount)), + } + unsignedTxBytes, _ := util.GetTransactionBytes(tx, false) + tx.Signature = signature.Sign( + unsignedTxBytes, + tx.SenderAccountType, + tx.SenderAccountAddress, + seed, + ) + signedTxBytes, _ := util.GetTransactionBytes(tx, true) + var signedTxByteString string + for _, b := range signedTxBytes { + signedTxByteString += fmt.Sprintf("%v, ", b) + } + logger.Printf("tx-bytes:byte = %v", signedTxByteString) + logger.Printf("tx-bytes:hex = %s", hex.EncodeToString(signedTxBytes)) + } else { + logger.Error("unknown command") + } + }, + } + return txCmd +} diff --git a/common/database/migration.go b/common/database/migration.go index 20dff971e..b67b9d606 100644 --- a/common/database/migration.go +++ b/common/database/migration.go @@ -28,6 +28,7 @@ func (m *Migration) Init() error { if m.Query != nil { rows, _ := m.Query.ExecuteSelect("SELECT version FROM migration;") if rows != nil { + defer rows.Close() var version int _ = rows.Scan(&version) m.CurrentVersion = &version diff --git a/common/model/transaction.pb.go b/common/model/transaction.pb.go index 54404269f..f38e5bb93 100644 --- a/common/model/transaction.pb.go +++ b/common/model/transaction.pb.go @@ -1,13 +1,11 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // source: model/transaction.proto -package model +package model // import "github.com/zoobc/zoobc-core/common/model" -import ( - fmt "fmt" - proto "github.com/golang/protobuf/proto" - math "math" -) +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -18,7 +16,7 @@ var _ = math.Inf // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package // Transaction represent the transaction data structure stored in the database type Transaction struct { @@ -53,17 +51,16 @@ func (m *Transaction) Reset() { *m = Transaction{} } func (m *Transaction) String() string { return proto.CompactTextString(m) } func (*Transaction) ProtoMessage() {} func (*Transaction) Descriptor() ([]byte, []int) { - return fileDescriptor_8333001f09b34082, []int{0} + return fileDescriptor_transaction_64b47023b10c35be, []int{0} } - func (m *Transaction) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_Transaction.Unmarshal(m, b) } func (m *Transaction) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_Transaction.Marshal(b, m, deterministic) } -func (m *Transaction) XXX_Merge(src proto.Message) { - xxx_messageInfo_Transaction.Merge(m, src) +func (dst *Transaction) XXX_Merge(src proto.Message) { + xxx_messageInfo_Transaction.Merge(dst, src) } func (m *Transaction) XXX_Size() int { return xxx_messageInfo_Transaction.Size(m) @@ -229,15 +226,99 @@ func (m *Transaction) GetSignature() []byte { return nil } -// XXX_OneofWrappers is for the internal use of the proto package. -func (*Transaction) XXX_OneofWrappers() []interface{} { - return []interface{}{ +// XXX_OneofFuncs is for the internal use of the proto package. +func (*Transaction) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) { + return _Transaction_OneofMarshaler, _Transaction_OneofUnmarshaler, _Transaction_OneofSizer, []interface{}{ (*Transaction_EmptyTransactionBody)(nil), (*Transaction_SendMoneyTransactionBody)(nil), (*Transaction_NodeRegistrationTransactionBody)(nil), } } +func _Transaction_OneofMarshaler(msg proto.Message, b *proto.Buffer) error { + m := msg.(*Transaction) + // TransactionBody + switch x := m.TransactionBody.(type) { + case *Transaction_EmptyTransactionBody: + b.EncodeVarint(15<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.EmptyTransactionBody); err != nil { + return err + } + case *Transaction_SendMoneyTransactionBody: + b.EncodeVarint(16<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.SendMoneyTransactionBody); err != nil { + return err + } + case *Transaction_NodeRegistrationTransactionBody: + b.EncodeVarint(17<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.NodeRegistrationTransactionBody); err != nil { + return err + } + case nil: + default: + return fmt.Errorf("Transaction.TransactionBody has unexpected type %T", x) + } + return nil +} + +func _Transaction_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) { + m := msg.(*Transaction) + switch tag { + case 15: // TransactionBody.emptyTransactionBody + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(EmptyTransactionBody) + err := b.DecodeMessage(msg) + m.TransactionBody = &Transaction_EmptyTransactionBody{msg} + return true, err + case 16: // TransactionBody.sendMoneyTransactionBody + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(SendMoneyTransactionBody) + err := b.DecodeMessage(msg) + m.TransactionBody = &Transaction_SendMoneyTransactionBody{msg} + return true, err + case 17: // TransactionBody.nodeRegistrationTransactionBody + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(NodeRegistrationTransactionBody) + err := b.DecodeMessage(msg) + m.TransactionBody = &Transaction_NodeRegistrationTransactionBody{msg} + return true, err + default: + return false, nil + } +} + +func _Transaction_OneofSizer(msg proto.Message) (n int) { + m := msg.(*Transaction) + // TransactionBody + switch x := m.TransactionBody.(type) { + case *Transaction_EmptyTransactionBody: + s := proto.Size(x.EmptyTransactionBody) + n += 1 // tag and wire + n += proto.SizeVarint(uint64(s)) + n += s + case *Transaction_SendMoneyTransactionBody: + s := proto.Size(x.SendMoneyTransactionBody) + n += 2 // tag and wire + n += proto.SizeVarint(uint64(s)) + n += s + case *Transaction_NodeRegistrationTransactionBody: + s := proto.Size(x.NodeRegistrationTransactionBody) + n += 2 // tag and wire + n += proto.SizeVarint(uint64(s)) + n += s + case nil: + default: + panic(fmt.Sprintf("proto: unexpected type %T in oneof", x)) + } + return n +} + type EmptyTransactionBody struct { XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` @@ -248,17 +329,16 @@ func (m *EmptyTransactionBody) Reset() { *m = EmptyTransactionBody{} } func (m *EmptyTransactionBody) String() string { return proto.CompactTextString(m) } func (*EmptyTransactionBody) ProtoMessage() {} func (*EmptyTransactionBody) Descriptor() ([]byte, []int) { - return fileDescriptor_8333001f09b34082, []int{1} + return fileDescriptor_transaction_64b47023b10c35be, []int{1} } - func (m *EmptyTransactionBody) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_EmptyTransactionBody.Unmarshal(m, b) } func (m *EmptyTransactionBody) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_EmptyTransactionBody.Marshal(b, m, deterministic) } -func (m *EmptyTransactionBody) XXX_Merge(src proto.Message) { - xxx_messageInfo_EmptyTransactionBody.Merge(m, src) +func (dst *EmptyTransactionBody) XXX_Merge(src proto.Message) { + xxx_messageInfo_EmptyTransactionBody.Merge(dst, src) } func (m *EmptyTransactionBody) XXX_Size() int { return xxx_messageInfo_EmptyTransactionBody.Size(m) @@ -280,17 +360,16 @@ func (m *SendMoneyTransactionBody) Reset() { *m = SendMoneyTransactionBo func (m *SendMoneyTransactionBody) String() string { return proto.CompactTextString(m) } func (*SendMoneyTransactionBody) ProtoMessage() {} func (*SendMoneyTransactionBody) Descriptor() ([]byte, []int) { - return fileDescriptor_8333001f09b34082, []int{2} + return fileDescriptor_transaction_64b47023b10c35be, []int{2} } - func (m *SendMoneyTransactionBody) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_SendMoneyTransactionBody.Unmarshal(m, b) } func (m *SendMoneyTransactionBody) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_SendMoneyTransactionBody.Marshal(b, m, deterministic) } -func (m *SendMoneyTransactionBody) XXX_Merge(src proto.Message) { - xxx_messageInfo_SendMoneyTransactionBody.Merge(m, src) +func (dst *SendMoneyTransactionBody) XXX_Merge(src proto.Message) { + xxx_messageInfo_SendMoneyTransactionBody.Merge(dst, src) } func (m *SendMoneyTransactionBody) XXX_Size() int { return xxx_messageInfo_SendMoneyTransactionBody.Size(m) @@ -325,17 +404,16 @@ func (m *NodeRegistrationTransactionBody) Reset() { *m = NodeRegistratio func (m *NodeRegistrationTransactionBody) String() string { return proto.CompactTextString(m) } func (*NodeRegistrationTransactionBody) ProtoMessage() {} func (*NodeRegistrationTransactionBody) Descriptor() ([]byte, []int) { - return fileDescriptor_8333001f09b34082, []int{3} + return fileDescriptor_transaction_64b47023b10c35be, []int{3} } - func (m *NodeRegistrationTransactionBody) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_NodeRegistrationTransactionBody.Unmarshal(m, b) } func (m *NodeRegistrationTransactionBody) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_NodeRegistrationTransactionBody.Marshal(b, m, deterministic) } -func (m *NodeRegistrationTransactionBody) XXX_Merge(src proto.Message) { - xxx_messageInfo_NodeRegistrationTransactionBody.Merge(m, src) +func (dst *NodeRegistrationTransactionBody) XXX_Merge(src proto.Message) { + xxx_messageInfo_NodeRegistrationTransactionBody.Merge(dst, src) } func (m *NodeRegistrationTransactionBody) XXX_Size() int { return xxx_messageInfo_NodeRegistrationTransactionBody.Size(m) @@ -395,7 +473,7 @@ func (m *NodeRegistrationTransactionBody) GetPoown() *ProofOfOwnership { return nil } -//TODO: shall we move this to a different file? +// TODO: shall we move this to a different file? type ProofOfOwnership struct { AccountType uint32 `protobuf:"varint,1,opt,name=AccountType,proto3" json:"AccountType,omitempty"` AccountAddress string `protobuf:"bytes,2,opt,name=AccountAddress,proto3" json:"AccountAddress,omitempty"` @@ -411,17 +489,16 @@ func (m *ProofOfOwnership) Reset() { *m = ProofOfOwnership{} } func (m *ProofOfOwnership) String() string { return proto.CompactTextString(m) } func (*ProofOfOwnership) ProtoMessage() {} func (*ProofOfOwnership) Descriptor() ([]byte, []int) { - return fileDescriptor_8333001f09b34082, []int{4} + return fileDescriptor_transaction_64b47023b10c35be, []int{4} } - func (m *ProofOfOwnership) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_ProofOfOwnership.Unmarshal(m, b) } func (m *ProofOfOwnership) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_ProofOfOwnership.Marshal(b, m, deterministic) } -func (m *ProofOfOwnership) XXX_Merge(src proto.Message) { - xxx_messageInfo_ProofOfOwnership.Merge(m, src) +func (dst *ProofOfOwnership) XXX_Merge(src proto.Message) { + xxx_messageInfo_ProofOfOwnership.Merge(dst, src) } func (m *ProofOfOwnership) XXX_Size() int { return xxx_messageInfo_ProofOfOwnership.Size(m) @@ -479,17 +556,16 @@ func (m *GetTransactionRequest) Reset() { *m = GetTransactionRequest{} } func (m *GetTransactionRequest) String() string { return proto.CompactTextString(m) } func (*GetTransactionRequest) ProtoMessage() {} func (*GetTransactionRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_8333001f09b34082, []int{5} + return fileDescriptor_transaction_64b47023b10c35be, []int{5} } - func (m *GetTransactionRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_GetTransactionRequest.Unmarshal(m, b) } func (m *GetTransactionRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_GetTransactionRequest.Marshal(b, m, deterministic) } -func (m *GetTransactionRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetTransactionRequest.Merge(m, src) +func (dst *GetTransactionRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetTransactionRequest.Merge(dst, src) } func (m *GetTransactionRequest) XXX_Size() int { return xxx_messageInfo_GetTransactionRequest.Size(m) @@ -521,17 +597,16 @@ func (m *GetTransactionsRequest) Reset() { *m = GetTransactionsRequest{} func (m *GetTransactionsRequest) String() string { return proto.CompactTextString(m) } func (*GetTransactionsRequest) ProtoMessage() {} func (*GetTransactionsRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_8333001f09b34082, []int{6} + return fileDescriptor_transaction_64b47023b10c35be, []int{6} } - func (m *GetTransactionsRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_GetTransactionsRequest.Unmarshal(m, b) } func (m *GetTransactionsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_GetTransactionsRequest.Marshal(b, m, deterministic) } -func (m *GetTransactionsRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetTransactionsRequest.Merge(m, src) +func (dst *GetTransactionsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetTransactionsRequest.Merge(dst, src) } func (m *GetTransactionsRequest) XXX_Size() int { return xxx_messageInfo_GetTransactionsRequest.Size(m) @@ -556,6 +631,83 @@ func (m *GetTransactionsRequest) GetOffset() uint64 { return 0 } +type PostTransactionRequest struct { + // Signed transaction bytes + TransactionBytes []byte `protobuf:"bytes,1,opt,name=TransactionBytes,proto3" json:"TransactionBytes,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *PostTransactionRequest) Reset() { *m = PostTransactionRequest{} } +func (m *PostTransactionRequest) String() string { return proto.CompactTextString(m) } +func (*PostTransactionRequest) ProtoMessage() {} +func (*PostTransactionRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_transaction_64b47023b10c35be, []int{7} +} +func (m *PostTransactionRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_PostTransactionRequest.Unmarshal(m, b) +} +func (m *PostTransactionRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_PostTransactionRequest.Marshal(b, m, deterministic) +} +func (dst *PostTransactionRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_PostTransactionRequest.Merge(dst, src) +} +func (m *PostTransactionRequest) XXX_Size() int { + return xxx_messageInfo_PostTransactionRequest.Size(m) +} +func (m *PostTransactionRequest) XXX_DiscardUnknown() { + xxx_messageInfo_PostTransactionRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_PostTransactionRequest proto.InternalMessageInfo + +func (m *PostTransactionRequest) GetTransactionBytes() []byte { + if m != nil { + return m.TransactionBytes + } + return nil +} + +type PostTransactionResponse struct { + Transaction *Transaction `protobuf:"bytes,1,opt,name=Transaction,proto3" json:"Transaction,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *PostTransactionResponse) Reset() { *m = PostTransactionResponse{} } +func (m *PostTransactionResponse) String() string { return proto.CompactTextString(m) } +func (*PostTransactionResponse) ProtoMessage() {} +func (*PostTransactionResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_transaction_64b47023b10c35be, []int{8} +} +func (m *PostTransactionResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_PostTransactionResponse.Unmarshal(m, b) +} +func (m *PostTransactionResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_PostTransactionResponse.Marshal(b, m, deterministic) +} +func (dst *PostTransactionResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_PostTransactionResponse.Merge(dst, src) +} +func (m *PostTransactionResponse) XXX_Size() int { + return xxx_messageInfo_PostTransactionResponse.Size(m) +} +func (m *PostTransactionResponse) XXX_DiscardUnknown() { + xxx_messageInfo_PostTransactionResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_PostTransactionResponse proto.InternalMessageInfo + +func (m *PostTransactionResponse) GetTransaction() *Transaction { + if m != nil { + return m.Transaction + } + return nil +} + type GetTransactionsResponse struct { // Number of transactions in total Total uint64 `protobuf:"varint,1,opt,name=Total,proto3" json:"Total,omitempty"` @@ -572,17 +724,16 @@ func (m *GetTransactionsResponse) Reset() { *m = GetTransactionsResponse func (m *GetTransactionsResponse) String() string { return proto.CompactTextString(m) } func (*GetTransactionsResponse) ProtoMessage() {} func (*GetTransactionsResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_8333001f09b34082, []int{7} + return fileDescriptor_transaction_64b47023b10c35be, []int{9} } - func (m *GetTransactionsResponse) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_GetTransactionsResponse.Unmarshal(m, b) } func (m *GetTransactionsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_GetTransactionsResponse.Marshal(b, m, deterministic) } -func (m *GetTransactionsResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetTransactionsResponse.Merge(m, src) +func (dst *GetTransactionsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetTransactionsResponse.Merge(dst, src) } func (m *GetTransactionsResponse) XXX_Size() int { return xxx_messageInfo_GetTransactionsResponse.Size(m) @@ -627,17 +778,16 @@ func (m *PostUnconfirmedTransactionRequest) Reset() { *m = PostUnconfirm func (m *PostUnconfirmedTransactionRequest) String() string { return proto.CompactTextString(m) } func (*PostUnconfirmedTransactionRequest) ProtoMessage() {} func (*PostUnconfirmedTransactionRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_8333001f09b34082, []int{8} + return fileDescriptor_transaction_64b47023b10c35be, []int{10} } - func (m *PostUnconfirmedTransactionRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_PostUnconfirmedTransactionRequest.Unmarshal(m, b) } func (m *PostUnconfirmedTransactionRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_PostUnconfirmedTransactionRequest.Marshal(b, m, deterministic) } -func (m *PostUnconfirmedTransactionRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_PostUnconfirmedTransactionRequest.Merge(m, src) +func (dst *PostUnconfirmedTransactionRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_PostUnconfirmedTransactionRequest.Merge(dst, src) } func (m *PostUnconfirmedTransactionRequest) XXX_Size() int { return xxx_messageInfo_PostUnconfirmedTransactionRequest.Size(m) @@ -677,61 +827,67 @@ func init() { proto.RegisterType((*ProofOfOwnership)(nil), "model.ProofOfOwnership") proto.RegisterType((*GetTransactionRequest)(nil), "model.GetTransactionRequest") proto.RegisterType((*GetTransactionsRequest)(nil), "model.GetTransactionsRequest") + proto.RegisterType((*PostTransactionRequest)(nil), "model.PostTransactionRequest") + proto.RegisterType((*PostTransactionResponse)(nil), "model.PostTransactionResponse") proto.RegisterType((*GetTransactionsResponse)(nil), "model.GetTransactionsResponse") proto.RegisterType((*PostUnconfirmedTransactionRequest)(nil), "model.PostUnconfirmedTransactionRequest") } -func init() { proto.RegisterFile("model/transaction.proto", fileDescriptor_8333001f09b34082) } - -var fileDescriptor_8333001f09b34082 = []byte{ - // 782 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x55, 0x51, 0x8f, 0x22, 0x45, - 0x10, 0xde, 0x61, 0x16, 0x4e, 0x0a, 0x76, 0x8f, 0xed, 0x70, 0x4b, 0x27, 0x5e, 0x5c, 0x9c, 0x98, - 0x73, 0x72, 0xf1, 0x20, 0xc1, 0x8b, 0xf1, 0x15, 0x5c, 0x57, 0x36, 0xae, 0x2e, 0xf6, 0xa2, 0x0f, - 0x26, 0x3e, 0x0c, 0x33, 0x05, 0x4c, 0x8e, 0xe9, 0xc6, 0xe9, 0xc6, 0x0b, 0x26, 0xfe, 0x1c, 0xff, - 0x83, 0xf1, 0xc7, 0x19, 0xd3, 0x3d, 0xc3, 0x32, 0x33, 0x0c, 0xae, 0x2f, 0x84, 0xfa, 0xaa, 0xfa, - 0xfb, 0xba, 0xaa, 0xab, 0x6a, 0xa0, 0x13, 0x89, 0x00, 0x57, 0x7d, 0x15, 0x7b, 0x5c, 0x7a, 0xbe, - 0x0a, 0x05, 0xef, 0xad, 0x63, 0xa1, 0x04, 0xa9, 0x1a, 0x87, 0xf3, 0x4f, 0x0d, 0x1a, 0xd3, 0xbd, - 0x93, 0x50, 0x78, 0xf6, 0x13, 0xc6, 0x32, 0x14, 0x9c, 0x5a, 0x5d, 0xcb, 0x3d, 0x63, 0x3b, 0x93, - 0x9c, 0x43, 0xe5, 0xf6, 0x9a, 0x56, 0xba, 0x96, 0x6b, 0xb3, 0xca, 0xed, 0xb5, 0x8e, 0x1c, 0xad, - 0x84, 0xff, 0xee, 0xf6, 0x9a, 0xda, 0x06, 0xdc, 0x99, 0xe4, 0x12, 0x6a, 0x63, 0x0c, 0x17, 0x4b, - 0x45, 0x4f, 0x0d, 0x45, 0x6a, 0x91, 0xcf, 0xe0, 0xe2, 0x01, 0x79, 0x80, 0xf1, 0xd0, 0xf7, 0xc5, - 0x86, 0xab, 0xe9, 0x76, 0x8d, 0xb4, 0x6a, 0x42, 0x0e, 0x1d, 0x64, 0x00, 0xed, 0x1c, 0x38, 0x0c, - 0x82, 0x18, 0xa5, 0xa4, 0xb5, 0xae, 0xe5, 0xd6, 0x59, 0xa9, 0x4f, 0x9f, 0x61, 0xe8, 0x87, 0xeb, - 0x10, 0xb9, 0xca, 0x8a, 0x3c, 0x33, 0x22, 0xa5, 0x3e, 0xf2, 0x25, 0x74, 0x8a, 0xf8, 0x4e, 0xea, - 0x03, 0x23, 0x75, 0xcc, 0x4d, 0x5c, 0x78, 0x9e, 0x29, 0x9d, 0x11, 0xaa, 0x1b, 0xa1, 0x22, 0x4c, - 0x5a, 0x60, 0xdf, 0x20, 0x52, 0x30, 0x75, 0xd2, 0x7f, 0xc9, 0x4b, 0xa8, 0x4f, 0xc3, 0x08, 0xa5, - 0xf2, 0xa2, 0x35, 0x6d, 0x18, 0x7c, 0x0f, 0x14, 0x98, 0xc7, 0x9e, 0x5c, 0xd2, 0x66, 0xd7, 0x72, - 0x9b, 0xac, 0x08, 0x93, 0xb7, 0xf0, 0x22, 0x03, 0x8d, 0x44, 0xb0, 0xbd, 0x43, 0xbe, 0x50, 0x4b, - 0x7a, 0x66, 0x6e, 0x52, 0xee, 0xd4, 0x75, 0x2a, 0x38, 0x46, 0x5b, 0x85, 0x92, 0x9e, 0x1b, 0x91, - 0x52, 0x1f, 0xf9, 0x01, 0xda, 0x18, 0xad, 0xd5, 0xb6, 0xe0, 0xa4, 0xcf, 0xbb, 0x96, 0xdb, 0x18, - 0x7c, 0xd8, 0x33, 0xfd, 0xd4, 0xfb, 0xba, 0x24, 0x64, 0x7c, 0xc2, 0x4a, 0x8f, 0x92, 0x5f, 0x80, - 0x4a, 0xe4, 0xc1, 0x77, 0x82, 0xe3, 0x01, 0x6d, 0xcb, 0xd0, 0x5e, 0xa5, 0xb4, 0x0f, 0x47, 0xc2, - 0xc6, 0x27, 0xec, 0x28, 0x05, 0x89, 0xe1, 0x8a, 0x8b, 0x00, 0x19, 0x2e, 0x42, 0xa9, 0x62, 0xcf, - 0xbc, 0x46, 0x41, 0xe5, 0xc2, 0xa8, 0xbc, 0x4a, 0x55, 0xbe, 0xff, 0xef, 0xe8, 0xf1, 0x09, 0x7b, - 0x8a, 0x50, 0xbf, 0xeb, 0x43, 0xb8, 0xe0, 0x9e, 0xda, 0xc4, 0x48, 0x89, 0x29, 0xe7, 0x1e, 0x18, - 0x5d, 0xe4, 0xde, 0x55, 0x1f, 0x70, 0x2e, 0xa1, 0x5d, 0x56, 0x33, 0x67, 0x00, 0xf4, 0x58, 0xd2, - 0x7a, 0xc0, 0x86, 0x91, 0xee, 0x44, 0x33, 0xa3, 0x36, 0x4b, 0x2d, 0xe7, 0xaf, 0x0a, 0x5c, 0x3d, - 0x91, 0x03, 0xf9, 0x04, 0xce, 0x74, 0xc8, 0x64, 0x33, 0x5b, 0x85, 0xfe, 0xb7, 0xb8, 0x35, 0x14, - 0x4d, 0x96, 0x07, 0x49, 0x17, 0x1a, 0xd9, 0xf9, 0xa9, 0x98, 0x66, 0xca, 0x42, 0xe4, 0x15, 0x9c, - 0x17, 0xa6, 0xc5, 0x36, 0xd3, 0x52, 0x40, 0x49, 0x0f, 0x48, 0xf6, 0x3a, 0x99, 0xc5, 0x60, 0xb3, - 0x12, 0x8f, 0x56, 0xd6, 0x57, 0xd9, 0x91, 0x56, 0x0d, 0x69, 0x16, 0xd2, 0x19, 0xdc, 0x09, 0xff, - 0x1d, 0x06, 0x23, 0x6f, 0xe5, 0x71, 0x1f, 0xcd, 0x46, 0xb0, 0x59, 0x1e, 0x24, 0x6f, 0xa0, 0x3a, - 0x11, 0xe2, 0x3d, 0x37, 0xb3, 0xdf, 0x18, 0x74, 0xd2, 0x27, 0x9e, 0xc4, 0x42, 0xcc, 0xef, 0xe7, - 0xf7, 0xef, 0x39, 0xc6, 0x72, 0x19, 0xae, 0x59, 0x12, 0xe5, 0xfc, 0x6d, 0x41, 0xab, 0xe8, 0x2b, - 0x56, 0xc1, 0xfa, 0x3f, 0x55, 0xa8, 0x94, 0x56, 0xe1, 0x25, 0xd4, 0x67, 0x7a, 0x3b, 0x9a, 0x51, - 0xb6, 0x93, 0xb6, 0x78, 0x04, 0xb4, 0x4e, 0x62, 0x64, 0xb7, 0x66, 0x16, 0xd2, 0xe7, 0xe5, 0x63, - 0x5b, 0x55, 0x93, 0xf3, 0x8f, 0x80, 0xf3, 0x29, 0xbc, 0xf8, 0x06, 0x55, 0xe6, 0xa5, 0x19, 0xfe, - 0xba, 0x41, 0xa9, 0xd2, 0x9d, 0x6d, 0xed, 0x76, 0xb6, 0x73, 0x03, 0x97, 0xf9, 0x40, 0xb9, 0x8b, - 0x6c, 0x43, 0xf5, 0x2e, 0x8c, 0x42, 0x95, 0x26, 0x99, 0x18, 0xba, 0xd1, 0xee, 0xe7, 0x73, 0x89, - 0xca, 0xa4, 0x75, 0xca, 0x52, 0xcb, 0xf9, 0x03, 0x3a, 0x07, 0x3c, 0x72, 0x2d, 0xb8, 0x44, 0x4d, - 0x34, 0x15, 0xca, 0x5b, 0x19, 0xa2, 0x53, 0x96, 0x18, 0x1a, 0xfd, 0xca, 0x34, 0x6c, 0xd2, 0x49, - 0x89, 0x41, 0xbe, 0x80, 0x66, 0x96, 0x83, 0xda, 0x5d, 0xdb, 0x6d, 0x0c, 0x48, 0xfa, 0x54, 0xd9, - 0x7c, 0x72, 0x71, 0xce, 0x9f, 0x16, 0x7c, 0x3c, 0x11, 0x52, 0xfd, 0xc8, 0x7d, 0xc1, 0xe7, 0x61, - 0x1c, 0x61, 0x50, 0x92, 0xfc, 0xdb, 0xdc, 0x97, 0xcd, 0xdc, 0xa7, 0x9c, 0x3c, 0xf7, 0x01, 0x7c, - 0x0d, 0xad, 0x61, 0x1c, 0x87, 0xbf, 0x79, 0xab, 0xfd, 0x7e, 0x4e, 0x2e, 0x7d, 0x80, 0x93, 0x8f, - 0x00, 0x6e, 0x10, 0x27, 0x18, 0xeb, 0x0d, 0x99, 0x7e, 0x05, 0x33, 0xc8, 0xe8, 0xf5, 0xcf, 0xee, - 0x22, 0x54, 0xcb, 0xcd, 0xac, 0xe7, 0x8b, 0xa8, 0xff, 0xbb, 0x10, 0x33, 0x3f, 0xf9, 0x7d, 0xe3, - 0x8b, 0x18, 0xfb, 0xbe, 0x88, 0x22, 0xc1, 0xfb, 0xe6, 0x42, 0xb3, 0x9a, 0xf9, 0x2c, 0x7f, 0xfe, - 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7b, 0xf6, 0xee, 0xd2, 0xb1, 0x07, 0x00, 0x00, +func init() { + proto.RegisterFile("model/transaction.proto", fileDescriptor_transaction_64b47023b10c35be) +} + +var fileDescriptor_transaction_64b47023b10c35be = []byte{ + // 810 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x56, 0x51, 0x6f, 0xe3, 0x44, + 0x10, 0xae, 0xe3, 0xa6, 0x47, 0x27, 0x6d, 0xaf, 0x5d, 0xe5, 0x9a, 0x95, 0x38, 0xd1, 0x60, 0xa1, + 0x23, 0x3a, 0x71, 0xa9, 0x14, 0x4e, 0x88, 0xd7, 0x86, 0x52, 0x52, 0x51, 0x68, 0xd8, 0x16, 0x1e, + 0x90, 0x78, 0x70, 0xec, 0x49, 0x62, 0x5d, 0xbc, 0x1b, 0xbc, 0x1b, 0x4e, 0x41, 0xe2, 0xe7, 0xf0, + 0x1f, 0x10, 0x3f, 0x0e, 0xa1, 0x1d, 0x3b, 0xed, 0xc6, 0x71, 0x38, 0x74, 0x2f, 0x55, 0xe7, 0x9b, + 0xd9, 0xef, 0xdb, 0x99, 0x9d, 0x19, 0x07, 0x5a, 0xa9, 0x8a, 0x71, 0x76, 0x6e, 0xb2, 0x50, 0xea, + 0x30, 0x32, 0x89, 0x92, 0xdd, 0x79, 0xa6, 0x8c, 0x62, 0x75, 0x72, 0x04, 0xff, 0xec, 0x41, 0xe3, + 0xfe, 0xd1, 0xc9, 0x38, 0x3c, 0xf9, 0x09, 0x33, 0x9d, 0x28, 0xc9, 0xbd, 0xb6, 0xd7, 0x39, 0x14, + 0x2b, 0x93, 0x1d, 0x41, 0xed, 0xfa, 0x92, 0xd7, 0xda, 0x5e, 0xc7, 0x17, 0xb5, 0xeb, 0x4b, 0x1b, + 0xd9, 0x9f, 0xa9, 0xe8, 0xcd, 0xf5, 0x25, 0xf7, 0x09, 0x5c, 0x99, 0xec, 0x14, 0xf6, 0x06, 0x98, + 0x4c, 0xa6, 0x86, 0xef, 0x12, 0x45, 0x61, 0xb1, 0xcf, 0xe0, 0xe4, 0x0e, 0x65, 0x8c, 0xd9, 0x45, + 0x14, 0xa9, 0x85, 0x34, 0xf7, 0xcb, 0x39, 0xf2, 0x3a, 0x85, 0x6c, 0x3a, 0x58, 0x0f, 0x9a, 0x6b, + 0xe0, 0x45, 0x1c, 0x67, 0xa8, 0x35, 0xdf, 0x6b, 0x7b, 0x9d, 0x7d, 0x51, 0xe9, 0xb3, 0x67, 0x04, + 0x46, 0xc9, 0x3c, 0x41, 0x69, 0x5c, 0x91, 0x27, 0x24, 0x52, 0xe9, 0x63, 0x5f, 0x42, 0xab, 0x8c, + 0xaf, 0xa4, 0x3e, 0x20, 0xa9, 0x6d, 0x6e, 0xd6, 0x81, 0xa7, 0x4e, 0xe9, 0x48, 0x68, 0x9f, 0x84, + 0xca, 0x30, 0x3b, 0x06, 0xff, 0x0a, 0x91, 0x03, 0xd5, 0xc9, 0xfe, 0xcb, 0x9e, 0xc3, 0xfe, 0x7d, + 0x92, 0xa2, 0x36, 0x61, 0x3a, 0xe7, 0x0d, 0xc2, 0x1f, 0x81, 0x12, 0xf3, 0x20, 0xd4, 0x53, 0x7e, + 0xd0, 0xf6, 0x3a, 0x07, 0xa2, 0x0c, 0xb3, 0xd7, 0xf0, 0xcc, 0x81, 0xfa, 0x2a, 0x5e, 0xde, 0xa0, + 0x9c, 0x98, 0x29, 0x3f, 0xa4, 0x9b, 0x54, 0x3b, 0x6d, 0x9d, 0x4a, 0x8e, 0xfe, 0xd2, 0xa0, 0xe6, + 0x47, 0x24, 0x52, 0xe9, 0x63, 0x3f, 0x40, 0x13, 0xd3, 0xb9, 0x59, 0x96, 0x9c, 0xfc, 0x69, 0xdb, + 0xeb, 0x34, 0x7a, 0x1f, 0x76, 0xa9, 0x9f, 0xba, 0x5f, 0x57, 0x84, 0x0c, 0x76, 0x44, 0xe5, 0x51, + 0xf6, 0x0b, 0x70, 0x8d, 0x32, 0xfe, 0x4e, 0x49, 0xdc, 0xa0, 0x3d, 0x26, 0xda, 0xb3, 0x82, 0xf6, + 0x6e, 0x4b, 0xd8, 0x60, 0x47, 0x6c, 0xa5, 0x60, 0x19, 0x9c, 0x49, 0x15, 0xa3, 0xc0, 0x49, 0xa2, + 0x4d, 0x16, 0xd2, 0x6b, 0x94, 0x54, 0x4e, 0x48, 0xe5, 0x45, 0xa1, 0xf2, 0xfd, 0x7f, 0x47, 0x0f, + 0x76, 0xc4, 0xbb, 0x08, 0xed, 0xbb, 0xde, 0x25, 0x13, 0x19, 0x9a, 0x45, 0x86, 0x9c, 0x51, 0x39, + 0x1f, 0x81, 0xfe, 0xc9, 0xda, 0xbb, 0xda, 0x03, 0xc1, 0x29, 0x34, 0xab, 0x6a, 0x16, 0xf4, 0x80, + 0x6f, 0x4b, 0xda, 0x0e, 0xd8, 0x45, 0x6a, 0x3b, 0x91, 0x66, 0xd4, 0x17, 0x85, 0x15, 0xfc, 0x55, + 0x83, 0xb3, 0x77, 0xe4, 0xc0, 0x3e, 0x81, 0x43, 0x1b, 0x32, 0x5c, 0x8c, 0x66, 0x49, 0xf4, 0x2d, + 0x2e, 0x89, 0xe2, 0x40, 0xac, 0x83, 0xac, 0x0d, 0x0d, 0x77, 0x7e, 0x6a, 0xd4, 0x4c, 0x2e, 0xc4, + 0x5e, 0xc0, 0x51, 0x69, 0x5a, 0x7c, 0x9a, 0x96, 0x12, 0xca, 0xba, 0xc0, 0xdc, 0xeb, 0x38, 0x8b, + 0xc1, 0x17, 0x15, 0x1e, 0xab, 0x6c, 0xaf, 0xb2, 0x22, 0xad, 0x13, 0xa9, 0x0b, 0xd9, 0x0c, 0x6e, + 0x54, 0xf4, 0x06, 0xe3, 0x7e, 0x38, 0x0b, 0x65, 0x84, 0xb4, 0x11, 0x7c, 0xb1, 0x0e, 0xb2, 0x57, + 0x50, 0x1f, 0x2a, 0xf5, 0x56, 0xd2, 0xec, 0x37, 0x7a, 0xad, 0xe2, 0x89, 0x87, 0x99, 0x52, 0xe3, + 0xdb, 0xf1, 0xed, 0x5b, 0x89, 0x99, 0x9e, 0x26, 0x73, 0x91, 0x47, 0x05, 0x7f, 0x7b, 0x70, 0x5c, + 0xf6, 0x95, 0xab, 0xe0, 0xfd, 0x9f, 0x2a, 0xd4, 0x2a, 0xab, 0xf0, 0x1c, 0xf6, 0x47, 0x76, 0x3b, + 0xd2, 0x28, 0xfb, 0x79, 0x5b, 0x3c, 0x00, 0x56, 0x27, 0x37, 0xdc, 0xad, 0xe9, 0x42, 0xf6, 0xbc, + 0x7e, 0x68, 0xab, 0x7a, 0x7e, 0xfe, 0x01, 0x08, 0x3e, 0x85, 0x67, 0xdf, 0xa0, 0x71, 0x5e, 0x5a, + 0xe0, 0xaf, 0x0b, 0xd4, 0xa6, 0xd8, 0xd9, 0xde, 0x6a, 0x67, 0x07, 0x57, 0x70, 0xba, 0x1e, 0xa8, + 0x57, 0x91, 0x4d, 0xa8, 0xdf, 0x24, 0x69, 0x62, 0x8a, 0x24, 0x73, 0xc3, 0x36, 0xda, 0xed, 0x78, + 0xac, 0xd1, 0x50, 0x5a, 0xbb, 0xa2, 0xb0, 0x82, 0x4b, 0x38, 0x1d, 0x2a, 0x5d, 0xa5, 0xf8, 0x12, + 0x8e, 0xdd, 0x8e, 0xa3, 0xad, 0x92, 0x77, 0xd8, 0x06, 0x1e, 0xdc, 0x42, 0x6b, 0x83, 0x45, 0xcf, + 0x95, 0xd4, 0xc8, 0x5e, 0xaf, 0x7d, 0x95, 0x88, 0xa1, 0xd1, 0x63, 0xc5, 0x1b, 0xba, 0x07, 0xdc, + 0xb0, 0xe0, 0x0f, 0x68, 0x6d, 0xa4, 0x57, 0x10, 0x36, 0xa1, 0x7e, 0xaf, 0x4c, 0x38, 0x23, 0xaa, + 0x5d, 0x91, 0x1b, 0x16, 0xfd, 0x8a, 0xe6, 0x28, 0x6f, 0xf0, 0xdc, 0x60, 0x5f, 0xc0, 0x81, 0xcb, + 0xc1, 0xfd, 0xb6, 0xbf, 0x45, 0x7d, 0x2d, 0x2e, 0xf8, 0xd3, 0x83, 0x8f, 0x6d, 0x42, 0x3f, 0xca, + 0x48, 0xc9, 0x71, 0x92, 0xa5, 0x18, 0x57, 0x54, 0xe8, 0xbd, 0x52, 0xb3, 0x75, 0xbd, 0xc8, 0xb2, + 0xe4, 0xb7, 0x70, 0xf6, 0xf8, 0xd9, 0xc8, 0x2f, 0xbd, 0x81, 0xb3, 0x8f, 0x00, 0xae, 0x10, 0x87, + 0x98, 0xd9, 0x32, 0x17, 0x1f, 0x67, 0x07, 0xe9, 0xbf, 0xfc, 0xb9, 0x33, 0x49, 0xcc, 0x74, 0x31, + 0xea, 0x46, 0x2a, 0x3d, 0xff, 0x5d, 0xa9, 0x51, 0x94, 0xff, 0x7d, 0x15, 0xa9, 0x0c, 0xcf, 0x23, + 0x95, 0xa6, 0x4a, 0x9e, 0xd3, 0x85, 0x46, 0x7b, 0xf4, 0x6b, 0xe1, 0xf3, 0x7f, 0x03, 0x00, 0x00, + 0xff, 0xff, 0x29, 0x2c, 0x41, 0xef, 0x48, 0x08, 0x00, 0x00, } diff --git a/common/query/accountBalanceQuery.go b/common/query/accountBalanceQuery.go index bd93b7eba..adbb7f25a 100644 --- a/common/query/accountBalanceQuery.go +++ b/common/query/accountBalanceQuery.go @@ -16,7 +16,7 @@ type ( } // AccountBalanceQueryInterface interface that implemented by AccountBalanceQuery AccountBalanceQueryInterface interface { - GetAccountBalanceByAccountID() string + GetAccountBalanceByAccountID(accountID []byte) (string, interface{}) InsertAccountBalance(accountBalance *model.AccountBalance) (str string, args []interface{}) AddAccountBalance(balance int64, causedFields map[string]interface{}) [][]interface{} AddAccountSpendableBalance(balance int64, causedFields map[string]interface{}) (str string, args []interface{}) @@ -39,25 +39,45 @@ func NewAccountBalanceQuery() *AccountBalanceQuery { TableName: "account_balance", } } -func (q *AccountBalanceQuery) GetAccountBalanceByAccountID() string { - return fmt.Sprintf(`SELECT %s FROM %s WHERE account_id = ? AND latest = 1`, strings.Join(q.Fields, ","), q.TableName) +func (q *AccountBalanceQuery) GetAccountBalanceByAccountID(accountID []byte) (query string, args interface{}) { + return fmt.Sprintf(`SELECT %s FROM %s WHERE account_id = ? AND latest = 1`, + strings.Join(q.Fields, ","), q.TableName), accountID } func (q *AccountBalanceQuery) AddAccountBalance(balance int64, causedFields map[string]interface{}) [][]interface{} { - var queries [][]interface{} - updateVersionQuery := fmt.Sprintf("UPDATE %s SET latest = false WHERE account_id = ? AND block_height = %d - 1 AND latest = true", - q.TableName, causedFields["block_height"]) + var ( + queries [][]interface{} + updateVersionQuery string + ) + // insert account if account not in account balance yet + insertBalanceQuery := fmt.Sprintf("INSERT INTO %s (account_id, block_height, spendable_balance, balance, pop_revenue, latest) "+ + "SELECT ?, %d, 0, 0, 0, 1 WHERE NOT EXISTS (SELECT account_id FROM %s WHERE account_id = ?)", q.TableName, + causedFields["block_height"], q.TableName) + // update or insert new account_balance row updateBalanceQuery := fmt.Sprintf("INSERT INTO %s (account_id, block_height, spendable_balance, balance, pop_revenue, latest) "+ - "VALUES (?, %d, %d, %d, 0, true) ON CONFLICT(account_id, block_height) DO UPDATE SET spendable_balance = spendable_balance + %d, "+ - "balance = balance + %d", q.TableName, causedFields["block_height"], balance, balance, balance, balance) + "SELECT account_id, %d, spendable_balance + %d, balance + %d, pop_revenue, latest FROM account_balance WHERE account_id = ? AND "+ + "latest = 1 ON CONFLICT(account_id, block_height) DO UPDATE SET (spendable_balance, balance) = (SELECT "+ + "spendable_balance + %d, balance + %d FROM %s WHERE account_id = ? AND latest = 1)", + q.TableName, causedFields["block_height"], balance, balance, balance, balance, q.TableName) + queries = append(queries, []interface{}{ - updateVersionQuery, causedFields["account_id"], + insertBalanceQuery, causedFields["account_id"], causedFields["account_id"], }, []interface{}{ - updateBalanceQuery, causedFields["account_id"], + updateBalanceQuery, causedFields["account_id"], causedFields["account_id"], }, ) + if causedFields["block_height"].(uint32) != 0 { + // set previous version record to latest = false + updateVersionQuery = fmt.Sprintf("UPDATE %s SET latest = false WHERE account_id = ? AND block_height != %d AND latest = true", + q.TableName, causedFields["block_height"]) + queries = append(queries, + []interface{}{ + updateVersionQuery, causedFields["account_id"], + }, + ) + } return queries } diff --git a/common/query/accountBalanceQuery_test.go b/common/query/accountBalanceQuery_test.go index 387f96bef..8aef23aff 100644 --- a/common/query/accountBalanceQuery_test.go +++ b/common/query/accountBalanceQuery_test.go @@ -4,6 +4,8 @@ import ( "reflect" "testing" + "github.com/DATA-DOG/go-sqlmock" + "github.com/zoobc/zoobc-core/common/model" ) @@ -40,7 +42,7 @@ var mockAccountBalanceQuery = &AccountBalanceQuery{ var causedFields = map[string]interface{}{ "account_id": []byte{1}, - "block_height": 1, + "block_height": uint32(1), } var mockAccountBalance = &model.AccountBalance{ @@ -54,12 +56,16 @@ var mockAccountBalance = &model.AccountBalance{ func TestAccountBalanceQuery_GetAccountBalanceByAccountID(t *testing.T) { t.Run("GetAccountBalanceByAccountID", func(t *testing.T) { - res := mockAccountBalanceQuery.GetAccountBalanceByAccountID() + res, arg := mockAccountBalanceQuery.GetAccountBalanceByAccountID([]byte{1}) want := "SELECT account_id,block_height,spendable_balance,balance,pop_revenue,latest " + "FROM account_balance WHERE account_id = ? AND latest = 1" + wantArg := []byte{1} if res != want { t.Errorf("string not match:\nget: %s\nwant: %s", res, want) } + if !reflect.DeepEqual(arg, wantArg) { + t.Errorf("argument not match:\nget: %v\nwant: %v", arg, wantArg) + } }) } @@ -69,12 +75,18 @@ func TestAccountBalanceQuery_AddAccountBalance(t *testing.T) { res := mockAccountBalanceQuery.AddAccountBalance(100, causedFields) var want [][]interface{} want = append(want, []interface{}{ - "UPDATE account_balance SET latest = false WHERE account_id = ? AND block_height = 1 - 1 AND latest = true", - causedFields["account_id"], + "INSERT INTO account_balance (account_id, block_height, spendable_balance, balance, pop_revenue, latest) SELECT ?, " + + "1, 0, 0, 0, 1 WHERE NOT EXISTS (SELECT account_id FROM account_balance WHERE account_id = ?)", + causedFields["account_id"], causedFields["account_id"], }, []interface{}{ - "INSERT INTO account_balance (account_id, block_height, spendable_balance, balance, pop_revenue, latest) VALUES " + - "(?, 1, 100, 100, 0, true) ON CONFLICT(account_id, block_height) DO UPDATE SET spendable_balance = spendable_balance " + - "+ 100, balance = balance + 100", causedFields["account_id"], + "INSERT INTO account_balance (account_id, block_height, spendable_balance, balance, pop_revenue, latest) SELECT account_id, " + + "1, spendable_balance + 100, balance + 100, pop_revenue, latest FROM account_balance WHERE account_id = ? AND latest = 1 " + + "ON CONFLICT(account_id, block_height) DO UPDATE SET (spendable_balance, balance) = (SELECT spendable_balance + 100, balance " + + "+ 100 FROM account_balance WHERE account_id = ? AND latest = 1)", + causedFields["account_id"], causedFields["account_id"], + }, []interface{}{ + "UPDATE account_balance SET latest = false WHERE account_id = ? AND block_height != 1 AND latest = true", + causedFields["account_id"], }) if !reflect.DeepEqual(res, want) { t.Errorf("string not match:\nget: %s\nwant: %s", res, want) @@ -129,3 +141,20 @@ func TestAccountBalanceQuery_ExtractModel(t *testing.T) { } }) } + +func TestAccountBalanceQuery_BuildModel(t *testing.T) { + t.Run("AccountBalanceQuery-BuildModel:success", func(t *testing.T) { + db, mock, _ := sqlmock.New() + defer db.Close() + mock.ExpectQuery("foo").WillReturnRows(sqlmock.NewRows([]string{ + "AccountID", "BlockHeight", "SpendableBalance", "Balance", "PopRevenue", "Latest"}). + AddRow(mockAccountBalance.AccountID, mockAccountBalance.BlockHeight, mockAccountBalance.SpendableBalance, + mockAccountBalance.Balance, mockAccountBalance.PopRevenue, mockAccountBalance.Latest)) + rows, _ := db.Query("foo") + var tempAccount []*model.AccountBalance + res := mockAccountBalanceQuery.BuildModel(tempAccount, rows) + if !reflect.DeepEqual(res[0], mockAccountBalance) { + t.Errorf("arguments returned wrong: get: %v\nwant: %v", res, mockAccount) + } + }) +} diff --git a/common/query/accountQuery.go b/common/query/accountQuery.go index 613168bdc..3d965e488 100644 --- a/common/query/accountQuery.go +++ b/common/query/accountQuery.go @@ -16,7 +16,7 @@ type ( AccountQueryInterface interface { GetAccountByID(accountID []byte) (str string, args []interface{}) - GetAccountByIDs(ids [][]byte) (str string, args [][]byte) + GetAccountByIDs(ids [][]byte) (str string, args []interface{}) InsertAccount(account *model.Account) (str string, args []interface{}) ExtractModel(account *model.Account) []interface{} BuildModel(accounts []*model.Account, rows *sql.Rows) []*model.Account @@ -39,13 +39,16 @@ func (aq *AccountQuery) GetAccountByID(accountID []byte) (str string, args []int } // GetAccountByIDs return query string to get accounts by multiple IDs -func (aq *AccountQuery) GetAccountByIDs(ids [][]byte) (str string, args [][]byte) { +func (aq *AccountQuery) GetAccountByIDs(ids [][]byte) (str string, args []interface{}) { + for _, id := range ids { + args = append(args, id) + } return fmt.Sprintf( "SELECT %s FROM %s WHERE id in (%s)", strings.Join(aq.Fields, ","), aq.TableName, fmt.Sprintf("? %s", strings.Repeat(",?", len(ids)-1)), - ), ids + ), args } func (aq *AccountQuery) InsertAccount(account *model.Account) (str string, args []interface{}) { diff --git a/common/query/accountQuery_test.go b/common/query/accountQuery_test.go index e37e3677b..ae12f4952 100644 --- a/common/query/accountQuery_test.go +++ b/common/query/accountQuery_test.go @@ -60,8 +60,8 @@ func TestAccountQuery_GetAccountByIDs(t *testing.T) { argIn := [][]byte{mockAccount.ID, {2}} q, args := mockAccountQuery.GetAccountByIDs(argIn) wantQ := "SELECT id,account_type,address FROM account WHERE id in (? ,?)" - wantArg := [][]byte{ - mockAccount.ID, {2}, + wantArg := []interface{}{ + mockAccount.ID, []byte{2}, } if q != wantQ { t.Errorf("query returned wrong: get: %s\nwant: %s", q, wantQ) diff --git a/common/query/executor.go b/common/query/executor.go index f77bb007f..984e0c67f 100644 --- a/common/query/executor.go +++ b/common/query/executor.go @@ -76,12 +76,13 @@ error will be nil otherwise. */ func (qe *Executor) ExecuteStatement(query string, args ...interface{}) (sql.Result, error) { qe.Lock() - defer qe.Unlock() stmt, err := qe.Db.Prepare(query) if err != nil { return nil, err } + defer stmt.Close() + defer qe.Unlock() result, err := stmt.Exec(args...) if err != nil { @@ -160,6 +161,7 @@ func (qe *Executor) ExecuteTransactions(queries [][]interface{}) error { // note: rollback is called in this function if commit fail, to avoid locking complication func (qe *Executor) CommitTx() error { err := qe.Tx.Commit() + defer qe.Unlock() // either success or not struct access should be unlocked once done if err != nil { _ = qe.Tx.Rollback() diff --git a/common/schema b/common/schema index 6c0dd4f61..6930ffec0 160000 --- a/common/schema +++ b/common/schema @@ -1 +1 @@ -Subproject commit 6c0dd4f611c1eac56aabc42255864cb6e06cd738 +Subproject commit 6930ffec0cd9fea649d0ccdedcd716b93c31c1ed diff --git a/common/service/transaction.pb.go b/common/service/transaction.pb.go index 9ce28a3bf..0077b3ca8 100644 --- a/common/service/transaction.pb.go +++ b/common/service/transaction.pb.go @@ -1,18 +1,17 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // source: service/transaction.proto -package service +package service // import "github.com/zoobc/zoobc-core/common/service" + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" +import model "github.com/zoobc/zoobc-core/common/model" +import _ "google.golang.org/genproto/googleapis/api/annotations" import ( - context "context" - fmt "fmt" - proto "github.com/golang/protobuf/proto" - model "github.com/zoobc/zoobc-core/common/model" - _ "google.golang.org/genproto/googleapis/api/annotations" + context "golang.org/x/net/context" grpc "google.golang.org/grpc" - codes "google.golang.org/grpc/codes" - status "google.golang.org/grpc/status" - math "math" ) // Reference imports to suppress errors if they are not otherwise used. @@ -24,27 +23,7 @@ var _ = math.Inf // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package - -func init() { proto.RegisterFile("service/transaction.proto", fileDescriptor_e672968ede58c6fc) } - -var fileDescriptor_e672968ede58c6fc = []byte{ - // 222 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x2c, 0x4e, 0x2d, 0x2a, - 0xcb, 0x4c, 0x4e, 0xd5, 0x2f, 0x29, 0x4a, 0xcc, 0x2b, 0x4e, 0x4c, 0x2e, 0xc9, 0xcc, 0xcf, 0xd3, - 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x87, 0x4a, 0x49, 0x89, 0xe7, 0xe6, 0xa7, 0xa4, 0xe6, - 0x60, 0xaa, 0x90, 0x92, 0x49, 0xcf, 0xcf, 0x4f, 0xcf, 0x49, 0xd5, 0x4f, 0x2c, 0xc8, 0xd4, 0x4f, - 0xcc, 0xcb, 0xcb, 0x2f, 0x49, 0x04, 0x49, 0x16, 0x43, 0x64, 0x8d, 0x7e, 0x33, 0x72, 0x09, 0x85, - 0x20, 0xf4, 0x04, 0x43, 0x4c, 0x13, 0xaa, 0xe4, 0xe2, 0x77, 0x4f, 0x2d, 0x41, 0x92, 0x28, 0x16, - 0x92, 0xd5, 0x03, 0xdb, 0xa0, 0x87, 0x26, 0x1e, 0x94, 0x5a, 0x58, 0x9a, 0x5a, 0x5c, 0x22, 0x25, - 0x87, 0x4b, 0xba, 0xb8, 0x20, 0x3f, 0xaf, 0x38, 0x55, 0x49, 0xbd, 0xe9, 0xf2, 0x93, 0xc9, 0x4c, - 0x8a, 0x42, 0xf2, 0xfa, 0x65, 0x86, 0xc8, 0xae, 0xd4, 0x47, 0xb7, 0x27, 0x8b, 0x8b, 0x0f, 0x55, - 0x48, 0x48, 0x06, 0xab, 0xd1, 0x30, 0x8b, 0x85, 0xa0, 0xb2, 0x48, 0x52, 0x4a, 0x6a, 0x60, 0xcb, - 0x14, 0x84, 0xe4, 0xf0, 0x5b, 0xe6, 0xa4, 0x13, 0xa5, 0x95, 0x9e, 0x59, 0x92, 0x51, 0x9a, 0xa4, - 0x97, 0x9c, 0x9f, 0xab, 0x5f, 0x95, 0x9f, 0x9f, 0x94, 0x0c, 0x21, 0x75, 0x93, 0xf3, 0x8b, 0x52, - 0xf5, 0x93, 0xf3, 0x73, 0x73, 0xf3, 0xf3, 0xf4, 0xa1, 0x41, 0x9c, 0xc4, 0x06, 0x0e, 0x32, 0x63, - 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0x1c, 0xa5, 0xa1, 0x1e, 0x8f, 0x01, 0x00, 0x00, -} +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package // Reference imports to suppress errors if they are not otherwise used. var _ context.Context @@ -60,6 +39,7 @@ const _ = grpc.SupportPackageIsVersion4 type TransactionServiceClient interface { GetTransactions(ctx context.Context, in *model.GetTransactionsRequest, opts ...grpc.CallOption) (*model.GetTransactionsResponse, error) GetTransaction(ctx context.Context, in *model.GetTransactionRequest, opts ...grpc.CallOption) (*model.Transaction, error) + PostTransaction(ctx context.Context, in *model.PostTransactionRequest, opts ...grpc.CallOption) (*model.PostTransactionResponse, error) } type transactionServiceClient struct { @@ -88,21 +68,20 @@ func (c *transactionServiceClient) GetTransaction(ctx context.Context, in *model return out, nil } +func (c *transactionServiceClient) PostTransaction(ctx context.Context, in *model.PostTransactionRequest, opts ...grpc.CallOption) (*model.PostTransactionResponse, error) { + out := new(model.PostTransactionResponse) + err := c.cc.Invoke(ctx, "/service.TransactionService/PostTransaction", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // TransactionServiceServer is the server API for TransactionService service. type TransactionServiceServer interface { GetTransactions(context.Context, *model.GetTransactionsRequest) (*model.GetTransactionsResponse, error) GetTransaction(context.Context, *model.GetTransactionRequest) (*model.Transaction, error) -} - -// UnimplementedTransactionServiceServer can be embedded to have forward compatible implementations. -type UnimplementedTransactionServiceServer struct { -} - -func (*UnimplementedTransactionServiceServer) GetTransactions(ctx context.Context, req *model.GetTransactionsRequest) (*model.GetTransactionsResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetTransactions not implemented") -} -func (*UnimplementedTransactionServiceServer) GetTransaction(ctx context.Context, req *model.GetTransactionRequest) (*model.Transaction, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetTransaction not implemented") + PostTransaction(context.Context, *model.PostTransactionRequest) (*model.PostTransactionResponse, error) } func RegisterTransactionServiceServer(s *grpc.Server, srv TransactionServiceServer) { @@ -145,6 +124,24 @@ func _TransactionService_GetTransaction_Handler(srv interface{}, ctx context.Con return interceptor(ctx, in, info, handler) } +func _TransactionService_PostTransaction_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(model.PostTransactionRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TransactionServiceServer).PostTransaction(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/service.TransactionService/PostTransaction", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TransactionServiceServer).PostTransaction(ctx, req.(*model.PostTransactionRequest)) + } + return interceptor(ctx, in, info, handler) +} + var _TransactionService_serviceDesc = grpc.ServiceDesc{ ServiceName: "service.TransactionService", HandlerType: (*TransactionServiceServer)(nil), @@ -157,7 +154,35 @@ var _TransactionService_serviceDesc = grpc.ServiceDesc{ MethodName: "GetTransaction", Handler: _TransactionService_GetTransaction_Handler, }, + { + MethodName: "PostTransaction", + Handler: _TransactionService_PostTransaction_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "service/transaction.proto", } + +func init() { + proto.RegisterFile("service/transaction.proto", fileDescriptor_transaction_0e75a8e7f913159d) +} + +var fileDescriptor_transaction_0e75a8e7f913159d = []byte{ + // 245 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x2c, 0x4e, 0x2d, 0x2a, + 0xcb, 0x4c, 0x4e, 0xd5, 0x2f, 0x29, 0x4a, 0xcc, 0x2b, 0x4e, 0x4c, 0x2e, 0xc9, 0xcc, 0xcf, 0xd3, + 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x87, 0x4a, 0x49, 0x89, 0xe7, 0xe6, 0xa7, 0xa4, 0xe6, + 0x60, 0xaa, 0x90, 0x92, 0x49, 0xcf, 0xcf, 0x4f, 0xcf, 0x49, 0xd5, 0x4f, 0x2c, 0xc8, 0xd4, 0x4f, + 0xcc, 0xcb, 0xcb, 0x2f, 0x49, 0x04, 0x49, 0x16, 0x43, 0x64, 0x8d, 0xbe, 0x31, 0x71, 0x09, 0x85, + 0x20, 0xf4, 0x04, 0x43, 0x4c, 0x13, 0xaa, 0xe4, 0xe2, 0x77, 0x4f, 0x2d, 0x41, 0x92, 0x28, 0x16, + 0x92, 0xd5, 0x03, 0xdb, 0xa0, 0x87, 0x26, 0x1e, 0x94, 0x5a, 0x58, 0x9a, 0x5a, 0x5c, 0x22, 0x25, + 0x87, 0x4b, 0xba, 0xb8, 0x20, 0x3f, 0xaf, 0x38, 0x55, 0x49, 0xbd, 0xe9, 0xf2, 0x93, 0xc9, 0x4c, + 0x8a, 0x42, 0xf2, 0xfa, 0x65, 0x86, 0xc8, 0xae, 0xd4, 0x47, 0xb7, 0x27, 0x8b, 0x8b, 0x0f, 0x55, + 0x48, 0x48, 0x06, 0xab, 0xd1, 0x30, 0x8b, 0x85, 0xa0, 0xb2, 0x48, 0x52, 0x4a, 0x6a, 0x60, 0xcb, + 0x14, 0x84, 0xe4, 0xf0, 0x5b, 0x06, 0xf2, 0x66, 0x40, 0x7e, 0x31, 0x8a, 0x10, 0xcc, 0x9b, 0x68, + 0xe2, 0xe8, 0xde, 0xc4, 0x90, 0x46, 0xf5, 0xa6, 0x12, 0x86, 0x37, 0xd1, 0x34, 0x38, 0xe9, 0x44, + 0x69, 0xa5, 0x67, 0x96, 0x64, 0x94, 0x26, 0xe9, 0x25, 0xe7, 0xe7, 0xea, 0x57, 0xe5, 0xe7, 0x27, + 0x25, 0x43, 0x48, 0xdd, 0xe4, 0xfc, 0xa2, 0x54, 0xfd, 0xe4, 0xfc, 0xdc, 0xdc, 0xfc, 0x3c, 0x7d, + 0x68, 0xec, 0x26, 0xb1, 0x81, 0x63, 0xcb, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0x9a, 0x04, 0xcf, + 0x12, 0x0a, 0x02, 0x00, 0x00, +} diff --git a/common/service/transaction.pb.gw.go b/common/service/transaction.pb.gw.go index bd8816c62..8a96103c1 100644 --- a/common/service/transaction.pb.gw.go +++ b/common/service/transaction.pb.gw.go @@ -37,10 +37,7 @@ func request_TransactionService_GetTransactions_0(ctx context.Context, marshaler var protoReq model.GetTransactionsRequest var metadata runtime.ServerMetadata - if err := req.ParseForm(); err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_TransactionService_GetTransactions_0); err != nil { + if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_TransactionService_GetTransactions_0); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -57,14 +54,28 @@ func request_TransactionService_GetTransaction_0(ctx context.Context, marshaler var protoReq model.GetTransactionRequest var metadata runtime.ServerMetadata - if err := req.ParseForm(); err != nil { + if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_TransactionService_GetTransaction_0); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_TransactionService_GetTransaction_0); err != nil { + + msg, err := client.GetTransaction(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +var ( + filter_TransactionService_PostTransaction_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_TransactionService_PostTransaction_0(ctx context.Context, marshaler runtime.Marshaler, client TransactionServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq model.PostTransactionRequest + var metadata runtime.ServerMetadata + + if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_TransactionService_PostTransaction_0); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - msg, err := client.GetTransaction(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + msg, err := client.PostTransaction(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } @@ -147,17 +158,41 @@ func RegisterTransactionServiceHandlerClient(ctx context.Context, mux *runtime.S }) + mux.Handle("POST", pattern_TransactionService_PostTransaction_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_TransactionService_PostTransaction_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_TransactionService_PostTransaction_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } var ( - pattern_TransactionService_GetTransactions_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "transaction", "GetTransactions"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_TransactionService_GetTransactions_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "transaction", "GetTransactions"}, "")) - pattern_TransactionService_GetTransaction_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "transaction", "GetTransaction"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_TransactionService_GetTransaction_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "transaction", "GetTransaction"}, "")) + + pattern_TransactionService_PostTransaction_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "transaction", "PostTransaction"}, "")) ) var ( forward_TransactionService_GetTransactions_0 = runtime.ForwardResponseMessage forward_TransactionService_GetTransaction_0 = runtime.ForwardResponseMessage + + forward_TransactionService_PostTransaction_0 = runtime.ForwardResponseMessage ) diff --git a/common/transaction/empty.go b/common/transaction/empty.go index f5e0b803c..fc8833484 100644 --- a/common/transaction/empty.go +++ b/common/transaction/empty.go @@ -14,6 +14,10 @@ func (tx *TXEmpty) ApplyConfirmed() error { func (tx *TXEmpty) ApplyUnconfirmed() error { return nil } + +func (tx *TXEmpty) UndoApplyUnconfirmed() error { + return nil +} func (tx *TXEmpty) Validate() error { return nil } diff --git a/common/transaction/nodeRegistration.go b/common/transaction/nodeRegistration.go index 727dec34d..8bcc3a5f3 100644 --- a/common/transaction/nodeRegistration.go +++ b/common/transaction/nodeRegistration.go @@ -56,6 +56,10 @@ func (tx *NodeRegistration) ApplyUnconfirmed() error { return nil } +func (tx *NodeRegistration) UndoApplyUnconfirmed() error { + return nil +} + // Validate validate node registration transaction and tx body func (tx *NodeRegistration) Validate() error { return nil diff --git a/common/transaction/nodeRegistration_test.go b/common/transaction/nodeRegistration_test.go index 7b3be9e85..bcd732e79 100644 --- a/common/transaction/nodeRegistration_test.go +++ b/common/transaction/nodeRegistration_test.go @@ -70,3 +70,54 @@ func TestNodeRegistration_ApplyUnconfirmed(t *testing.T) { }) } } + +func TestNodeRegistration_UndoApplyUnconfirmed(t *testing.T) { + type fields struct { + Body *model.NodeRegistrationTransactionBody + Fee int64 + SenderAddress string + SenderAccountType uint32 + Height uint32 + AccountBalanceQuery query.AccountBalanceQueryInterface + AccountQuery query.AccountQueryInterface + NodeRegistrationQuery query.NodeRegistrationQueryInterface + QueryExecutor query.ExecutorInterface + } + tests := []struct { + name string + fields fields + wantErr bool + }{ + { + name: "NodeRegistration-UndoApplyUnconfirmed:default", + fields: fields{ + Body: &model.NodeRegistrationTransactionBody{ + NodePublicKey: []byte{}, + AccountType: 0, + }, + Fee: 1, + AccountBalanceQuery: query.NewAccountBalanceQuery(), + QueryExecutor: &mockApplyUnconfirmedQueryExecutor{}, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tx := &NodeRegistration{ + Body: tt.fields.Body, + Fee: tt.fields.Fee, + SenderAddress: tt.fields.SenderAddress, + SenderAccountType: tt.fields.SenderAccountType, + Height: tt.fields.Height, + AccountBalanceQuery: tt.fields.AccountBalanceQuery, + AccountQuery: tt.fields.AccountQuery, + NodeRegistrationQuery: tt.fields.NodeRegistrationQuery, + QueryExecutor: tt.fields.QueryExecutor, + } + if err := tx.UndoApplyUnconfirmed(); (err != nil) != tt.wantErr { + t.Errorf("NodeRegistration.UndoApplyUnconfirmed() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/common/transaction/sendMoney.go b/common/transaction/sendMoney.go index 903c96f20..966e93218 100644 --- a/common/transaction/sendMoney.go +++ b/common/transaction/sendMoney.go @@ -13,6 +13,7 @@ import ( // SendMoney is Transaction Type that implemented TypeAction type SendMoney struct { Body *model.SendMoneyTransactionBody + Fee int64 SenderAddress string SenderAccountType uint32 RecipientAddress string @@ -34,7 +35,6 @@ __If Not Genesis__: `sender.balance` = current - amount */ func (tx *SendMoney) ApplyConfirmed() error { - // todo: undo apply unconfirmed for non-genesis transaction var ( recipientAccount model.Account senderAccount model.Account @@ -45,6 +45,13 @@ func (tx *SendMoney) ApplyConfirmed() error { return err } + if tx.Height > 0 { + err = tx.UndoApplyUnconfirmed() + if err != nil { + return err + } + } + recipientAccount = model.Account{ ID: util.CreateAccountIDFromAddress(tx.RecipientAccountType, tx.RecipientAddress), AccountType: tx.RecipientAccountType, @@ -67,7 +74,7 @@ func (tx *SendMoney) ApplyConfirmed() error { ) // update sender accountBalanceSenderQ := tx.AccountBalanceQuery.AddAccountBalance( - -tx.Body.Amount, + -(tx.Body.Amount + tx.Fee), map[string]interface{}{ "account_id": senderAccount.ID, "block_height": tx.Height, @@ -94,13 +101,36 @@ func (tx *SendMoney) ApplyUnconfirmed() error { err error ) - if err := tx.Validate(); err != nil { + // update sender + accountBalanceSenderQ, accountBalanceSenderQArgs := tx.AccountBalanceQuery.AddAccountSpendableBalance( + -(tx.Body.Amount + tx.Fee), + map[string]interface{}{ + "account_id": util.CreateAccountIDFromAddress( + tx.SenderAccountType, + tx.SenderAddress, + ), + }, + ) + err = tx.QueryExecutor.ExecuteTransaction(accountBalanceSenderQ, accountBalanceSenderQArgs...) + if err != nil { return err } + return nil +} + +/* +UndoApplyUnconfirmed is used to undo the previous applied unconfirmed tx action +this will be called on apply confirmed or when rollback occurred +*/ +func (tx *SendMoney) UndoApplyUnconfirmed() error { + var ( + err error + ) + // update sender accountBalanceSenderQ, accountBalanceSenderQArgs := tx.AccountBalanceQuery.AddAccountSpendableBalance( - -tx.Body.Amount, + tx.Body.Amount+tx.Fee, map[string]interface{}{ "account_id": util.CreateAccountIDFromAddress( tx.SenderAccountType, @@ -108,7 +138,7 @@ func (tx *SendMoney) ApplyUnconfirmed() error { ), }, ) - _, err = tx.QueryExecutor.ExecuteStatement(accountBalanceSenderQ, accountBalanceSenderQArgs...) + err = tx.QueryExecutor.ExecuteTransaction(accountBalanceSenderQ, accountBalanceSenderQArgs...) if err != nil { return err } @@ -147,7 +177,7 @@ func (tx *SendMoney) Validate() error { util.CreateAccountIDFromAddress(tx.RecipientAccountType, tx.RecipientAddress), }) - err = tx.QueryExecutor.ExecuteSelectRow(query.GetTotalRecordOfSelect(accounts), accountArgs).Scan(&count) + err = tx.QueryExecutor.ExecuteSelectRow(query.GetTotalRecordOfSelect(accounts), accountArgs...).Scan(&count) if err != nil { return err } @@ -155,11 +185,10 @@ func (tx *SendMoney) Validate() error { if count <= 1 { return fmt.Errorf("count recipient and sender got: %d", count) } - - if rows, err := tx.QueryExecutor.ExecuteSelect( - tx.AccountBalanceQuery.GetAccountBalanceByAccountID(), - util.CreateAccountIDFromAddress(tx.SenderAccountType, tx.SenderAddress), - ); err != nil { + senderID := util.CreateAccountIDFromAddress(tx.SenderAccountType, tx.SenderAddress) + senderQ, senderArg := tx.AccountBalanceQuery.GetAccountBalanceByAccountID(senderID) + rows, err := tx.QueryExecutor.ExecuteSelect(senderQ, senderArg) + if err != nil { return err } else if rows.Next() { _ = rows.Scan( @@ -171,6 +200,7 @@ func (tx *SendMoney) Validate() error { &accountBalance.Latest, ) } + defer rows.Close() if accountBalance.SpendableBalance < tx.Body.GetAmount() { return errors.New("transaction amount not enough") diff --git a/common/transaction/sendMoney_test.go b/common/transaction/sendMoney_test.go index b3585e19d..69951b0f0 100644 --- a/common/transaction/sendMoney_test.go +++ b/common/transaction/sendMoney_test.go @@ -27,8 +27,9 @@ type ( executorValidateSuccess struct { query.Executor } - executorApplySuccess struct { - executorValidateSuccess + + executorApplyUnconfirmedSuccess struct { + query.Executor } executorFailUpdateAccount struct { @@ -38,24 +39,11 @@ type ( executorSuccessUpdateAccount struct { query.Executor } -) -func (*executorApplySuccess) ExecuteTransactionStatements(queries [][]interface{}) ([]sql.Result, error) { - db, mock, err := sqlmock.New() - if err != nil { - return nil, err + executorUnconfirmedFail struct { + query.ExecutorInterface } - - mock.ExpectBegin() - mock.ExpectPrepare(regexp.QuoteMeta("")).ExpectExec(). - WithArgs(1).WillReturnResult(sqlmock.NewResult(1, 1)) - - tx, _ := db.Begin() - stmt, _ := tx.Prepare("") - result, _ := stmt.Exec("") - err = tx.Commit() - return []sql.Result{result}, err -} +) func (*executorValidateSuccess) ExecuteSelectRow(qStr string, args ...interface{}) *sql.Row { db, mock, _ := sqlmock.New() @@ -78,22 +66,6 @@ func (*executorValidateSuccess) ExecuteSelect(qStr string, args ...interface{}) ).AddRow(1, 2, 50, 50, 0, 1)) return db.Query(qStr, 1) } -func (*executorValidateSuccess) ExecuteTransactionStatements(queries [][]interface{}) ([]sql.Result, error) { - db, mock, err := sqlmock.New() - if err != nil { - return nil, err - } - - mock.ExpectBegin() - mock.ExpectPrepare(regexp.QuoteMeta("")).ExpectExec(). - WithArgs(1).WillReturnResult(sqlmock.NewResult(1, 1)) - - tx, _ := db.Begin() - stmt, _ := tx.Prepare("") - result, _ := stmt.Exec("") - err = tx.Commit() - return []sql.Result{result}, err -} func (*executorAccountCreateSuccess) ExecuteTransaction(qStr string, args ...interface{}) error { return nil @@ -115,22 +87,7 @@ func (*executorAccountCreateSuccess) ExecuteSelect(qStr string, args ...interfac )) return db.Query(qStr, 1) } -func (*executorAccountCreateSuccess) ExecuteTransactionStatements(queries [][]interface{}) ([]sql.Result, error) { - db, mock, err := sqlmock.New() - if err != nil { - return nil, err - } - mock.ExpectBegin() - mock.ExpectPrepare(regexp.QuoteMeta("")).ExpectExec(). - WithArgs(1).WillReturnResult(sqlmock.NewResult(1, 1)) - - tx, _ := db.Begin() - stmt, _ := tx.Prepare("") - result, _ := stmt.Exec("") - err = tx.Commit() - return []sql.Result{result}, err -} func (*executorAccountCreateSuccess) ExecuteSelectRow(qStr string, args ...interface{}) *sql.Row { db, mock, _ := sqlmock.New() @@ -161,6 +118,10 @@ func (*executorSuccessUpdateAccount) ExecuteTransactions([][]interface{}) error return nil } +func (*executorSuccessUpdateAccount) ExecuteTransaction(qStr string, args ...interface{}) error { + return nil +} + func (*executorAccountCountSuccess) ExecuteSelectRow(qStr string, args ...interface{}) *sql.Row { db, mock, _ := sqlmock.New() mock.ExpectQuery(regexp.QuoteMeta(qStr)).WithArgs(1, 2).WillReturnRows(sqlmock.NewRows([]string{ @@ -182,6 +143,18 @@ func (*executorAccountCountSuccess) ExecuteSelect(qStr string, args ...interface return db.Query(qStr, 1) } +func (*executorAccountCountSuccess) ExecuteTransaction(qStr string, args ...interface{}) error { + return nil +} + +func (*executorUnconfirmedFail) ExecuteTransaction(qStr string, args ...interface{}) error { + return errors.New("MockedError") +} + +func (*executorApplyUnconfirmedSuccess) ExecuteTransaction(qStr string, args ...interface{}) error { + return nil +} + func TestSendMoney_Validate(t *testing.T) { type fields struct { Body *model.SendMoneyTransactionBody @@ -359,7 +332,7 @@ func TestSendMoney_ApplyUnconfirmed(t *testing.T) { wantErr bool }{ { - name: "wantError:ValidateInvalid", + name: "wantError:ExecuteTransactionFail", fields: fields{ Body: &model.SendMoneyTransactionBody{ Amount: 10, @@ -371,11 +344,7 @@ func TestSendMoney_ApplyUnconfirmed(t *testing.T) { RecipientAddress: "BCZEGOb3WNx3fDOVf9ZS4EjvOIv_UeW4TVBQJ_6tHKlE", AccountQuery: query.NewAccountQuery(), AccountBalanceQuery: query.NewAccountBalanceQuery(), - QueryExecutor: &executorAccountCountSuccess{ - query.Executor{ - Db: db, - }, - }, + QueryExecutor: &executorUnconfirmedFail{}, }, wantErr: true, }, @@ -392,15 +361,9 @@ func TestSendMoney_ApplyUnconfirmed(t *testing.T) { RecipientAddress: "BCZEGOb3WNx3fDOVf9ZS4EjvOIv_UeW4TVBQJ_6tHKlE", AccountQuery: query.NewAccountQuery(), AccountBalanceQuery: query.NewAccountBalanceQuery(), - QueryExecutor: &executorApplySuccess{ - executorValidateSuccess{ - query.Executor{ - Db: db, - }, - }, - }, + QueryExecutor: &executorApplyUnconfirmedSuccess{}, }, - wantErr: true, + wantErr: false, }, } for _, tt := range tests { @@ -464,6 +427,27 @@ func TestSendMoney_ApplyConfirmed(t *testing.T) { }, wantErr: true, }, + { + name: "wantError:UndoUnconfirmedInvalid", + fields: fields{ + Body: &model.SendMoneyTransactionBody{ + Amount: 10, + }, + Height: 1, + SenderAccountType: 0, + SenderAddress: "BCZEGOb3WNx3fDOVf9ZS4EjvOIv_UeW4TVBQJ_6tHKlE", + RecipientAccountType: 0, + RecipientAddress: "BCZEGOb3WNx3fDOVf9ZS4EjvOIv_UeW4TVBQJ_6tHKlE", + AccountQuery: query.NewAccountQuery(), + AccountBalanceQuery: query.NewAccountBalanceQuery(), + QueryExecutor: &executorAccountCountFail{ + query.Executor{ + Db: db, + }, + }, + }, + wantErr: true, + }, { name: "wantFail:deductAddMoneyFail", fields: fields{ @@ -584,3 +568,75 @@ func TestSendMoney_GetSize(t *testing.T) { } }) } + +func TestSendMoney_UndoApplyUnconfirmed(t *testing.T) { + type fields struct { + Body *model.SendMoneyTransactionBody + SenderAddress string + SenderAccountType uint32 + RecipientAddress string + RecipientAccountType uint32 + Height uint32 + AccountBalanceQuery query.AccountBalanceQueryInterface + AccountQuery query.AccountQueryInterface + QueryExecutor query.ExecutorInterface + } + tests := []struct { + name string + fields fields + wantErr bool + }{ + { + name: "UndoApplyUnconfirmed:success", + fields: fields{ + Body: &model.SendMoneyTransactionBody{ + Amount: 10, + }, + Height: 1, + SenderAccountType: 0, + SenderAddress: "BCZEGOb3WNx3fDOVf9ZS4EjvOIv_UeW4TVBQJ_6tHKlE", + RecipientAccountType: 0, + RecipientAddress: "BCZEGOb3WNx3fDOVf9ZS4EjvOIv_UeW4TVBQJ_6tHKlE", + AccountQuery: query.NewAccountQuery(), + AccountBalanceQuery: query.NewAccountBalanceQuery(), + QueryExecutor: &executorAccountCountSuccess{}, + }, + wantErr: false, + }, + { + name: "UndoApplyUnconfirmed:executeTransactionFail/", + fields: fields{ + Body: &model.SendMoneyTransactionBody{ + Amount: 10, + }, + Height: 1, + SenderAccountType: 0, + SenderAddress: "BCZEGOb3WNx3fDOVf9ZS4EjvOIv_UeW4TVBQJ_6tHKlE", + RecipientAccountType: 0, + RecipientAddress: "BCZEGOb3WNx3fDOVf9ZS4EjvOIv_UeW4TVBQJ_6tHKlE", + AccountQuery: query.NewAccountQuery(), + AccountBalanceQuery: query.NewAccountBalanceQuery(), + QueryExecutor: &executorAccountCountFail{}, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tx := &SendMoney{ + Body: tt.fields.Body, + SenderAddress: tt.fields.SenderAddress, + SenderAccountType: tt.fields.SenderAccountType, + RecipientAddress: tt.fields.RecipientAddress, + RecipientAccountType: tt.fields.RecipientAccountType, + Height: tt.fields.Height, + AccountBalanceQuery: tt.fields.AccountBalanceQuery, + AccountQuery: tt.fields.AccountQuery, + QueryExecutor: tt.fields.QueryExecutor, + } + if err := tx.UndoApplyUnconfirmed(); (err != nil) != tt.wantErr { + t.Errorf("SendMoney.UndoApplyUnconfirmed() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/common/transaction/transaction.go b/common/transaction/transaction.go index 2ccabd454..a319cb76c 100644 --- a/common/transaction/transaction.go +++ b/common/transaction/transaction.go @@ -10,6 +10,7 @@ type ( TypeAction interface { ApplyConfirmed() error ApplyUnconfirmed() error + UndoApplyUnconfirmed() error Validate() error GetAmount() int64 GetSize() uint32 @@ -35,8 +36,12 @@ func (ts *TypeSwitcher) GetTransactionType(tx *model.Transaction) TypeAction { case 1: switch buf[1] { case 0: + sendMoneyTxAmount := util.ConvertBytesToUint64(tx.GetTransactionBodyBytes()) return &SendMoney{ - Body: tx.GetSendMoneyTransactionBody(), + Body: &model.SendMoneyTransactionBody{ + Amount: int64(sendMoneyTxAmount), + }, + Fee: tx.Fee, SenderAddress: tx.GetSenderAccountAddress(), SenderAccountType: tx.GetSenderAccountType(), RecipientAddress: tx.GetRecipientAccountAddress(), diff --git a/common/transaction/transaction_test.go b/common/transaction/transaction_test.go index 3d44ad490..24b841469 100644 --- a/common/transaction/transaction_test.go +++ b/common/transaction/transaction_test.go @@ -5,6 +5,8 @@ import ( "reflect" "testing" + "github.com/zoobc/zoobc-core/common/util" + "github.com/zoobc/zoobc-core/common/model" "github.com/zoobc/zoobc-core/common/query" ) @@ -39,7 +41,8 @@ func TestTypeSwitcher_GetTransactionType(t *testing.T) { Amount: 10, }, }, - TransactionType: binary.LittleEndian.Uint32([]byte{1, 0, 0, 0}), + TransactionBodyBytes: util.ConvertUint64ToBytes(10), + TransactionType: binary.LittleEndian.Uint32([]byte{1, 0, 0, 0}), }, }, want: &SendMoney{ diff --git a/common/util/transaction.go b/common/util/transaction.go index 810a4db8a..9028784a1 100644 --- a/common/util/transaction.go +++ b/common/util/transaction.go @@ -180,11 +180,12 @@ func ValidateTransaction( // validate sender account senderAccountID := CreateAccountIDFromAddress(tx.SenderAccountType, tx.SenderAccountAddress) - sqlQ := accountBalanceQuery.GetAccountBalanceByAccountID() - rows, err := queryExecutor.ExecuteSelect(sqlQ, senderAccountID) + sqlQ, arg := accountBalanceQuery.GetAccountBalanceByAccountID(senderAccountID) + rows, err := queryExecutor.ExecuteSelect(sqlQ, arg) if err != nil { return err } + defer rows.Close() res := accountBalanceQuery.BuildModel([]*model.AccountBalance{}, rows) if len(res) == 0 { return errors.New("TxSenderNotFound") diff --git a/core/service/blockCoreService.go b/core/service/blockCoreService.go index c81a404cd..153639a7b 100644 --- a/core/service/blockCoreService.go +++ b/core/service/blockCoreService.go @@ -185,6 +185,9 @@ func (bs *BlockService) PushBlock(previousBlock, block *model.Block) error { transactions := block.GetTransactions() if len(transactions) > 0 { for _, tx := range block.GetTransactions() { + // assign block id and block height to tx + tx.BlockID = block.ID + tx.Height = block.Height // validate the transaction if err := util.ValidateTransaction(tx, bs.QueryExecutor, bs.AccountBalanceQuery, true); err != nil { return err @@ -192,8 +195,6 @@ func (bs *BlockService) PushBlock(previousBlock, block *model.Block) error { // validate tx body and apply/perform transaction-specific logic err := bs.ActionTypeSwitcher.GetTransactionType(tx).ApplyConfirmed() // todo: make this mockable if err == nil { - tx.BlockID = block.ID - tx.Height = block.Height transactionInsertQuery, transactionInsertValue := bs.TransactionQuery.InsertTransaction(tx) err := bs.QueryExecutor.ExecuteTransaction(transactionInsertQuery, transactionInsertValue...) if err != nil { @@ -321,7 +322,7 @@ func (bs *BlockService) RemoveMempoolTransactions(transactions []*model.Transact for _, tx := range transactions { idsStr = append(idsStr, strconv.FormatInt(tx.ID, 10)) } - _, err := bs.QueryExecutor.ExecuteStatement(bs.MempoolQuery.DeleteMempoolTransactions(), strings.Join(idsStr, ",")) + err := bs.QueryExecutor.ExecuteTransaction(bs.MempoolQuery.DeleteMempoolTransactions(), strings.Join(idsStr, ",")) if err != nil { return err } diff --git a/core/service/blockCoreService_test.go b/core/service/blockCoreService_test.go index 00e5d9ec6..8456c5550 100644 --- a/core/service/blockCoreService_test.go +++ b/core/service/blockCoreService_test.go @@ -92,7 +92,7 @@ func (*mockQueryExecutorFail) ExecuteStatement(qe string, args ...interface{}) ( func (*mockQueryExecutorFail) BeginTx() error { return nil } func (*mockQueryExecutorFail) ExecuteTransaction(qStr string, args ...interface{}) error { - return nil + return errors.New("mockError:deleteMempoolFail") } func (*mockQueryExecutorFail) CommitTx() error { return errors.New("mockError:commitFail") } @@ -627,7 +627,7 @@ func TestBlockService_PushBlock(t *testing.T) { BlockSignature: []byte{}, }, }, - wantErr: false, + wantErr: true, }, } for _, tt := range tests { diff --git a/core/service/mempoolCoreService.go b/core/service/mempoolCoreService.go index 956c81322..5f36a48eb 100644 --- a/core/service/mempoolCoreService.go +++ b/core/service/mempoolCoreService.go @@ -23,6 +23,7 @@ type ( GetMempoolTransaction(id int64) (*model.MempoolTransaction, error) AddMempoolTransaction(mpTx *model.MempoolTransaction) error SelectTransactionsFromMempool(blockTimestamp int64) ([]*model.MempoolTransaction, error) + ValidateMempoolTransaction(mpTx *model.MempoolTransaction) error } // MempoolService contains all transactions in mempool plus a mux to manage locks in concurrency @@ -117,15 +118,10 @@ func (mps *MempoolService) AddMempoolTransaction(mpTx *model.MempoolTransaction) return errors.New("DatabaseError") } - if err := mps.ValidateMempoolTransaction(mpTx); err != nil { - return err - } - - result, err := mps.QueryExecutor.ExecuteStatement(mps.MempoolQuery.InsertMempoolTransaction(), mps.MempoolQuery.ExtractModel(mpTx)...) + err = mps.QueryExecutor.ExecuteTransaction(mps.MempoolQuery.InsertMempoolTransaction(), mps.MempoolQuery.ExtractModel(mpTx)...) if err != nil { return err } - log.Printf("got new mempool transaction, %v", result) return nil } @@ -175,8 +171,8 @@ func (mps *MempoolService) SelectTransactionsFromMempool(blockTimestamp int64) ( continue } // compute transaction expiration time - txExpirationTime := tx.Timestamp + constant.TransactionExpirationOffset - if blockTimestamp == 0 || txExpirationTime > blockTimestamp { + txExpirationTime := blockTimestamp + constant.TransactionExpirationOffset + if blockTimestamp > 0 && tx.Timestamp > txExpirationTime { continue } diff --git a/core/service/mempoolCoreService_test.go b/core/service/mempoolCoreService_test.go index 10f222f03..c05363597 100644 --- a/core/service/mempoolCoreService_test.go +++ b/core/service/mempoolCoreService_test.go @@ -3,7 +3,6 @@ package service import ( "database/sql" "errors" - "math" "reflect" "regexp" "testing" @@ -49,6 +48,10 @@ func (*mockMempoolQueryExecutorSuccess) ExecuteStatement(qe string, args ...inte return nil, nil } +func (*mockMempoolQueryExecutorSuccess) ExecuteTransaction(qe string, args ...interface{}) error { + return nil +} + type mockMempoolQueryExecutorFail struct { query.Executor } @@ -74,6 +77,10 @@ func (*mockMempoolQueryExecutorFail) ExecuteStatement(qe string, args ...interfa return nil, errors.New("MockedError") } +func (*mockMempoolQueryExecutorFail) ExecuteTransaction(qe string, args ...interface{}) error { + return errors.New("MockedError") +} + func buildTransaction(timestamp int64, sender, recipient string) *model.Transaction { return &model.Transaction{ Version: 1, @@ -308,7 +315,7 @@ func TestMempoolService_SelectTransactionsFromMempool(t *testing.T) { ActionTypeSwitcher: &transaction.TypeSwitcher{}, }, args: args{ - blockTimestamp: math.MaxInt64, + blockTimestamp: 1562893106, }, want: []*model.MempoolTransaction{ { diff --git a/core/smith/blockchainProcessor.go b/core/smith/blockchainProcessor.go index 2ec13d2b7..53d7f08a4 100644 --- a/core/smith/blockchainProcessor.go +++ b/core/smith/blockchainProcessor.go @@ -134,8 +134,6 @@ func (bp *BlockchainProcessor) StartSmithing() error { if err != nil { return err } - allBlocks, _ := bp.BlockService.GetBlocks() - log.Printf("block pushed: %d\n", len(allBlocks)) stop = true } } diff --git a/main.go b/main.go index 412bdb5d6..951dce8f2 100644 --- a/main.go +++ b/main.go @@ -69,6 +69,11 @@ func init() { queryExecutor = query.NewQueryExecutor(db) } +func startServices(queryExecutor query.ExecutorInterface) { + p2pService() + api.Start(apiRPCPort, apiHTTPPort, queryExecutor, p2pServiceInstance) +} + func p2pService() { myAddress := viper.GetString("myAddress") peerPort := viper.GetUint32("peerPort") @@ -79,11 +84,6 @@ func p2pService() { go p2pServiceInstance.StartP2P() } -func startServices(queryExecutor *query.Executor) { - p2pService() - api.Start(apiRPCPort, apiHTTPPort, queryExecutor, p2pServiceInstance) -} - func main() { migration := database.Migration{Query: queryExecutor}