mirror of
https://github.com/bigchaindb/js-bigchaindb-driver.git
synced 2024-11-22 09:46:58 +01:00
Merge pull request #304 from bigchaindb/delegated-signature
Delegated signature
This commit is contained in:
commit
6dbafa8fad
19
README.md
19
README.md
@ -62,6 +62,8 @@ import driver from 'bigchaindb-driver'
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
const driver = require('bigchaindb-driver')
|
const driver = require('bigchaindb-driver')
|
||||||
|
const base58 = require('bs58');
|
||||||
|
const { Ed25519Sha256 } = require('crypto-conditions');
|
||||||
|
|
||||||
// BigchainDB server instance (e.g. https://example.com/api/v1/)
|
// BigchainDB server instance (e.g. https://example.com/api/v1/)
|
||||||
const API_PATH = 'http://localhost:9984/api/v1/'
|
const API_PATH = 'http://localhost:9984/api/v1/'
|
||||||
@ -89,6 +91,21 @@ const tx = driver.Transaction.makeCreateTransaction(
|
|||||||
// Sign the transaction with private keys
|
// Sign the transaction with private keys
|
||||||
const txSigned = driver.Transaction.signTransaction(tx, alice.privateKey)
|
const txSigned = driver.Transaction.signTransaction(tx, alice.privateKey)
|
||||||
|
|
||||||
|
// Or use delegateSignTransaction to provide your own signature function
|
||||||
|
function signTransaction() {
|
||||||
|
// get privateKey from somewhere
|
||||||
|
const privateKeyBuffer = Buffer.from(base58.decode(alice.privateKey))
|
||||||
|
return function sign(transaction, input, transactionHash) {
|
||||||
|
const ed25519Fulfillment = new Ed25519Sha256();
|
||||||
|
ed25519Fulfillment.sign(
|
||||||
|
Buffer.from(transactionHash, 'hex'),
|
||||||
|
privateKeyBuffer
|
||||||
|
);
|
||||||
|
return ed25519Fulfillment.serializeUri();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const txSigned = driver.Transaction.delegateSignTransaction(tx, signTransaction())
|
||||||
|
|
||||||
// Send the transaction off to BigchainDB
|
// Send the transaction off to BigchainDB
|
||||||
const conn = new driver.Connection(API_PATH)
|
const conn = new driver.Connection(API_PATH)
|
||||||
|
|
||||||
@ -193,7 +210,7 @@ See the file named [RELEASE_PROCESS.md](RELEASE_PROCESS.md).
|
|||||||
## Authors
|
## Authors
|
||||||
|
|
||||||
* inspired by [`js-bigchaindb-quickstart`](https://github.com/sohkai/js-bigchaindb-quickstart) of @sohkhai [thanks]
|
* inspired by [`js-bigchaindb-quickstart`](https://github.com/sohkai/js-bigchaindb-quickstart) of @sohkhai [thanks]
|
||||||
* BigchainDB <devs@bigchaindb.com>
|
* BigchainDB <contact@ipdb.global>
|
||||||
* BigchainDB contributors
|
* BigchainDB contributors
|
||||||
|
|
||||||
## Licenses
|
## Licenses
|
||||||
|
@ -33,7 +33,7 @@ services:
|
|||||||
retries: 3
|
retries: 3
|
||||||
command: -l DEBUG start
|
command: -l DEBUG start
|
||||||
tendermint:
|
tendermint:
|
||||||
image: tendermint/tendermint:0.22.8
|
image: tendermint/tendermint:v0.31.5
|
||||||
# volumes:
|
# volumes:
|
||||||
# - ./tmdata:/tendermint
|
# - ./tmdata:/tendermint
|
||||||
entrypoint: ''
|
entrypoint: ''
|
||||||
|
@ -255,4 +255,30 @@ export default class Transaction {
|
|||||||
signedTx.id = sha256Hash(serializedSignedTransaction)
|
signedTx.id = sha256Hash(serializedSignedTransaction)
|
||||||
return signedTx
|
return signedTx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delegate signing of the given `transaction` returning a new copy of `transaction`
|
||||||
|
* that's been signed.
|
||||||
|
* @param {Object} transaction Transaction to sign. `transaction` is not modified.
|
||||||
|
* @param {Function} signFn Function signing the transaction, expected to return the fulfillment.
|
||||||
|
* @returns {Object} The signed version of `transaction`.
|
||||||
|
*/
|
||||||
|
static delegateSignTransaction(transaction, signFn) {
|
||||||
|
const signedTx = clone(transaction)
|
||||||
|
const serializedTransaction =
|
||||||
|
Transaction.serializeTransactionIntoCanonicalString(transaction)
|
||||||
|
|
||||||
|
signedTx.inputs.forEach((input) => {
|
||||||
|
const transactionUniqueFulfillment = input.fulfills ? serializedTransaction
|
||||||
|
.concat(input.fulfills.transaction_id)
|
||||||
|
.concat(input.fulfills.output_index) : serializedTransaction
|
||||||
|
const transactionHash = sha256Hash(transactionUniqueFulfillment)
|
||||||
|
const fulfillmentUri = signFn(input, transactionHash)
|
||||||
|
input.fulfillment = fulfillmentUri
|
||||||
|
})
|
||||||
|
|
||||||
|
const serializedSignedTransaction = Transaction.serializeTransactionIntoCanonicalString(signedTx)
|
||||||
|
signedTx.id = sha256Hash(serializedSignedTransaction)
|
||||||
|
return signedTx
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
// Code is Apache-2.0 and docs are CC-BY-4.0
|
// Code is Apache-2.0 and docs are CC-BY-4.0
|
||||||
|
|
||||||
import test from 'ava'
|
import test from 'ava'
|
||||||
|
import base58 from 'bs58'
|
||||||
|
import { Ed25519Sha256 } from 'crypto-conditions'
|
||||||
import { Transaction, Ed25519Keypair } from '../src'
|
import { Transaction, Ed25519Keypair } from '../src'
|
||||||
// TODO: Find out if ava has something like conftest, if so put this there.
|
// TODO: Find out if ava has something like conftest, if so put this there.
|
||||||
|
|
||||||
@ -31,6 +33,21 @@ export const bob = new Ed25519Keypair()
|
|||||||
export const bobCondition = Transaction.makeEd25519Condition(bob.publicKey)
|
export const bobCondition = Transaction.makeEd25519Condition(bob.publicKey)
|
||||||
export const bobOutput = Transaction.makeOutput(bobCondition)
|
export const bobOutput = Transaction.makeOutput(bobCondition)
|
||||||
|
|
||||||
|
export function delegatedSignTransaction(...keyPairs) {
|
||||||
|
return function sign(input, transactionHash) {
|
||||||
|
const filteredKeyPairs = keyPairs.filter(({ publicKey }) =>
|
||||||
|
input.owners_before.includes(publicKey))
|
||||||
|
const ed25519Fulfillment = new Ed25519Sha256()
|
||||||
|
filteredKeyPairs.forEach(keyPair => {
|
||||||
|
const privateKey = Buffer.from(base58.decode(keyPair.privateKey))
|
||||||
|
ed25519Fulfillment.sign(
|
||||||
|
Buffer.from(transactionHash, 'hex'),
|
||||||
|
privateKey
|
||||||
|
)
|
||||||
|
})
|
||||||
|
return ed25519Fulfillment.serializeUri()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: https://github.com/avajs/ava/issues/1190
|
// TODO: https://github.com/avajs/ava/issues/1190
|
||||||
test('', () => 'dirty hack. TODO: Exclude this file from being run by ava')
|
test('', () => 'dirty hack. TODO: Exclude this file from being run by ava')
|
||||||
|
@ -13,7 +13,8 @@ import {
|
|||||||
bob,
|
bob,
|
||||||
bobOutput,
|
bobOutput,
|
||||||
asset,
|
asset,
|
||||||
metaData
|
metaData,
|
||||||
|
delegatedSignTransaction
|
||||||
} from '../constants'
|
} from '../constants'
|
||||||
|
|
||||||
|
|
||||||
@ -202,6 +203,58 @@ test('Valid TRANSFER transaction with multiple Ed25519 inputs from different tra
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('Valid CREATE transaction using delegateSign with default node', t => {
|
||||||
|
const conn = new Connection()
|
||||||
|
|
||||||
|
const tx = Transaction.makeCreateTransaction(
|
||||||
|
asset(),
|
||||||
|
metaData,
|
||||||
|
[aliceOutput],
|
||||||
|
alice.publicKey
|
||||||
|
)
|
||||||
|
|
||||||
|
const txSigned = Transaction.delegateSignTransaction(
|
||||||
|
tx,
|
||||||
|
delegatedSignTransaction(alice)
|
||||||
|
)
|
||||||
|
|
||||||
|
return conn.postTransaction(txSigned)
|
||||||
|
.then(resTx => {
|
||||||
|
t.truthy(resTx)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Valid TRANSFER transaction with multiple Ed25519 inputs using delegateSign', t => {
|
||||||
|
const conn = new Connection(API_PATH)
|
||||||
|
const createTx = Transaction.makeCreateTransaction(
|
||||||
|
asset(),
|
||||||
|
metaData,
|
||||||
|
[aliceOutput, bobOutput],
|
||||||
|
alice.publicKey
|
||||||
|
)
|
||||||
|
const createTxSigned = Transaction.signTransaction(
|
||||||
|
createTx,
|
||||||
|
alice.privateKey
|
||||||
|
)
|
||||||
|
|
||||||
|
return conn.postTransactionCommit(createTxSigned)
|
||||||
|
.then(() => {
|
||||||
|
const transferTx = Transaction.makeTransferTransaction(
|
||||||
|
[{ tx: createTxSigned, output_index: 0 }, { tx: createTxSigned, output_index: 1 }],
|
||||||
|
[Transaction.makeOutput(aliceCondition, '2')],
|
||||||
|
metaData
|
||||||
|
)
|
||||||
|
|
||||||
|
const transferTxSigned = Transaction.delegateSignTransaction(
|
||||||
|
transferTx,
|
||||||
|
delegatedSignTransaction(alice, bob)
|
||||||
|
)
|
||||||
|
|
||||||
|
return conn.postTransactionCommit(transferTxSigned)
|
||||||
|
.then(resTx => t.truthy(resTx))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
test('Search for spent and unspent outputs of a given public key', t => {
|
test('Search for spent and unspent outputs of a given public key', t => {
|
||||||
const conn = new Connection(API_PATH)
|
const conn = new Connection(API_PATH)
|
||||||
const carol = new Ed25519Keypair()
|
const carol = new Ed25519Keypair()
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
import test from 'ava'
|
import test from 'ava'
|
||||||
import cc from 'crypto-conditions'
|
import cc from 'crypto-conditions'
|
||||||
import { Ed25519Keypair, Transaction, ccJsonLoad } from '../../src'
|
import { Ed25519Keypair, Transaction, ccJsonLoad } from '../../src'
|
||||||
|
import { delegatedSignTransaction } from '../constants'
|
||||||
import sha256Hash from '../../src/sha256Hash'
|
import sha256Hash from '../../src/sha256Hash'
|
||||||
|
|
||||||
test('Ed25519 condition encoding', t => {
|
test('Ed25519 condition encoding', t => {
|
||||||
@ -96,6 +97,24 @@ test('Fulfillment correctly formed', t => {
|
|||||||
))
|
))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('Delegated signature is correct', t => {
|
||||||
|
const alice = new Ed25519Keypair()
|
||||||
|
|
||||||
|
const txCreate = Transaction.makeCreateTransaction(
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
[Transaction.makeOutput(Transaction.makeEd25519Condition(alice.publicKey))],
|
||||||
|
alice.publicKey
|
||||||
|
)
|
||||||
|
|
||||||
|
const signCreateTransaction = Transaction.signTransaction(txCreate, alice.privateKey)
|
||||||
|
const delegatedSignCreateTransaction = Transaction.delegateSignTransaction(
|
||||||
|
txCreate,
|
||||||
|
delegatedSignTransaction(alice)
|
||||||
|
)
|
||||||
|
t.deepEqual(signCreateTransaction, delegatedSignCreateTransaction)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
test('CryptoConditions JSON load', t => {
|
test('CryptoConditions JSON load', t => {
|
||||||
const cond = ccJsonLoad({
|
const cond = ccJsonLoad({
|
||||||
|
Loading…
Reference in New Issue
Block a user