From 461ad48dc88d733da79ec76857122c5378e8504f Mon Sep 17 00:00:00 2001 From: Will Lahti Date: Wed, 15 Mar 2017 14:21:44 -0400 Subject: [PATCH] Implement GetTxTimestamp() for chaincode stub This change sets the transaction timestamp in the proposal's ChannelHeader and then implements stub.GetTxTimestamp() to retrieve that timestamp. https://jira.hyperledger.org/browse/FAB-2701 Change-Id: I95ed9004294a80d1e277dae67970a9735d9cb71b Signed-off-by: Will Lahti --- core/chaincode/shim/chaincode.go | 17 +++++++++++++---- core/chaincode/shim/interfaces.go | 6 +++--- core/chaincode/shim/mockstub.go | 14 ++++++++++++-- core/chaincode/shim/mockstub_test.go | 12 ++++++++++++ protos/utils/proputils.go | 4 ++++ 5 files changed, 44 insertions(+), 9 deletions(-) diff --git a/core/chaincode/shim/chaincode.go b/core/chaincode/shim/chaincode.go index 94709d6b6e7..e4c98487bc8 100644 --- a/core/chaincode/shim/chaincode.go +++ b/core/chaincode/shim/chaincode.go @@ -558,11 +558,20 @@ func (stub *ChaincodeStub) GetArgsSlice() ([]byte, error) { return res, nil } -// GetTxTimestamp returns transaction created timestamp, which is currently -// taken from the peer receiving the transaction. Note that this timestamp -// may not be the same with the other peers' time. +// GetTxTimestamp returns the timestamp when the transaction was created. This +// is taken from the transaction ChannelHeader, so it will be the same across +// all endorsers. func (stub *ChaincodeStub) GetTxTimestamp() (*timestamp.Timestamp, error) { - return nil, nil + hdr, err := utils.GetHeader(stub.proposal.Header) + if err != nil { + return nil, err + } + chdr, err := utils.UnmarshalChannelHeader(hdr.ChannelHeader) + if err != nil { + return nil, err + } + + return chdr.GetTimestamp(), nil } // ------------- ChaincodeEvent API ---------------------- diff --git a/core/chaincode/shim/interfaces.go b/core/chaincode/shim/interfaces.go index 92b743ce18c..0a09a0064dd 100644 --- a/core/chaincode/shim/interfaces.go +++ b/core/chaincode/shim/interfaces.go @@ -118,9 +118,9 @@ type ChaincodeStubInterface interface { // GetArgsSlice returns the arguments to the stub call as a byte array GetArgsSlice() ([]byte, error) - // GetTxTimestamp returns transaction created timestamp, which is currently - // taken from the peer receiving the transaction. Note that this timestamp - // may not be the same with the other peers' time. + // GetTxTimestamp returns the timestamp when the transaction was created. This + // is taken from the transaction ChannelHeader, so it will be the same across + // all endorsers. GetTxTimestamp() (*timestamp.Timestamp, error) // SetEvent saves the event to be sent when a transaction is made part of a block diff --git a/core/chaincode/shim/mockstub.go b/core/chaincode/shim/mockstub.go index e4e5abf4ace..444fc730971 100644 --- a/core/chaincode/shim/mockstub.go +++ b/core/chaincode/shim/mockstub.go @@ -25,6 +25,7 @@ import ( "strings" "github.com/golang/protobuf/ptypes/timestamp" + "github.com/hyperledger/fabric/common/util" pb "github.com/hyperledger/fabric/protos/peer" "github.com/op/go-logging" ) @@ -57,6 +58,8 @@ type MockStub struct { // stores a transaction uuid while being Invoked / Deployed // TODO if a chaincode uses recursion this may need to be a stack of TxIDs or possibly a reference counting map TxID string + + TxTimestamp *timestamp.Timestamp } func (stub *MockStub) GetTxID() string { @@ -92,6 +95,7 @@ func (stub *MockStub) GetFunctionAndParameters() (function string, params []stri // MockStub doesn't support concurrent transactions at present. func (stub *MockStub) MockTransactionStart(txid string) { stub.TxID = txid + stub.setTxTimestamp(util.CreateUtcTimestamp()) } // End a mocked transaction, clearing the UUID. @@ -270,9 +274,15 @@ func (stub *MockStub) GetArgsSlice() ([]byte, error) { return nil, nil } -// Not implemented +func (stub *MockStub) setTxTimestamp(time *timestamp.Timestamp) { + stub.TxTimestamp = time +} + func (stub *MockStub) GetTxTimestamp() (*timestamp.Timestamp, error) { - return nil, nil + if stub.TxTimestamp == nil { + return nil, errors.New("TxTimestamp not set.") + } + return stub.TxTimestamp, nil } // Not implemented diff --git a/core/chaincode/shim/mockstub_test.go b/core/chaincode/shim/mockstub_test.go index e297c593970..32203ed8baa 100644 --- a/core/chaincode/shim/mockstub_test.go +++ b/core/chaincode/shim/mockstub_test.go @@ -219,3 +219,15 @@ func TestGetStateByPartialCompositeKeyCollision(t *testing.T) { t.FailNow() } } + +func TestGetTxTimestamp(t *testing.T) { + stub := NewMockStub("GetTxTimestamp", nil) + stub.MockTransactionStart("init") + + timestamp, err := stub.GetTxTimestamp() + if timestamp == nil || err != nil { + t.FailNow() + } + + stub.MockTransactionEnd("init") +} diff --git a/protos/utils/proputils.go b/protos/utils/proputils.go index eaa893b2073..e563b405102 100644 --- a/protos/utils/proputils.go +++ b/protos/utils/proputils.go @@ -27,6 +27,7 @@ import ( "github.com/golang/protobuf/proto" "github.com/hyperledger/fabric/bccsp" "github.com/hyperledger/fabric/bccsp/factory" + "github.com/hyperledger/fabric/common/util" "github.com/hyperledger/fabric/core/chaincode/platforms" "github.com/hyperledger/fabric/core/crypto/primitives" "github.com/hyperledger/fabric/protos/common" @@ -361,9 +362,12 @@ func CreateChaincodeProposalWithTxIDNonceAndTransient(txid string, typ common.He // get a more appropriate mechanism to handle it in. var epoch uint64 = 0 + timestamp := util.CreateUtcTimestamp() + hdr := &common.Header{ChannelHeader: MarshalOrPanic(&common.ChannelHeader{ Type: int32(typ), TxId: txid, + Timestamp: timestamp, ChannelId: chainID, Extension: ccHdrExtBytes, Epoch: epoch}),