Skip to content

Commit e163d3f

Browse files
committed
[FAB-15743] Align Context
- Node.js (JavaScript and TypeScript) pass the context on a per tx function basis. - The context is effectively the 'Fabric Transaction Context' Contains the txid and channel id - This is need for any interaction with the world state for example and is specific to the specific transaction that is executed - Getting hold of the stub api that is applicable to this transaction is via the context (ctx.getStub()) - Future releases will adjust the API used to a more ledger focussed api - Feedback on improvements to this api welcome (jira issues pls) Change-Id: Ibb4237726a1401cc9f9d12e297acb1b9424be967 Signed-off-by: Matthew B. White <[email protected]>
1 parent f87de8e commit e163d3f

31 files changed

+812
-585
lines changed

fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/Context.java

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,41 @@
1111
/**
1212
*
1313
* This context is available to all 'transaction functions' and provides the
14-
* transaction context.
14+
* transaction context. It also provides access to the APIs for the world state
15+
* using {@link #getStub()}
16+
* <p>
17+
* Applications can implement their own versions if they wish to add
18+
* functionality. All subclasses MUST implement a constructor, for example
19+
* <pre>
20+
* {@code
1521
*
16-
* It also provides access to the APIs for the world state. {@see ChaincodeStub}
22+
* public MyContext extends Context {
23+
*
24+
* public MyContext(ChaincodeStub stub) {
25+
* super(stub);
26+
* }
27+
* }
28+
*
29+
*}
30+
*</pre>
1731
*
18-
* Applications can implement their own versions if they wish to add
19-
* functionality.
2032
*/
21-
public interface Context extends ChaincodeStub {
33+
public class Context {
34+
protected ChaincodeStub stub;
35+
36+
/**
37+
* Constructor
38+
* @param stub Instance of the {@link ChaincodeStub} to use
39+
*/
40+
public Context(ChaincodeStub stub) {
41+
this.stub = stub;
42+
}
43+
44+
/**
45+
*
46+
* @return ChaincodeStub instance to use
47+
*/
48+
public ChaincodeStub getStub() {
49+
return this.stub;
50+
}
2251
}

fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/ContextFactory.java

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,6 @@
66

77
package org.hyperledger.fabric.contract;
88

9-
import java.lang.reflect.InvocationHandler;
10-
import java.lang.reflect.Method;
11-
import java.lang.reflect.Proxy;
12-
139
import org.hyperledger.fabric.shim.ChaincodeStub;
1410

1511
/**
@@ -26,25 +22,9 @@ static synchronized public ContextFactory getInstance() {
2622
return cf;
2723
}
2824

29-
public synchronized Context createContext(final ChaincodeStub stub) {
30-
Context newContext = (Context) Proxy.newProxyInstance(this.getClass().getClassLoader(),
31-
new Class[] { Context.class }, new ContextInvocationHandler(stub));
25+
public Context createContext(final ChaincodeStub stub) {
26+
Context newContext = new Context(stub);
3227
return newContext;
3328
}
3429

35-
static class ContextInvocationHandler implements InvocationHandler {
36-
37-
private ChaincodeStub stub;
38-
39-
ContextInvocationHandler(final ChaincodeStub stub) {
40-
this.stub = stub;
41-
}
42-
43-
@Override
44-
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
45-
Method m = ChaincodeStub.class.getMethod(method.getName(), method.getParameterTypes());
46-
return m.invoke(stub, args);
47-
}
48-
}
49-
5030
}

fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/ContractInterface.java

Lines changed: 74 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,39 +6,96 @@
66

77
package org.hyperledger.fabric.contract;
88

9+
import org.hyperledger.fabric.contract.annotation.Contract;
10+
import org.hyperledger.fabric.contract.annotation.Transaction;
911
import org.hyperledger.fabric.shim.ChaincodeStub;
1012

1113
/**
12-
* Interface all contracts should implement
14+
* All Contracts should implement this interface, in addition to the
15+
* {@link Contract} annotation.
16+
* <p>
17+
* All the of the methods on this is inteface have default implementations; for
18+
* many contracts it may not be needed to sub-class these.
19+
* <p>
20+
* Each method on the Contract that is marked with the {@link Transaction}
21+
* annotation is considered a Transaction Function. This is eligible for
22+
* calling. Each transaction function is supplied with it's first parameter
23+
* being a {@link org.hyperledger.fabric.contract.Context} the other parameters
24+
* are at the developer's discretion.
25+
* <p>
26+
* The sequence of calls is
27+
*
28+
* <pre>
29+
* createContext() -> beforeTransaction() -> the transaction function -> afterTransaction()
30+
* </pre>
31+
* <p>
32+
* If any of these functions throws an exception it is considered an error case,
33+
* and the whole transaction is failed. The {@link org.hyperledger.fabric.contract.Context} is
34+
* a very important object as it provides transactional context for access to current transaction id,
35+
* ledger state, etc.
36+
* <p>
37+
* <b>Note on Threading</b>
38+
* <p>
39+
* All code should be 'Thread Friendly'. Each method must not rely on instance
40+
* fields or class side variables for storage. Nor should they use any
41+
* ThreadLocal Storage. Ledger data is stored via the ledger api available via
42+
* the {@link Context}.
43+
* <p>
44+
* If information needs to be passed from the {@link #beforeTransaction(Context)}
45+
* {@link #afterTransaction(Context, Object)} or between separate transaction functions when
46+
* called directory, then a subclass of the
47+
* {@link Context} should be provided.
48+
* <p>
49+
*
1350
*/
1451
public interface ContractInterface {
15-
/**
16-
* Returns instance of context associated with current invocation
17-
* @return
18-
*/
19-
default Context getContext() { throw new IllegalStateException("getContext default implementation can't be directly invoked"); }
2052

2153
/**
22-
* Create context from {@link ChaincodeStub}, default impl provided, but can be overwritten by contract
23-
* @param stub
24-
* @return
54+
* Create context from {@link ChaincodeStub}, default impl provided, but can be
55+
* overwritten by contract
56+
*
57+
* @param stub Instance of the ChaincodeStub to use for this transaction
58+
* @return instance of the context to use for the current transaciton being
59+
* executed
2560
*/
26-
default Context createContext(ChaincodeStub stub) { return ContextFactory.getInstance().createContext(stub); }
61+
default Context createContext(ChaincodeStub stub) {
62+
return ContextFactory.getInstance().createContext(stub);
63+
}
2764

2865
/**
29-
* Invoked once method for transaction not exist in contract
66+
* Invoked for any transaction that does not exist.
67+
*
68+
* This will throw an exception. If you wish to alter the exception thrown or if
69+
* you wish to consider requests for transactions that don't exist as not an
70+
* error, subclass this method.
71+
*
72+
* @param ctx the context as created by {@link #createContext(ChaincodeStub)}.
3073
*/
31-
default void unknownTransaction() {
32-
throw new IllegalStateException("Undefined contract method called");
74+
default void unknownTransaction(Context ctx) {
75+
throw new IllegalStateException("Undefined contract method called");
3376
}
3477

3578
/**
36-
* Invoke before each transaction method
79+
* Invoked once before each transaction.
80+
*
81+
* Any exceptions thrown will fail the transaction, and neither the required
82+
* transaction or the {@link #afterTransaction(Context, Object)} will be called
83+
*
84+
* @param ctx the context as created by {@link #createContext(ChaincodeStub)}.
3785
*/
38-
default void beforeTransaction() {}
86+
default void beforeTransaction(Context ctx) {
87+
}
3988

4089
/**
41-
* Invoke after each transaction method
90+
* Invoked once after each transaction.
91+
*
92+
* Any exceptions thrown will fail the transaction.
93+
*
94+
* @param ctx the context as created by {@link #createContext(ChaincodeStub)}.
95+
* @param result The object returned from the transaction function if any. As
96+
* this is a Java object and therefore pass-by-reference it is
97+
* possible to modify this object.
4298
*/
43-
default void afterTransaction() {}
99+
default void afterTransaction(Context ctx, Object result) {
100+
}
44101
}

fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/ContractRuntimeException.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77

88
/**
99
* Specific RuntimeException for events that occur in the calling and handling
10-
* of the Contracts.
11-
*
12-
* At some future point we wish to add more diagnostic information into this,
10+
* of the Contracts, NOT within the contract logic itself.
11+
* <p>
12+
* <B>FUTURE</b> At some future point we wish to add more diagnostic information into this,
1313
* for example current tx id
1414
*
1515
*/

fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/annotation/Contract.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,31 @@
1818
* Class level annotation that identifies this class as being a contract. Can
1919
* supply information and an alternative name for the contract rather than the
2020
* classname
21-
*
21+
* <p>
2222
* The Info object can be supplied to provide additional information about the
2323
* contract; the format of this uses the OpenAPI v3 specification of info
24-
* {@see io.swagger.v3.oas.annotations.info.Info}
24+
* {@link io.swagger.v3.oas.annotations.info.Info}
2525
*
2626
*/
2727
@Retention(RUNTIME)
2828
@Target(ElementType.TYPE)
2929
public @interface Contract {
30+
31+
/**
32+
* The Info object can be supplied to provide additional information about the
33+
* contract; the format of this uses the OpenAPI v3 Info format
34+
*
35+
*
36+
* @return OpenAPI v3 specification of info
37+
* {@link io.swagger.v3.oas.annotations.info.Info}
38+
*/
3039
Info info();
3140

41+
/**
42+
* Normally the name of the class is used to refer to the contract (name without package).
43+
* This can be altered if wished.
44+
*
45+
* @return Name of the contract to be used instead of the Classname
46+
*/
3247
String name() default "";
3348
}

fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/annotation/DataType.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,14 @@
1414
/**
1515
* Class level annotation indicating this class represents one of the complex
1616
* types that can be returned or passed to the transaction functions.
17-
*
18-
* This will appear within the metadata
17+
* <p>
18+
* These datatypes are used (within the current implementation) for determining the data flow protocol
19+
* from the Contracts to the SDK and for permitting a fully formed Interface Definition to be created for the
20+
* contract.
21+
* <p>
22+
* Complex types can appear within this definition, and these are identified using this annotation.
23+
* <p>
24+
* <b>FUTURE</b> To take these annotations are also utilize them for leverage storage
1925
*/
2026
@Retention(RUNTIME)
2127
@Target(ElementType.TYPE)

fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/annotation/Property.java

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,34 @@
1212
import java.lang.annotation.Target;
1313

1414
/**
15-
* Property level annotation defining a property of the class (identified by
16-
* {@link @DataType}) Can also be used on the paratemers of transaction
17-
* functions
15+
* Field and parameter level annotation defining a property of the class
16+
* (identified by {@link DataType}) Can also be used on the paratemers of
17+
* transaction functions
18+
* <p>
19+
* Example of using this annotation
20+
*
21+
* <pre>
22+
*
23+
* // max 15 character string, a-z with spaces
24+
* &#64;Property(schema = { "pattern", "^[a-zA-Z\\s]{0,15}$" })
25+
* private String text;
26+
*
27+
* // How friendly is this on a scale of 1-5, 1 being formal, 5 being familar
28+
* &#64;Property(schema = { "minimum", "1", "maximum", "5" })
29+
* private int friendlyness = 1;
30+
*
31+
* </pre>
1832
*/
1933
@Retention(RUNTIME)
2034
@Target({ ElementType.FIELD, ElementType.PARAMETER })
2135
public @interface Property {
36+
37+
/**
38+
* Allows each property to be defined a detail set of rules to determine the
39+
* valid types of this data. The format follows the syntax of the OpenAPI Schema
40+
* object.
41+
*
42+
* @return String array of the key-value pairs of the schema
43+
*/
2244
String[] schema() default {};
2345
}

fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/annotation/Transaction.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,12 @@
1313
import java.lang.annotation.Target;
1414

1515
/**
16-
* Method level annotation indicating the function to be a callable transaction
17-
* function
16+
* Method level annotation indicating the method to be a callable transaction
17+
* function.
18+
* <p>
19+
* These funcitons are called in client SDKs by the combination of <pre> [contractname]:[transactioname] </pre>
20+
* Unless specified other wise, the contract name is the class name (without package) and the transactio
21+
* name is the method name.
1822
*/
1923
@Retention(RUNTIME)
2024
@Target(METHOD)
@@ -26,13 +30,13 @@
2630
* FALSE indicates that this is intended to be called with the evaluate
2731
* semantics
2832
*
29-
* @return
33+
* @return boolean, default is true
3034
*/
3135
boolean submit() default true;
3236

3337
/**
3438
* The name of the callable transaction if it should be different to the method
35-
* name
39+
* name.
3640
*
3741
* @return the transaction name
3842
*/

fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/execution/JSONTransactionSerializer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public JSONTransactionSerializer(TypeRegistry typeRegistry) {
4545
*
4646
* @param value
4747
* @param ts
48-
* @return
48+
* @return Byte buffer
4949
*/
5050
public byte[] toBuffer(Object value, TypeSchema ts) {
5151
logger.debug(() -> "Schema to convert is " + ts);

0 commit comments

Comments
 (0)