Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
What's new
7
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Switch to GitLab Next
Sign in / Register
Toggle navigation
Open sidebar
Bitcoin Please
Causes Cash
Commits
e1fd49b3
Commit
e1fd49b3
authored
Jul 08, 2020
by
Bitcoin Please
Browse files
Added support for backing Flipstarter campaigns.
parent
9802a50f
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
482 additions
and
69 deletions
+482
-69
src/components/Account/Banner.vue
src/components/Account/Banner.vue
+2
-7
src/components/Blank.vue
src/components/Blank.vue
+2
-7
src/store/modules/wallet/getters/getAccounts.js
src/store/modules/wallet/getters/getAccounts.js
+4
-4
src/store/modules/wallet/getters/getAddress.js
src/store/modules/wallet/getters/getAddress.js
+5
-5
src/views/Account/Backing.vue
src/views/Account/Backing.vue
+457
-17
src/views/Account/Dashboard.vue
src/views/Account/Dashboard.vue
+2
-7
src/views/Account/Profile.vue
src/views/Account/Profile.vue
+2
-7
src/views/Account/Wallet.vue
src/views/Account/Wallet.vue
+6
-8
src/views/Blank.vue
src/views/Blank.vue
+2
-7
No files found.
src/components/Account/Banner.vue
View file @
e1fd49b3
...
...
@@ -73,15 +73,10 @@ export default {
},
},
methods
:
{
...
mapActions
([
'
displayError
'
,
'
displayNotification
'
,
...
mapActions
(
'
campaigns
'
,
[
'
addCampaign
'
,
]),
// ...mapActions('campaigns', [
// 'addCampaign',
// ]),
},
created
:
function
()
{
/* Set owner slug. */
...
...
src/components/Blank.vue
View file @
e1fd49b3
...
...
@@ -37,15 +37,10 @@ export default {
// ]),
},
methods: {
...mapActions([
'
displayError
'
,
'
displayNotification
'
,
...mapActions(
'
campaigns
'
, [
'
addCampaign
'
,
]),
// ...mapActions(
'
campaigns
'
, [
//
'
addCampaign
'
,
// ]),
},
created: function () {
//
...
...
src/store/modules/wallet/getters/getAccounts.js
View file @
e1fd49b3
...
...
@@ -60,13 +60,13 @@ const getAccounts = (state, getters) => {
loadPath
(
getters
,
accounts
,
1
,
i
)
}
/* Loop through ALL (c
hange
) indexes (inclusive). */
for
(
let
i
=
0
;
i
<=
wallet
.
accounts
.
c
hange
;
i
++
)
{
/* Loop through ALL (c
auses
) indexes (inclusive). */
for
(
let
i
=
0
;
i
<=
wallet
.
accounts
.
c
auses
;
i
++
)
{
loadPath
(
getters
,
accounts
,
6767
,
i
)
}
/* Loop through ALL (
change
) indexes (inclusive). */
for
(
let
i
=
0
;
i
<=
wallet
.
accounts
.
change
;
i
++
)
{
/* Loop through ALL (
nito
) indexes (inclusive). */
for
(
let
i
=
0
;
i
<=
wallet
.
accounts
.
nito
;
i
++
)
{
loadPath
(
getters
,
accounts
,
7867
,
i
)
}
// console.log('GET ACCOUNTS (accounts):', accounts)
...
...
src/store/modules/wallet/getters/getAddress.js
View file @
e1fd49b3
...
...
@@ -62,12 +62,12 @@ const getAddress = (state, getters) => (_account) => {
/* Initialize child node. */
const
childNode
=
hdNode
.
deriveChild
(
path
)
/* Set (receiving) address
s
. */
const
address
s
=
Nito
.
Address
.
toCashAddress
(
childNode
)
// console.log('GET ADDRESS (receiving address
s
)', address
s
)
/* Set (receiving) address. */
const
address
=
Nito
.
Address
.
toCashAddress
(
childNode
)
// console.log('GET ADDRESS (receiving address)', address)
/* Return address
s
. */
return
address
s
/* Return address. */
return
address
}
/* Export module. */
...
...
src/views/Account/Backing.vue
View file @
e1fd49b3
...
...
@@ -90,7 +90,7 @@
</div>
<button
v-if=
"
parsedDetails
"
v-if=
"
userPledge
"
class=
"btn-primary mt-3"
@
click=
"confirmFlipstarter"
:disabled=
"pledgeAuth"
...
...
@@ -98,8 +98,8 @@
Confirm Flipstarter Pledge
</button>
<div
v-if=
"
parsedDetails
"
class=
"row mt-3 parsedJson"
>
<pre>
{{
parsedDetails
}}
</pre>
<div
v-if=
"
userPledge
"
class=
"row mt-3 parsedJson"
>
<pre>
{{
userPledge
}}
</pre>
</div>
</div>
...
...
@@ -130,6 +130,12 @@
</
template
>
<
script
>
/* Initialize vuex. */
import
{
mapActions
,
mapGetters
}
from
'
vuex
'
/* Import modules. */
import
Nito
from
'
nitojs
'
/* Import components. */
import
Footer
from
'
@/components/Footer.vue
'
import
Header
from
'
@/components/Header.vue
'
...
...
@@ -151,26 +157,25 @@ export default {
ownerSlug
:
null
,
backing
:
null
,
accepting
:
null
,
pledgeDetails
:
null
,
pledgeAuth
:
null
pledgeAuth
:
null
,
}
},
computed
:
{
/**
* Pledge Authorization
*/
// pledgeAuth() {
// if (this.pledgeAuth) {
// return this.pledgeAuth
// } else {
// return null
// }
// },
...
mapGetters
(
'
wallet
'
,
[
'
getAddress
'
,
'
getBalance
'
,
'
getDerivationPath
'
,
'
getHDNode
'
,
'
getWallet
'
,
]),
/**
* Parsed Details
*/
parsedDetails
()
{
userPledge
()
{
if
(
!
this
.
pledgeDetails
)
{
return
null
}
...
...
@@ -199,6 +204,10 @@ export default {
},
methods
:
{
...
mapActions
(
'
wallet
'
,
[
'
updateCoins
'
,
]),
/**
* Accept Flipstarter (Pledge)
*
...
...
@@ -209,11 +218,442 @@ export default {
this
.
accepting
=
!
this
.
accepting
},
/**
* Parse Key Hash Unlock Script
*/
parseKeyHashUnlockScript
(
unlockScript
)
{
/**
* The signature is the first pushed item, with a varying length
* depending on signature type.
*/
const
signature
=
unlockScript
.
slice
(
1
,
-
34
)
/* The public key is the last pushed item of 33 bytes. */
const
publicKey
=
unlockScript
.
slice
(
-
33
)
/* Return the parsed unlock script. */
return
{
publicKey
,
signature
}
},
/**
* Encode Output Value
*
* Encodes a number of satoshis to be used as part of an output
* structure in a raw transaction.
*/
encodeOutputValue
(
_satoshis
)
{
// Check if the provided satoshis is of the correct type.
if
(
isNaN
(
_satoshis
))
{
throw
`Cannot encode output value, provided satoshis '
${
_satoshis
}
' is not a number.`
}
// Check if the provided satoshis is an integer.
if
(
!
Number
.
isInteger
(
_satoshis
))
{
throw
`Cannot encode output value, provided satoshis '
${
_satoshis
}
' is not an integer.`
}
// Check if the provided satoshis is a positive number.
if
(
_satoshis
<
0
)
{
throw
`Cannot encode output value, provided satoshis '
${
_satoshis
}
' is negative.`
}
// Check if the provided satoshis is within our accepted number range.
if
(
_satoshis
>
Math
.
pow
(
2
,
53
))
{
throw
`Cannot encode output value, provided satoshis '
${
_satoshis
}
' is larger than javacripts 53bit limit.`
}
/* Allocate 8 bytes. */
const
value
=
Buffer
.
alloc
(
8
)
/* Split the number into high and low bits. */
const
highValue
=
Math
.
floor
(
_satoshis
/
Math
.
pow
(
2
,
32
))
const
lowValue
=
_satoshis
%
Math
.
pow
(
2
,
32
)
/* Write the satoshi number to the buffer in 64bit. */
value
.
writeUInt32LE
(
highValue
,
4
)
value
.
writeUInt32LE
(
lowValue
,
0
)
// Return the encoded value.
return
value
},
/**
* Decode Output Value
*/
decodeOutputValue
(
_value
)
{
// TODO: Properly validate and error check.
// Parhse the high and low value sets.
const
highValue
=
_value
.
readUInt32LE
(
4
);
const
lowValue
=
_value
.
readUInt32LE
(
0
);
// Return the decoded value.
return
highValue
*
Math
.
pow
(
2
,
32
)
+
lowValue
;
},
/**
* Assemble Signature Hash Digest
*/
assembleSighashDigest
(
previousTransactionHash
,
previousTransactionOutputIndex
,
previousTransactionOutputValue
,
inputLockScript
)
{
/* Initialize an empty array of outpoints. */
const
transactionOutpoints
=
[]
/* Initialize value. */
let
value
=
null
/* Set campaign value. */
const
campaignValue
=
this
.
userPledge
.
outputs
[
0
].
value
console
.
log
(
'
Campaign value:
'
,
campaignValue
)
/* Set value. */
value
=
this
.
encodeOutputValue
(
campaignValue
)
console
.
log
(
'
Encoded value:
'
,
value
)
/* Set campaign address. */
const
campaignAddress
=
this
.
userPledge
.
outputs
[
0
].
address
console
.
log
(
'
Campaign address:
'
,
campaignAddress
)
/* Set locking script. */
const
locking_script
=
Nito
.
Address
.
toPubKeyHash
(
campaignAddress
)
console
.
log
(
'
Campaign (locking_script):
'
,
locking_script
)
/* Set current output. */
// NOTE: This is the pledge recipient.
// FIXME: Allow multiple pledge recipients.
const
thisOutputs
=
[{
value
,
locking_script
}]
/* Add each output in the current contract. */
for
(
const
currentOutput
in
thisOutputs
)
{
// Add the output value.
transactionOutpoints
.
push
(
thisOutputs
[
currentOutput
].
value
)
// Add the output lockscript.
transactionOutpoints
.
push
(
Nito
.
Utils
.
varBuf
(
thisOutputs
[
currentOutput
].
locking_script
)
)
}
console
.
log
(
'
Transaction outpoints:
'
,
transactionOutpoints
)
/* Set version. */
const
nVersion
=
Buffer
.
from
(
'
02000000
'
,
'
hex
'
)
/* Set hash previous output. */
const
hashPrevouts
=
Buffer
.
from
(
''
.
padStart
(
64
,
'
0
'
),
'
hex
'
)
/* Set hash sequence. */
const
hashSequence
=
Buffer
.
from
(
''
.
padStart
(
64
,
'
0
'
),
'
hex
'
)
/* Set outpoint. */
const
outpoint
=
Buffer
.
concat
([
Nito
.
Utils
.
reverseBuffer
(
previousTransactionHash
),
previousTransactionOutputIndex
,
])
/* Set script code. */
const
scriptCode
=
Buffer
.
concat
([
Buffer
.
from
(
'
19
'
,
'
hex
'
),
inputLockScript
,
])
/* Set (transaction) value. */
value
=
previousTransactionOutputValue
/* Set sequence. */
const
nSequence
=
Buffer
.
from
(
'
FFFFFFFF
'
,
'
hex
'
)
/* Set hash outputs. */
// const hashOutputs = bitbox.Crypto.hash256(
// Buffer.concat(transactionOutpoints)
// )
const
hashOutputs
=
Nito
.
Crypto
.
hash
(
Buffer
.
concat
(
transactionOutpoints
),
'
sha256sha256
'
)
/* Set locktime. */
const
nLocktime
=
Buffer
.
from
(
'
00000000
'
,
'
hex
'
)
/* Set signature hash type. */
const
sighashType
=
Buffer
.
from
(
'
c1000000
'
,
'
hex
'
)
// console.log('nVersion', nVersion);
// console.log('hashPrevouts', hashPrevouts);
// console.log('hashSequence', hashSequence);
// console.log('outpoint', outpoint);
// console.log('scriptCode', scriptCode);
// console.log('value', value);
// console.log('nSequence', nSequence);
// console.log('hashOutputs', hashOutputs);
// console.log('nLocktime', nLocktime);
// console.log('sighashType', sighashType);
/* Construct signature hash message. */
const
sighashMessage
=
Buffer
.
concat
([
nVersion
,
hashPrevouts
,
hashSequence
,
outpoint
,
scriptCode
,
value
,
nSequence
,
hashOutputs
,
nLocktime
,
sighashType
,
])
/* Create signature hash digest (of message). */
const
sighashDigest
=
Nito
.
Crypto
.
hash
(
sighashMessage
,
'
sha256sha256
'
)
/* Return signature hash digest. */
return
sighashDigest
},
/**
* Load Funds
*/
async
loadFunds
(
_coin
,
_address
,
_satoshis
)
{
console
.
log
(
'
SENDING COIN
'
,
_coin
)
if
(
!
_address
)
{
return
console
.
error
(
'
NO output address!
'
)
}
/* Build receivers. */
const
receivers
=
[
{
address
:
_address
,
satoshis
:
_satoshis
,
}
]
/* Set auto fee (flag). */
const
autoFee
=
false
const
results
=
await
Nito
.
Transaction
.
sendCoin
(
_coin
,
receivers
,
autoFee
)
.
catch
(
err
=>
console
.
error
(
err
))
console
.
log
(
'
OUTBOX SEND COIN (results):
'
,
results
)
if
(
results
)
{
/* Set message. */
const
message
=
`Your coins have been sent successfully!`
console
.
log
(
'
MESSAGE
'
,
message
)
/* Display notification. */
// this.$notify({
// message,
// icon: 'ti-info-alt', // ti-info-alt | ti-alert
// verticalAlign: 'top',
// horizontalAlign: 'right',
// type: 'info', // info | danger
// // timeout: 0, // 0: persistent | 5000: default
// })
/* Wait a bit then update coins. */
// FIXME: How long should we wait?
// Probably better to update coins w/out on-chain query?
setTimeout
(()
=>
{
/* Update coins. */
// FIXME: Why is this blocking the entire initial UI setup??
this
.
updateCoins
()
},
2000
)
}
else
{
/* Set message. */
const
message
=
`Oops! Something went wrong and your coin(s) were NOT sent.`
console
.
error
(
'
MESSAGE
'
,
message
)
/* Display notification. */
// this.$notify({
// message,
// icon: 'ti-alert', // ti-info-alt | ti-alert
// verticalAlign: 'top',
// horizontalAlign: 'right',
// type: 'danger', // info | danger
// // timeout: 0, // 0: persistent | 5000: default
// })
}
},
/**
* Confirm Flipstarter (Pledge)
*
* Transfers the pledge amount into a dedicated UTXO.
*/
confirmFlipstarter
()
{
this
.
pledgeAuth
=
'
show me something special!
'
async
confirmFlipstarter
()
{
/* Initialize wallet. */
const
wallet
=
this
.
getWallet
console
.
log
(
'
WALLET
'
,
wallet
)
/* Set accounts. */
const
accounts
=
wallet
.
accounts
console
.
log
(
'
ACCOUNTS
'
,
accounts
)
/* Set coins. */
const
coins
=
wallet
.
coins
console
.
log
(
'
COINS
'
,
coins
)
/* Validate accounts. */
if
(
!
accounts
)
{
return
null
}
/* Validate coins. */
if
(
!
coins
)
{
return
null
}
const
spendable
=
Object
.
keys
(
coins
).
filter
(
coinId
=>
{
return
coins
[
coinId
].
status
===
'
active
'
})
console
.
log
(
'
SPENDABLE
'
,
spendable
)
let
isPledged
=
false
let
sourceCoin
=
null
/* Set donation amount. */
const
donation
=
this
.
userPledge
.
donation
.
amount
console
.
log
(
'
DONATION
'
,
donation
)
/* Loop through all spendables. */
spendable
.
forEach
(
coinId
=>
{
if
(
coins
[
coinId
].
status
===
'
active
'
&&
coins
[
coinId
].
satoshis
===
donation
)
{
/* Set flag. */
isPledged
=
true
/* Set source coin. */
sourceCoin
=
coins
[
coinId
]
}
})
/* Validate pledge balance. */
if
(
!
isPledged
)
{
console
.
error
(
'
MAKE THE DEPOSIT
'
)
/* Initialize current (coin) index. */
const
currentIndex
=
accounts
[
'
causes
'
]
console
.
log
(
'
FLIPSTARTER (currentIndex):
'
,
currentIndex
)
/* Set (Causes Cash) chain. */
const
chain
=
6767
/* Set derivation path. */
const
path
=
this
.
getDerivationPath
(
chain
,
currentIndex
)
console
.
log
(
'
FLIPSTARTER (path)
'
,
path
)
/* Set source coin. */
sourceCoin
=
coins
[
spendable
[
1
]]
// FIXME: Search for minimum sufficient coin(s).
console
.
log
(
'
SOURCE COIN
'
,
sourceCoin
)
/* Set pledge address. */
const
pledgeAddress
=
this
.
getAddress
(
'
causes
'
)
console
.
log
(
'
PLEDGE ADDRESS
'
,
pledgeAddress
)
/* Retrieve (address) balances. */
// const pledgeBalance = await Nito.Address.balance(pledgeAddress)
// console.log('PLEDGE BALANCE', pledgeBalance)
this
.
loadFunds
(
sourceCoin
,
pledgeAddress
,
donation
)
}
else
{
console
.
error
(
'
RE-USE CURRENT DEPOSIT
'
,
sourceCoin
)
this
.
makePledge
(
sourceCoin
)
}
},
/**
* Make Pledge
*/
makePledge
(
_coin
)
{
/* Initialize HD node. */
// const hdNode = this.getHDNode
/* Initialize verification key. */
// const verificationKey = hdNode.deriveChild(_path)
const
verificationKey
=
Nito
.
Purse
.
fromWIF
(
_coin
.
wif
)
// console.log('verificationKey', verificationKey)
/* Set public key. */
const
publicKey
=
verificationKey
.
publicKey
.
toString
()
console
.
log
(
'
\n
Public key:
'
,
publicKey
)
/* Set cash address. */
const
cashAddress
=
Nito
.
Address
.
toCashAddress
(
verificationKey
)
console
.
log
(
'
FLIPSTARTER (pledge address)
'
,
cashAddress
)
console
.
log
(
'
USER PLEDGE
'
,
this
.
userPledge
)
const
alias
=
this
.
userPledge
.
data
.
alias
console
.
log
(
'
ALIAS:
'
,
alias
)
const
comment
=
this
.
userPledge
.
data
.
comment
console
.
log
(
'
COMMENT:
'
,
comment
)
if
(
!
_coin
.
txid
||
!
_coin
.
satoshis
)
{
return
console
.
error
(
'
No UTXO available for pledge.
'
)
}
const
previousTransactionHash
=
_coin
.
txid
const
previousTransactionOutputValue
=
this
.
encodeOutputValue
(
_coin
.
satoshis
)
const
previousTransactionOutputIndex
=
'
00000000
'
const
inputLockScript
=
Nito
.
Address
.
toPubKeyHash
(
cashAddress
)
// Validate commitment signature
const
verificationMessage
=
this
.
assembleSighashDigest
(
Buffer
.
from
(
previousTransactionHash
,
'
hex
'
),
Buffer
.
from
(
previousTransactionOutputIndex
,
'
hex
'
),
Buffer
.
from
(
previousTransactionOutputValue
,
'
hex
'
),
Buffer
.
from
(
inputLockScript
,
'
hex
'
)
)
console
.
log
(
'
verificationMessage
'
,
verificationMessage
.
toString
(
'
hex
'
))
const
pledgeSig
=
Nito
.
Account
.
sign
(
verificationMessage
,
verificationKey
)
console
.
log
(
'
PLEDGE SIGNATURE
'
,
pledgeSig
)
const
previous_output_transaction_hash
=
previousTransactionHash
const
previous_output_index
=
0
const
sequence_number
=
4294967295
const
unlocking_script
=
(
pledgeSig
.
toString
().
slice
(
2
,
4
)
===
'
44
'
?
'
47
'
:
'
48
'
)
+
// FIXME??
pledgeSig
.
toString
()
+
'
c1
'
+
// NOTE: sigHashType
'
21
'
+
publicKey
console
.
log
(
'
UNLOCKING SCRIPT:
'
,
unlocking_script
)
const
assuranceOutput
=
{
inputs
:
[{
previous_output_transaction_hash
,
previous_output_index
,
sequence_number
,
unlocking_script
,
}],
data
:
{
alias
,
comment
,
},
data_signature
:
null
}
console
.
log
(
'
ASSURANCE OUTPUT:
'
,
assuranceOutput
)
/* Encode assurance pledge. */
const
encodedPledge
=
Buffer
.
from
(
JSON
.
stringify
(
assuranceOutput
)).
toString
(
'
base64
'
)
console
.
log
(
'
Flipstarter encoded pledge (base64):
'
,
encodedPledge
)
/* Update pledge authorization. */
this
.
pledgeAuth
=
encodedPledge
},
},
...
...
src/views/Account/Dashboard.vue
View file @
e1fd49b3
...
...
@@ -116,15 +116,10 @@ export default {
]),
},
methods
:
{