Skip to content

Commit

Permalink
Merge pull request #139 from kaleido-io/noto
Browse files Browse the repository at this point in the history
Noto enhancements and fixes
  • Loading branch information
peterbroadhurst authored Sep 13, 2024
2 parents 8d24e18 + e20f84a commit 2404bbe
Show file tree
Hide file tree
Showing 26 changed files with 546 additions and 242 deletions.
1 change: 0 additions & 1 deletion domains/noto/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ task copySolidity(type: Copy) {
from fileTree(configurations.contractCompile.asPath) {
include 'contracts/domains/noto/NotoFactory.sol/NotoFactory.json'
include 'contracts/domains/noto/Noto.sol/Noto.json'
include 'contracts/domains/noto/NotoSelfSubmitFactory.sol/NotoSelfSubmitFactory.json'
include 'contracts/domains/noto/NotoSelfSubmit.sol/NotoSelfSubmit.json'
}
into 'internal/noto/abis'
Expand Down
2 changes: 1 addition & 1 deletion domains/noto/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ go 1.22.5

require (
github.com/go-resty/resty/v2 v2.14.0
github.com/hyperledger/firefly-common v1.4.8
github.com/hyperledger/firefly-signer v1.1.14
github.com/kaleido-io/paladin/core v0.0.0-00010101000000-000000000000
github.com/kaleido-io/paladin/toolkit v0.0.0-00010101000000-000000000000
Expand Down Expand Up @@ -37,6 +36,7 @@ require (
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/hyperledger-labs/zeto/go-sdk v0.0.0-20240905213624-43a614759076 // indirect
github.com/hyperledger/firefly-common v1.4.8 // indirect
github.com/iden3/go-iden3-crypto v0.0.16 // indirect
github.com/iden3/go-rapidsnark/prover v0.0.10 // indirect
github.com/iden3/go-rapidsnark/types v0.0.2 // indirect
Expand Down
112 changes: 84 additions & 28 deletions domains/noto/internal/noto/e2e_noto_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,16 @@ import (

"github.com/go-resty/resty/v2"

"github.com/hyperledger/firefly-common/pkg/log"
"github.com/hyperledger/firefly-signer/pkg/abi"
"github.com/hyperledger/firefly-signer/pkg/ethtypes"
"github.com/hyperledger/firefly-signer/pkg/rpcbackend"
"github.com/kaleido-io/paladin/core/pkg/blockindexer"
"github.com/kaleido-io/paladin/core/pkg/ethclient"
"github.com/kaleido-io/paladin/core/pkg/testbed"
"github.com/kaleido-io/paladin/domains/noto/pkg/types"
"github.com/kaleido-io/paladin/toolkit/pkg/algorithms"
"github.com/kaleido-io/paladin/toolkit/pkg/domain"
"github.com/kaleido-io/paladin/toolkit/pkg/log"
"github.com/kaleido-io/paladin/toolkit/pkg/plugintk"
"github.com/kaleido-io/paladin/toolkit/pkg/tktypes"
"github.com/stretchr/testify/assert"
Expand All @@ -46,7 +50,7 @@ func toJSON(t *testing.T, v any) []byte {
return result
}

func mapConfig(t *testing.T, config *types.Config) (m map[string]any) {
func mapConfig(t *testing.T, config *types.DomainConfig) (m map[string]any) {
configJSON, err := json.Marshal(&config)
require.NoError(t, err)
err = json.Unmarshal(configJSON, &m)
Expand Down Expand Up @@ -75,22 +79,38 @@ func deployContracts(ctx context.Context, t *testing.T, contracts map[string][]b
return deployed
}

func newTestDomain(t *testing.T, domainName string, config *types.Config) (context.CancelFunc, *Noto, rpcbackend.Backend) {
var domain *Noto
func newNotoDomain(t *testing.T, config *types.DomainConfig) (*Noto, *testbed.TestbedDomain) {
var domain Noto
return &domain, &testbed.TestbedDomain{
Config: mapConfig(t, config),
Plugin: plugintk.NewDomain(func(callbacks plugintk.DomainCallbacks) plugintk.DomainAPI {
domain.Callbacks = callbacks
return &domain
}),
}
}

func newTestbed(t *testing.T, domains map[string]*testbed.TestbedDomain) (context.CancelFunc, testbed.Testbed, rpcbackend.Backend) {
tb := testbed.NewTestBed()
plugin := plugintk.NewDomain(func(callbacks plugintk.DomainCallbacks) plugintk.DomainAPI {
domain = &Noto{Callbacks: callbacks}
return domain
})
url, done, err := tb.StartForTest("../../testbed.config.yaml", map[string]*testbed.TestbedDomain{
domainName: {
Config: mapConfig(t, config),
Plugin: plugin,
},
})
require.NoError(t, err)
url, done, err := tb.StartForTest("../../testbed.config.yaml", domains)
assert.NoError(t, err)
rpc := rpcbackend.NewRPCClient(resty.New().SetBaseURL(url))
return done, domain, rpc
return done, tb, rpc
}

func functionBuilder(ctx context.Context, t *testing.T, eth ethclient.EthClient, abi abi.ABI, functionName string) ethclient.ABIFunctionRequestBuilder {
abiClient, err := eth.ABI(ctx, abi)
assert.NoError(t, err)
fn, err := abiClient.Function(ctx, functionName)
assert.NoError(t, err)
return fn.R(ctx)
}

func waitFor(ctx context.Context, t *testing.T, tb testbed.Testbed, txHash *tktypes.Bytes32, err error) *blockindexer.IndexedTransaction {
require.NoError(t, err)
tx, err := tb.Components().BlockIndexer().WaitForTransaction(ctx, *txHash)
assert.NoError(t, err)
return tx
}

func TestNoto(t *testing.T) {
Expand All @@ -108,15 +128,25 @@ func TestNoto(t *testing.T) {
log.L(ctx).Infof("%s deployed to %s", name, address)
}

done, noto, rpc := newTestDomain(t, domainName, &types.Config{
noto, notoTestbed := newNotoDomain(t, &types.DomainConfig{
FactoryAddress: contracts["factory"],
})
done, tb, rpc := newTestbed(t, map[string]*testbed.TestbedDomain{
domainName: notoTestbed,
})
defer done()

_, notaryKey, err := tb.Components().KeyManager().ResolveKey(ctx, notaryName, algorithms.ECDSA_SECP256K1_PLAINBYTES)
require.NoError(t, err)
_, recipient1Key, err := tb.Components().KeyManager().ResolveKey(ctx, recipient1Name, algorithms.ECDSA_SECP256K1_PLAINBYTES)
require.NoError(t, err)

log.L(ctx).Infof("Deploying an instance of Noto")
var notoAddress ethtypes.Address0xHex
rpcerr := rpc.CallRPC(ctx, &notoAddress, "testbed_deploy",
domainName, &types.ConstructorParams{Notary: notaryName})
domainName, &types.ConstructorParams{
Notary: notaryName,
})
if rpcerr != nil {
require.NoError(t, rpcerr.Error())
}
Expand All @@ -142,7 +172,7 @@ func TestNoto(t *testing.T) {
require.NoError(t, err)
require.Len(t, coins, 1)
assert.Equal(t, int64(100), coins[0].Amount.Int64())
assert.Equal(t, notaryName, coins[0].Owner)
assert.Equal(t, notaryKey, coins[0].Owner.String())

log.L(ctx).Infof("Attempt mint from non-notary (should fail)")
rpcerr = rpc.CallRPC(ctx, &boolResult, "testbed_invoke", &tktypes.PrivateContractInvoke{
Expand Down Expand Up @@ -192,13 +222,13 @@ func TestNoto(t *testing.T) {
// This should have been spent
// TODO: why does it still exist?
assert.Equal(t, int64(100), coins[0].Amount.Int64())
assert.Equal(t, notaryName, coins[0].Owner)
assert.Equal(t, notaryKey, coins[0].Owner.String())

// These are the expected coins after the transfer
assert.Equal(t, int64(50), coins[1].Amount.Int64())
assert.Equal(t, recipient1Name, coins[1].Owner)
assert.Equal(t, recipient1Key, coins[1].Owner.String())
assert.Equal(t, int64(50), coins[2].Amount.Int64())
assert.Equal(t, notaryName, coins[2].Owner)
assert.Equal(t, notaryKey, coins[2].Owner.String())

log.L(ctx).Infof("Transfer 50 from recipient1 to recipient2")
rpcerr = rpc.CallRPC(ctx, &boolResult, "testbed_invoke", &tktypes.PrivateContractInvoke{
Expand Down Expand Up @@ -227,23 +257,49 @@ func TestNotoSelfSubmit(t *testing.T) {

log.L(ctx).Infof("Deploying Noto factory")
contractSource := map[string][]byte{
"factory": notoSelfSubmitFactoryJSON,
"factory": notoFactoryJSON,
"noto": notoSelfSubmitJSON,
}
contracts := deployContracts(ctx, t, contractSource)
for name, address := range contracts {
log.L(ctx).Infof("%s deployed to %s", name, address)
}

done, noto, rpc := newTestDomain(t, domainName, &types.Config{
FactoryAddress: contracts["factory"],
Variant: "NotoSelfSubmit",
factoryAddress, err := ethtypes.NewAddress(contracts["factory"])
require.NoError(t, err)

noto, notoTestbed := newNotoDomain(t, &types.DomainConfig{
FactoryAddress: factoryAddress.String(),
})
done, tb, rpc := newTestbed(t, map[string]*testbed.TestbedDomain{
domainName: notoTestbed,
})
defer done()

_, notaryKey, err := tb.Components().KeyManager().ResolveKey(ctx, notaryName, algorithms.ECDSA_SECP256K1_PLAINBYTES)
require.NoError(t, err)

eth := tb.Components().EthClientFactory().HTTPClient()
notoFactory := domain.LoadBuild(notoFactoryJSON)
txHash, err := functionBuilder(ctx, t, eth, notoFactory.ABI, "registerImplementation").
Signer(notaryName).
To(factoryAddress).
Input(map[string]any{
"name": "selfsubmit",
"implementation": contracts["noto"],
}).
SignAndSend()
require.NoError(t, err)
waitFor(ctx, t, tb, txHash, err)

log.L(ctx).Infof("Deploying an instance of Noto")
var notoAddress ethtypes.Address0xHex
rpcerr := rpc.CallRPC(ctx, &notoAddress, "testbed_deploy",
domainName, &types.ConstructorParams{Notary: notaryName})
domainName, &types.ConstructorParams{
Notary: notaryName,
Implementation: "selfsubmit",
},
)
if rpcerr != nil {
require.NoError(t, rpcerr.Error())
}
Expand All @@ -269,7 +325,7 @@ func TestNotoSelfSubmit(t *testing.T) {
require.NoError(t, err)
assert.Len(t, coins, 1)
assert.Equal(t, int64(100), coins[0].Amount.Int64())
assert.Equal(t, notaryName, coins[0].Owner)
assert.Equal(t, notaryKey, coins[0].Owner.String())

log.L(ctx).Infof("Transfer 50 from notary to recipient1")
rpcerr = rpc.CallRPC(ctx, &boolResult, "testbed_invoke", &tktypes.PrivateContractInvoke{
Expand Down
25 changes: 20 additions & 5 deletions domains/noto/internal/noto/handler_mint.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"encoding/json"
"fmt"

"github.com/hyperledger/firefly-signer/pkg/ethtypes"
"github.com/kaleido-io/paladin/domains/noto/pkg/types"
"github.com/kaleido-io/paladin/toolkit/pkg/algorithms"
"github.com/kaleido-io/paladin/toolkit/pkg/domain"
Expand All @@ -45,6 +46,8 @@ func (h *mintHandler) ValidateParams(ctx context.Context, params string) (interf
}

func (h *mintHandler) Init(ctx context.Context, tx *types.ParsedTransaction, req *pb.InitTransactionRequest) (*pb.InitTransactionResponse, error) {
params := tx.Params.(*types.MintParams)

if req.Transaction.From != tx.DomainConfig.NotaryLookup {
return nil, fmt.Errorf("mint can only be initiated by notary")
}
Expand All @@ -54,21 +57,31 @@ func (h *mintHandler) Init(ctx context.Context, tx *types.ParsedTransaction, req
Lookup: tx.DomainConfig.NotaryLookup,
Algorithm: algorithms.ECDSA_SECP256K1_PLAINBYTES,
},
// TODO: should we also resolve "To" party?
{
Lookup: params.To,
Algorithm: algorithms.ECDSA_SECP256K1_PLAINBYTES,
},
},
}, nil
}

func (h *mintHandler) Assemble(ctx context.Context, tx *types.ParsedTransaction, req *pb.AssembleTransactionRequest) (*pb.AssembleTransactionResponse, error) {
params := tx.Params.(*types.MintParams)

notary := domain.FindVerifier(tx.DomainConfig.NotaryLookup, req.ResolvedVerifiers)
notary := domain.FindVerifier(tx.DomainConfig.NotaryLookup, algorithms.ECDSA_SECP256K1_PLAINBYTES, req.ResolvedVerifiers)
if notary == nil || notary.Verifier != tx.DomainConfig.NotaryAddress {
// TODO: do we need to verify every time?
return nil, fmt.Errorf("notary resolved to unexpected address")
}
to := domain.FindVerifier(params.To, algorithms.ECDSA_SECP256K1_PLAINBYTES, req.ResolvedVerifiers)
if to == nil {
return nil, fmt.Errorf("error verifying recipient address")
}
toAddress, err := ethtypes.NewAddress(to.Verifier)
if err != nil {
return nil, err
}

_, outputStates, err := h.noto.prepareOutputs(params.To, params.Amount)
_, outputStates, err := h.noto.prepareOutputs(*toAddress, params.Amount)
if err != nil {
return nil, err
}
Expand All @@ -79,6 +92,8 @@ func (h *mintHandler) Assemble(ctx context.Context, tx *types.ParsedTransaction,
OutputStates: outputStates,
},
AttestationPlan: []*pb.AttestationRequest{
// Notary will endorse the assembled transaction (by submitting to the ledger)
// Note no additional attestation using req.Transaction.From, because it is guaranteed to be the notary
{
Name: "notary",
AttestationType: pb.AttestationType_ENDORSE,
Expand Down Expand Up @@ -121,7 +136,7 @@ func (h *mintHandler) Prepare(ctx context.Context, tx *types.ParsedTransaction,

params := map[string]interface{}{
"outputs": outputs,
"signature": "0x",
"signature": "0x", // no signature, because requester AND submitter are always the notary
"data": req.Transaction.TransactionId,
}
paramsJSON, err := json.Marshal(params)
Expand Down
Loading

0 comments on commit 2404bbe

Please sign in to comment.