Is it possible to use (compose-capability) with the coin.TRANSFER capability? - pact-lang

My contract contains a function that transfers funds with coin.transfer if some conditions are met. It is guarded by a capability (let's call it TRANSFER_WRAPPER). However, when a user calls this function, they must scope their signature to both the TRANSFER_WRAPPER and the coin.TRANSFER capabilities.
Instead, I would like to use compose-capability within TRANSFER_WRAPPER so they only need to scope their signature to the wrapper. For example:
(module test "sender-keyset"
(defcap TRANSFER_WRAPPER (receiver:string amount:decimal)
(compose-capability (coin.TRANSFER "sender" receiver amount))
)
(defun transfer-wrapper (receiver:string amount:decimal)
(with-capability (TRANSFER_WRAPPER receiver amount)
(coin.transfer "sender" receiver amount)
)
)
)
However, attempting to use this produces an unexpected error from the coin-v4 contract:
./root/coin-v4.pact:50:4: Enforce non-upgradeability
at ./root/coin-v4.pact:50:4: (enforce false "Enforce non-upgradeability")
at ./root/coin-v4.pact:49:2: (GOVERNANCE)
at ./test.pact:3:4: (compose-capability (coin.TRANSFER "sender" "receiver" 20.0))
at ./test.pact:7:21: (TRANSFER_WRAPPER "sender" "receiver" 20.0)
at ./test.pact:7:4: (with-capability (test.TRANSFER_WRAPPER "sender" "receiver" 20.0) [(coin.transfer "sender" "receiver" 20.0)])
You can reproduce the error by placing the Pact snippet above in a file called test.pact, and the coin-v4, fungible-xchain-v1, and fungible-v2 contracts into a sibling root directory.
Then, paste the contents of this snippet in a test.repl file and run it with pact test.repl:
(env-data { "sender-keyset": { "keys": [ "sender-key" ], "pred": "keys-all" } })
(env-sigs [{"key": "sender-key", "caps": []}])
(begin-tx)
; The test depends on the 'coin-v4' contract, which in turn depends on these.
(load "./root/fungible-v2.pact")
(load "./root/fungible-xchain-v1.pact")
(load "./root/coin-v4.pact")
(define-keyset "sender-keyset" (read-keyset "sender-keyset"))
(load "./test.pact")
(commit-tx)
(env-sigs [{"key": "sender-key", "caps": [(test.TRANSFER_WRAPPER "sender" "receiver" 100.0)]}])
(test.transfer-wrapper "sender" "receiver" 20.0)

No, you cannot compose another module's capabilities, and your user must scope coin.TRANSFER explicitly, by design.
This ensures that the user's authority over debiting their account can never be delegated away, that the user has explicit and visible control over funds being transferred.
If this were not true, malfeasance could occur. For instance, your wrapper capability could misprepresent how much money was to be transferred, and/or your code could transfer twice, or to a different account, etc.
This is also how Pact avoids the dangerous stateful authorizations seen in EVM contracts. With the TRANSFER capability the user is explicitly allowing any code to execute the transfer but only for that transaction, and only for up to the total amount indicated. So it is actually quite helpful for dapps as it avoids bugs from leftover authorizations.

Related

How to configure checkpointing on an XTDB node using AWS S3

I am using XTDB 1.21.0 deployed on AWS/ECS (Fargate) with checkpoints configured (frequency 30 minutes) and stored on an S3 bucket (RocksDB). After a couple of successful checkpoints, they seem to be constantly failing with an XTDB warning due to an exception in the HTTP request to AWS, as shown below:
This leaves the S3 buckets with incomplete checkpoints (i.e., a Folder containing a set of SSTs and other RocksDB files and no associated EDN index file):
XTDB documentation mentions the fact that an optional S3configurator can be passed to the node configuration and after a bit of Googling around I figured that makeClient should be overridden so that connectionAcquisitionTimeout can be set:
NettyNioAsyncHttpClient.builder()
.maxConcurrency(200)
.connectionAcquisitionTimeout(Duration.ofMillis(20000))
I am not too familiar with NETTY so would appreciate if someone could help with the right incantation.
Also I am configuring the XT node from an EDN file, and haven't figure out how to write a S3 configurator in an EDN file (or if it is even possible).
Thanks in advance!
This can happen for large datasets where the default S3 client used will create a new async request for each object (for which the number of objects may be very large, particularly if using the RockDBs index). Internally it uses the connectionAcquisitionTimeout as a type of backpressure to ensure that incoming requests don't wait indefinitely for a connection from the connection pool, however, in this case we're the only source of these requests and we definitely want the requests to complete before starting the nodes so it's reasonable to set the connectionAcquisitionTimeout to something very high (the default is only 10 seconds). A good choice of limit might be something like the maximum amount of time you want to wait for the node to start before failing.
This appears to be a non-optional parameter of the SDK for what I can only assume is a sensible default strategy for requests coming from an external source, in our case we essentially want it to behave as if it was a synchronous operation.
Configuring this in Clojure with xtdb would look something like this:
(ns foo.db
(:require
[xtdb.api :as xtdb]
[xtdb.checkpoint]
[xtdb.rocksdb]
[xtdb.s3.checkpoint])
(:import
(java.time Duration)
(software.amazon.awssdk.http.nio.netty NettyNioAsyncHttpClient)
(software.amazon.awssdk.services.s3 S3AsyncClient)
(xtdb.checkpoint Checkpointer)
(xtdb.s3 S3Configurator)))
(def s3-configurator
(reify S3Configurator
(makeClient [this]
(.. (S3AsyncClient/builder)
(httpClientBuilder
(.. (NettyNioAsyncHttpClient/builder)
(connectionAcquisitionTimeout
(Duration/ofSeconds 600)) ;; Set a high limit here
;; We can rely on the defaults for maxConcurrency and
;; maxPendingConnectionAcquires
;; (maxConcurrency (Integer. 200))
;; (maxPendingConnectionAcquires (Integer. 10000))
))
(build)))))
(defn start-node!
[]
(xtdb/start-node
{:xtdb/index-store
{:kv-store {:xtdb/module 'xtdb.rocksdb/->kv-store
:db-dir "/var/xtdb/idxs"
:checkpointer {:xtdb/module 'xtdb.checkpoint/->checkpointer
:store {:xtdb/module 'xtdb.s3.checkpoint/->cp-store
:configurator (constantly s3-configurator)
:bucket "checkpoints"}
:approx-frequency "PT3H"}}}}))

How to deploy a smart contract on KADENA

Please how do I deploy a smart contract to the testnet or mainnet WITHOUT Chainweaver web UI? I know I need a YAML file for that, but what do I do with it and where exactly do I send it?
Do I need to run a pact server, chainweb api or...? I couldn't find any guide for that
Step 0: Install the Prerequisites
Install Pact
Step 1: Create the Pact Module
We will be deploying the following pact module. For simplicity's sake the pact code we are deploying is not using a transaction's data field (read-keyset is one such pact function that makes use of this field). Otherwise, the accompanying YAML file will have to change. We also assume that this pact code is saved as test.pact.
(namespace 'free)
(module someModuleName AUTONOMOUS
(defcap AUTONOMOUS ()
true)
(defun dummy ()
(+ 1 2)
)
)
Step 2: Create the YAML file
The following YAML file will be used along with pact -a to sign and produce the escaped JSON needed to submit a transaction to Testnet.
codeFile: /Users/linda.ortega.cordoves/pact/test.pact
networkId: testnet04
publicMeta:
chainId: "0"
gasLimit: 1000
ttl: 28000
creationTime: 1585056536
sender: "testing"
gasPrice: 0.00001
keyPairs:
- public: 1d877a7b4524b6724a6ae708cf9ea7396d6ee9d17b10098b7793800177669c1d
secret: 33fcd94b8a42057bd4e3190f8983e3a73ec96c3f60df95c9e2aa3f13602c714f
nonce: step02
This file makes a couple of assumptions that might change depending on your specific implementation:
The full path of the pact we want to upload is: /Users/linda.ortega.cordoves/pact/test.pact
We want to submit a transaction to Testnet, whose network id is testnet04
We want to submit to the zero'th chain on Testnet, which has a chain id of "0"
That the current creation time in UNIX Epoch time is 1585056536 seconds. This value MUST CHANGE, so calculate it by either navigating to this website or running date +%s on the command line.
That "testing" is the account paying for gas (aka the "sender") on the Testnet network. To create a Testnet account and fund it some coins, navigate to the Testnet Coin Faucet. You will need to have generated an ED22519 public-private key pair to use the faucet. You can use pact -g to generate this key pair. Make sure to save it somewhere save.
That the key pair specified in "keyPairs" corresponds to the key pair used to create the gas payer account, which in this example is "testing". This must change from the defaults provided.
That we saved this YAML file as /Users/linda.ortega.cordoves/pact/test.yaml.
Step 3: Submit Transaction to Testnet
We will now submit the example pact module we created by hitting the /send endpoint of a Testnet node. In the command line, run the following command:
pact -a /Users/linda.ortega.cordoves/pact/test.yaml | curl -H "Content-Type: application/json" -d #- https://us1.testnet.chainweb.com/chainweb/0.0/testnet04/chain/0/pact/api/v1/send
Some of the assumptions we made when creating the YAML file become important here:
The network id must match the node endpoint we submit to. Since the network id we chose is testnet04, we must submit to /chainweb/0.0/testnet04/. And the node we submit to (in this case us1.testnet.chainweb.com) must have this network id.
The chain id must also match. We chose chain id of "0", so we must submit to /chain/0/.
That we saved the yaml file to /Users/linda.ortega.cordoves/pact/test.yaml.
If we submitted the transaction successfully we will see the following:
{"requestKeys":["Vetli41gi_S4-dZlro0visI8QT15brHoPe4vxMmhdek"]}
This means that our transaction was successfully added to the blockchain's mempool and is waiting to be mined. Make note of the request key returned from /send as we will use it when polling for the result of the transaction.
It is also possible that our transaction will fail node validation when we attempt to submit it. If this happens, you will receive a validation failure message instead of the request key.
Step 4: Verify the Result of the Transaction
We will now try to get the results of the transaction we submitted to the Testnet network by hitting the /poll endpoint. In the command line, run the following command:
curl -H "Content-Type: application/json" -d '{"requestKeys":["Vetli41gi_S4-dZlro0visI8QT15brHoPe4vxMmhdek"]}' -X POST https://us1.testnet.chainweb.com/chainweb/0.0/testnet04/chain/0/pact/api/v1/poll
Again, we make a couple of assumptions in this step:
That the Testnet node we want to poll from is us1.testnet.chainweb.com.
That the network id is testnet04. Note that part of the endpoint is /chainweb/0.0/testnet04/.
That the chain id we are polling from is chain "0". Note that part of the endpoint is /chain/0/.
That the request key we are polling for is Vetli41gi_S4-dZlro0visI8QT15brHoPe4vxMmhdek.
If the transaction was successfully mined and thus added to the blockchain, then /poll will return the following JSON object:
{
"Vetli41gi_S4-dZlro0visI8QT15brHoPe4vxMmhdek": {
"gas": 58,
"result": {
"status": "success",
"data": "Loaded module free.linda-test, hash n0g99JhWnO2F7X7f8o_zcAiSHBAWS_QSAfn4yUaqpps"
},
"reqKey": "Vetli41gi_S4-dZlro0visI8QT15brHoPe4vxMmhdek",
"logs": "0KzZQDJmEgnAKvPnO20UeGoE7KGCIN22nhjraeyp1aw",
"metaData": {
"blockTime": 1585056990071469,
"prevBlockHash": "dIYmpjBQge9yw0Yzhn0Sau-wJFwsLOFBmGbV3_0xYeE",
"blockHash": "yULpC5C-7tzRcc9sWm-f1bOC3JDvtxwT61hruW0aXrA",
"blockHeight": 261712
},
"continuation": null,
"txId": 266084
}
}
Please note that it is possible that a transaction fails at the pact level, but still gets added to the blockchain and gas gets charged. If this happens the result.status field will be failure.
If a transaction has not be mined yet, /poll will return {}. Keep retrying until you receive the JSON object shown above.
source: https://gist.github.com/LindaOrtega/1c219f887d9782c6745dbd827bdbfb4d

What are the possible values for X-AppEngine-TaskRetryReason header in Google app engine request headers?

Basically, I am facing an issue while n number of taskqueues are running in the Google Cloud Platform. There is no error in code or server but the execution of the taskqueues got terminated due to instance unavailability by which it will trigger a taskqueue again and again.
I know a few reasons by which this kind of termination process takes place.
Reasons:
Instance Unavailable
App Error / AppEngine Error
Memory Exceeded
I want to know the other possible values for the X-AppEngine-TaskRetryReason header.
For example (the response of GAE):
self.request.headers {'Content_Length': '432', 'Content-Length': '432', 'X-Appengine-Current-Namespace': '75f4910a-b925-4945-92f0-b214a459f0be', 'X-Appengine-Taskexecutioncount': '1', 'X-Appengine-Tasketa': '1624452214.545367', 'User-Agent': 'AppEngine-Google; (+http://code.google.com/appengine)', 'X-Appengine-Taskpreviousresponse': '503', 'Host': 'payqa-dot-hw-pay.qa.appspot.com', 'X-Appengine-Taskretrycount': '2', 'Referer': 'http://payqa-dot-hw-pay.qa-.appspot.com/pay/runpayroll', 'Content_Type': 'application/octet-stream', 'X-Cloud-Trace-Context': 'd44fdfd56bc7733afb3169fb354b80ed/6659926505008598367', 'Traceparent': '00-d44fdfd56bc7733afb3169fb354b80ed-5c6ccfded93f0d5f-00', 'X-Appengine-Queuename': 'payroll', 'X-Appengine-Taskname': '21925984910338157231', 'Content-Type': 'application/octet-stream', 'X-Appengine-Country': 'ZZ', **'X-Appengine-Taskretryreason': 'Instance Unavailable'**}
Like I mentioned in the comments there is no listing in the documentation for the possible values of X-AppEngine-TaskRetryReason and it only states that it represents:
The reason for retrying the task.
That being said there is two possibilities why this happens, either this has no specific value and just spits out whatever message it is passed to it by the actual class or component that generated the failure of the execution of the tasks or this is not being shared because the Google Cloud team did not considered it necessary.
Either way if you want to know why this happens and what values you can expect, you should open a Customer issue in Google's Issue Tracker so you can check why this is not shared in the documentation with their Engineering team.

Google AppEngine application log assigned to the wrong request log

When I look at the logs in the Google Log Viewer for my GAE project, I see that often the logs that I write myself in the code are assigned to the wrong request. Most of the time the log is assigned to the request directly after the request that produced the log entry.
As the root of every application log in GAE must be a request, this means that the wrong request is sometimes marked as error, because another request before produced an error, but the log is somehow assigned to the request after that.
I don't really do anything special, I use Ktor as my servlet and have an interceptor that creates a log when an exception occurs before returning status 500.
I use Java logging via SLF4J with the google cloud logging handler, but before that I used logback via SLf4J and had the same problem.
The content of the logs itself is also correct, the returned status of the request, the level of the log entry, the message, everything is ok.
I thought that it may be because I use kotlin and switch coroutine contexts during a single request, but in some cases the point where I write the log and where I send the response are exactly next to each other, so I'm not sure if kotlin has anything to do with it.
My logging.properties:
# To use this configuration, add to system properties : -Djava.util.logging.config.file="/path/to/file"
#
.level = INFO
# it is recommended that io.grpc and sun.net logging level is kept at INFO level,
# as both these packages are used by Stackdriver internals and can result in verbose / initialization problems.
io.grpc.netty.level=INFO
sun.net.level=INFO
handlers=com.google.cloud.logging.LoggingHandler
# default : java.log
com.google.cloud.logging.LoggingHandler.log=custom_log
# default : INFO
com.google.cloud.logging.LoggingHandler.level=INFO
# default : ERROR
com.google.cloud.logging.LoggingHandler.flushLevel=WARNING
# default : auto-detected, fallback "global"
#com.google.cloud.logging.LoggingHandler.resourceType=container
# custom formatter
com.google.cloud.logging.LoggingHandler.formatter=java.util.logging.SimpleFormatter
java.util.logging.SimpleFormatter.format=%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS %4$-6s %2$s %5$s%6$s%n
#optional enhancers (to add additional fields, labels)
#com.google.cloud.logging.LoggingHandler.enhancers=com.example.logging.jul.enhancers.ExampleEnhancer
My logging relevant dependencies:
implementation "org.slf4j:slf4j-jdk14:1.7.30"
implementation "com.google.cloud:google-cloud-logging:1.100.0"
An example logging call:
exception<Throwable> { e ->
logger().error("Error", e)
call.respondText(e.message ?: "", ContentType.Text.Plain, HttpStatusCode.InternalServerError)
}
with logger() being:
import org.slf4j.Logger
import org.slf4j.LoggerFactory
inline fun <reified T : Any> T.logger(): Logger = LoggerFactory.getLogger(T::class.java)
Edit:
An example of the log in Google cloud. The first request has the query parameter GAID=cdda802e-fb9c-47ad-0794d394c913, but as you can see the error log for that request is in the one below, marked in red.

Silverlight RIA Services - How To Best Handle Client Auth Session Timeout?

I built an app with Silverlight4, RIA Services, and I'm using ASP.NET Membership for authentication/authorization.
My web.config has this:
<system.web>
<sessionState timeout="20"/>
<authentication mode="Forms">
<forms name="_ASPXAUTH" timeout="20"/>
</authentication>
I have read a number of different strategies on how to deal with auth/session timeout on the client side. That is: if the client is idle for x minutes (20 here), and then they do something with the UI that triggers a RIA/WCF call, I want to trap on that event and deal with appropriately (e.g. take them back to the login screen) -- in a nutshell: I need a way to differentiate from a bona-fide server side DomainException vs. an auth failure because the session timed out.
AFAIK: there is no typed exception or property that can determine this. The only way I've been able to determine this -- which seems like a hack: is to inspect the Error's Message string and look for something like "Access denied" or "denied". For example: something like this:
if (ex.Message.Contains("denied"))
// this is probably an auth failure b/c of a session timeout
So, this is what I'm currently doing, and it works if I run and debug either with the built-in server from VS2010, or if I run in localhost IIS. If I set the timeout to 1 minute, login, wait more than a minute and trigger another call, I breakpoint on the exception and enter the if code block above and all is well.
Then I deploy the app to a remote IIS7 server and I try the same test and it doesn't work. So, I added log tracing, and here's the event where the exception happened:
<E2ETraceEvent xmlns="http://schemas.microsoft.com/2004/06/E2ETraceEvent">
<System xmlns="http://schemas.microsoft.com/2004/06/windows/eventlog/system">
<EventID>131076</EventID>
<Type>3</Type>
<SubType Name="Error">0</SubType>
<Level>2</Level>
<TimeCreated SystemTime="2011-10-30T22:13:54.6425781Z" />
<Source Name="System.ServiceModel" />
<Correlation ActivityID="{20c26991-372f-430f-913b-1b72a261863d}" />
<Execution ProcessName="w3wp" ProcessID="4316" ThreadID="24" />
<Channel />
<Computer>TESTPROD-HOST</Computer>
</System>
<ApplicationData>
<TraceData>
<DataItem>
<TraceRecord xmlns="http://schemas.microsoft.com/2004/10/E2ETraceEvent/TraceRecord" Severity="Error">
<TraceIdentifier>http://msdn.microsoft.com/en-US/library/System.ServiceModel.Diagnostics.TraceHandledException.aspx</TraceIdentifier>
<Description>Handling an exception.</Description>
<AppDomain>/LM/W3SVC/1/ROOT/sla-2-129644844652558594</AppDomain>
<Exception>
<ExceptionType>System.ServiceModel.FaultException`1[[System.ServiceModel.DomainServices.Hosting.DomainServiceFault, System.ServiceModel.DomainServices.Hosting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]], System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType>
<Message></Message>
<StackTrace>
at System.ServiceModel.DomainServices.Hosting.QueryOperationBehavior`1.QueryOperationInvoker.InvokeCore(Object instance, Object[] inputs, Object[]& outputs)
at System.ServiceModel.DomainServices.Hosting.DomainOperationInvoker.Invoke(Object instance, Object[] inputs, Object[]& outputs)
at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage31(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)
</StackTrace>
<ExceptionString>System.ServiceModel.FaultException`1[System.ServiceModel.DomainServices.Hosting.DomainServiceFault]: (Fault Detail is equal to System.ServiceModel.DomainServices.Hosting.DomainServiceFault).</ExceptionString>
</Exception>
</TraceRecord>
</DataItem>
</TraceData>
</ApplicationData>
</E2ETraceEvent>
The problem is that I don't have the string in the error message that indicates "denied" or "Access denied" - and I am unsure as to why this solution works in localhost IIS or VS2010 host but not in a remote IIS7 server. Is there some obscure configuration setting that I'm missing here? Is there a better way to do this in general?
You've probably gotten by this by now, but this article describes using the DomainOperationException and checking the error codes.
dex.ErrorCode == ErrorCodes.NotAuthenticated || dex.ErrorCode == ErrorCodes.Unauthorized
For convenient access (and in case the we loose access to the blog) here's the blog article by Josh Eastburn:
A question that comes up often from developers who are working with Silverlight and WCF RIA Services: why does my Silverlight application throw an exception when it has been idle for a period of time? As you might expect, it is due to the authenticated session timing out. But it isn’t quite that straightforward. Because Silverlight uses a client/server architecture, the client can operate independent of the server for an indefinite period of time. It is only when the Silverlight client makes a call to the server that the server-side timeout is realized. There are a few options to handle the client-server timeout issue (and you may be able to come up with a few more): If you aren’t concerned with the security implications of removing a session timeout, you can either increase the timeout setting in web.config, or create a DispatcherTimer in the Silverlight client that calls a simple method on the server to act as a "Keep Alive." Add a DispatcherTimer to the Silverlight client that stays in sync with the server-side timeout and warn/prompt the user keep the session active before the time expires or have them re-authenticate if it has already expired. However, this requires extra effort to keep the timers in sync when new server requests are made. Allow the server to handle the timeout as it normally would and handle the timeout gracefully on the Silverlight client. This means that the timeout is determined by server call activity, NOT activity confined the Silverlight client (i.e. accessing client-side data in the context). Of these three options, I find the third to be the best balance of security and usability while at the same time not adding unnecessary complexity to the application. In order to handle these server-side timeouts globally, you can add the following logic in either the Application_UnhandledException method in App.xaml.cs or in your global ViewModel loading construct if you have one:
// Check for Server-Side Session Timeout Exception
var dex = e.ExceptionObject as DomainOperationException;
if ((dex != null) && (dex.ErrorCode == ErrorCodes.NotAuthenticated || dex.ErrorCode == ErrorCodes.Unauthorized) && WebContext.Current.User.IsAuthenticated)
{
// A server-side timeout has occurred. Call LoadUser which will automatically
// authenticate if "Remember Me" was checked, or prompt for the user to log on again
WebContext.Current.Authentication.LoadUser(Application_UserLoaded, null);
e.Handled = true;
}
The following constants are defined within the ErrorCodes class:
public static class ErrorCodes
{
public const int NotAuthenticated = 0xA01;
public const int Unauthorized = 401;
}
When the server-side session times out, any subsequent calls will return a DomainOperationException. By inspecting the returned ErrorCode, you can determine if it is an authentication error and handle it accordingly. In my example, I am calling WebContext.Current.Authentication.LoadUser() which will attempt to re-authenticate the user if possible. Even if the user can not be automatically re-authenticated, it will call back to my Application_UserLoaded method. There I can check WebContext.Current.User.IsAuthenticated to determine whether to proceed with the previous operation or if I need to redirect back to the home page and reprompt for login. Here is an example of some code in the Appliation_UserLoaded callback that shows a login dialog if the user is not authenticated:
// Determine if the user is authenticated
if (!WebContext.Current.User.IsAuthenticated)
{
// Show login dialog automatically
LoginRegistrationWindow loginWindow = new LoginRegistrationWindow();
loginWindow.Show();
}
To test your code, you can set your timeout value in web.config to a
small value so timeouts occur quickly:
<authentication mode="Forms">
<forms name=".Falafel_ASPXAUTH" timeout="1" />
</authentication>
If you’d like to see all of this code in a working solution, check out our Silverlight RIA Template on CodePlex.

Resources